Loading libs/hwui/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -181,6 +181,7 @@ hwui_c_includes += \ external/skia/include/private \ external/skia/src/core \ external/skia/src/effects \ external/skia/src/image \ external/harfbuzz_ng/src \ external/freetype/include Loading libs/hwui/TreeInfo.h +3 −0 Original line number Diff line number Diff line Loading @@ -116,6 +116,9 @@ public: bool canDrawThisFrame = true; } out; // This flag helps to disable projection for receiver nodes that do not have any backward // projected children. bool hasBackwardProjectedNodes = false; // TODO: Damage calculations }; Loading libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +54 −45 Original line number Diff line number Diff line Loading @@ -24,8 +24,41 @@ namespace android { namespace uirenderer { namespace skiapipeline { void RenderNodeDrawable::drawBackwardsProjectedNodes(SkCanvas* canvas, const SkiaDisplayList& displayList, int nestLevel) { LOG_ALWAYS_FATAL_IF(0 == nestLevel && !displayList.mProjectionReceiver); for (auto& child : displayList.mChildNodes) { const RenderProperties& childProperties = child.getNodeProperties(); //immediate children cannot be projected on their parent if (childProperties.getProjectBackwards() && nestLevel > 0) { SkAutoCanvasRestore acr2(canvas, true); //Apply recorded matrix, which is a total matrix saved at recording time to avoid //replaying all DL commands. canvas->concat(child.getRecordedMatrix()); child.drawContent(canvas); } //skip walking sub-nodes if current display list contains a receiver with exception of //level 0, which is a known receiver if (0 == nestLevel || !displayList.containsProjectionReceiver()) { SkAutoCanvasRestore acr(canvas, true); SkMatrix nodeMatrix; mat4 hwuiMatrix(child.getRecordedMatrix()); auto childNode = child.getRenderNode(); childNode->applyViewPropertyTransforms(hwuiMatrix); hwuiMatrix.copyTo(nodeMatrix); canvas->concat(nodeMatrix); SkiaDisplayList* childDisplayList = static_cast<SkiaDisplayList*>( (const_cast<DisplayList*>(childNode->getDisplayList()))); if (childDisplayList) { drawBackwardsProjectedNodes(canvas, *childDisplayList, nestLevel+1); } } } } static void clipOutline(const Outline& outline, SkCanvas* canvas, const SkRect* pendingClip) { SkASSERT(outline.willClip()); Rect possibleRect; float radius; LOG_ALWAYS_FATAL_IF(!outline.getAsRoundRect(&possibleRect, &radius), Loading Loading @@ -74,53 +107,25 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) { SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList(); SkAutoCanvasRestore acr(canvas, true); const RenderProperties& properties = this->getNodeProperties(); if (displayList->mIsProjectionReceiver) { // this node is a projection receiver. We will gather the projected nodes as we draw our // children, and then draw them on top of this node's content. std::vector<ProjectedChild> newList; for (auto& child : displayList->mChildNodes) { // our direct children are not supposed to project into us (nodes project to, at the // nearest, their grandparents). So we "delay" the list's activation one level by // passing it into mNextProjectedChildrenTarget rather than mProjectedChildrenTarget. child.mProjectedChildrenTarget = mNextProjectedChildrenTarget; child.mNextProjectedChildrenTarget = &newList; } // draw ourselves and our children. As a side effect, this will add projected nodes to // newList. this->drawContent(canvas); bool willClip = properties.getOutline().willClip(); if (willClip) { canvas->save(); clipOutline(properties.getOutline(), canvas, nullptr); } // draw the collected projected nodes for (auto& projectedChild : newList) { canvas->setMatrix(projectedChild.matrix); projectedChild.node->drawContent(canvas); } if (willClip) { canvas->restore(); //pass this outline to the children that may clip backward projected nodes displayList->mProjectedOutline = displayList->containsProjectionReceiver() ? &properties.getOutline() : nullptr; if (!properties.getProjectBackwards()) { drawContent(canvas); if (mProjectedDisplayList) { acr.restore(); //draw projected children using parent matrix LOG_ALWAYS_FATAL_IF(!mProjectedDisplayList->mProjectedOutline); const bool shouldClip = mProjectedDisplayList->mProjectedOutline->getPath(); SkAutoCanvasRestore acr2(canvas, shouldClip); canvas->setMatrix(mProjectedDisplayList->mProjectedReceiverParentMatrix); if (shouldClip) { clipOutline(*mProjectedDisplayList->mProjectedOutline, canvas, nullptr); } } else { if (properties.getProjectBackwards() && mProjectedChildrenTarget) { // We are supposed to project this node, so add it to the list and do not actually draw // yet. It will be drawn by its projection receiver. mProjectedChildrenTarget->push_back({ this, canvas->getTotalMatrix() }); return; } for (auto& child : displayList->mChildNodes) { // storing these values in the nodes themselves is a bit ugly; they should "really" be // function parameters, but we have to go through the preexisting draw() method and // therefore cannot add additional parameters to it child.mProjectedChildrenTarget = mNextProjectedChildrenTarget; child.mNextProjectedChildrenTarget = mNextProjectedChildrenTarget; drawBackwardsProjectedNodes(canvas, *mProjectedDisplayList); } this->drawContent(canvas); } mProjectedChildrenTarget = nullptr; mNextProjectedChildrenTarget = nullptr; displayList->mProjectedOutline = nullptr; } static bool layerNeedsPaint(const LayerProperties& properties, Loading Loading @@ -148,6 +153,10 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { if (mComposeLayer) { setViewProperties(properties, canvas, &alphaMultiplier); } SkiaDisplayList* displayList = (SkiaDisplayList*)mRenderNode->getDisplayList(); if (displayList->containsProjectionReceiver()) { displayList->mProjectedReceiverParentMatrix = canvas->getTotalMatrix(); } //TODO should we let the bound of the drawable do this for us? const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight()); Loading libs/hwui/pipeline/skia/RenderNodeDrawable.h +25 −25 Original line number Diff line number Diff line Loading @@ -29,24 +29,14 @@ class RenderProperties; namespace skiapipeline { class SkiaDisplayList; /** * This drawable wraps a RenderNode and enables it to be recorded into a list * of Skia drawing commands. */ class RenderNodeDrawable : public SkDrawable { public: /** * This struct contains a pointer to a node that is to be * projected into the drawing order of its closest ancestor * (excluding its parent) that is marked as a projection * receiver. The matrix is used to ensure that the node is * drawn with same matrix as it would have prior to projection. */ struct ProjectedChild { const RenderNodeDrawable* node; const SkMatrix matrix; }; /** * Creates a new RenderNodeDrawable backed by a render node. * Loading Loading @@ -86,6 +76,14 @@ public: */ const SkMatrix& getRecordedMatrix() const { return mRecordedTransform; } /** * Sets a pointer to a display list of the parent render node. The display list is used when * drawing backward projected nodes, when this node is a projection receiver. */ void setProjectedDisplayList(SkiaDisplayList* projectedDisplayList) { mProjectedDisplayList = projectedDisplayList; } protected: /* * Return the (conservative) bounds of what the drawable will draw. Loading @@ -107,6 +105,16 @@ private: */ sp<RenderNode> mRenderNode; /** * Walks recursively the display list and draws the content of backward projected nodes. * * @param canvas used to draw the backward projected nodes * @param displayList is a display list that contains a projection receiver * @param nestLevel should be always 0. Used to track how far we are from the receiver. */ void drawBackwardsProjectedNodes(SkCanvas* canvas, const SkiaDisplayList& displayList, int nestLevel = 0); /** * Applies the rendering properties of a view onto a SkCanvas. */ Loading @@ -126,19 +134,6 @@ private: */ const bool mComposeLayer; /** * List to which we will add any projected children we encounter while walking our descendents. * This pointer is valid only while the node (including its children) is actively being drawn. */ std::vector<ProjectedChild>* mProjectedChildrenTarget = nullptr; /** * The value to which we should set our children's mProjectedChildrenTarget. We use two pointers * (mProjectedChildrenTarget and mNextProjectedChildrenTarget) because we need to skip over our * parent when looking for a projection receiver. */ std::vector<ProjectedChild>* mNextProjectedChildrenTarget = nullptr; /* * True if the render node is in a reordering section */ Loading @@ -148,6 +143,11 @@ private: * Draw the content into a canvas, depending on the render node layer type and mComposeLayer. */ void drawContent(SkCanvas* canvas) const; /* * display list that is searched for any render nodes with getProjectBackwards==true */ SkiaDisplayList* mProjectedDisplayList = nullptr; }; }; // namespace skiapipeline Loading libs/hwui/pipeline/skia/SkiaDisplayList.cpp +18 −3 Original line number Diff line number Diff line Loading @@ -64,16 +64,31 @@ bool SkiaDisplayList::prepareListAndChildren(TreeInfo& info, bool functorsNeedLa info.canvasContext.unpinImages(); } bool hasBackwardProjectedNodesHere = false; bool hasBackwardProjectedNodesSubtree= false; for (auto& child : mChildNodes) { hasBackwardProjectedNodesHere |= child.getNodeProperties().getProjectBackwards(); RenderNode* childNode = child.getRenderNode(); Matrix4 mat4(child.getRecordedMatrix()); info.damageAccumulator->pushTransform(&mat4); // TODO: a layer is needed if the canvas is rotated or has a non-rect clip bool childFunctorsNeedLayer = functorsNeedLayer; childFn(childNode, info, childFunctorsNeedLayer); info.hasBackwardProjectedNodes = false; childFn(childNode, info, functorsNeedLayer); hasBackwardProjectedNodesSubtree |= info.hasBackwardProjectedNodes; info.damageAccumulator->popTransform(); } //The purpose of next block of code is to reset projected display list if there are no //backward projected nodes. This speeds up drawing, by avoiding an extra walk of the tree if (mProjectionReceiver) { mProjectionReceiver->setProjectedDisplayList(hasBackwardProjectedNodesSubtree ? this : nullptr); info.hasBackwardProjectedNodes = hasBackwardProjectedNodesHere; } else { info.hasBackwardProjectedNodes = hasBackwardProjectedNodesSubtree || hasBackwardProjectedNodesHere; } bool isDirty = false; for (auto& vectorDrawable : mVectorDrawables) { // If any vector drawable in the display list needs update, damage the node. Loading @@ -86,7 +101,7 @@ bool SkiaDisplayList::prepareListAndChildren(TreeInfo& info, bool functorsNeedLa } void SkiaDisplayList::reset(SkRect bounds) { mIsProjectionReceiver = false; mProjectionReceiver = nullptr; mDrawable->reset(bounds); Loading Loading
libs/hwui/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -181,6 +181,7 @@ hwui_c_includes += \ external/skia/include/private \ external/skia/src/core \ external/skia/src/effects \ external/skia/src/image \ external/harfbuzz_ng/src \ external/freetype/include Loading
libs/hwui/TreeInfo.h +3 −0 Original line number Diff line number Diff line Loading @@ -116,6 +116,9 @@ public: bool canDrawThisFrame = true; } out; // This flag helps to disable projection for receiver nodes that do not have any backward // projected children. bool hasBackwardProjectedNodes = false; // TODO: Damage calculations }; Loading
libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +54 −45 Original line number Diff line number Diff line Loading @@ -24,8 +24,41 @@ namespace android { namespace uirenderer { namespace skiapipeline { void RenderNodeDrawable::drawBackwardsProjectedNodes(SkCanvas* canvas, const SkiaDisplayList& displayList, int nestLevel) { LOG_ALWAYS_FATAL_IF(0 == nestLevel && !displayList.mProjectionReceiver); for (auto& child : displayList.mChildNodes) { const RenderProperties& childProperties = child.getNodeProperties(); //immediate children cannot be projected on their parent if (childProperties.getProjectBackwards() && nestLevel > 0) { SkAutoCanvasRestore acr2(canvas, true); //Apply recorded matrix, which is a total matrix saved at recording time to avoid //replaying all DL commands. canvas->concat(child.getRecordedMatrix()); child.drawContent(canvas); } //skip walking sub-nodes if current display list contains a receiver with exception of //level 0, which is a known receiver if (0 == nestLevel || !displayList.containsProjectionReceiver()) { SkAutoCanvasRestore acr(canvas, true); SkMatrix nodeMatrix; mat4 hwuiMatrix(child.getRecordedMatrix()); auto childNode = child.getRenderNode(); childNode->applyViewPropertyTransforms(hwuiMatrix); hwuiMatrix.copyTo(nodeMatrix); canvas->concat(nodeMatrix); SkiaDisplayList* childDisplayList = static_cast<SkiaDisplayList*>( (const_cast<DisplayList*>(childNode->getDisplayList()))); if (childDisplayList) { drawBackwardsProjectedNodes(canvas, *childDisplayList, nestLevel+1); } } } } static void clipOutline(const Outline& outline, SkCanvas* canvas, const SkRect* pendingClip) { SkASSERT(outline.willClip()); Rect possibleRect; float radius; LOG_ALWAYS_FATAL_IF(!outline.getAsRoundRect(&possibleRect, &radius), Loading Loading @@ -74,53 +107,25 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) { SkiaDisplayList* displayList = (SkiaDisplayList*)renderNode->getDisplayList(); SkAutoCanvasRestore acr(canvas, true); const RenderProperties& properties = this->getNodeProperties(); if (displayList->mIsProjectionReceiver) { // this node is a projection receiver. We will gather the projected nodes as we draw our // children, and then draw them on top of this node's content. std::vector<ProjectedChild> newList; for (auto& child : displayList->mChildNodes) { // our direct children are not supposed to project into us (nodes project to, at the // nearest, their grandparents). So we "delay" the list's activation one level by // passing it into mNextProjectedChildrenTarget rather than mProjectedChildrenTarget. child.mProjectedChildrenTarget = mNextProjectedChildrenTarget; child.mNextProjectedChildrenTarget = &newList; } // draw ourselves and our children. As a side effect, this will add projected nodes to // newList. this->drawContent(canvas); bool willClip = properties.getOutline().willClip(); if (willClip) { canvas->save(); clipOutline(properties.getOutline(), canvas, nullptr); } // draw the collected projected nodes for (auto& projectedChild : newList) { canvas->setMatrix(projectedChild.matrix); projectedChild.node->drawContent(canvas); } if (willClip) { canvas->restore(); //pass this outline to the children that may clip backward projected nodes displayList->mProjectedOutline = displayList->containsProjectionReceiver() ? &properties.getOutline() : nullptr; if (!properties.getProjectBackwards()) { drawContent(canvas); if (mProjectedDisplayList) { acr.restore(); //draw projected children using parent matrix LOG_ALWAYS_FATAL_IF(!mProjectedDisplayList->mProjectedOutline); const bool shouldClip = mProjectedDisplayList->mProjectedOutline->getPath(); SkAutoCanvasRestore acr2(canvas, shouldClip); canvas->setMatrix(mProjectedDisplayList->mProjectedReceiverParentMatrix); if (shouldClip) { clipOutline(*mProjectedDisplayList->mProjectedOutline, canvas, nullptr); } } else { if (properties.getProjectBackwards() && mProjectedChildrenTarget) { // We are supposed to project this node, so add it to the list and do not actually draw // yet. It will be drawn by its projection receiver. mProjectedChildrenTarget->push_back({ this, canvas->getTotalMatrix() }); return; } for (auto& child : displayList->mChildNodes) { // storing these values in the nodes themselves is a bit ugly; they should "really" be // function parameters, but we have to go through the preexisting draw() method and // therefore cannot add additional parameters to it child.mProjectedChildrenTarget = mNextProjectedChildrenTarget; child.mNextProjectedChildrenTarget = mNextProjectedChildrenTarget; drawBackwardsProjectedNodes(canvas, *mProjectedDisplayList); } this->drawContent(canvas); } mProjectedChildrenTarget = nullptr; mNextProjectedChildrenTarget = nullptr; displayList->mProjectedOutline = nullptr; } static bool layerNeedsPaint(const LayerProperties& properties, Loading Loading @@ -148,6 +153,10 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { if (mComposeLayer) { setViewProperties(properties, canvas, &alphaMultiplier); } SkiaDisplayList* displayList = (SkiaDisplayList*)mRenderNode->getDisplayList(); if (displayList->containsProjectionReceiver()) { displayList->mProjectedReceiverParentMatrix = canvas->getTotalMatrix(); } //TODO should we let the bound of the drawable do this for us? const SkRect bounds = SkRect::MakeWH(properties.getWidth(), properties.getHeight()); Loading
libs/hwui/pipeline/skia/RenderNodeDrawable.h +25 −25 Original line number Diff line number Diff line Loading @@ -29,24 +29,14 @@ class RenderProperties; namespace skiapipeline { class SkiaDisplayList; /** * This drawable wraps a RenderNode and enables it to be recorded into a list * of Skia drawing commands. */ class RenderNodeDrawable : public SkDrawable { public: /** * This struct contains a pointer to a node that is to be * projected into the drawing order of its closest ancestor * (excluding its parent) that is marked as a projection * receiver. The matrix is used to ensure that the node is * drawn with same matrix as it would have prior to projection. */ struct ProjectedChild { const RenderNodeDrawable* node; const SkMatrix matrix; }; /** * Creates a new RenderNodeDrawable backed by a render node. * Loading Loading @@ -86,6 +76,14 @@ public: */ const SkMatrix& getRecordedMatrix() const { return mRecordedTransform; } /** * Sets a pointer to a display list of the parent render node. The display list is used when * drawing backward projected nodes, when this node is a projection receiver. */ void setProjectedDisplayList(SkiaDisplayList* projectedDisplayList) { mProjectedDisplayList = projectedDisplayList; } protected: /* * Return the (conservative) bounds of what the drawable will draw. Loading @@ -107,6 +105,16 @@ private: */ sp<RenderNode> mRenderNode; /** * Walks recursively the display list and draws the content of backward projected nodes. * * @param canvas used to draw the backward projected nodes * @param displayList is a display list that contains a projection receiver * @param nestLevel should be always 0. Used to track how far we are from the receiver. */ void drawBackwardsProjectedNodes(SkCanvas* canvas, const SkiaDisplayList& displayList, int nestLevel = 0); /** * Applies the rendering properties of a view onto a SkCanvas. */ Loading @@ -126,19 +134,6 @@ private: */ const bool mComposeLayer; /** * List to which we will add any projected children we encounter while walking our descendents. * This pointer is valid only while the node (including its children) is actively being drawn. */ std::vector<ProjectedChild>* mProjectedChildrenTarget = nullptr; /** * The value to which we should set our children's mProjectedChildrenTarget. We use two pointers * (mProjectedChildrenTarget and mNextProjectedChildrenTarget) because we need to skip over our * parent when looking for a projection receiver. */ std::vector<ProjectedChild>* mNextProjectedChildrenTarget = nullptr; /* * True if the render node is in a reordering section */ Loading @@ -148,6 +143,11 @@ private: * Draw the content into a canvas, depending on the render node layer type and mComposeLayer. */ void drawContent(SkCanvas* canvas) const; /* * display list that is searched for any render nodes with getProjectBackwards==true */ SkiaDisplayList* mProjectedDisplayList = nullptr; }; }; // namespace skiapipeline Loading
libs/hwui/pipeline/skia/SkiaDisplayList.cpp +18 −3 Original line number Diff line number Diff line Loading @@ -64,16 +64,31 @@ bool SkiaDisplayList::prepareListAndChildren(TreeInfo& info, bool functorsNeedLa info.canvasContext.unpinImages(); } bool hasBackwardProjectedNodesHere = false; bool hasBackwardProjectedNodesSubtree= false; for (auto& child : mChildNodes) { hasBackwardProjectedNodesHere |= child.getNodeProperties().getProjectBackwards(); RenderNode* childNode = child.getRenderNode(); Matrix4 mat4(child.getRecordedMatrix()); info.damageAccumulator->pushTransform(&mat4); // TODO: a layer is needed if the canvas is rotated or has a non-rect clip bool childFunctorsNeedLayer = functorsNeedLayer; childFn(childNode, info, childFunctorsNeedLayer); info.hasBackwardProjectedNodes = false; childFn(childNode, info, functorsNeedLayer); hasBackwardProjectedNodesSubtree |= info.hasBackwardProjectedNodes; info.damageAccumulator->popTransform(); } //The purpose of next block of code is to reset projected display list if there are no //backward projected nodes. This speeds up drawing, by avoiding an extra walk of the tree if (mProjectionReceiver) { mProjectionReceiver->setProjectedDisplayList(hasBackwardProjectedNodesSubtree ? this : nullptr); info.hasBackwardProjectedNodes = hasBackwardProjectedNodesHere; } else { info.hasBackwardProjectedNodes = hasBackwardProjectedNodesSubtree || hasBackwardProjectedNodesHere; } bool isDirty = false; for (auto& vectorDrawable : mVectorDrawables) { // If any vector drawable in the display list needs update, damage the node. Loading @@ -86,7 +101,7 @@ bool SkiaDisplayList::prepareListAndChildren(TreeInfo& info, bool functorsNeedLa } void SkiaDisplayList::reset(SkRect bounds) { mIsProjectionReceiver = false; mProjectionReceiver = nullptr; mDrawable->reset(bounds); Loading