Loading core/jni/android_view_Surface.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -504,7 +504,7 @@ static jlong create(JNIEnv* env, jclass clazz, jlong rootNodePtr, jlong surfaceP ContextFactory factory; RenderProxy* proxy = new RenderProxy(false, rootNode, &factory); proxy->loadSystemProperties(); proxy->setSwapBehavior(kSwap_discardBuffer); proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer); proxy->initialize(surface); // Shadows can't be used via this interface, so just set the light source // to all 0s. Loading libs/hwui/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ hwui_src_files := \ renderstate/Stencil.cpp \ renderstate/TextureState.cpp \ renderthread/CanvasContext.cpp \ renderthread/OpenGLPipeline.cpp \ renderthread/DrawFrameTask.cpp \ renderthread/EglManager.cpp \ renderthread/RenderProxy.cpp \ Loading libs/hwui/renderthread/CanvasContext.cpp +100 −136 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ #include "renderstate/RenderState.h" #include "renderstate/Stencil.h" #include "protos/hwui.pb.h" #include "OpenGLPipeline.h" #include "utils/GLUtils.h" #include "utils/TimeUtils.h" Loading Loading @@ -65,9 +66,11 @@ CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) { auto renderType = Properties::getRenderPipelineType(); switch (renderType) { case RenderPipelineType::OpenGL: return new CanvasContext(thread, translucent, rootRenderNode, contextFactory); return new CanvasContext(thread, translucent, rootRenderNode, contextFactory, std::make_unique<OpenGLPipeline>(thread)); case RenderPipelineType::SkiaGL: //TODO: implement SKIA GL LOG_ALWAYS_FATAL("skiaGL canvas type not implemented."); Loading @@ -84,14 +87,15 @@ CanvasContext* CanvasContext::create(RenderThread& thread, } CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) RenderNode* rootRenderNode, IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline) : mRenderThread(thread) , mEglManager(thread.eglManager()) , mOpaque(!translucent) , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord())) , mJankTracker(thread.timeLord().frameIntervalNanos()) , mProfiler(mFrames) , mContentDrawBounds(0, 0, 0, 0) { , mContentDrawBounds(0, 0, 0, 0) , mRenderPipeline(std::move(renderPipeline)) { mRenderNodes.emplace_back(rootRenderNode); mRenderThread.renderState().registerCanvasContext(this); mProfiler.setDensity(mRenderThread.mainDisplayInfo().density); Loading @@ -115,20 +119,11 @@ void CanvasContext::setSurface(Surface* surface) { mNativeSurface = surface; if (mEglSurface != EGL_NO_SURFACE) { mEglManager.destroySurface(mEglSurface); mEglSurface = EGL_NO_SURFACE; } if (surface) { mEglSurface = mEglManager.createSurface(surface); } bool hasSurface = mRenderPipeline->setSurface(surface, mSwapBehavior); mFrameNumber = -1; if (mEglSurface != EGL_NO_SURFACE) { const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer); mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); if (hasSurface) { mHaveNewSurface = true; mSwapHistory.clear(); } else { Loading Loading @@ -157,9 +152,7 @@ void CanvasContext::setStopped(bool stopped) { mStopped = stopped; if (mStopped) { mRenderThread.removeFrameCallback(this); if (mEglManager.isCurrent(mEglSurface)) { mEglManager.makeCurrent(EGL_NO_SURFACE); } mRenderPipeline->onStop(); } else if (mIsDirty && hasSurface()) { mRenderThread.postFrameCallback(this); } Loading @@ -184,14 +177,23 @@ void CanvasContext::setOpaque(bool opaque) { bool CanvasContext::makeCurrent() { if (mStopped) return false; // TODO: Figure out why this workaround is needed, see b/13913604 // In the meantime this matches the behavior of GLRenderer, so it is not a regression EGLint error = 0; mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface, &error); if (error) { auto result = mRenderPipeline->makeCurrent(); switch (result) { case MakeCurrentResult::AlreadyCurrent: return true; case MakeCurrentResult::Failed: mHaveNewSurface = true; setSurface(nullptr); return false; case MakeCurrentResult::Succeeded: mHaveNewSurface = true; return true; default: LOG_ALWAYS_FATAL("unexpected result %d from IRenderPipeline::makeCurrent", (int32_t) result); } return !error; return true; } static bool wasSkipped(FrameInfo* info) { Loading Loading @@ -328,9 +330,6 @@ void CanvasContext::notifyFramePending() { } void CanvasContext::draw() { LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, "drawRenderNode called on a context with no surface!"); SkRect dirty; mDamageAccumulator.finish(&dirty); Loading @@ -342,98 +341,27 @@ void CanvasContext::draw() { mCurrentFrameInfo->markIssueDrawCommandsStart(); Frame frame = mEglManager.beginFrame(mEglSurface); if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) { // can't rely on prior content of window if viewport size changes dirty.setEmpty(); mLastFrameWidth = frame.width(); mLastFrameHeight = frame.height(); } else if (mHaveNewSurface || frame.bufferAge() == 0) { // New surface needs a full draw dirty.setEmpty(); } else { if (!dirty.isEmpty() && !dirty.intersect(0, 0, frame.width(), frame.height())) { ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", SK_RECT_ARGS(dirty), frame.width(), frame.height()); dirty.setEmpty(); } profiler().unionDirty(&dirty); } if (dirty.isEmpty()) { dirty.set(0, 0, frame.width(), frame.height()); } Frame frame = mRenderPipeline->getFrame(); // At this point dirty is the area of the screen to update. However, // the area of the frame we need to repaint is potentially different, so // stash the screen area for later SkRect screenDirty(dirty); SkRect windowDirty = computeDirtyRect(frame, &dirty); // If the buffer age is 0 we do a full-screen repaint (handled above) // If the buffer age is 1 the buffer contents are the same as they were // last frame so there's nothing to union() against // Therefore we only care about the > 1 case. if (frame.bufferAge() > 1) { if (frame.bufferAge() > (int) mSwapHistory.size()) { // We don't have enough history to handle this old of a buffer // Just do a full-draw dirty.set(0, 0, frame.width(), frame.height()); } else { // At this point we haven't yet added the latest frame // to the damage history (happens below) // So we need to damage for (int i = mSwapHistory.size() - 1; i > ((int) mSwapHistory.size()) - frame.bufferAge(); i--) { dirty.join(mSwapHistory[i].damage); } } } mEglManager.damageFrame(frame, dirty); auto& caches = Caches::getInstance(); FrameBuilder frameBuilder(dirty, frame.width(), frame.height(), mLightGeometry, caches); frameBuilder.deferLayers(mLayerUpdateQueue); mLayerUpdateQueue.clear(); frameBuilder.deferRenderNodeScene(mRenderNodes, mContentDrawBounds); BakedOpRenderer renderer(caches, mRenderThread.renderState(), mOpaque, mLightInfo); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); profiler().draw(&renderer); bool drew = renderer.didDraw(); // post frame cleanup caches.clearGarbage(); caches.pathCache.trim(); caches.tessellationCache.trim(); #if DEBUG_MEMORY_USAGE mCaches.dumpMemoryUsage(); #else if (CC_UNLIKELY(Properties::debugLevel & kDebugMemory)) { caches.dumpMemoryUsage(); } #endif bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue, mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes, &(profiler())); waitOnFences(); GL_CHECKPOINT(LOW); bool requireSwap = false; bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap); // Even if we decided to cancel the frame, from the perspective of jank // metrics the frame was swapped at this point mCurrentFrameInfo->markSwapBuffers(); mIsDirty = false; if (drew || mEglManager.damageRequiresSwap()) { if (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty))) { if (requireSwap) { if (!didSwap) { //some error happened setSurface(nullptr); } SwapHistory& swap = mSwapHistory.next(); swap.damage = screenDirty; swap.damage = windowDirty; swap.swapCompletedTime = systemTime(CLOCK_MONOTONIC); swap.vsyncTime = mRenderThread.timeLord().latestVsync(); mHaveNewSurface = false; Loading Loading @@ -469,7 +397,7 @@ void CanvasContext::draw() { // Called by choreographer to do an RT-driven animation void CanvasContext::doFrame() { if (CC_UNLIKELY(mEglSurface == EGL_NO_SURFACE)) return; if (!mRenderPipeline->isSurfaceReady()) return; prepareAndDraw(nullptr); } Loading Loading @@ -519,7 +447,7 @@ void CanvasContext::freePrefetchedLayers(TreeObserver* observer) { void CanvasContext::buildLayer(RenderNode* node, TreeObserver* observer) { ATRACE_CALL(); if (!mEglManager.hasEglContext()) return; if (!mRenderPipeline->isContextReady()) return; // buildLayer() will leave the tree in an unknown state, so we must stop drawing stopDrawing(); Loading @@ -536,37 +464,24 @@ void CanvasContext::buildLayer(RenderNode* node, TreeObserver* observer) { // purposes when the frame is actually drawn node->setPropertyFieldsDirty(RenderNode::GENERIC); static const std::vector< sp<RenderNode> > emptyNodeList; auto& caches = Caches::getInstance(); FrameBuilder frameBuilder(mLayerUpdateQueue, mLightGeometry, caches); mLayerUpdateQueue.clear(); BakedOpRenderer renderer(caches, mRenderThread.renderState(), mOpaque, mLightInfo); LOG_ALWAYS_FATAL_IF(renderer.didDraw(), "shouldn't draw in buildlayer case"); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue, mOpaque, mLightInfo); node->incStrong(nullptr); mPrefetchedLayers.insert(node); } bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { layer->apply(); return Readback::copyTextureLayerInto(mRenderThread, *(layer->backingLayer()), bitmap) == CopyResult::Success; return mRenderPipeline->copyLayerInto(layer, bitmap); } void CanvasContext::destroyHardwareResources(TreeObserver* observer) { stopDrawing(); if (mEglManager.hasEglContext()) { if (mRenderPipeline->isContextReady()) { freePrefetchedLayers(observer); for (const sp<RenderNode>& node : mRenderNodes) { node->destroyHardwareResources(observer); } Caches& caches = Caches::getInstance(); // Make sure to release all the textures we were owning as there won't // be another draw caches.textureCache.resetMarkInUse(this); mRenderThread.renderState().flush(Caches::FlushMode::Layers); mRenderPipeline->onDestroyHardwareResources(); } } Loading @@ -584,8 +499,7 @@ void CanvasContext::trimMemory(RenderThread& thread, int level) { } Layer* CanvasContext::createTextureLayer() { mEglManager.initialize(); return LayerRenderer::createTextureLayer(mRenderThread.renderState()); return mRenderPipeline->createTextureLayer(); } void CanvasContext::setTextureAtlas(RenderThread& thread, Loading Loading @@ -665,8 +579,8 @@ void CanvasContext::waitOnFences() { class CanvasContext::FuncTaskProcessor : public TaskProcessor<bool> { public: explicit FuncTaskProcessor(Caches& caches) : TaskProcessor<bool>(&caches.tasks) {} explicit FuncTaskProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {} virtual void onProcess(const sp<Task<bool> >& task) override { FuncTask* t = static_cast<FuncTask*>(task.get()); Loading @@ -677,7 +591,7 @@ public: void CanvasContext::enqueueFrameWork(std::function<void()>&& func) { if (!mFrameWorkProcessor.get()) { mFrameWorkProcessor = new FuncTaskProcessor(Caches::getInstance()); mFrameWorkProcessor = new FuncTaskProcessor(mRenderPipeline->getTaskManager()); } sp<FuncTask> task(new FuncTask()); task->func = func; Loading @@ -698,6 +612,56 @@ bool CanvasContext::isSkiaEnabled() { return RenderPipelineType::SkiaGL == renderType || RenderPipelineType::Vulkan == renderType; } SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) { // can't rely on prior content of window if viewport size changes dirty->setEmpty(); mLastFrameWidth = frame.width(); mLastFrameHeight = frame.height(); } else if (mHaveNewSurface || frame.bufferAge() == 0) { // New surface needs a full draw dirty->setEmpty(); } else { if (!dirty->isEmpty() && !dirty->intersect(0, 0, frame.width(), frame.height())) { ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", SK_RECT_ARGS(*dirty), frame.width(), frame.height()); dirty->setEmpty(); } profiler().unionDirty(dirty); } if (dirty->isEmpty()) { dirty->set(0, 0, frame.width(), frame.height()); } // At this point dirty is the area of the window to update. However, // the area of the frame we need to repaint is potentially different, so // stash the screen area for later SkRect windowDirty(*dirty); // If the buffer age is 0 we do a full-screen repaint (handled above) // If the buffer age is 1 the buffer contents are the same as they were // last frame so there's nothing to union() against // Therefore we only care about the > 1 case. if (frame.bufferAge() > 1) { if (frame.bufferAge() > (int) mSwapHistory.size()) { // We don't have enough history to handle this old of a buffer // Just do a full-draw dirty->set(0, 0, frame.width(), frame.height()); } else { // At this point we haven't yet added the latest frame // to the damage history (happens below) // So we need to damage for (int i = mSwapHistory.size() - 1; i > ((int) mSwapHistory.size()) - frame.bufferAge(); i--) { dirty->join(mSwapHistory[i].damage); } } } return windowDirty; } } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ libs/hwui/renderthread/CanvasContext.h +7 −10 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ #include "FrameInfoVisualizer.h" #include "FrameMetricsReporter.h" #include "IContextFactory.h" #include "IRenderPipeline.h" #include "LayerUpdateQueue.h" #include "RenderNode.h" #include "thread/Task.h" Loading Loading @@ -56,11 +57,7 @@ class RenderState; namespace renderthread { class EglManager; enum SwapBehavior { kSwap_default, kSwap_discardBuffer, }; class Frame; // This per-renderer class manages the bridge between the global EGL context // and the render surface. Loading Loading @@ -164,7 +161,7 @@ public: private: CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory); IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline); friend class RegisterFrameCallbackTask; // TODO: Replace with something better for layer & other GL object Loading @@ -179,21 +176,20 @@ private: bool isSwapChainStuffed(); SkRect computeDirtyRect(const Frame& frame, SkRect* dirty); EGLint mLastFrameWidth = 0; EGLint mLastFrameHeight = 0; RenderThread& mRenderThread; EglManager& mEglManager; sp<Surface> mNativeSurface; EGLSurface mEglSurface = EGL_NO_SURFACE; // stopped indicates the CanvasContext will reject actual redraw operations, // and defer repaint until it is un-stopped bool mStopped = false; // CanvasContext is dirty if it has received an update that it has not // painted onto its surface. bool mIsDirty = false; bool mBufferPreserved = false; SwapBehavior mSwapBehavior = kSwap_default; SwapBehavior mSwapBehavior = SwapBehavior::kSwap_default; struct SwapHistory { SkRect damage; nsecs_t vsyncTime; Loading Loading @@ -238,6 +234,7 @@ private: std::vector< sp<FuncTask> > mFrameFences; sp<TaskProcessor<bool> > mFrameWorkProcessor; std::unique_ptr<IRenderPipeline> mRenderPipeline; }; } /* namespace renderthread */ Loading libs/hwui/renderthread/IRenderPipeline.h 0 → 100644 +76 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "FrameInfoVisualizer.h" #include "EglManager.h" #include <SkRect.h> #include <utils/RefBase.h> namespace android { class Surface; namespace uirenderer { class DeferredLayerUpdater; namespace renderthread { enum class SwapBehavior { kSwap_default, kSwap_discardBuffer, }; enum class MakeCurrentResult { AlreadyCurrent, Failed, Succeeded }; class IRenderPipeline { public: virtual MakeCurrentResult makeCurrent() = 0; virtual Frame getFrame() = 0; virtual bool draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, const FrameBuilder::LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, bool opaque, const BakedOpRenderer::LightInfo& lightInfo, const std::vector< sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler) = 0; virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) = 0; virtual bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) = 0; virtual Layer* createTextureLayer() = 0; virtual bool setSurface(Surface* window, SwapBehavior swapBehavior) = 0; virtual void onStop() = 0; virtual bool isSurfaceReady() = 0; virtual bool isContextReady() = 0; virtual void onDestroyHardwareResources() = 0; virtual void renderLayers(const FrameBuilder::LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, bool opaque, const BakedOpRenderer::LightInfo& lightInfo) = 0; virtual TaskManager* getTaskManager() = 0; virtual ~IRenderPipeline() {} }; } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ Loading
core/jni/android_view_Surface.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -504,7 +504,7 @@ static jlong create(JNIEnv* env, jclass clazz, jlong rootNodePtr, jlong surfaceP ContextFactory factory; RenderProxy* proxy = new RenderProxy(false, rootNode, &factory); proxy->loadSystemProperties(); proxy->setSwapBehavior(kSwap_discardBuffer); proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer); proxy->initialize(surface); // Shadows can't be used via this interface, so just set the light source // to all 0s. Loading
libs/hwui/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ hwui_src_files := \ renderstate/Stencil.cpp \ renderstate/TextureState.cpp \ renderthread/CanvasContext.cpp \ renderthread/OpenGLPipeline.cpp \ renderthread/DrawFrameTask.cpp \ renderthread/EglManager.cpp \ renderthread/RenderProxy.cpp \ Loading
libs/hwui/renderthread/CanvasContext.cpp +100 −136 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ #include "renderstate/RenderState.h" #include "renderstate/Stencil.h" #include "protos/hwui.pb.h" #include "OpenGLPipeline.h" #include "utils/GLUtils.h" #include "utils/TimeUtils.h" Loading Loading @@ -65,9 +66,11 @@ CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) { auto renderType = Properties::getRenderPipelineType(); switch (renderType) { case RenderPipelineType::OpenGL: return new CanvasContext(thread, translucent, rootRenderNode, contextFactory); return new CanvasContext(thread, translucent, rootRenderNode, contextFactory, std::make_unique<OpenGLPipeline>(thread)); case RenderPipelineType::SkiaGL: //TODO: implement SKIA GL LOG_ALWAYS_FATAL("skiaGL canvas type not implemented."); Loading @@ -84,14 +87,15 @@ CanvasContext* CanvasContext::create(RenderThread& thread, } CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory) RenderNode* rootRenderNode, IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline) : mRenderThread(thread) , mEglManager(thread.eglManager()) , mOpaque(!translucent) , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord())) , mJankTracker(thread.timeLord().frameIntervalNanos()) , mProfiler(mFrames) , mContentDrawBounds(0, 0, 0, 0) { , mContentDrawBounds(0, 0, 0, 0) , mRenderPipeline(std::move(renderPipeline)) { mRenderNodes.emplace_back(rootRenderNode); mRenderThread.renderState().registerCanvasContext(this); mProfiler.setDensity(mRenderThread.mainDisplayInfo().density); Loading @@ -115,20 +119,11 @@ void CanvasContext::setSurface(Surface* surface) { mNativeSurface = surface; if (mEglSurface != EGL_NO_SURFACE) { mEglManager.destroySurface(mEglSurface); mEglSurface = EGL_NO_SURFACE; } if (surface) { mEglSurface = mEglManager.createSurface(surface); } bool hasSurface = mRenderPipeline->setSurface(surface, mSwapBehavior); mFrameNumber = -1; if (mEglSurface != EGL_NO_SURFACE) { const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer); mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); if (hasSurface) { mHaveNewSurface = true; mSwapHistory.clear(); } else { Loading Loading @@ -157,9 +152,7 @@ void CanvasContext::setStopped(bool stopped) { mStopped = stopped; if (mStopped) { mRenderThread.removeFrameCallback(this); if (mEglManager.isCurrent(mEglSurface)) { mEglManager.makeCurrent(EGL_NO_SURFACE); } mRenderPipeline->onStop(); } else if (mIsDirty && hasSurface()) { mRenderThread.postFrameCallback(this); } Loading @@ -184,14 +177,23 @@ void CanvasContext::setOpaque(bool opaque) { bool CanvasContext::makeCurrent() { if (mStopped) return false; // TODO: Figure out why this workaround is needed, see b/13913604 // In the meantime this matches the behavior of GLRenderer, so it is not a regression EGLint error = 0; mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface, &error); if (error) { auto result = mRenderPipeline->makeCurrent(); switch (result) { case MakeCurrentResult::AlreadyCurrent: return true; case MakeCurrentResult::Failed: mHaveNewSurface = true; setSurface(nullptr); return false; case MakeCurrentResult::Succeeded: mHaveNewSurface = true; return true; default: LOG_ALWAYS_FATAL("unexpected result %d from IRenderPipeline::makeCurrent", (int32_t) result); } return !error; return true; } static bool wasSkipped(FrameInfo* info) { Loading Loading @@ -328,9 +330,6 @@ void CanvasContext::notifyFramePending() { } void CanvasContext::draw() { LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, "drawRenderNode called on a context with no surface!"); SkRect dirty; mDamageAccumulator.finish(&dirty); Loading @@ -342,98 +341,27 @@ void CanvasContext::draw() { mCurrentFrameInfo->markIssueDrawCommandsStart(); Frame frame = mEglManager.beginFrame(mEglSurface); if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) { // can't rely on prior content of window if viewport size changes dirty.setEmpty(); mLastFrameWidth = frame.width(); mLastFrameHeight = frame.height(); } else if (mHaveNewSurface || frame.bufferAge() == 0) { // New surface needs a full draw dirty.setEmpty(); } else { if (!dirty.isEmpty() && !dirty.intersect(0, 0, frame.width(), frame.height())) { ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", SK_RECT_ARGS(dirty), frame.width(), frame.height()); dirty.setEmpty(); } profiler().unionDirty(&dirty); } if (dirty.isEmpty()) { dirty.set(0, 0, frame.width(), frame.height()); } Frame frame = mRenderPipeline->getFrame(); // At this point dirty is the area of the screen to update. However, // the area of the frame we need to repaint is potentially different, so // stash the screen area for later SkRect screenDirty(dirty); SkRect windowDirty = computeDirtyRect(frame, &dirty); // If the buffer age is 0 we do a full-screen repaint (handled above) // If the buffer age is 1 the buffer contents are the same as they were // last frame so there's nothing to union() against // Therefore we only care about the > 1 case. if (frame.bufferAge() > 1) { if (frame.bufferAge() > (int) mSwapHistory.size()) { // We don't have enough history to handle this old of a buffer // Just do a full-draw dirty.set(0, 0, frame.width(), frame.height()); } else { // At this point we haven't yet added the latest frame // to the damage history (happens below) // So we need to damage for (int i = mSwapHistory.size() - 1; i > ((int) mSwapHistory.size()) - frame.bufferAge(); i--) { dirty.join(mSwapHistory[i].damage); } } } mEglManager.damageFrame(frame, dirty); auto& caches = Caches::getInstance(); FrameBuilder frameBuilder(dirty, frame.width(), frame.height(), mLightGeometry, caches); frameBuilder.deferLayers(mLayerUpdateQueue); mLayerUpdateQueue.clear(); frameBuilder.deferRenderNodeScene(mRenderNodes, mContentDrawBounds); BakedOpRenderer renderer(caches, mRenderThread.renderState(), mOpaque, mLightInfo); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); profiler().draw(&renderer); bool drew = renderer.didDraw(); // post frame cleanup caches.clearGarbage(); caches.pathCache.trim(); caches.tessellationCache.trim(); #if DEBUG_MEMORY_USAGE mCaches.dumpMemoryUsage(); #else if (CC_UNLIKELY(Properties::debugLevel & kDebugMemory)) { caches.dumpMemoryUsage(); } #endif bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue, mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes, &(profiler())); waitOnFences(); GL_CHECKPOINT(LOW); bool requireSwap = false; bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap); // Even if we decided to cancel the frame, from the perspective of jank // metrics the frame was swapped at this point mCurrentFrameInfo->markSwapBuffers(); mIsDirty = false; if (drew || mEglManager.damageRequiresSwap()) { if (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty))) { if (requireSwap) { if (!didSwap) { //some error happened setSurface(nullptr); } SwapHistory& swap = mSwapHistory.next(); swap.damage = screenDirty; swap.damage = windowDirty; swap.swapCompletedTime = systemTime(CLOCK_MONOTONIC); swap.vsyncTime = mRenderThread.timeLord().latestVsync(); mHaveNewSurface = false; Loading Loading @@ -469,7 +397,7 @@ void CanvasContext::draw() { // Called by choreographer to do an RT-driven animation void CanvasContext::doFrame() { if (CC_UNLIKELY(mEglSurface == EGL_NO_SURFACE)) return; if (!mRenderPipeline->isSurfaceReady()) return; prepareAndDraw(nullptr); } Loading Loading @@ -519,7 +447,7 @@ void CanvasContext::freePrefetchedLayers(TreeObserver* observer) { void CanvasContext::buildLayer(RenderNode* node, TreeObserver* observer) { ATRACE_CALL(); if (!mEglManager.hasEglContext()) return; if (!mRenderPipeline->isContextReady()) return; // buildLayer() will leave the tree in an unknown state, so we must stop drawing stopDrawing(); Loading @@ -536,37 +464,24 @@ void CanvasContext::buildLayer(RenderNode* node, TreeObserver* observer) { // purposes when the frame is actually drawn node->setPropertyFieldsDirty(RenderNode::GENERIC); static const std::vector< sp<RenderNode> > emptyNodeList; auto& caches = Caches::getInstance(); FrameBuilder frameBuilder(mLayerUpdateQueue, mLightGeometry, caches); mLayerUpdateQueue.clear(); BakedOpRenderer renderer(caches, mRenderThread.renderState(), mOpaque, mLightInfo); LOG_ALWAYS_FATAL_IF(renderer.didDraw(), "shouldn't draw in buildlayer case"); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue, mOpaque, mLightInfo); node->incStrong(nullptr); mPrefetchedLayers.insert(node); } bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { layer->apply(); return Readback::copyTextureLayerInto(mRenderThread, *(layer->backingLayer()), bitmap) == CopyResult::Success; return mRenderPipeline->copyLayerInto(layer, bitmap); } void CanvasContext::destroyHardwareResources(TreeObserver* observer) { stopDrawing(); if (mEglManager.hasEglContext()) { if (mRenderPipeline->isContextReady()) { freePrefetchedLayers(observer); for (const sp<RenderNode>& node : mRenderNodes) { node->destroyHardwareResources(observer); } Caches& caches = Caches::getInstance(); // Make sure to release all the textures we were owning as there won't // be another draw caches.textureCache.resetMarkInUse(this); mRenderThread.renderState().flush(Caches::FlushMode::Layers); mRenderPipeline->onDestroyHardwareResources(); } } Loading @@ -584,8 +499,7 @@ void CanvasContext::trimMemory(RenderThread& thread, int level) { } Layer* CanvasContext::createTextureLayer() { mEglManager.initialize(); return LayerRenderer::createTextureLayer(mRenderThread.renderState()); return mRenderPipeline->createTextureLayer(); } void CanvasContext::setTextureAtlas(RenderThread& thread, Loading Loading @@ -665,8 +579,8 @@ void CanvasContext::waitOnFences() { class CanvasContext::FuncTaskProcessor : public TaskProcessor<bool> { public: explicit FuncTaskProcessor(Caches& caches) : TaskProcessor<bool>(&caches.tasks) {} explicit FuncTaskProcessor(TaskManager* taskManager) : TaskProcessor<bool>(taskManager) {} virtual void onProcess(const sp<Task<bool> >& task) override { FuncTask* t = static_cast<FuncTask*>(task.get()); Loading @@ -677,7 +591,7 @@ public: void CanvasContext::enqueueFrameWork(std::function<void()>&& func) { if (!mFrameWorkProcessor.get()) { mFrameWorkProcessor = new FuncTaskProcessor(Caches::getInstance()); mFrameWorkProcessor = new FuncTaskProcessor(mRenderPipeline->getTaskManager()); } sp<FuncTask> task(new FuncTask()); task->func = func; Loading @@ -698,6 +612,56 @@ bool CanvasContext::isSkiaEnabled() { return RenderPipelineType::SkiaGL == renderType || RenderPipelineType::Vulkan == renderType; } SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) { // can't rely on prior content of window if viewport size changes dirty->setEmpty(); mLastFrameWidth = frame.width(); mLastFrameHeight = frame.height(); } else if (mHaveNewSurface || frame.bufferAge() == 0) { // New surface needs a full draw dirty->setEmpty(); } else { if (!dirty->isEmpty() && !dirty->intersect(0, 0, frame.width(), frame.height())) { ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", SK_RECT_ARGS(*dirty), frame.width(), frame.height()); dirty->setEmpty(); } profiler().unionDirty(dirty); } if (dirty->isEmpty()) { dirty->set(0, 0, frame.width(), frame.height()); } // At this point dirty is the area of the window to update. However, // the area of the frame we need to repaint is potentially different, so // stash the screen area for later SkRect windowDirty(*dirty); // If the buffer age is 0 we do a full-screen repaint (handled above) // If the buffer age is 1 the buffer contents are the same as they were // last frame so there's nothing to union() against // Therefore we only care about the > 1 case. if (frame.bufferAge() > 1) { if (frame.bufferAge() > (int) mSwapHistory.size()) { // We don't have enough history to handle this old of a buffer // Just do a full-draw dirty->set(0, 0, frame.width(), frame.height()); } else { // At this point we haven't yet added the latest frame // to the damage history (happens below) // So we need to damage for (int i = mSwapHistory.size() - 1; i > ((int) mSwapHistory.size()) - frame.bufferAge(); i--) { dirty->join(mSwapHistory[i].damage); } } } return windowDirty; } } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */
libs/hwui/renderthread/CanvasContext.h +7 −10 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ #include "FrameInfoVisualizer.h" #include "FrameMetricsReporter.h" #include "IContextFactory.h" #include "IRenderPipeline.h" #include "LayerUpdateQueue.h" #include "RenderNode.h" #include "thread/Task.h" Loading Loading @@ -56,11 +57,7 @@ class RenderState; namespace renderthread { class EglManager; enum SwapBehavior { kSwap_default, kSwap_discardBuffer, }; class Frame; // This per-renderer class manages the bridge between the global EGL context // and the render surface. Loading Loading @@ -164,7 +161,7 @@ public: private: CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory); IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline); friend class RegisterFrameCallbackTask; // TODO: Replace with something better for layer & other GL object Loading @@ -179,21 +176,20 @@ private: bool isSwapChainStuffed(); SkRect computeDirtyRect(const Frame& frame, SkRect* dirty); EGLint mLastFrameWidth = 0; EGLint mLastFrameHeight = 0; RenderThread& mRenderThread; EglManager& mEglManager; sp<Surface> mNativeSurface; EGLSurface mEglSurface = EGL_NO_SURFACE; // stopped indicates the CanvasContext will reject actual redraw operations, // and defer repaint until it is un-stopped bool mStopped = false; // CanvasContext is dirty if it has received an update that it has not // painted onto its surface. bool mIsDirty = false; bool mBufferPreserved = false; SwapBehavior mSwapBehavior = kSwap_default; SwapBehavior mSwapBehavior = SwapBehavior::kSwap_default; struct SwapHistory { SkRect damage; nsecs_t vsyncTime; Loading Loading @@ -238,6 +234,7 @@ private: std::vector< sp<FuncTask> > mFrameFences; sp<TaskProcessor<bool> > mFrameWorkProcessor; std::unique_ptr<IRenderPipeline> mRenderPipeline; }; } /* namespace renderthread */ Loading
libs/hwui/renderthread/IRenderPipeline.h 0 → 100644 +76 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include "FrameInfoVisualizer.h" #include "EglManager.h" #include <SkRect.h> #include <utils/RefBase.h> namespace android { class Surface; namespace uirenderer { class DeferredLayerUpdater; namespace renderthread { enum class SwapBehavior { kSwap_default, kSwap_discardBuffer, }; enum class MakeCurrentResult { AlreadyCurrent, Failed, Succeeded }; class IRenderPipeline { public: virtual MakeCurrentResult makeCurrent() = 0; virtual Frame getFrame() = 0; virtual bool draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, const FrameBuilder::LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, bool opaque, const BakedOpRenderer::LightInfo& lightInfo, const std::vector< sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler) = 0; virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) = 0; virtual bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) = 0; virtual Layer* createTextureLayer() = 0; virtual bool setSurface(Surface* window, SwapBehavior swapBehavior) = 0; virtual void onStop() = 0; virtual bool isSurfaceReady() = 0; virtual bool isContextReady() = 0; virtual void onDestroyHardwareResources() = 0; virtual void renderLayers(const FrameBuilder::LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, bool opaque, const BakedOpRenderer::LightInfo& lightInfo) = 0; virtual TaskManager* getTaskManager() = 0; virtual ~IRenderPipeline() {} }; } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */