Loading libs/hwui/FrameBuilder.cpp +2 −1 Original line number Original line Diff line number Diff line Loading @@ -37,7 +37,8 @@ FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip, const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches& caches) const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches& caches) : mCanvasState(*this) : mCanvasState(*this) , mCaches(caches) , mCaches(caches) , mLightRadius(lightGeometry.radius) { , mLightRadius(lightGeometry.radius) , mDrawFbo0(!nodes.empty()) { ATRACE_NAME("prepare drawing commands"); ATRACE_NAME("prepare drawing commands"); mLayerBuilders.reserve(layers.entries().size()); mLayerBuilders.reserve(layers.entries().size()); Loading libs/hwui/FrameBuilder.h +10 −6 Original line number Original line Diff line number Diff line Loading @@ -137,6 +137,7 @@ public: } } GL_CHECKPOINT(MODERATE); GL_CHECKPOINT(MODERATE); if (CC_LIKELY(mDrawFbo0)) { const LayerBuilder& fbo0 = *(mLayerBuilders[0]); const LayerBuilder& fbo0 = *(mLayerBuilders[0]); renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect); renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect); GL_CHECKPOINT(MODERATE); GL_CHECKPOINT(MODERATE); Loading @@ -144,6 +145,7 @@ public: GL_CHECKPOINT(MODERATE); GL_CHECKPOINT(MODERATE); renderer.endFrame(fbo0.repaintRect); renderer.endFrame(fbo0.repaintRect); } } } void dump() const { void dump() const { for (auto&& layer : mLayerBuilders) { for (auto&& layer : mLayerBuilders) { Loading Loading @@ -239,6 +241,8 @@ private: // contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches // contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches LinearAllocator mAllocator; LinearAllocator mAllocator; const bool mDrawFbo0; }; }; }; // namespace uirenderer }; // namespace uirenderer Loading libs/hwui/renderthread/CanvasContext.cpp +16 −4 Original line number Original line Diff line number Diff line Loading @@ -86,10 +86,12 @@ void CanvasContext::destroy() { freePrefetechedLayers(); freePrefetechedLayers(); destroyHardwareResources(); destroyHardwareResources(); mAnimationContext->destroy(); mAnimationContext->destroy(); #if !HWUI_NEW_OPS if (mCanvas) { if (mCanvas) { delete mCanvas; delete mCanvas; mCanvas = nullptr; mCanvas = nullptr; } } #endif } } void CanvasContext::setSurface(Surface* surface) { void CanvasContext::setSurface(Surface* surface) { Loading Loading @@ -587,9 +589,11 @@ void CanvasContext::freePrefetechedLayers() { void CanvasContext::buildLayer(RenderNode* node) { void CanvasContext::buildLayer(RenderNode* node) { ATRACE_CALL(); ATRACE_CALL(); if (!mEglManager.hasEglContext() || !mCanvas) { if (!mEglManager.hasEglContext()) return; return; #if !HWUI_NEW_OPS } if (!mCanvas) return; #endif // buildLayer() will leave the tree in an unknown state, so we must stop drawing // buildLayer() will leave the tree in an unknown state, so we must stop drawing stopDrawing(); stopDrawing(); Loading @@ -609,7 +613,15 @@ void CanvasContext::buildLayer(RenderNode* node) { node->setPropertyFieldsDirty(RenderNode::GENERIC); node->setPropertyFieldsDirty(RenderNode::GENERIC); #if HWUI_NEW_OPS #if HWUI_NEW_OPS // TODO: support buildLayer static const std::vector< sp<RenderNode> > emptyNodeList; auto& caches = Caches::getInstance(); FrameBuilder frameBuilder(mLayerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1, emptyNodeList, mLightGeometry, mContentDrawBounds, 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); #else #else mCanvas->markLayersAsBuildLayers(); mCanvas->markLayersAsBuildLayers(); mCanvas->flushLayerUpdates(); mCanvas->flushLayerUpdates(); Loading libs/hwui/renderthread/CanvasContext.h +2 −1 Original line number Original line Diff line number Diff line Loading @@ -196,10 +196,11 @@ private: RingBuffer<SwapHistory, 3> mSwapHistory; RingBuffer<SwapHistory, 3> mSwapHistory; bool mOpaque; bool mOpaque; OpenGLRenderer* mCanvas = nullptr; #if HWUI_NEW_OPS #if HWUI_NEW_OPS BakedOpRenderer::LightInfo mLightInfo; BakedOpRenderer::LightInfo mLightInfo; FrameBuilder::LightGeometry mLightGeometry = { {0, 0, 0}, 0 }; FrameBuilder::LightGeometry mLightGeometry = { {0, 0, 0}, 0 }; #else OpenGLRenderer* mCanvas = nullptr; #endif #endif bool mHaveNewSurface = false; bool mHaveNewSurface = false; Loading libs/hwui/tests/unit/FrameBuilderTests.cpp +102 −0 Original line number Original line Diff line number Diff line Loading @@ -30,6 +30,7 @@ namespace android { namespace uirenderer { namespace uirenderer { const LayerUpdateQueue sEmptyLayerUpdateQueue; const LayerUpdateQueue sEmptyLayerUpdateQueue; const std::vector< sp<RenderNode> > sEmptyNodeList; const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50}; const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50}; Loading Loading @@ -216,6 +217,49 @@ RENDERTHREAD_TEST(FrameBuilder, simpleBatching) { << "Expect number of ops = 2 * loop count"; << "Expect number of ops = 2 * loop count"; } } RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) { class EmptyNoFbo0TestRenderer : public TestRendererBase { public: void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { ADD_FAILURE() << "Primary frame draw not expected in this test"; } void endFrame(const Rect& repaintRect) override { ADD_FAILURE() << "Primary frame draw not expected in this test"; } }; // Pass empty node list, so no work is enqueued for Fbo0 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, sEmptyNodeList, sLightGeometry, Caches::getInstance()); EmptyNoFbo0TestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); } RENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) { class EmptyWithFbo0TestRenderer : public TestRendererBase { public: void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { EXPECT_EQ(0, mIndex++); } void endFrame(const Rect& repaintRect) override { EXPECT_EQ(1, mIndex++); } }; auto node = TestUtils::createNode(10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) { // no drawn content }); auto syncedNodeList = TestUtils::createSyncedNodeList(node); // Draw, but pass empty node list, so no work is done for primary frame FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, syncedNodeList, sLightGeometry, Caches::getInstance()); EmptyWithFbo0TestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced," " but fbo0 update lifecycle should still be observed"; } RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) { RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) { class AvoidOverdrawRectsTestRenderer : public TestRendererBase { class AvoidOverdrawRectsTestRenderer : public TestRendererBase { public: public: Loading Loading @@ -1152,6 +1196,64 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) { *(parent->getLayerHandle()) = nullptr; *(parent->getLayerHandle()) = nullptr; } } RENDERTHREAD_TEST(FrameBuilder, buildLayer) { class BuildLayerTestRenderer : public TestRendererBase { public: void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { EXPECT_EQ(0, mIndex++); EXPECT_EQ(100u, offscreenBuffer->viewportWidth); EXPECT_EQ(100u, offscreenBuffer->viewportHeight); EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect); } void onColorOp(const ColorOp& op, const BakedOpState& state) override { EXPECT_EQ(1, mIndex++); EXPECT_TRUE(state.computedState.transform.isIdentity()) << "Transform should be reset within layer"; EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect()) << "Damage rect should be used to clip layer content"; } void endLayer() override { EXPECT_EQ(2, mIndex++); } void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { ADD_FAILURE() << "Primary frame draw not expected in this test"; } void endFrame(const Rect& repaintRect) override { ADD_FAILURE() << "Primary frame draw not expected in this test"; } }; auto node = TestUtils::createNode(10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) { props.mutateLayerProperties().setType(LayerType::RenderLayer); canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode); }); OffscreenBuffer** layerHandle = node->getLayerHandle(); // create RenderNode's layer here in same way prepareTree would OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); *layerHandle = &layer; auto syncedNodeList = TestUtils::createSyncedNodeList(node); // only enqueue partial damage LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75)); // Draw, but pass empty node list, so no work is done for primary frame FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1, sEmptyNodeList, sLightGeometry, Caches::getInstance()); BuildLayerTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(3, renderer.getIndex()); // clean up layer pointer, so we can safely destruct RenderNode *layerHandle = nullptr; } static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) { static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) { SkPaint paint; SkPaint paint; paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel Loading Loading
libs/hwui/FrameBuilder.cpp +2 −1 Original line number Original line Diff line number Diff line Loading @@ -37,7 +37,8 @@ FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip, const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches& caches) const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches& caches) : mCanvasState(*this) : mCanvasState(*this) , mCaches(caches) , mCaches(caches) , mLightRadius(lightGeometry.radius) { , mLightRadius(lightGeometry.radius) , mDrawFbo0(!nodes.empty()) { ATRACE_NAME("prepare drawing commands"); ATRACE_NAME("prepare drawing commands"); mLayerBuilders.reserve(layers.entries().size()); mLayerBuilders.reserve(layers.entries().size()); Loading
libs/hwui/FrameBuilder.h +10 −6 Original line number Original line Diff line number Diff line Loading @@ -137,6 +137,7 @@ public: } } GL_CHECKPOINT(MODERATE); GL_CHECKPOINT(MODERATE); if (CC_LIKELY(mDrawFbo0)) { const LayerBuilder& fbo0 = *(mLayerBuilders[0]); const LayerBuilder& fbo0 = *(mLayerBuilders[0]); renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect); renderer.startFrame(fbo0.width, fbo0.height, fbo0.repaintRect); GL_CHECKPOINT(MODERATE); GL_CHECKPOINT(MODERATE); Loading @@ -144,6 +145,7 @@ public: GL_CHECKPOINT(MODERATE); GL_CHECKPOINT(MODERATE); renderer.endFrame(fbo0.repaintRect); renderer.endFrame(fbo0.repaintRect); } } } void dump() const { void dump() const { for (auto&& layer : mLayerBuilders) { for (auto&& layer : mLayerBuilders) { Loading Loading @@ -239,6 +241,8 @@ private: // contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches // contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches LinearAllocator mAllocator; LinearAllocator mAllocator; const bool mDrawFbo0; }; }; }; // namespace uirenderer }; // namespace uirenderer Loading
libs/hwui/renderthread/CanvasContext.cpp +16 −4 Original line number Original line Diff line number Diff line Loading @@ -86,10 +86,12 @@ void CanvasContext::destroy() { freePrefetechedLayers(); freePrefetechedLayers(); destroyHardwareResources(); destroyHardwareResources(); mAnimationContext->destroy(); mAnimationContext->destroy(); #if !HWUI_NEW_OPS if (mCanvas) { if (mCanvas) { delete mCanvas; delete mCanvas; mCanvas = nullptr; mCanvas = nullptr; } } #endif } } void CanvasContext::setSurface(Surface* surface) { void CanvasContext::setSurface(Surface* surface) { Loading Loading @@ -587,9 +589,11 @@ void CanvasContext::freePrefetechedLayers() { void CanvasContext::buildLayer(RenderNode* node) { void CanvasContext::buildLayer(RenderNode* node) { ATRACE_CALL(); ATRACE_CALL(); if (!mEglManager.hasEglContext() || !mCanvas) { if (!mEglManager.hasEglContext()) return; return; #if !HWUI_NEW_OPS } if (!mCanvas) return; #endif // buildLayer() will leave the tree in an unknown state, so we must stop drawing // buildLayer() will leave the tree in an unknown state, so we must stop drawing stopDrawing(); stopDrawing(); Loading @@ -609,7 +613,15 @@ void CanvasContext::buildLayer(RenderNode* node) { node->setPropertyFieldsDirty(RenderNode::GENERIC); node->setPropertyFieldsDirty(RenderNode::GENERIC); #if HWUI_NEW_OPS #if HWUI_NEW_OPS // TODO: support buildLayer static const std::vector< sp<RenderNode> > emptyNodeList; auto& caches = Caches::getInstance(); FrameBuilder frameBuilder(mLayerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1, emptyNodeList, mLightGeometry, mContentDrawBounds, 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); #else #else mCanvas->markLayersAsBuildLayers(); mCanvas->markLayersAsBuildLayers(); mCanvas->flushLayerUpdates(); mCanvas->flushLayerUpdates(); Loading
libs/hwui/renderthread/CanvasContext.h +2 −1 Original line number Original line Diff line number Diff line Loading @@ -196,10 +196,11 @@ private: RingBuffer<SwapHistory, 3> mSwapHistory; RingBuffer<SwapHistory, 3> mSwapHistory; bool mOpaque; bool mOpaque; OpenGLRenderer* mCanvas = nullptr; #if HWUI_NEW_OPS #if HWUI_NEW_OPS BakedOpRenderer::LightInfo mLightInfo; BakedOpRenderer::LightInfo mLightInfo; FrameBuilder::LightGeometry mLightGeometry = { {0, 0, 0}, 0 }; FrameBuilder::LightGeometry mLightGeometry = { {0, 0, 0}, 0 }; #else OpenGLRenderer* mCanvas = nullptr; #endif #endif bool mHaveNewSurface = false; bool mHaveNewSurface = false; Loading
libs/hwui/tests/unit/FrameBuilderTests.cpp +102 −0 Original line number Original line Diff line number Diff line Loading @@ -30,6 +30,7 @@ namespace android { namespace uirenderer { namespace uirenderer { const LayerUpdateQueue sEmptyLayerUpdateQueue; const LayerUpdateQueue sEmptyLayerUpdateQueue; const std::vector< sp<RenderNode> > sEmptyNodeList; const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50}; const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50}; Loading Loading @@ -216,6 +217,49 @@ RENDERTHREAD_TEST(FrameBuilder, simpleBatching) { << "Expect number of ops = 2 * loop count"; << "Expect number of ops = 2 * loop count"; } } RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) { class EmptyNoFbo0TestRenderer : public TestRendererBase { public: void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { ADD_FAILURE() << "Primary frame draw not expected in this test"; } void endFrame(const Rect& repaintRect) override { ADD_FAILURE() << "Primary frame draw not expected in this test"; } }; // Pass empty node list, so no work is enqueued for Fbo0 FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, sEmptyNodeList, sLightGeometry, Caches::getInstance()); EmptyNoFbo0TestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); } RENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) { class EmptyWithFbo0TestRenderer : public TestRendererBase { public: void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { EXPECT_EQ(0, mIndex++); } void endFrame(const Rect& repaintRect) override { EXPECT_EQ(1, mIndex++); } }; auto node = TestUtils::createNode(10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) { // no drawn content }); auto syncedNodeList = TestUtils::createSyncedNodeList(node); // Draw, but pass empty node list, so no work is done for primary frame FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, syncedNodeList, sLightGeometry, Caches::getInstance()); EmptyWithFbo0TestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced," " but fbo0 update lifecycle should still be observed"; } RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) { RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) { class AvoidOverdrawRectsTestRenderer : public TestRendererBase { class AvoidOverdrawRectsTestRenderer : public TestRendererBase { public: public: Loading Loading @@ -1152,6 +1196,64 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) { *(parent->getLayerHandle()) = nullptr; *(parent->getLayerHandle()) = nullptr; } } RENDERTHREAD_TEST(FrameBuilder, buildLayer) { class BuildLayerTestRenderer : public TestRendererBase { public: void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override { EXPECT_EQ(0, mIndex++); EXPECT_EQ(100u, offscreenBuffer->viewportWidth); EXPECT_EQ(100u, offscreenBuffer->viewportHeight); EXPECT_EQ(Rect(25, 25, 75, 75), repaintRect); } void onColorOp(const ColorOp& op, const BakedOpState& state) override { EXPECT_EQ(1, mIndex++); EXPECT_TRUE(state.computedState.transform.isIdentity()) << "Transform should be reset within layer"; EXPECT_EQ(Rect(25, 25, 75, 75), state.computedState.clipRect()) << "Damage rect should be used to clip layer content"; } void endLayer() override { EXPECT_EQ(2, mIndex++); } void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override { ADD_FAILURE() << "Primary frame draw not expected in this test"; } void endFrame(const Rect& repaintRect) override { ADD_FAILURE() << "Primary frame draw not expected in this test"; } }; auto node = TestUtils::createNode(10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) { props.mutateLayerProperties().setType(LayerType::RenderLayer); canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode); }); OffscreenBuffer** layerHandle = node->getLayerHandle(); // create RenderNode's layer here in same way prepareTree would OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); *layerHandle = &layer; auto syncedNodeList = TestUtils::createSyncedNodeList(node); // only enqueue partial damage LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75)); // Draw, but pass empty node list, so no work is done for primary frame FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1, sEmptyNodeList, sLightGeometry, Caches::getInstance()); BuildLayerTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(3, renderer.getIndex()); // clean up layer pointer, so we can safely destruct RenderNode *layerHandle = nullptr; } static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) { static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) { SkPaint paint; SkPaint paint; paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel Loading