diff options
| author | 2016-04-18 19:40:49 +0000 | |
|---|---|---|
| committer | 2016-04-18 19:40:50 +0000 | |
| commit | c79c3246c9a3e0d2aa34afd18fddc95a6aff0f30 (patch) | |
| tree | b5bcb883b826834e64951b59f9451e074b829773 | |
| parent | 77ca3629f60dd9a416938e625959d0bd750d1acd (diff) | |
| parent | 9cd1bbe5c9e14472e631d8cc10005613925f34af (diff) | |
Merge "Improve multi-window render clipping logic" into nyc-dev
| -rw-r--r-- | core/java/com/android/internal/policy/BackdropFrameRenderer.java | 15 | ||||
| -rw-r--r-- | libs/hwui/FrameBuilder.cpp | 152 | ||||
| -rw-r--r-- | libs/hwui/FrameBuilder.h | 41 | ||||
| -rw-r--r-- | libs/hwui/LayerBuilder.cpp | 3 | ||||
| -rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 11 | ||||
| -rw-r--r-- | libs/hwui/tests/common/TestUtils.h | 8 | ||||
| -rw-r--r-- | libs/hwui/tests/microbench/FrameBuilderBench.cpp | 53 | ||||
| -rw-r--r-- | libs/hwui/tests/unit/FrameBuilderTests.cpp | 370 | ||||
| -rw-r--r-- | libs/hwui/tests/unit/LeakCheckTests.cpp | 11 |
9 files changed, 450 insertions, 214 deletions
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java index 738aaca5d47d..b1598e7d16dd 100644 --- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java +++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java @@ -270,15 +270,12 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame mLastXOffset = xOffset; mLastYOffset = yOffset; - // Only clip the content to the bounds if we are not fullscreen. In the other case, we - // actually need to draw outside these. - if (mResizeMode == RESIZE_MODE_FREEFORM) { - mRenderer.setContentDrawBounds( - mLastXOffset, - mLastYOffset, - mLastXOffset + mLastContentWidth, - mLastYOffset + mLastCaptionHeight + mLastContentHeight); - } + // Inform the renderer of the content's new bounds + mRenderer.setContentDrawBounds( + mLastXOffset, + mLastYOffset, + mLastXOffset + mLastContentWidth, + mLastYOffset + mLastCaptionHeight + mLastContentHeight); // If this was the first call and redrawLocked got already called prior // to us, we should re-issue a redrawLocked now. diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp index 6fc74a585659..502f027029a7 100644 --- a/libs/hwui/FrameBuilder.cpp +++ b/libs/hwui/FrameBuilder.cpp @@ -31,18 +31,16 @@ namespace android { namespace uirenderer { -FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip, +FrameBuilder::FrameBuilder(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight, - const std::vector< sp<RenderNode> >& nodes, - const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches& caches) - : mCanvasState(*this) + const LightGeometry& lightGeometry, Caches& caches) + : mStdAllocator(mAllocator) + , mLayerBuilders(mStdAllocator) + , mLayerStack(mStdAllocator) + , mCanvasState(*this) , mCaches(caches) , mLightRadius(lightGeometry.radius) - , mDrawFbo0(!nodes.empty()) { - ATRACE_NAME("prepare drawing commands"); - - mLayerBuilders.reserve(layers.entries().size()); - mLayerStack.reserve(layers.entries().size()); + , mDrawFbo0(true) { // Prepare to defer Fbo0 auto fbo0 = mAllocator.create<LayerBuilder>(viewportWidth, viewportHeight, Rect(clip)); @@ -51,7 +49,31 @@ FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip, mCanvasState.initializeSaveStack(viewportWidth, viewportHeight, clip.fLeft, clip.fTop, clip.fRight, clip.fBottom, lightGeometry.center); +} + +FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, + const LightGeometry& lightGeometry, Caches& caches) + : mStdAllocator(mAllocator) + , mLayerBuilders(mStdAllocator) + , mLayerStack(mStdAllocator) + , mCanvasState(*this) + , mCaches(caches) + , mLightRadius(lightGeometry.radius) + , mDrawFbo0(false) { + // TODO: remove, with each layer on its own save stack + + // Prepare to defer Fbo0 (which will be empty) + auto fbo0 = mAllocator.create<LayerBuilder>(1, 1, Rect(1, 1)); + mLayerBuilders.push_back(fbo0); + mLayerStack.push_back(0); + mCanvasState.initializeSaveStack(1, 1, + 0, 0, 1, 1, + lightGeometry.center); + deferLayers(layers); +} + +void FrameBuilder::deferLayers(const LayerUpdateQueue& layers) { // Render all layers to be updated, in order. Defer in reverse order, so that they'll be // updated in the order they're passed in (mLayerBuilders are issued to Renderer in reverse) for (int i = layers.entries().size() - 1; i >= 0; i--) { @@ -76,10 +98,45 @@ FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip, restoreForLayer(); } } +} + +void FrameBuilder::deferRenderNode(RenderNode& renderNode) { + renderNode.computeOrdering(); + + mCanvasState.save(SaveFlags::MatrixClip); + deferNodePropsAndOps(renderNode); + mCanvasState.restore(); +} + +void FrameBuilder::deferRenderNode(float tx, float ty, Rect clipRect, RenderNode& renderNode) { + renderNode.computeOrdering(); + mCanvasState.save(SaveFlags::MatrixClip); + mCanvasState.translate(tx, ty); + mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, + SkRegion::kIntersect_Op); + deferNodePropsAndOps(renderNode); + mCanvasState.restore(); +} + +static Rect nodeBounds(RenderNode& node) { + auto& props = node.properties(); + return Rect(props.getLeft(), props.getTop(), + props.getRight(), props.getBottom()); +} + +void FrameBuilder::deferRenderNodeScene(const std::vector< sp<RenderNode> >& nodes, + const Rect& contentDrawBounds) { + if (nodes.size() < 1) return; + if (nodes.size() == 1) { + if (!nodes[0]->nothingToDraw()) { + deferRenderNode(*nodes[0]); + } + return; + } // It there are multiple render nodes, they are laid out as follows: // #0 - backdrop (content + caption) - // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds) + // #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop) // #2 - additional overlay nodes // Usually the backdrop cannot be seen since it will be entirely covered by the content. While // resizing however it might become partially visible. The following render loop will crop the @@ -88,45 +145,52 @@ FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip, // // Additional nodes will be drawn on top with no particular clipping semantics. - // The bounds of the backdrop against which the content should be clipped. - Rect backdropBounds = contentDrawBounds; // Usually the contents bounds should be mContentDrawBounds - however - we will // move it towards the fixed edge to give it a more stable appearance (for the moment). // If there is no content bounds we ignore the layering as stated above and start with 2. - int layer = (contentDrawBounds.isEmpty() || nodes.size() == 1) ? 2 : 0; - - for (const sp<RenderNode>& node : nodes) { - if (node->nothingToDraw()) continue; - node->computeOrdering(); - int count = mCanvasState.save(SaveFlags::MatrixClip); - - if (layer == 0) { - const RenderProperties& properties = node->properties(); - Rect targetBounds(properties.getLeft(), properties.getTop(), - properties.getRight(), properties.getBottom()); - // Move the content bounds towards the fixed corner of the backdrop. - const int x = targetBounds.left; - const int y = targetBounds.top; - // Remember the intersection of the target bounds and the intersection bounds against - // which we have to crop the content. - backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight()); - backdropBounds.doIntersect(targetBounds); - } else if (layer == 1) { - // We shift and clip the content to match its final location in the window. - const float left = contentDrawBounds.left; - const float top = contentDrawBounds.top; - const float dx = backdropBounds.left - left; - const float dy = backdropBounds.top - top; - const float width = backdropBounds.getWidth(); - const float height = backdropBounds.getHeight(); - mCanvasState.translate(dx, dy); - // It gets cropped against the bounds of the backdrop to stay inside. - mCanvasState.clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op); + + // Backdrop bounds in render target space + const Rect backdrop = nodeBounds(*nodes[0]); + + // Bounds that content will fill in render target space (note content node bounds may be bigger) + Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight()); + content.translate(backdrop.left, backdrop.top); + if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) { + // Content doesn't entirely overlap backdrop, so fill around content (right/bottom) + + // Note: in the future, if content doesn't snap to backdrop's left/top, this may need to + // also fill left/top. Currently, both 2up and freeform position content at the top/left of + // the backdrop, so this isn't necessary. + if (content.right < backdrop.right) { + // draw backdrop to right side of content + deferRenderNode(0, 0, Rect(content.right, backdrop.top, + backdrop.right, backdrop.bottom), *nodes[0]); + } + if (content.bottom < backdrop.bottom) { + // draw backdrop to bottom of content + // Note: bottom fill uses content left/right, to avoid overdrawing left/right fill + deferRenderNode(0, 0, Rect(content.left, content.bottom, + content.right, backdrop.bottom), *nodes[0]); } + } + + if (!backdrop.isEmpty()) { + // content node translation to catch up with backdrop + float dx = contentDrawBounds.left - backdrop.left; + float dy = contentDrawBounds.top - backdrop.top; - deferNodePropsAndOps(*node); - mCanvasState.restoreToCount(count); - layer++; + Rect contentLocalClip = backdrop; + contentLocalClip.translate(dx, dy); + deferRenderNode(-dx, -dy, contentLocalClip, *nodes[1]); + } else { + deferRenderNode(*nodes[1]); + } + + // remaining overlay nodes, simply defer + for (size_t index = 2; index < nodes.size(); index++) { + if (!nodes[index]->nothingToDraw()) { + deferRenderNode(*nodes[index]); + } } } diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h index a6fd7616ffcb..b9154435c1e5 100644 --- a/libs/hwui/FrameBuilder.h +++ b/libs/hwui/FrameBuilder.h @@ -37,8 +37,8 @@ class OffscreenBuffer; class Rect; /** - * Traverses all of the drawing commands from the layers and RenderNodes passed into it, preparing - * them to be rendered. + * Processes, optimizes, and stores rendering commands from RenderNodes and + * LayerUpdateQueue, building content needed to render a frame. * * Resolves final drawing state for each operation (including clip, alpha and matrix), and then * reorder and merge each op as it is resolved for drawing efficiency. Each layer of content (either @@ -60,21 +60,21 @@ public: float radius; }; - // TODO: remove - FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip, + FrameBuilder(const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight, - const std::vector< sp<RenderNode> >& nodes, - const LightGeometry& lightGeometry, - Caches& caches) - : FrameBuilder(layers, clip, viewportWidth, viewportHeight, - nodes, lightGeometry, Rect(), caches) {} + const LightGeometry& lightGeometry, Caches& caches); - FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip, - uint32_t viewportWidth, uint32_t viewportHeight, - const std::vector< sp<RenderNode> >& nodes, - const LightGeometry& lightGeometry, - const Rect &contentDrawBounds, - Caches& caches); + FrameBuilder(const LayerUpdateQueue& layerUpdateQueue, + const LightGeometry& lightGeometry, Caches& caches); + + void deferLayers(const LayerUpdateQueue& layers); + + void deferRenderNode(RenderNode& renderNode); + + void deferRenderNode(float tx, float ty, Rect clipRect, RenderNode& renderNode); + + void deferRenderNodeScene(const std::vector< sp<RenderNode> >& nodes, + const Rect& contentDrawBounds); virtual ~FrameBuilder() {} @@ -223,8 +223,12 @@ private: MAP_DEFERRABLE_OPS(X) #undef X + // contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches + LinearAllocator mAllocator; + LinearStdAllocator<void*> mStdAllocator; + // List of every deferred layer's render state. Replayed in reverse order to render a frame. - std::vector<LayerBuilder*> mLayerBuilders; + LsaVector<LayerBuilder*> mLayerBuilders; /* * Stack of indices within mLayerBuilders representing currently active layers. If drawing @@ -238,7 +242,7 @@ private: * won't be in mLayerStack. This is because it can be replayed, but can't have any more drawing * ops added to it. */ - std::vector<size_t> mLayerStack; + LsaVector<size_t> mLayerStack; CanvasState mCanvasState; @@ -246,9 +250,6 @@ private: float mLightRadius; - // contains single-frame objects, such as BakedOpStates, LayerBuilders, Batches - LinearAllocator mAllocator; - const bool mDrawFbo0; }; diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp index eea11bff7d8a..30007772b1bc 100644 --- a/libs/hwui/LayerBuilder.cpp +++ b/libs/hwui/LayerBuilder.cpp @@ -244,7 +244,8 @@ void LayerBuilder::onDeferOp(LinearAllocator& allocator, const BakedOpState* bak if (CC_UNLIKELY(activeUnclippedSaveLayers.empty() && bakedState->computedState.opaqueOverClippedBounds - && bakedState->computedState.clippedBounds.contains(repaintRect))) { + && bakedState->computedState.clippedBounds.contains(repaintRect) + && !Properties::debugOverdraw)) { // discard all deferred drawing ops, since new one will occlude them clear(); } diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 890d4a1ea224..904501e093b4 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -356,9 +356,13 @@ void CanvasContext::draw() { #if HWUI_NEW_OPS auto& caches = Caches::getInstance(); - FrameBuilder frameBuilder(mLayerUpdateQueue, dirty, frame.width(), frame.height(), - mRenderNodes, mLightGeometry, mContentDrawBounds, caches); + 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); @@ -623,8 +627,7 @@ void CanvasContext::buildLayer(RenderNode* node, TreeObserver* observer) { #if HWUI_NEW_OPS static const std::vector< sp<RenderNode> > emptyNodeList; auto& caches = Caches::getInstance(); - FrameBuilder frameBuilder(mLayerUpdateQueue, SkRect::MakeWH(1, 1), 1, 1, - emptyNodeList, mLightGeometry, mContentDrawBounds, caches); + FrameBuilder frameBuilder(mLayerUpdateQueue, mLightGeometry, caches); mLayerUpdateQueue.clear(); BakedOpRenderer renderer(caches, mRenderThread.renderState(), mOpaque, mLightInfo); diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 5f4ebc022bf8..dbaefa470b50 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -171,11 +171,9 @@ public: syncHierarchyPropertiesAndDisplayListImpl(node.get()); } - static std::vector<sp<RenderNode>> createSyncedNodeList(sp<RenderNode>& node) { - TestUtils::syncHierarchyPropertiesAndDisplayList(node); - std::vector<sp<RenderNode>> vec; - vec.emplace_back(node); - return vec; + static sp<RenderNode>& getSyncedNode(sp<RenderNode>& node) { + syncHierarchyPropertiesAndDisplayList(node); + return node; } typedef std::function<void(renderthread::RenderThread& thread)> RtCallback; diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp index 0aef620c7429..84ef9c2575f5 100644 --- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp +++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp @@ -35,11 +35,10 @@ using namespace android::uirenderer; using namespace android::uirenderer::renderthread; using namespace android::uirenderer::test; -const LayerUpdateQueue sEmptyLayerUpdateQueue; const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50}; const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 }; -static std::vector<sp<RenderNode>> createTestNodeList() { +static sp<RenderNode> createTestNode() { auto node = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, RecordingCanvas& canvas) { SkBitmap bitmap = TestUtils::createSkBitmap(10, 10); @@ -56,31 +55,33 @@ static std::vector<sp<RenderNode>> createTestNodeList() { canvas.restore(); }); TestUtils::syncHierarchyPropertiesAndDisplayList(node); - std::vector<sp<RenderNode>> vec; - vec.emplace_back(node); - return vec; + return node; } void BM_FrameBuilder_defer(benchmark::State& state) { - auto nodes = createTestNodeList(); - while (state.KeepRunning()) { - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, - nodes, sLightGeometry, Caches::getInstance()); - benchmark::DoNotOptimize(&frameBuilder); - } + TestUtils::runOnRenderThread([&state](RenderThread& thread) { + auto node = createTestNode(); + while (state.KeepRunning()) { + FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*node); + benchmark::DoNotOptimize(&frameBuilder); + } + }); } BENCHMARK(BM_FrameBuilder_defer); void BM_FrameBuilder_deferAndRender(benchmark::State& state) { TestUtils::runOnRenderThread([&state](RenderThread& thread) { - auto nodes = createTestNodeList(); + auto node = createTestNode(); RenderState& renderState = thread.renderState(); Caches& caches = Caches::getInstance(); while (state.KeepRunning()) { - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, - nodes, sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, + sLightGeometry, caches); + frameBuilder.deferRenderNode(*node); BakedOpRenderer renderer(caches, renderState, true, sLightInfo); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); @@ -90,7 +91,7 @@ void BM_FrameBuilder_deferAndRender(benchmark::State& state) { } BENCHMARK(BM_FrameBuilder_deferAndRender); -static std::vector<sp<RenderNode>> getSyncedSceneNodes(const char* sceneName) { +static sp<RenderNode> getSyncedSceneNode(const char* sceneName) { gDisplay = getBuiltInDisplay(); // switch to real display if present TestContext testContext; @@ -103,9 +104,7 @@ static std::vector<sp<RenderNode>> getSyncedSceneNodes(const char* sceneName) { }); TestUtils::syncHierarchyPropertiesAndDisplayList(rootNode); - std::vector<sp<RenderNode>> nodes; - nodes.emplace_back(rootNode); - return nodes; + return rootNode; } static auto SCENES = { @@ -116,11 +115,12 @@ void BM_FrameBuilder_defer_scene(benchmark::State& state) { TestUtils::runOnRenderThread([&state](RenderThread& thread) { const char* sceneName = *(SCENES.begin() + state.range_x()); state.SetLabel(sceneName); - auto nodes = getSyncedSceneNodes(sceneName); + auto node = getSyncedSceneNode(sceneName); while (state.KeepRunning()) { - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, - SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h, - nodes, sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(gDisplay.w, gDisplay.h), + gDisplay.w, gDisplay.h, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*node); benchmark::DoNotOptimize(&frameBuilder); } }); @@ -131,15 +131,16 @@ void BM_FrameBuilder_deferAndRender_scene(benchmark::State& state) { TestUtils::runOnRenderThread([&state](RenderThread& thread) { const char* sceneName = *(SCENES.begin() + state.range_x()); state.SetLabel(sceneName); - auto nodes = getSyncedSceneNodes(sceneName); + auto node = getSyncedSceneNode(sceneName); RenderState& renderState = thread.renderState(); Caches& caches = Caches::getInstance(); while (state.KeepRunning()) { - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, - SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h, - nodes, sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(gDisplay.w, gDisplay.h), + gDisplay.w, gDisplay.h, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*node); BakedOpRenderer renderer(caches, renderState, true, sLightInfo); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp index ebc1c8002304..0f16b1512586 100644 --- a/libs/hwui/tests/unit/FrameBuilderTests.cpp +++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp @@ -29,11 +29,8 @@ namespace android { namespace uirenderer { -const LayerUpdateQueue sEmptyLayerUpdateQueue; -const std::vector< sp<RenderNode> > sEmptyNodeList; const FrameBuilder::LightGeometry sLightGeometry = { {100, 100, 100}, 50}; - /** * Virtual class implemented by each test to redirect static operation / state transitions to * virtual methods. @@ -136,8 +133,10 @@ RENDERTHREAD_TEST(FrameBuilder, simple) { canvas.drawRect(0, 0, 100, 200, SkPaint()); canvas.drawBitmap(bitmap, 10, 10, nullptr); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + SimpleTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end @@ -162,8 +161,10 @@ RENDERTHREAD_TEST(FrameBuilder, simpleStroke) { strokedPaint.setStrokeWidth(10); canvas.drawPoint(50, 50, strokedPaint); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 200), 100, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + SimpleStrokeTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(1, renderer.getIndex()); @@ -177,8 +178,9 @@ RENDERTHREAD_TEST(FrameBuilder, simpleRejection) { canvas.drawRect(0, 0, 400, 400, SkPaint()); canvas.restore(); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); FailRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); @@ -211,15 +213,111 @@ RENDERTHREAD_TEST(FrameBuilder, simpleBatching) { } canvas.restore(); }); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); SimpleBatchingTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(2 * LOOPS, renderer.getIndex()) << "Expect number of ops = 2 * loop count"; } +RENDERTHREAD_TEST(FrameBuilder, deferRenderNode_translateClip) { + class DeferRenderNodeTranslateClipTestRenderer : public TestRendererBase { + public: + void onRectOp(const RectOp& op, const BakedOpState& state) override { + EXPECT_EQ(0, mIndex++); + EXPECT_EQ(Rect(5, 10, 55, 60), state.computedState.clippedBounds); + EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, + state.computedState.clipSideFlags); + } + }; + + auto node = TestUtils::createNode(0, 0, 100, 100, + [](RenderProperties& props, RecordingCanvas& canvas) { + canvas.drawRect(0, 0, 100, 100, SkPaint()); + }); + + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(5, 10, Rect(50, 50), // translate + clip node + *TestUtils::getSyncedNode(node)); + + DeferRenderNodeTranslateClipTestRenderer renderer; + frameBuilder.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(1, renderer.getIndex()); +} + +RENDERTHREAD_TEST(FrameBuilder, deferRenderNodeScene) { + class DeferRenderNodeSceneTestRenderer : public TestRendererBase { + public: + void onRectOp(const RectOp& op, const BakedOpState& state) override { + const Rect& clippedBounds = state.computedState.clippedBounds; + Matrix4 expected; + switch (mIndex++) { + case 0: + // background - left side + EXPECT_EQ(Rect(600, 100, 700, 500), clippedBounds); + expected.loadTranslate(100, 100, 0); + break; + case 1: + // background - top side + EXPECT_EQ(Rect(100, 400, 600, 500), clippedBounds); + expected.loadTranslate(100, 100, 0); + break; + case 2: + // content + EXPECT_EQ(Rect(100, 100, 700, 500), clippedBounds); + expected.loadTranslate(-50, -50, 0); + break; + case 3: + // overlay + EXPECT_EQ(Rect(0, 0, 800, 200), clippedBounds); + break; + default: + ADD_FAILURE() << "Too many rects observed"; + } + EXPECT_EQ(expected, state.computedState.transform); + } + }; + + std::vector<sp<RenderNode>> nodes; + SkPaint transparentPaint; + transparentPaint.setAlpha(128); + + // backdrop + nodes.push_back(TestUtils::createNode(100, 100, 700, 500, // 600x400 + [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) { + canvas.drawRect(0, 0, 600, 400, transparentPaint); + })); + + // content + Rect contentDrawBounds(150, 150, 650, 450); // 500x300 + nodes.push_back(TestUtils::createNode(0, 0, 800, 600, + [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) { + canvas.drawRect(0, 0, 800, 600, transparentPaint); + })); + + // overlay + nodes.push_back(TestUtils::createNode(0, 0, 800, 600, + [&transparentPaint](RenderProperties& props, RecordingCanvas& canvas) { + canvas.drawRect(0, 0, 800, 200, transparentPaint); + })); + + for (auto& node : nodes) { + TestUtils::syncHierarchyPropertiesAndDisplayList(node); + } + + FrameBuilder frameBuilder(SkRect::MakeWH(800, 600), 800, 600, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNodeScene(nodes, contentDrawBounds); + + DeferRenderNodeSceneTestRenderer renderer; + frameBuilder.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(4, renderer.getIndex()); +} + RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) { class EmptyNoFbo0TestRenderer : public TestRendererBase { public: @@ -231,9 +329,9 @@ RENDERTHREAD_TEST(FrameBuilder, empty_noFbo0) { } }; - // Pass empty node list, so no work is enqueued for Fbo0 - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - sEmptyNodeList, sLightGeometry, Caches::getInstance()); + // Use layer update constructor, so no work is enqueued for Fbo0 + LayerUpdateQueue emptyLayerUpdateQueue; + FrameBuilder frameBuilder(emptyLayerUpdateQueue, sLightGeometry, Caches::getInstance()); EmptyNoFbo0TestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); } @@ -252,11 +350,12 @@ RENDERTHREAD_TEST(FrameBuilder, empty_withFbo0) { [](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()); + // Draw, but pass node without draw content, so no work is done for primary frame + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + EmptyWithFbo0TestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(2, renderer.getIndex()) << "No drawing content produced," @@ -281,9 +380,9 @@ RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) { // Damage (and therefore clip) is same as last draw, subset of renderable area. // This means last op occludes other contents, and they'll be rejected to avoid overdraw. - SkRect damageRect = SkRect::MakeLTRB(10, 10, 190, 190); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, damageRect, 200, 200, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 190, 190), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); EXPECT_EQ(3u, node->getDisplayList()->getOps().size()) << "Recording must not have rejected ops, in order for this test to be valid"; @@ -324,9 +423,9 @@ RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) { canvas.drawBitmap(opaqueBitmap, 0, 0, nullptr); canvas.drawBitmap(transpBitmap, 0, 0, nullptr); }); - - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(50, 50), 50, 50, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(50, 50), 50, 50, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); EXPECT_EQ(5u, node->getDisplayList()->getOps().size()) << "Recording must not have rejected ops, in order for this test to be valid"; @@ -369,8 +468,10 @@ RENDERTHREAD_TEST(FrameBuilder, clippedMerging) { canvas.drawBitmap(bitmap, 40, 70, nullptr); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + ClippedMergingTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(4, renderer.getIndex()); @@ -397,8 +498,10 @@ RENDERTHREAD_TEST(FrameBuilder, textMerging) { TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + TextMergingTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops"; @@ -428,8 +531,11 @@ RENDERTHREAD_TEST(FrameBuilder, textStrikethrough) { TestUtils::drawUtf8ToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1)); } }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + + FrameBuilder frameBuilder(SkRect::MakeWH(200, 2000), 200, 2000, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + TextStrikethroughTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(2 * LOOPS, renderer.getIndex()) @@ -485,8 +591,9 @@ RENDERTHREAD_TEST(FrameBuilder, textStyle) { TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); } }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); TextStyleTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(3, renderer.getIndex()) << "Expect 3 ops"; @@ -516,8 +623,11 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer_clipLocalMatrix) { canvas.drawLayer(layerUpdater.get()); canvas.restore(); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + TextureLayerClipLocalMatrixTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(1, renderer.getIndex()); @@ -546,8 +656,10 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer_combineMatrices) { canvas.restore(); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + TextureLayerCombineMatricesTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(1, renderer.getIndex()); @@ -562,8 +674,11 @@ RENDERTHREAD_TEST(FrameBuilder, textureLayer_reject) { [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) { canvas.drawLayer(layerUpdater.get()); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + FailRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); } @@ -584,9 +699,10 @@ RENDERTHREAD_TEST(FrameBuilder, functor_reject) { canvas.callDrawGLFunction(&noopFunctor, nullptr); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - TestUtils::createSyncedNodeList(scrolledFunctorView), + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(scrolledFunctorView)); + FunctorTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(1, renderer.getIndex()) << "Functor should not be rejected"; @@ -608,9 +724,10 @@ RENDERTHREAD_TEST(FrameBuilder, deferColorOp_unbounded) { canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - TestUtils::createSyncedNodeList(unclippedColorView), + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(unclippedColorView)); + ColorTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(1, renderer.getIndex()) << "ColorOp should not be rejected"; @@ -654,8 +771,10 @@ TEST(FrameBuilder, renderNode) { canvas.restore(); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); + RenderNodeTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(2, renderer.getIndex()); @@ -678,9 +797,11 @@ RENDERTHREAD_TEST(FrameBuilder, clipped) { canvas.drawBitmap(bitmap, 0, 0, nullptr); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, - SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver - 200, 200, TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + // clip to small area, should see in receiver + FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 20, 30, 40), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + ClippedTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); } @@ -725,8 +846,11 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_simple) { canvas.drawRect(10, 10, 190, 190, SkPaint()); canvas.restore(); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + SaveLayerSimpleTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(5, renderer.getIndex()); @@ -806,8 +930,10 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_nested) { canvas.restore(); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(800, 800), 800, 800, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + SaveLayerNestedTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(12, renderer.getIndex()); @@ -826,8 +952,10 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayer_contentRejection) { canvas.restore(); canvas.restore(); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); FailRenderer renderer; // should see no ops, even within the layer, since the layer should be rejected @@ -869,8 +997,11 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_simple) { canvas.drawRect(0, 0, 200, 200, SkPaint()); canvas.restore(); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + SaveLayerUnclippedSimpleTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(4, renderer.getIndex()); @@ -923,8 +1054,11 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_mergedClears) { canvas.drawRect(0, 0, 100, 100, SkPaint()); canvas.restoreToCount(restoreTo); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + SaveLayerUnclippedMergedClearsTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(10, renderer.getIndex()) @@ -964,8 +1098,10 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_clearClip) { }); // draw with partial screen dirty, and assert we see that rect later - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(50, 50, 150, 150), 200, 200, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeLTRB(50, 50, 150, 150), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + SaveLayerUnclippedClearClipTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(4, renderer.getIndex()); @@ -981,8 +1117,10 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_reject) { }); // draw with partial screen dirty that doesn't intersect with savelayer - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + FailRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); } @@ -1046,8 +1184,11 @@ RENDERTHREAD_TEST(FrameBuilder, saveLayerUnclipped_complex) { canvas.restore(); canvas.restore(); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + + FrameBuilder frameBuilder(SkRect::MakeWH(600, 600), 600, 600, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + SaveLayerUnclippedComplexTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(13, renderer.getIndex()); @@ -1098,14 +1239,17 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_simple) { OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); *layerHandle = &layer; - auto syncedNodeList = TestUtils::createSyncedNodeList(node); + auto syncedNode = TestUtils::getSyncedNode(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)); - FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - syncedNodeList, sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferLayers(layerUpdateQueue); + frameBuilder.deferRenderNode(*syncedNode); + HwLayerSimpleTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(6, renderer.getIndex()); @@ -1202,14 +1346,17 @@ RENDERTHREAD_TEST(FrameBuilder, hwLayer_complex) { OffscreenBuffer parentLayer(renderThread.renderState(), Caches::getInstance(), 200, 200); *(parent->getLayerHandle()) = &parentLayer; - auto syncedList = TestUtils::createSyncedNodeList(parent); + auto syncedNode = TestUtils::getSyncedNode(parent); LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100)); layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200)); - FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - syncedList, sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferLayers(layerUpdateQueue); + frameBuilder.deferRenderNode(*syncedNode); + HwLayerComplexTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(14, renderer.getIndex()); @@ -1260,15 +1407,14 @@ RENDERTHREAD_TEST(FrameBuilder, buildLayer) { OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100); *layerHandle = &layer; - auto syncedNodeList = TestUtils::createSyncedNodeList(node); + TestUtils::syncHierarchyPropertiesAndDisplayList(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()); + FrameBuilder frameBuilder(layerUpdateQueue, sLightGeometry, Caches::getInstance()); BuildLayerTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(3, renderer.getIndex()); @@ -1315,8 +1461,10 @@ RENDERTHREAD_TEST(FrameBuilder, zReorder) { drawOrderedRect(&canvas, 8); drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, - TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); + ZReorderTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(10, renderer.getIndex()); @@ -1406,8 +1554,10 @@ RENDERTHREAD_TEST(FrameBuilder, projectionReorder) { canvas.restore(); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, - TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); + ProjectionReorderTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(3, renderer.getIndex()); @@ -1486,11 +1636,16 @@ RENDERTHREAD_TEST(FrameBuilder, projectionHwLayer) { layer.setWindowTransform(windowTransform); *layerHandle = &layer; - auto syncedList = TestUtils::createSyncedNodeList(parent); + auto syncedNode = TestUtils::getSyncedNode(parent); + LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(200, 200)); - FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400, - syncedList, sLightGeometry, Caches::getInstance()); + + FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferLayers(layerUpdateQueue); + frameBuilder.deferRenderNode(*syncedNode); + ProjectionHwLayerTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(6, renderer.getIndex()); @@ -1545,8 +1700,10 @@ RENDERTHREAD_TEST(FrameBuilder, projectionChildScroll) { canvas.drawRenderNode(child.get()); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400, - TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); + ProjectionChildScrollTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(2, renderer.getIndex()); @@ -1588,8 +1745,10 @@ RENDERTHREAD_TEST(FrameBuilder, shadow) { canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - TestUtils::createSyncedNodeList(parent), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); + ShadowTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(2, renderer.getIndex()); @@ -1632,9 +1791,10 @@ RENDERTHREAD_TEST(FrameBuilder, shadowSaveLayer) { canvas.restoreToCount(count); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - TestUtils::createSyncedNodeList(parent), + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); + ShadowSaveLayerTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(6, renderer.getIndex()); @@ -1681,12 +1841,15 @@ RENDERTHREAD_TEST(FrameBuilder, shadowHwLayer) { layer.setWindowTransform(windowTransform); *layerHandle = &layer; - auto syncedList = TestUtils::createSyncedNodeList(parent); + auto syncedNode = TestUtils::getSyncedNode(parent); LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100)); - FrameBuilder frameBuilder(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - syncedList, + + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 30}, Caches::getInstance()); + frameBuilder.deferLayers(layerUpdateQueue); + frameBuilder.deferRenderNode(*syncedNode); + ShadowHwLayerTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(5, renderer.getIndex()); @@ -1713,10 +1876,10 @@ RENDERTHREAD_TEST(FrameBuilder, shadowLayering) { canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get()); }); - - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - TestUtils::createSyncedNodeList(parent), + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); + ShadowLayeringTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(4, renderer.getIndex()); @@ -1743,9 +1906,10 @@ RENDERTHREAD_TEST(FrameBuilder, shadowClipping) { canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, - TestUtils::createSyncedNodeList(parent), + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, (FrameBuilder::LightGeometry) {{ 100, 100, 100 }, 50}, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(parent)); + ShadowClippingTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(2, renderer.getIndex()); @@ -1772,8 +1936,10 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac canvas.drawRect(0, 0, 100, 100, paint); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + PropertyTestRenderer renderer(opValidateCallback); frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op"; @@ -1915,10 +2081,12 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData, paint.setColor(SK_ColorWHITE); canvas.drawRect(0, 0, 10000, 10000, paint); }); - auto nodes = TestUtils::createSyncedNodeList(node); // sync before querying height + auto syncedNode = TestUtils::getSyncedNode(node); // sync before querying height + + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*syncedNode); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - nodes, sLightGeometry, Caches::getInstance()); SaveLayerAlphaClipTestRenderer renderer(outObservedData); frameBuilder.replayBakedOps<TestDispatcher>(renderer); @@ -1991,8 +2159,10 @@ RENDERTHREAD_TEST(FrameBuilder, clip_replace) { canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode); }); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(10, 10, 40, 40), 50, 50, - TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeLTRB(10, 10, 40, 40), 50, 50, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + ClipReplaceTestRenderer renderer; frameBuilder.replayBakedOps<TestDispatcher>(renderer); EXPECT_EQ(1, renderer.getIndex()); diff --git a/libs/hwui/tests/unit/LeakCheckTests.cpp b/libs/hwui/tests/unit/LeakCheckTests.cpp index e2fc376db50d..6148b33eceb8 100644 --- a/libs/hwui/tests/unit/LeakCheckTests.cpp +++ b/libs/hwui/tests/unit/LeakCheckTests.cpp @@ -26,7 +26,6 @@ using namespace android; using namespace android::uirenderer; -const LayerUpdateQueue sEmptyLayerUpdateQueue; const FrameBuilder::LightGeometry sLightGeometery = { {100, 100, 100}, 50}; const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 }; @@ -43,8 +42,9 @@ RENDERTHREAD_TEST(LeakCheck, saveLayer_overdrawRejection) { RenderState& renderState = renderThread.renderState(); Caches& caches = Caches::getInstance(); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, - TestUtils::createSyncedNodeList(node), sLightGeometery, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, + sLightGeometery, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); BakedOpRenderer renderer(caches, renderState, true, sLightInfo); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); } @@ -59,8 +59,9 @@ RENDERTHREAD_TEST(LeakCheck, saveLayerUnclipped_simple) { RenderState& renderState = renderThread.renderState(); Caches& caches = Caches::getInstance(); - FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, - TestUtils::createSyncedNodeList(node), sLightGeometery, Caches::getInstance()); + FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, + sLightGeometery, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); BakedOpRenderer renderer(caches, renderState, true, sLightInfo); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); } |