From 8d1f2120fe80b23ab03c7168e3b6b2d13bafe2e7 Mon Sep 17 00:00:00 2001 From: Chris Craik Date: Tue, 24 Nov 2015 16:40:09 -0800 Subject: [PATCH] Support projection in OpReorderer bug:22480459 Change-Id: Iceb71732dc50957cfb47fa1ba9b8e18e6fc51132 --- libs/hwui/Android.mk | 4 +- libs/hwui/DisplayList.h | 2 +- libs/hwui/DisplayListCanvas.h | 1 + libs/hwui/DisplayListOp.h | 14 +- libs/hwui/OpReorderer.cpp | 69 +++++++--- libs/hwui/OpReorderer.h | 13 +- libs/hwui/RecordedOp.h | 12 +- libs/hwui/RenderNode.cpp | 44 +++--- libs/hwui/RenderNode.h | 13 +- libs/hwui/microbench/OpReordererBench.cpp | 49 ++++--- libs/hwui/tests/scenes/OvalAnimation.cpp | 7 +- libs/hwui/tests/scenes/PartialDamageAnimation.cpp | 2 +- libs/hwui/tests/scenes/RectGridAnimation.cpp | 2 +- libs/hwui/tests/scenes/SaveLayerAnimation.cpp | 2 +- libs/hwui/unit_tests/LayerUpdateQueueTests.cpp | 2 +- libs/hwui/unit_tests/OpReordererTests.cpp | 159 ++++++++++++++++++---- libs/hwui/unit_tests/RecordingCanvasTests.cpp | 43 ++++++ libs/hwui/utils/TestUtils.h | 25 ++-- 18 files changed, 325 insertions(+), 138 deletions(-) diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index cc68fb292a8f..9e615759f827 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -275,7 +275,9 @@ LOCAL_MULTILIB := both LOCAL_MODULE_STEM_32 := hwuimicro LOCAL_MODULE_STEM_64 := hwuimicro64 LOCAL_SHARED_LIBRARIES := $(hwui_shared_libraries) -LOCAL_CFLAGS := $(hwui_cflags) +LOCAL_CFLAGS := \ + $(hwui_cflags) \ + -DHWUI_NULL_GPU LOCAL_C_INCLUDES += bionic/benchmarks/ LOCAL_WHOLE_STATIC_LIBRARIES := libhwui_static_null_gpu diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index 00c4e2d47e4c..60cc7bab64dd 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -132,7 +132,7 @@ public: DisplayList(); ~DisplayList(); - // index of DisplayListOp restore, after which projected descendents should be drawn + // index of DisplayListOp restore, after which projected descendants should be drawn int projectionReceiveIndex; const LsaVector& getChunks() const { return chunks; } diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h index bf98f79d4d5a..72fc100ebd1d 100644 --- a/libs/hwui/DisplayListCanvas.h +++ b/libs/hwui/DisplayListCanvas.h @@ -55,6 +55,7 @@ class DeferredDisplayList; class DeferredLayerUpdater; class DisplayListOp; class DrawOp; +class DrawRenderNodeOp; class RenderNode; class StateOp; diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 977b53c31f46..bd11d0acf176 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -1386,19 +1386,19 @@ public: : DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), nullptr) , renderNode(renderNode) , mRecordedWithPotentialStencilClip(!clipIsSimple || !transformFromParent.isSimple()) - , mTransformFromParent(transformFromParent) - , mSkipInOrderDraw(false) {} + , localMatrix(transformFromParent) + , skipInOrderDraw(false) {} virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, bool useQuickReject) override { - if (renderNode->isRenderable() && !mSkipInOrderDraw) { + if (renderNode->isRenderable() && !skipInOrderDraw) { renderNode->defer(deferStruct, level + 1); } } virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level, bool useQuickReject) override { - if (renderNode->isRenderable() && !mSkipInOrderDraw) { + if (renderNode->isRenderable() && !skipInOrderDraw) { renderNode->replay(replayStruct, level + 1); } } @@ -1439,7 +1439,7 @@ private: /** * Records transform vs parent, used for computing total transform without rerunning DL contents */ - const mat4 mTransformFromParent; + const mat4 localMatrix; /** * Holds the transformation between the projection surface ViewGroup and this RenderNode @@ -1449,8 +1449,8 @@ private: * * Note: doesn't include transformation within the RenderNode, or its properties. */ - mat4 mTransformFromCompositingAncestor; - bool mSkipInOrderDraw; + mat4 transformFromCompositingAncestor; + bool skipInOrderDraw; }; /** diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp index 5e954ae6e971..9cbd9c2d9ffc 100644 --- a/libs/hwui/OpReorderer.cpp +++ b/libs/hwui/OpReorderer.cpp @@ -330,6 +330,7 @@ OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip, for (int i = layers.entries().size() - 1; i >= 0; i--) { RenderNode* layerNode = layers.entries()[i].renderNode; const Rect& layerDamage = layers.entries()[i].damage; + layerNode->computeOrdering(); // map current light center into RenderNode's coordinate space Vector3 lightCenter = mCanvasState.currentSnapshot()->getRelativeLightCenter(); @@ -339,7 +340,7 @@ OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip, layerDamage, lightCenter, nullptr, layerNode); if (layerNode->getDisplayList()) { - deferDisplayList(*(layerNode->getDisplayList())); + deferNodeOps(*layerNode); } restoreForLayer(); } @@ -347,6 +348,7 @@ OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip, // Defer Fbo0 for (const sp& node : nodes) { if (node->nothingToDraw()) continue; + node->computeOrdering(); int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag); deferNodePropsAndOps(*node); @@ -354,20 +356,6 @@ OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip, } } -OpReorderer::OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList, - const Vector3& lightCenter) - : mCanvasState(*this) { - ATRACE_NAME("prepare drawing commands"); - // Prepare to defer Fbo0 - mLayerReorderers.emplace_back(viewportWidth, viewportHeight, - Rect(viewportWidth, viewportHeight)); - mLayerStack.push_back(0); - mCanvasState.initializeSaveStack(viewportWidth, viewportHeight, - 0, 0, viewportWidth, viewportHeight, lightCenter); - - deferDisplayList(displayList); -} - void OpReorderer::onViewportInitialized() {} void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {} @@ -462,10 +450,10 @@ void OpReorderer::deferNodePropsAndOps(RenderNode& node) { Matrix4::identity(), saveLayerBounds, &saveLayerPaint)); - deferDisplayList(*(node.getDisplayList())); + deferNodeOps(node); onEndLayerOp(*new (mAllocator) EndLayerOp()); } else { - deferDisplayList(*(node.getDisplayList())); + deferNodeOps(node); } } } @@ -610,18 +598,53 @@ void OpReorderer::deferShadow(const RenderNodeOp& casterNodeOp) { } } +void OpReorderer::deferProjectedChildren(const RenderNode& renderNode) { + const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath(); + int count = mCanvasState.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + + // can't be null, since DL=null node rejection happens before deferNodePropsAndOps + const DisplayList& displayList = *(renderNode.getDisplayList()); + + const RecordedOp* op = (displayList.getOps()[displayList.projectionReceiveIndex]); + const RenderNodeOp* backgroundOp = static_cast(op); + const RenderProperties& backgroundProps = backgroundOp->renderNode->properties(); + + // Transform renderer to match background we're projecting onto + // (by offsetting canvas by translationX/Y of background rendernode, since only those are set) + mCanvasState.translate(backgroundProps.getTranslationX(), backgroundProps.getTranslationY()); + + // If the projection receiver has an outline, we mask projected content to it + // (which we know, apriori, are all tessellated paths) + mCanvasState.setProjectionPathMask(mAllocator, projectionReceiverOutline); + + // draw projected nodes + for (size_t i = 0; i < renderNode.mProjectedNodes.size(); i++) { + RenderNodeOp* childOp = renderNode.mProjectedNodes[i]; + + int restoreTo = mCanvasState.save(SkCanvas::kMatrix_SaveFlag); + mCanvasState.concatMatrix(childOp->transformFromCompositingAncestor); + deferRenderNodeOp(*childOp); + mCanvasState.restoreToCount(restoreTo); + } + + mCanvasState.restoreToCount(count); +} + /** * Used to define a list of lambdas referencing private OpReorderer::onXXXXOp() methods. * - * This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas. E.g. a - * BitmapOp op then would be dispatched to OpReorderer::onBitmapOp(const BitmapOp&) + * This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas. + * E.g. a BitmapOp op then would be dispatched to OpReorderer::onBitmapOp(const BitmapOp&) */ #define OP_RECEIVER(Type) \ [](OpReorderer& reorderer, const RecordedOp& op) { reorderer.on##Type(static_cast(op)); }, -void OpReorderer::deferDisplayList(const DisplayList& displayList) { +void OpReorderer::deferNodeOps(const RenderNode& renderNode) { static std::function receivers[] = { MAP_OPS(OP_RECEIVER) }; + + // can't be null, since DL=null node rejection happens before deferNodePropsAndOps + const DisplayList& displayList = *(renderNode.getDisplayList()); for (const DisplayList::Chunk& chunk : displayList.getChunks()) { FatVector zTranslatedNodes; buildZSortedChildList(&zTranslatedNodes, displayList, chunk); @@ -630,6 +653,12 @@ void OpReorderer::deferDisplayList(const DisplayList& displayList) { for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) { const RecordedOp* op = displayList.getOps()[opIndex]; receivers[op->opId](*this, *op); + + if (CC_UNLIKELY(!renderNode.mProjectedNodes.empty() + && displayList.projectionReceiveIndex >= 0 + && static_cast(opIndex) == displayList.projectionReceiveIndex)) { + deferProjectedChildren(renderNode); + } } defer3dChildren(ChildrenSelectMode::Positive, zTranslatedNodes); } diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h index 976f41323efa..00df8b0951e6 100644 --- a/libs/hwui/OpReorderer.h +++ b/libs/hwui/OpReorderer.h @@ -124,9 +124,6 @@ public: uint32_t viewportWidth, uint32_t viewportHeight, const std::vector< sp >& nodes, const Vector3& lightCenter); - OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList, - const Vector3& lightCenter); - virtual ~OpReorderer() {} /** @@ -202,15 +199,17 @@ private: return BakedOpState::tryConstruct(mAllocator, *mCanvasState.currentSnapshot(), recordedOp); } - // should always be surrounded by a save/restore pair + // should always be surrounded by a save/restore pair, and not called if DisplayList is null void deferNodePropsAndOps(RenderNode& node); + template + void defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes); + void deferShadow(const RenderNodeOp& casterOp); - void deferDisplayList(const DisplayList& displayList); + void deferProjectedChildren(const RenderNode& renderNode); - template - void defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes); + void deferNodeOps(const RenderNode& renderNode); void deferRenderNodeOp(const RenderNodeOp& op); diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h index 127dca5be440..b4a201ed2374 100644 --- a/libs/hwui/RecordedOp.h +++ b/libs/hwui/RecordedOp.h @@ -97,7 +97,17 @@ struct RenderNodeOp : RecordedOp { RenderNodeOp(BASE_PARAMS_PAINTLESS, RenderNode* renderNode) : SUPER_PAINTLESS(RenderNodeOp) , renderNode(renderNode) {} - RenderNode * renderNode; // not const, since drawing modifies it (somehow...) + RenderNode * renderNode; // not const, since drawing modifies it + + /** + * Holds the transformation between the projection surface ViewGroup and this RenderNode + * drawing instance. Represents any translations / transformations done within the drawing of + * the compositing ancestor ViewGroup's draw, before the draw of the View represented by this + * DisplayList draw instance. + * + * Note: doesn't include transformation within the RenderNode, or its properties. + */ + Matrix4 transformFromCompositingAncestor; bool skipInOrderDraw = false; }; diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 3f24f44e52da..ae690fdef4c7 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -487,7 +487,7 @@ void RenderNode::prepareSubTree(TreeInfo& info, bool functorsNeedLayer, DisplayL info.damageAccumulator->pushTransform(&op->localMatrix); bool childFunctorsNeedLayer = functorsNeedLayer; // TODO! || op->mRecordedWithPotentialStencilClip; #else - info.damageAccumulator->pushTransform(&op->mTransformFromParent); + info.damageAccumulator->pushTransform(&op->localMatrix); bool childFunctorsNeedLayer = functorsNeedLayer // Recorded with non-rect clip, or canvas-rotated by parent || op->mRecordedWithPotentialStencilClip; @@ -658,7 +658,6 @@ void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) * which are flagged to not draw in the standard draw loop. */ void RenderNode::computeOrdering() { -#if !HWUI_NEW_OPS ATRACE_CALL(); mProjectedNodes.clear(); @@ -666,43 +665,41 @@ void RenderNode::computeOrdering() { // transform properties are applied correctly to top level children if (mDisplayList == nullptr) return; for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) { - DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i]; + renderNodeOp_t* childOp = mDisplayList->getChildren()[i]; childOp->renderNode->computeOrderingImpl(childOp, &mProjectedNodes, &mat4::identity()); } -#endif } void RenderNode::computeOrderingImpl( - DrawRenderNodeOp* opState, - std::vector* compositedChildrenOfProjectionSurface, + renderNodeOp_t* opState, + std::vector* compositedChildrenOfProjectionSurface, const mat4* transformFromProjectionSurface) { -#if !HWUI_NEW_OPS mProjectedNodes.clear(); if (mDisplayList == nullptr || mDisplayList->isEmpty()) return; // TODO: should avoid this calculation in most cases // TODO: just calculate single matrix, down to all leaf composited elements Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface); - localTransformFromProjectionSurface.multiply(opState->mTransformFromParent); + localTransformFromProjectionSurface.multiply(opState->localMatrix); if (properties().getProjectBackwards()) { // composited projectee, flag for out of order draw, save matrix, and store in proj surface - opState->mSkipInOrderDraw = true; - opState->mTransformFromCompositingAncestor = localTransformFromProjectionSurface; + opState->skipInOrderDraw = true; + opState->transformFromCompositingAncestor = localTransformFromProjectionSurface; compositedChildrenOfProjectionSurface->push_back(opState); } else { // standard in order draw - opState->mSkipInOrderDraw = false; + opState->skipInOrderDraw = false; } if (mDisplayList->getChildren().size() > 0) { const bool isProjectionReceiver = mDisplayList->projectionReceiveIndex >= 0; bool haveAppliedPropertiesToProjection = false; for (unsigned int i = 0; i < mDisplayList->getChildren().size(); i++) { - DrawRenderNodeOp* childOp = mDisplayList->getChildren()[i]; + renderNodeOp_t* childOp = mDisplayList->getChildren()[i]; RenderNode* child = childOp->renderNode; - std::vector* projectionChildren = nullptr; + std::vector* projectionChildren = nullptr; const mat4* projectionTransform = nullptr; if (isProjectionReceiver && !child->properties().getProjectBackwards()) { // if receiving projections, collect projecting descendant @@ -723,7 +720,6 @@ void RenderNode::computeOrderingImpl( child->computeOrderingImpl(childOp, projectionChildren, projectionTransform); } } -#endif } class DeferOperationHandler { @@ -793,10 +789,10 @@ void RenderNode::buildZSortedChildList(const DisplayList::Chunk& chunk, if (!MathUtils::isZero(childZ) && chunk.reorderChildren) { zTranslatedNodes.push_back(ZDrawRenderNodeOpPair(childZ, childOp)); - childOp->mSkipInOrderDraw = true; + childOp->skipInOrderDraw = true; } else if (!child->properties().getProjectBackwards()) { // regular, in order drawing DisplayList - childOp->mSkipInOrderDraw = false; + childOp->skipInOrderDraw = false; } } @@ -913,7 +909,7 @@ void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode, // attempt to render the shadow if the caster about to be drawn is its caster, // OR if its caster's Z value is similar to the previous potential caster if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) { - caster->issueDrawShadowOperation(casterOp->mTransformFromParent, handler); + caster->issueDrawShadowOperation(casterOp->localMatrix, handler); lastCasterZ = casterZ; // must do this even if current caster not casting a shadow shadowIndex++; @@ -927,10 +923,10 @@ void RenderNode::issueOperationsOf3dChildren(ChildrenSelectMode mode, DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value; - renderer.concatMatrix(childOp->mTransformFromParent); - childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone + renderer.concatMatrix(childOp->localMatrix); + childOp->skipInOrderDraw = false; // this is horrible, I'm so sorry everyone handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds()); - childOp->mSkipInOrderDraw = true; + childOp->skipInOrderDraw = true; renderer.restoreToCount(restoreTo); drawIndex++; @@ -967,14 +963,14 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& // draw projected nodes for (size_t i = 0; i < mProjectedNodes.size(); i++) { - DrawRenderNodeOp* childOp = mProjectedNodes[i]; + renderNodeOp_t* childOp = mProjectedNodes[i]; // matrix save, concat, and restore can be done safely without allocating operations int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); - renderer.concatMatrix(childOp->mTransformFromCompositingAncestor); - childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone + renderer.concatMatrix(childOp->transformFromCompositingAncestor); + childOp->skipInOrderDraw = false; // this is horrible, I'm so sorry everyone handler(childOp, renderer.getSaveCount() - 1, properties().getClipToBounds()); - childOp->mSkipInOrderDraw = true; + childOp->skipInOrderDraw = true; renderer.restoreToCount(restoreTo); } diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 83d1b5888b64..b6f50b111ab5 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -51,20 +51,22 @@ class OpReorderer; class Rect; class SkiaShader; - #if HWUI_NEW_OPS class OffscreenBuffer; +struct RenderNodeOp; typedef OffscreenBuffer layer_t; +typedef RenderNodeOp renderNodeOp_t; #else class Layer; typedef Layer layer_t; +typedef DrawRenderNodeOp renderNodeOp_t; #endif class ClipRectOp; +class DrawRenderNodeOp; class SaveLayerOp; class SaveOp; class RestoreToCountOp; -class DrawRenderNodeOp; class TreeInfo; namespace proto { @@ -85,6 +87,7 @@ class RenderNode; */ class RenderNode : public VirtualLightRefBase { friend class TestUtils; // allow TestUtils to access syncDisplayList / syncProperties +friend class OpReorderer; public: enum DirtyPropertyMask { GENERIC = 1 << 1, @@ -221,8 +224,8 @@ private: PositiveZChildren }; - void computeOrderingImpl(DrawRenderNodeOp* opState, - std::vector* compositedChildrenOfProjectionSurface, + void computeOrderingImpl(renderNodeOp_t* opState, + std::vector* compositedChildrenOfProjectionSurface, const mat4* transformFromProjectionSurface); template @@ -305,7 +308,7 @@ private: */ // for projection surfaces, contains a list of all children items - std::vector mProjectedNodes; + std::vector mProjectedNodes; // How many references our parent(s) have to us. Typically this should alternate // between 2 and 1 (when a staging push happens we inc first then dec) diff --git a/libs/hwui/microbench/OpReordererBench.cpp b/libs/hwui/microbench/OpReordererBench.cpp index eea0c7f9ccbe..fc5698823f09 100644 --- a/libs/hwui/microbench/OpReordererBench.cpp +++ b/libs/hwui/microbench/OpReordererBench.cpp @@ -18,10 +18,12 @@ #include "BakedOpState.h" #include "BakedOpRenderer.h" +#include "LayerUpdateQueue.h" #include "OpReorderer.h" #include "RecordedOp.h" #include "RecordingCanvas.h" #include "utils/TestUtils.h" +#include "Vector.h" #include "microbench/MicroBench.h" #include @@ -29,26 +31,38 @@ using namespace android; using namespace android::uirenderer; -auto sReorderingDisplayList = TestUtils::createDisplayList(200, 200, [](RecordingCanvas& canvas) { - SkBitmap bitmap = TestUtils::createSkBitmap(10, 10); - SkPaint paint; +const LayerUpdateQueue sEmptyLayerUpdateQueue; +const Vector3 sLightCenter = {100, 100, 100}; - // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects. - // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group. - canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); - for (int i = 0; i < 30; i++) { - canvas.translate(0, 10); - canvas.drawRect(0, 0, 10, 10, paint); - canvas.drawBitmap(bitmap, 5, 0, nullptr); - } - canvas.restore(); -}); +static std::vector> createTestNodeList() { + auto node = TestUtils::createNode(0, 0, 200, 200, + [](RenderProperties& props, RecordingCanvas& canvas) { + SkBitmap bitmap = TestUtils::createSkBitmap(10, 10); + SkPaint paint; + + // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects. + // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group. + canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + for (int i = 0; i < 30; i++) { + canvas.translate(0, 10); + canvas.drawRect(0, 0, 10, 10, paint); + canvas.drawBitmap(bitmap, 5, 0, nullptr); + } + canvas.restore(); + }); + TestUtils::syncHierarchyPropertiesAndDisplayList(node); + std::vector> vec; + vec.emplace_back(node); + return vec; +} BENCHMARK_NO_ARG(BM_OpReorderer_defer); void BM_OpReorderer_defer::Run(int iters) { + auto nodes = createTestNodeList(); StartBenchmarkTiming(); for (int i = 0; i < iters; i++) { - OpReorderer reorderer(200, 200, *sReorderingDisplayList, (Vector3) { 100, 100, 100 }); + OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, + nodes, sLightCenter); MicroBench::DoNotOptimize(&reorderer); } StopBenchmarkTiming(); @@ -57,13 +71,16 @@ void BM_OpReorderer_defer::Run(int iters) { BENCHMARK_NO_ARG(BM_OpReorderer_deferAndRender); void BM_OpReorderer_deferAndRender::Run(int iters) { TestUtils::runOnRenderThread([this, iters](renderthread::RenderThread& thread) { + auto nodes = createTestNodeList(); + BakedOpRenderer::LightInfo lightInfo = {50.0f, 128, 128 }; + RenderState& renderState = thread.renderState(); Caches& caches = Caches::getInstance(); - BakedOpRenderer::LightInfo lightInfo = { 50.0f, 128, 128 }; StartBenchmarkTiming(); for (int i = 0; i < iters; i++) { - OpReorderer reorderer(200, 200, *sReorderingDisplayList, (Vector3) { 100, 100, 100 }); + OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, + nodes, sLightCenter); BakedOpRenderer renderer(caches, renderState, true, lightInfo); reorderer.replayBakedOps(renderer); diff --git a/libs/hwui/tests/scenes/OvalAnimation.cpp b/libs/hwui/tests/scenes/OvalAnimation.cpp index 919a53d118b3..936aba184c88 100644 --- a/libs/hwui/tests/scenes/OvalAnimation.cpp +++ b/libs/hwui/tests/scenes/OvalAnimation.cpp @@ -29,17 +29,14 @@ public: sp card; void createContent(int width, int height, TestCanvas& canvas) override { canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); - canvas.insertReorderBarrier(true); - - card = TestUtils::createNode(0, 0, 200, 200, [](TestCanvas& canvas) { + card = TestUtils::createNode(0, 0, 200, 200, + [](RenderProperties& props, TestCanvas& canvas) { SkPaint paint; paint.setAntiAlias(true); paint.setColor(0xFF000000); canvas.drawOval(0, 0, 200, 200, paint); }); - canvas.drawRenderNode(card.get()); - canvas.insertReorderBarrier(false); } void doFrame(int frameNr) override { diff --git a/libs/hwui/tests/scenes/PartialDamageAnimation.cpp b/libs/hwui/tests/scenes/PartialDamageAnimation.cpp index 0fba4eb5f9ff..c31ddd1d531b 100644 --- a/libs/hwui/tests/scenes/PartialDamageAnimation.cpp +++ b/libs/hwui/tests/scenes/PartialDamageAnimation.cpp @@ -44,7 +44,7 @@ public: SkColor color = COLORS[static_cast((y / dp(116))) % 4]; sp card = TestUtils::createNode(x, y, x + dp(100), y + dp(100), - [color](TestCanvas& canvas) { + [color](RenderProperties& props, TestCanvas& canvas) { canvas.drawColor(color, SkXfermode::kSrcOver_Mode); }); canvas.drawRenderNode(card.get()); diff --git a/libs/hwui/tests/scenes/RectGridAnimation.cpp b/libs/hwui/tests/scenes/RectGridAnimation.cpp index 254f8280cb66..a1f04d67c785 100644 --- a/libs/hwui/tests/scenes/RectGridAnimation.cpp +++ b/libs/hwui/tests/scenes/RectGridAnimation.cpp @@ -34,7 +34,7 @@ public: canvas.insertReorderBarrier(true); card = TestUtils::createNode(50, 50, 250, 250, - [](TestCanvas& canvas) { + [](RenderProperties& props, TestCanvas& canvas) { canvas.drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode); SkRegion region; diff --git a/libs/hwui/tests/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/scenes/SaveLayerAnimation.cpp index c62dd19968c3..c73e97ba003c 100644 --- a/libs/hwui/tests/scenes/SaveLayerAnimation.cpp +++ b/libs/hwui/tests/scenes/SaveLayerAnimation.cpp @@ -32,7 +32,7 @@ public: canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background card = TestUtils::createNode(0, 0, 200, 200, - [](TestCanvas& canvas) { + [](RenderProperties& props, TestCanvas& canvas) { canvas.saveLayerAlpha(0, 0, 200, 200, 128, SkCanvas::kClipToLayer_SaveFlag); canvas.drawColor(0xFF00FF00, SkXfermode::kSrcOver_Mode); // outer, unclipped canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag); diff --git a/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp b/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp index 05fd08a3340a..cc15cc6d2628 100644 --- a/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp +++ b/libs/hwui/unit_tests/LayerUpdateQueueTests.cpp @@ -31,7 +31,7 @@ TEST(LayerUpdateQueue, construct) { // sync node properties, so properties() reflects correct width and height static sp createSyncedNode(uint32_t width, uint32_t height) { - sp node = TestUtils::createNode(0, 0, width, height); + sp node = TestUtils::createNode(0, 0, width, height, nullptr); TestUtils::syncHierarchyPropertiesAndDisplayList(node); return node; } diff --git a/libs/hwui/unit_tests/OpReordererTests.cpp b/libs/hwui/unit_tests/OpReordererTests.cpp index d76086c9cfcd..607329cf0683 100644 --- a/libs/hwui/unit_tests/OpReordererTests.cpp +++ b/libs/hwui/unit_tests/OpReordererTests.cpp @@ -111,25 +111,29 @@ TEST(OpReorderer, simple) { } }; - auto dl = TestUtils::createDisplayList(100, 200, [](RecordingCanvas& canvas) { + auto node = TestUtils::createNode(0, 0, 100, 200, + [](RenderProperties& props, RecordingCanvas& canvas) { SkBitmap bitmap = TestUtils::createSkBitmap(25, 25); canvas.drawRect(0, 0, 100, 200, SkPaint()); canvas.drawBitmap(bitmap, 10, 10, nullptr); }); - OpReorderer reorderer(100, 200, *dl, sLightCenter); + OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, + createSyncedNodeList(node), sLightCenter); SimpleTestRenderer renderer; reorderer.replayBakedOps(renderer); EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end } TEST(OpReorderer, simpleRejection) { - auto dl = TestUtils::createDisplayList(200, 200, [](RecordingCanvas& canvas) { + auto node = TestUtils::createNode(0, 0, 200, 200, + [](RenderProperties& props, RecordingCanvas& canvas) { canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); // intersection should be empty canvas.drawRect(0, 0, 400, 400, SkPaint()); canvas.restore(); }); - OpReorderer reorderer(200, 200, *dl, sLightCenter); + OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, + createSyncedNodeList(node), sLightCenter); FailRenderer renderer; reorderer.replayBakedOps(renderer); @@ -147,7 +151,8 @@ TEST(OpReorderer, simpleBatching) { } }; - auto dl = TestUtils::createDisplayList(200, 200, [](RecordingCanvas& canvas) { + auto node = TestUtils::createNode(0, 0, 200, 200, + [](RenderProperties& props, RecordingCanvas& canvas) { SkBitmap bitmap = TestUtils::createSkBitmap(10, 10); // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects. @@ -161,7 +166,8 @@ TEST(OpReorderer, simpleBatching) { canvas.restore(); }); - OpReorderer reorderer(200, 200, *dl, sLightCenter); + OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, + createSyncedNodeList(node), sLightCenter); SimpleBatchingTestRenderer renderer; reorderer.replayBakedOps(renderer); EXPECT_EQ(2 * LOOPS, renderer.getIndex()) @@ -179,7 +185,8 @@ TEST(OpReorderer, textStrikethroughBatching) { EXPECT_TRUE(mIndex++ < LOOPS) << "Text should be beneath all strikethrough rects"; } }; - auto dl = TestUtils::createDisplayList(200, 2000, [](RecordingCanvas& canvas) { + auto node = TestUtils::createNode(0, 0, 200, 2000, + [](RenderProperties& props, RecordingCanvas& canvas) { SkPaint textPaint; textPaint.setAntiAlias(true); textPaint.setTextSize(20); @@ -188,7 +195,8 @@ TEST(OpReorderer, textStrikethroughBatching) { TestUtils::drawTextToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1)); } }); - OpReorderer reorderer(200, 2000, *dl, sLightCenter); + OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000, + createSyncedNodeList(node), sLightCenter); TextStrikethroughTestRenderer renderer; reorderer.replayBakedOps(renderer); EXPECT_EQ(2 * LOOPS, renderer.getIndex()) @@ -214,14 +222,16 @@ TEST(OpReorderer, renderNode) { } }; - sp child = TestUtils::createNode(10, 10, 110, 110, [](RecordingCanvas& canvas) { + auto child = TestUtils::createNode(10, 10, 110, 110, + [](RenderProperties& props, RecordingCanvas& canvas) { SkPaint paint; paint.setColor(SK_ColorWHITE); canvas.drawRect(0, 0, 100, 100, paint); }); RenderNode* childPtr = child.get(); - sp parent = TestUtils::createNode(0, 0, 200, 200, [childPtr](RecordingCanvas& canvas) { + auto parent = TestUtils::createNode(0, 0, 200, 200, + [childPtr](RenderProperties& props, RecordingCanvas& canvas) { SkPaint paint; paint.setColor(SK_ColorDKGRAY); canvas.drawRect(0, 0, 200, 200, paint); @@ -249,7 +259,8 @@ TEST(OpReorderer, clipped) { } }; - sp node = TestUtils::createNode(0, 0, 200, 200, [](RecordingCanvas& canvas) { + auto node = TestUtils::createNode(0, 0, 200, 200, + [](RenderProperties& props, RecordingCanvas& canvas) { SkBitmap bitmap = TestUtils::createSkBitmap(200, 200); canvas.drawBitmap(bitmap, 0, 0, nullptr); }); @@ -291,13 +302,14 @@ TEST(OpReorderer, saveLayerSimple) { } }; - auto dl = TestUtils::createDisplayList(200, 200, [](RecordingCanvas& canvas) { + auto node = TestUtils::createNode(0, 0, 200, 200, + [](RenderProperties& props, RecordingCanvas& canvas) { canvas.saveLayerAlpha(10, 10, 190, 190, 128, SkCanvas::kClipToLayer_SaveFlag); canvas.drawRect(10, 10, 190, 190, SkPaint()); canvas.restore(); }); - - OpReorderer reorderer(200, 200, *dl, sLightCenter); + OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, + createSyncedNodeList(node), sLightCenter); SaveLayerSimpleTestRenderer renderer; reorderer.replayBakedOps(renderer); EXPECT_EQ(4, renderer.getIndex()); @@ -354,7 +366,8 @@ TEST(OpReorderer, saveLayerNested) { } }; - auto dl = TestUtils::createDisplayList(800, 800, [](RecordingCanvas& canvas) { + auto node = TestUtils::createNode(0, 0, 800, 800, + [](RenderProperties& props, RecordingCanvas& canvas) { canvas.saveLayerAlpha(0, 0, 800, 800, 128, SkCanvas::kClipToLayer_SaveFlag); { canvas.drawRect(0, 0, 800, 800, SkPaint()); @@ -367,14 +380,16 @@ TEST(OpReorderer, saveLayerNested) { canvas.restore(); }); - OpReorderer reorderer(800, 800, *dl, sLightCenter); + OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800, + createSyncedNodeList(node), sLightCenter); SaveLayerNestedTestRenderer renderer; reorderer.replayBakedOps(renderer); EXPECT_EQ(10, renderer.getIndex()); } TEST(OpReorderer, saveLayerContentRejection) { - auto dl = TestUtils::createDisplayList(200, 200, [](RecordingCanvas& canvas) { + auto node = TestUtils::createNode(0, 0, 200, 200, + [](RenderProperties& props, RecordingCanvas& canvas) { canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); canvas.clipRect(200, 200, 400, 400, SkRegion::kIntersect_Op); canvas.saveLayerAlpha(200, 200, 400, 400, 128, SkCanvas::kClipToLayer_SaveFlag); @@ -385,7 +400,8 @@ TEST(OpReorderer, saveLayerContentRejection) { canvas.restore(); canvas.restore(); }); - OpReorderer reorderer(200, 200, *dl, sLightCenter); + OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, + createSyncedNodeList(node), sLightCenter); FailRenderer renderer; // should see no ops, even within the layer, since the layer should be rejected @@ -424,7 +440,7 @@ RENDERTHREAD_TEST(OpReorderer, hwLayerSimple) { } }; - sp node = TestUtils::createNode(10, 10, 110, 110, + auto node = TestUtils::createNode(10, 10, 110, 110, [](RenderProperties& props, RecordingCanvas& canvas) { props.mutateLayerProperties().setType(LayerType::RenderLayer); SkPaint paint; @@ -562,7 +578,7 @@ static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) } static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder, float z) { auto node = TestUtils::createNode(0, 0, 100, 100, - [expectedDrawOrder](RecordingCanvas& canvas) { + [expectedDrawOrder](RenderProperties& props, RecordingCanvas& canvas) { drawOrderedRect(&canvas, expectedDrawOrder); }); node->mutateStagingProperties().setTranslationZ(z); @@ -579,7 +595,7 @@ TEST(OpReorderer, zReorder) { }; auto parent = TestUtils::createNode(0, 0, 100, 100, - [](RecordingCanvas& canvas) { + [](RenderProperties& props, RecordingCanvas& canvas) { drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder drawOrderedRect(&canvas, 1); canvas.insertReorderBarrier(true); @@ -600,10 +616,93 @@ TEST(OpReorderer, zReorder) { EXPECT_EQ(10, renderer.getIndex()); }; +TEST(OpReorderer, projectionReorder) { + static const int scrollX = 5; + static const int scrollY = 10; + class ProjectionReorderTestRenderer : public TestRendererBase { + public: + void onRectOp(const RectOp& op, const BakedOpState& state) override { + const int index = mIndex++; + + Matrix4 expectedMatrix; + switch (index) { + case 0: + EXPECT_EQ(Rect(100, 100), op.unmappedBounds); + EXPECT_EQ(SK_ColorWHITE, op.paint->getColor()); + expectedMatrix.loadIdentity(); + break; + case 1: + EXPECT_EQ(Rect(-10, -10, 60, 60), op.unmappedBounds); + EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor()); + expectedMatrix.loadTranslate(50, 50, 0); // TODO: should scroll be respected here? + break; + case 2: + EXPECT_EQ(Rect(100, 50), op.unmappedBounds); + EXPECT_EQ(SK_ColorBLUE, op.paint->getColor()); + expectedMatrix.loadTranslate(-scrollX, 50 - scrollY, 0); + break; + default: + ADD_FAILURE(); + } + EXPECT_MATRIX_APPROX_EQ(expectedMatrix, state.computedState.transform); + } + }; + + /** + * Construct a tree of nodes, where the root (A) has a receiver background (B), and a child (C) + * with a projecting child (P) of its own. P would normally draw between B and C's "background" + * draw, but because it is projected backwards, it's drawn in between B and C. + * + * The parent is scrolled by scrollX/scrollY, but this does not affect the background + * (which isn't affected by scroll). + */ + auto receiverBackground = TestUtils::createNode(0, 0, 100, 100, + [](RenderProperties& properties, RecordingCanvas& canvas) { + properties.setProjectionReceiver(true); + // scroll doesn't apply to background, so undone via translationX/Y + // NOTE: translationX/Y only! no other transform properties may be set for a proj receiver! + properties.setTranslationX(scrollX); + properties.setTranslationY(scrollY); + + SkPaint paint; + paint.setColor(SK_ColorWHITE); + canvas.drawRect(0, 0, 100, 100, paint); + }); + auto projectingRipple = TestUtils::createNode(50, 0, 100, 50, + [](RenderProperties& properties, RecordingCanvas& canvas) { + properties.setProjectBackwards(true); + properties.setClipToBounds(false); + SkPaint paint; + paint.setColor(SK_ColorDKGRAY); + canvas.drawRect(-10, -10, 60, 60, paint); + }); + auto child = TestUtils::createNode(0, 50, 100, 100, + [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) { + SkPaint paint; + paint.setColor(SK_ColorBLUE); + canvas.drawRect(0, 0, 100, 50, paint); + canvas.drawRenderNode(projectingRipple.get()); + }); + auto parent = TestUtils::createNode(0, 0, 100, 100, + [&receiverBackground, &child](RenderProperties& properties, RecordingCanvas& canvas) { + canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally) + canvas.drawRenderNode(receiverBackground.get()); + canvas.drawRenderNode(child.get()); + canvas.restore(); + }); + + OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100, + createSyncedNodeList(parent), sLightCenter); + ProjectionReorderTestRenderer renderer; + reorderer.replayBakedOps(renderer); + EXPECT_EQ(3, renderer.getIndex()); +} + // creates a 100x100 shadow casting node with provided translationZ static sp createWhiteRectShadowCaster(float translationZ) { return TestUtils::createNode(0, 0, 100, 100, - [translationZ] (RenderProperties& properties, RecordingCanvas& canvas) { + [translationZ](RenderProperties& properties, RecordingCanvas& canvas) { properties.setTranslationZ(translationZ); properties.mutableOutline().setRoundRect(0, 0, 100, 100, 0.0f, 1.0f); SkPaint paint; @@ -630,8 +729,8 @@ TEST(OpReorderer, shadow) { } }; - sp parent = TestUtils::createNode(0, 0, 200, 200, - [] (RecordingCanvas& canvas) { + auto parent = TestUtils::createNode(0, 0, 200, 200, + [](RenderProperties& props, RecordingCanvas& canvas) { canvas.insertReorderBarrier(true); canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); }); @@ -666,8 +765,8 @@ TEST(OpReorderer, shadowSaveLayer) { } }; - sp parent = TestUtils::createNode(0, 0, 200, 200, - [] (RecordingCanvas& canvas) { + auto parent = TestUtils::createNode(0, 0, 200, 200, + [](RenderProperties& props, RecordingCanvas& canvas) { // save/restore outside of reorderBarrier, so they don't get moved out of place canvas.translate(20, 10); int count = canvas.saveLayerAlpha(30, 50, 130, 150, 128, SkCanvas::kClipToLayer_SaveFlag); @@ -706,7 +805,7 @@ RENDERTHREAD_TEST(OpReorderer, shadowHwLayer) { } }; - sp parent = TestUtils::createNode(50, 60, 150, 160, + auto parent = TestUtils::createNode(50, 60, 150, 160, [](RenderProperties& props, RecordingCanvas& canvas) { props.mutateLayerProperties().setType(LayerType::RenderLayer); canvas.insertReorderBarrier(true); @@ -749,8 +848,8 @@ TEST(OpReorderer, shadowLayering) { EXPECT_TRUE(index == 2 || index == 3); } }; - sp parent = TestUtils::createNode(0, 0, 200, 200, - [] (RecordingCanvas& canvas) { + auto parent = TestUtils::createNode(0, 0, 200, 200, + [](RenderProperties& props, RecordingCanvas& canvas) { canvas.insertReorderBarrier(true); canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get()); canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get()); @@ -954,7 +1053,7 @@ TEST(OpReorderer, renderPropSaveLayerAlphaRotate) { SaveLayerAlphaData observedData; testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) { // Translate and rotate the view so that the only visible part is the top left corner of - // the view. It will form an isoceles right triangle with a long side length of 200 at the + // the view. It will form an isosceles right triangle with a long side length of 200 at the // bottom of the viewport. properties.setTranslationX(100); properties.setTranslationY(100); diff --git a/libs/hwui/unit_tests/RecordingCanvasTests.cpp b/libs/hwui/unit_tests/RecordingCanvasTests.cpp index c23d47e3fab4..ceb61af764ad 100644 --- a/libs/hwui/unit_tests/RecordingCanvasTests.cpp +++ b/libs/hwui/unit_tests/RecordingCanvasTests.cpp @@ -313,6 +313,49 @@ TEST(RecordingCanvas, saveLayer_rotateClipped) { EXPECT_EQ(3, count); } +TEST(RecordingCanvas, drawRenderNode_projection) { + sp background = TestUtils::createNode(50, 50, 150, 150, + [](RenderProperties& props, RecordingCanvas& canvas) { + SkPaint paint; + paint.setColor(SK_ColorWHITE); + canvas.drawRect(0, 0, 100, 100, paint); + }); + { + background->mutateStagingProperties().setProjectionReceiver(false); + + // NO RECEIVER PRESENT + auto dl = TestUtils::createDisplayList(200, 200, + [&background](RecordingCanvas& canvas) { + canvas.drawRect(0, 0, 100, 100, SkPaint()); + canvas.drawRenderNode(background.get()); + canvas.drawRect(0, 0, 100, 100, SkPaint()); + }); + EXPECT_EQ(-1, dl->projectionReceiveIndex) + << "no projection receiver should have been observed"; + } + { + background->mutateStagingProperties().setProjectionReceiver(true); + + // RECEIVER PRESENT + auto dl = TestUtils::createDisplayList(200, 200, + [&background](RecordingCanvas& canvas) { + canvas.drawRect(0, 0, 100, 100, SkPaint()); + canvas.drawRenderNode(background.get()); + canvas.drawRect(0, 0, 100, 100, SkPaint()); + }); + + ASSERT_EQ(3u, dl->getOps().size()) << "Must be three ops"; + auto op = dl->getOps()[1]; + EXPECT_EQ(RecordedOpId::RenderNodeOp, op->opId); + EXPECT_EQ(1, dl->projectionReceiveIndex) + << "correct projection receiver not identified"; + + // verify the behavior works even though projection receiver hasn't been sync'd yet + EXPECT_TRUE(background->stagingProperties().isProjectionReceiver()); + EXPECT_FALSE(background->properties().isProjectionReceiver()); + } +} + TEST(RecordingCanvas, insertReorderBarrier) { auto dl = TestUtils::createDisplayList(200, 200, [](RecordingCanvas& canvas) { canvas.drawRect(0, 0, 400, 400, SkPaint()); diff --git a/libs/hwui/utils/TestUtils.h b/libs/hwui/utils/TestUtils.h index f9fa242ec833..3a886f91833d 100644 --- a/libs/hwui/utils/TestUtils.h +++ b/libs/hwui/utils/TestUtils.h @@ -121,7 +121,7 @@ public: } static sp createNode(int left, int top, int right, int bottom, - std::function setup = nullptr) { + std::function setup) { #if HWUI_NULL_GPU // if RenderNodes are being sync'd/used, device info will be needed, since // DeviceInfo::maxTextureSize() affects layer property @@ -140,22 +140,6 @@ public: return node; } - static sp createNode(int left, int top, int right, int bottom, - std::function setup) { - return createNode(left, top, right, bottom, - [&setup](RenderProperties& props, TestCanvas& canvas) { - setup(props); - }); - } - - static sp createNode(int left, int top, int right, int bottom, - std::function setup) { - return createNode(left, top, right, bottom, - [&setup](RenderProperties& props, TestCanvas& canvas) { - setup(canvas); - }); - } - static void recordNode(RenderNode& node, std::function contentCallback) { TestCanvas canvas(node.stagingProperties().getWidth(), @@ -164,6 +148,13 @@ public: node.setStagingDisplayList(canvas.finishRecording()); } + /** + * Forces a sync of a tree of RenderNode, such that every descendant will have its staging + * properties and DisplayList moved to the render copies. + * + * Note: does not check dirtiness bits, so any non-staging DisplayLists will be discarded. + * For this reason, this should generally only be called once on a tree. + */ static void syncHierarchyPropertiesAndDisplayList(sp& node) { syncHierarchyPropertiesAndDisplayListImpl(node.get()); } -- 2.11.0