Loading libs/hwui/BakedOpDispatcher.cpp +7 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ #include <algorithm> #include <math.h> #include <SkPaintDefaults.h> #include <SkPathOps.h> namespace android { namespace uirenderer { Loading Loading @@ -527,6 +528,12 @@ void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, co SkPath path; SkRect rect = getBoundsOfFill(op); path.addOval(rect); if (state.computedState.localProjectionPathMask != nullptr) { // Mask the ripple path by the local space projection mask in local space. // Note that this can create CCW paths. Op(path, *state.computedState.localProjectionPathMask, kIntersect_SkPathOp, &path); } renderConvexPath(renderer, state, path, *(op.paint)); } } Loading libs/hwui/BakedOpState.cpp +18 −3 Original line number Diff line number Diff line Loading @@ -63,9 +63,22 @@ ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& s clipState = nullptr; clippedBounds.setEmpty(); } else { // Not rejected! compute true clippedBounds and clipSideFlags // Not rejected! compute true clippedBounds, clipSideFlags, and path mask clipSideFlags = computeClipSideFlags(clipRect, clippedBounds); clippedBounds.doIntersect(clipRect); if (CC_UNLIKELY(snapshot.projectionPathMask)) { // map projection path mask from render target space into op space, // so intersection with op geometry is possible Matrix4 inverseTransform; inverseTransform.loadInverse(transform); SkMatrix skInverseTransform; inverseTransform.copyTo(skInverseTransform); auto localMask = allocator.create<SkPath>(); snapshot.projectionPathMask->transform(skInverseTransform, localMask); localProjectionPathMask = localMask; } } } Loading @@ -73,13 +86,15 @@ ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& s : transform(*snapshot.transform) , clipState(snapshot.mutateClipArea().serializeClip(allocator)) , clippedBounds(clipState->rect) , clipSideFlags(OpClipSideFlags::Full) {} , clipSideFlags(OpClipSideFlags::Full) , localProjectionPathMask(nullptr) {} ResolvedRenderState::ResolvedRenderState(const ClipRect* clipRect, const Rect& dstRect) : transform(Matrix4::identity()) , clipState(clipRect) , clippedBounds(dstRect) , clipSideFlags(computeClipSideFlags(clipRect->rect, dstRect)) { , clipSideFlags(computeClipSideFlags(clipRect->rect, dstRect)) , localProjectionPathMask(nullptr) { clippedBounds.doIntersect(clipRect->rect); } Loading libs/hwui/BakedOpState.h +1 −4 Original line number Diff line number Diff line Loading @@ -88,6 +88,7 @@ public: const ClipBase* clipState = nullptr; Rect clippedBounds; int clipSideFlags = 0; const SkPath* localProjectionPathMask = nullptr; }; /** Loading Loading @@ -154,7 +155,6 @@ public: // simple state (straight pointer/value storage): const float alpha; const RoundRectClipState* roundRectClipState; const ProjectionPathMask* projectionPathMask; const RecordedOp* op; private: Loading @@ -165,21 +165,18 @@ private: : computedState(allocator, snapshot, recordedOp, expandForStroke) , alpha(snapshot.alpha) , roundRectClipState(snapshot.roundRectClipState) , projectionPathMask(snapshot.projectionPathMask) , op(&recordedOp) {} BakedOpState(LinearAllocator& allocator, Snapshot& snapshot, const ShadowOp* shadowOpPtr) : computedState(allocator, snapshot) , alpha(snapshot.alpha) , roundRectClipState(snapshot.roundRectClipState) , projectionPathMask(snapshot.projectionPathMask) , op(shadowOpPtr) {} BakedOpState(const ClipRect* clipRect, const Rect& dstRect, const RecordedOp& recordedOp) : computedState(clipRect, dstRect) , alpha(1.0f) , roundRectClipState(nullptr) , projectionPathMask(nullptr) , op(&recordedOp) {} }; Loading libs/hwui/FrameBuilder.cpp +24 −20 Original line number Diff line number Diff line Loading @@ -389,34 +389,38 @@ void FrameBuilder::deferShadow(const RenderNodeOp& casterNodeOp) { } void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) { const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath(); int count = mCanvasState.save(SaveFlags::MatrixClip); const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath(); // 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<const RenderNodeOp*>(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); SkPath transformedMaskPath; // on stack, since BakedOpState makes a deep copy if (projectionReceiverOutline) { // transform the mask for this projector into render target space // TODO: consider combining both transforms by stashing transform instead of applying SkMatrix skCurrentTransform; mCanvasState.currentTransform()->copyTo(skCurrentTransform); projectionReceiverOutline->transform( skCurrentTransform, &transformedMaskPath); mCanvasState.setProjectionPathMask(mAllocator, &transformedMaskPath); } // draw projected nodes for (size_t i = 0; i < renderNode.mProjectedNodes.size(); i++) { RenderNodeOp* childOp = renderNode.mProjectedNodes[i]; RenderNode& childNode = *childOp->renderNode; // Draw child if it has content, but ignore state in childOp - matrix already applied to // transformFromCompositingAncestor, and record-time clip is ignored when projecting if (!childNode.nothingToDraw()) { int restoreTo = mCanvasState.save(SaveFlags::MatrixClip); int restoreTo = mCanvasState.save(SaveFlags::Matrix); // Apply transform between ancestor and projected descendant mCanvasState.concatMatrix(childOp->transformFromCompositingAncestor); deferRenderNodeOpImpl(*childOp); deferNodePropsAndOps(childNode); mCanvasState.restoreToCount(restoreTo); } } mCanvasState.restoreToCount(count); } Loading libs/hwui/LayerBuilder.cpp +4 −1 Original line number Diff line number Diff line Loading @@ -140,7 +140,10 @@ public: // Identical round rect clip state means both ops will clip in the same way, or not at all. // As the state objects are const, we can compare their pointers to determine mergeability if (lhs->roundRectClipState != rhs->roundRectClipState) return false; if (lhs->projectionPathMask != rhs->projectionPathMask) return false; // Local masks prevent merge, since they're potentially in different coordinate spaces if (lhs->computedState.localProjectionPathMask || rhs->computedState.localProjectionPathMask) return false; /* Clipping compatibility check * Loading Loading
libs/hwui/BakedOpDispatcher.cpp +7 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ #include <algorithm> #include <math.h> #include <SkPaintDefaults.h> #include <SkPathOps.h> namespace android { namespace uirenderer { Loading Loading @@ -527,6 +528,12 @@ void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, co SkPath path; SkRect rect = getBoundsOfFill(op); path.addOval(rect); if (state.computedState.localProjectionPathMask != nullptr) { // Mask the ripple path by the local space projection mask in local space. // Note that this can create CCW paths. Op(path, *state.computedState.localProjectionPathMask, kIntersect_SkPathOp, &path); } renderConvexPath(renderer, state, path, *(op.paint)); } } Loading
libs/hwui/BakedOpState.cpp +18 −3 Original line number Diff line number Diff line Loading @@ -63,9 +63,22 @@ ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& s clipState = nullptr; clippedBounds.setEmpty(); } else { // Not rejected! compute true clippedBounds and clipSideFlags // Not rejected! compute true clippedBounds, clipSideFlags, and path mask clipSideFlags = computeClipSideFlags(clipRect, clippedBounds); clippedBounds.doIntersect(clipRect); if (CC_UNLIKELY(snapshot.projectionPathMask)) { // map projection path mask from render target space into op space, // so intersection with op geometry is possible Matrix4 inverseTransform; inverseTransform.loadInverse(transform); SkMatrix skInverseTransform; inverseTransform.copyTo(skInverseTransform); auto localMask = allocator.create<SkPath>(); snapshot.projectionPathMask->transform(skInverseTransform, localMask); localProjectionPathMask = localMask; } } } Loading @@ -73,13 +86,15 @@ ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& s : transform(*snapshot.transform) , clipState(snapshot.mutateClipArea().serializeClip(allocator)) , clippedBounds(clipState->rect) , clipSideFlags(OpClipSideFlags::Full) {} , clipSideFlags(OpClipSideFlags::Full) , localProjectionPathMask(nullptr) {} ResolvedRenderState::ResolvedRenderState(const ClipRect* clipRect, const Rect& dstRect) : transform(Matrix4::identity()) , clipState(clipRect) , clippedBounds(dstRect) , clipSideFlags(computeClipSideFlags(clipRect->rect, dstRect)) { , clipSideFlags(computeClipSideFlags(clipRect->rect, dstRect)) , localProjectionPathMask(nullptr) { clippedBounds.doIntersect(clipRect->rect); } Loading
libs/hwui/BakedOpState.h +1 −4 Original line number Diff line number Diff line Loading @@ -88,6 +88,7 @@ public: const ClipBase* clipState = nullptr; Rect clippedBounds; int clipSideFlags = 0; const SkPath* localProjectionPathMask = nullptr; }; /** Loading Loading @@ -154,7 +155,6 @@ public: // simple state (straight pointer/value storage): const float alpha; const RoundRectClipState* roundRectClipState; const ProjectionPathMask* projectionPathMask; const RecordedOp* op; private: Loading @@ -165,21 +165,18 @@ private: : computedState(allocator, snapshot, recordedOp, expandForStroke) , alpha(snapshot.alpha) , roundRectClipState(snapshot.roundRectClipState) , projectionPathMask(snapshot.projectionPathMask) , op(&recordedOp) {} BakedOpState(LinearAllocator& allocator, Snapshot& snapshot, const ShadowOp* shadowOpPtr) : computedState(allocator, snapshot) , alpha(snapshot.alpha) , roundRectClipState(snapshot.roundRectClipState) , projectionPathMask(snapshot.projectionPathMask) , op(shadowOpPtr) {} BakedOpState(const ClipRect* clipRect, const Rect& dstRect, const RecordedOp& recordedOp) : computedState(clipRect, dstRect) , alpha(1.0f) , roundRectClipState(nullptr) , projectionPathMask(nullptr) , op(&recordedOp) {} }; Loading
libs/hwui/FrameBuilder.cpp +24 −20 Original line number Diff line number Diff line Loading @@ -389,34 +389,38 @@ void FrameBuilder::deferShadow(const RenderNodeOp& casterNodeOp) { } void FrameBuilder::deferProjectedChildren(const RenderNode& renderNode) { const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath(); int count = mCanvasState.save(SaveFlags::MatrixClip); const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath(); // 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<const RenderNodeOp*>(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); SkPath transformedMaskPath; // on stack, since BakedOpState makes a deep copy if (projectionReceiverOutline) { // transform the mask for this projector into render target space // TODO: consider combining both transforms by stashing transform instead of applying SkMatrix skCurrentTransform; mCanvasState.currentTransform()->copyTo(skCurrentTransform); projectionReceiverOutline->transform( skCurrentTransform, &transformedMaskPath); mCanvasState.setProjectionPathMask(mAllocator, &transformedMaskPath); } // draw projected nodes for (size_t i = 0; i < renderNode.mProjectedNodes.size(); i++) { RenderNodeOp* childOp = renderNode.mProjectedNodes[i]; RenderNode& childNode = *childOp->renderNode; // Draw child if it has content, but ignore state in childOp - matrix already applied to // transformFromCompositingAncestor, and record-time clip is ignored when projecting if (!childNode.nothingToDraw()) { int restoreTo = mCanvasState.save(SaveFlags::MatrixClip); int restoreTo = mCanvasState.save(SaveFlags::Matrix); // Apply transform between ancestor and projected descendant mCanvasState.concatMatrix(childOp->transformFromCompositingAncestor); deferRenderNodeOpImpl(*childOp); deferNodePropsAndOps(childNode); mCanvasState.restoreToCount(restoreTo); } } mCanvasState.restoreToCount(count); } Loading
libs/hwui/LayerBuilder.cpp +4 −1 Original line number Diff line number Diff line Loading @@ -140,7 +140,10 @@ public: // Identical round rect clip state means both ops will clip in the same way, or not at all. // As the state objects are const, we can compare their pointers to determine mergeability if (lhs->roundRectClipState != rhs->roundRectClipState) return false; if (lhs->projectionPathMask != rhs->projectionPathMask) return false; // Local masks prevent merge, since they're potentially in different coordinate spaces if (lhs->computedState.localProjectionPathMask || rhs->computedState.localProjectionPathMask) return false; /* Clipping compatibility check * Loading