Loading libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +10 −3 Original line number Diff line number Diff line Loading @@ -41,7 +41,7 @@ RenderNodeDrawable::~RenderNodeDrawable() { void RenderNodeDrawable::drawBackwardsProjectedNodes(SkCanvas* canvas, const SkiaDisplayList& displayList, int nestLevel) { int nestLevel) const { LOG_ALWAYS_FATAL_IF(0 == nestLevel && !displayList.mProjectionReceiver); for (auto& child : displayList.mChildNodes) { const RenderProperties& childProperties = child.getNodeProperties(); Loading Loading @@ -132,7 +132,7 @@ private: RenderNode& mNode; }; void RenderNodeDrawable::forceDraw(SkCanvas* canvas) { void RenderNodeDrawable::forceDraw(SkCanvas* canvas) const { RenderNode* renderNode = mRenderNode.get(); MarkDraw _marker{*canvas, *renderNode}; Loading Loading @@ -230,7 +230,14 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { // we need to restrict the portion of the surface drawn to the size of the renderNode. SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width()); SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height()); canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(), bounds, // If SKP recording is active save an annotation that indicates this drawImageRect // could also be rendered with the commands saved at ID associated with this node. if (CC_UNLIKELY(Properties::skpCaptureEnabled)) { canvas->drawAnnotation(bounds, String8::format( "SurfaceID|%" PRId64, renderNode->uniqueId()).c_str(), nullptr); } canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot(), bounds, bounds, &paint); if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) { Loading libs/hwui/pipeline/skia/RenderNodeDrawable.h +2 −2 Original line number Diff line number Diff line Loading @@ -58,7 +58,7 @@ public: * projection receiver then all projected children (excluding direct children) will be drawn * last. Any projected node not matching those requirements will not be drawn by this function. */ void forceDraw(SkCanvas* canvas); void forceDraw(SkCanvas* canvas) const; /** * Returns readonly render properties for this render node. Loading Loading @@ -113,7 +113,7 @@ private: * @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); int nestLevel = 0) const; /** * Applies the rendering properties of a view onto a SkCanvas. Loading libs/hwui/pipeline/skia/SkiaPipeline.cpp +115 −50 Original line number Diff line number Diff line Loading @@ -26,11 +26,11 @@ #include <SkPictureRecorder.h> #include <SkSerialProcs.h> #include "LightingInfo.h" #include "TreeInfo.h" #include "VectorDrawable.h" #include "thread/CommonPool.h" #include "tools/SkSharingProc.h" #include "utils/TraceUtils.h" #include "utils/String8.h" #include <unistd.h> Loading Loading @@ -90,7 +90,9 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) // only schedule repaint if node still on layer - possible it may have been // removed during a dropped frame, but layers may still remain scheduled so // as not to lose info on what portion is damaged if (CC_LIKELY(layerNode->getLayerSurface() != nullptr)) { if (CC_UNLIKELY(layerNode->getLayerSurface() == nullptr)) { continue; } SkASSERT(layerNode->getLayerSurface()); SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList(); if (!displayList || displayList->isEmpty()) { Loading @@ -109,6 +111,7 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) // TODO: put localized light center calculation and storage to a drawable related code. // It does not seem right to store something localized in a global state // fix here and in recordLayers const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw()); Vector3 transformedLightCenter(savedLightCenter); // map current light center into RenderNode's coordinate space Loading @@ -130,6 +133,7 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) RenderNodeDrawable root(layerNode, layerCanvas, false); root.forceDraw(layerCanvas); layerCanvas->restoreToCount(saveCount); LightingInfo::setLightCenterRaw(savedLightCenter); // cache the current context so that we can defer flushing it until Loading @@ -143,7 +147,6 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) cachedContext.reset(SkSafeRef(currentContext)); } } } if (cachedContext.get()) { ATRACE_NAME("flush layers"); Loading Loading @@ -275,12 +278,60 @@ bool SkiaPipeline::setupMultiFrameCapture() { } } SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) { // recurse through the rendernode's children, add any nodes which are layers to the queue. static void collectLayers(RenderNode* node, LayerUpdateQueue* layers) { SkiaDisplayList* dl = (SkiaDisplayList*)node->getDisplayList(); if (dl) { const auto& prop = node->properties(); if (node->hasLayer()) { layers->enqueueLayerWithDamage(node, Rect(prop.getWidth(), prop.getHeight())); } // The way to recurse through rendernodes is to call this with a lambda. dl->updateChildren([&](RenderNode* child) { collectLayers(child, layers); }); } } // record the provided layers to the provided canvas as self-contained skpictures. static void recordLayers(const LayerUpdateQueue& layers, SkCanvas* mskpCanvas) { const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw()); // Record the commands to re-draw each dirty layer into an SkPicture for (size_t i = 0; i < layers.entries().size(); i++) { RenderNode* layerNode = layers.entries()[i].renderNode.get(); const Rect& layerDamage = layers.entries()[i].damage; const RenderProperties& properties = layerNode->properties(); // Temporarily map current light center into RenderNode's coordinate space Vector3 transformedLightCenter(savedLightCenter); layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter); LightingInfo::setLightCenterRaw(transformedLightCenter); SkPictureRecorder layerRec; auto* recCanvas = layerRec.beginRecording(properties.getWidth(), properties.getHeight()); // This is not recorded but still causes clipping. recCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect()); RenderNodeDrawable root(layerNode, recCanvas, false); root.forceDraw(recCanvas); // Now write this picture into the SKP canvas with an annotation indicating what it is mskpCanvas->drawAnnotation(layerDamage.toSkRect(), String8::format( "OffscreenLayerDraw|%" PRId64, layerNode->uniqueId()).c_str(), nullptr); mskpCanvas->drawPicture(layerRec.finishRecordingAsPicture()); } LightingInfo::setLightCenterRaw(savedLightCenter); } SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface, RenderNode* root, const LayerUpdateQueue& dirtyLayers) { if (CC_LIKELY(!Properties::skpCaptureEnabled)) { return surface->getCanvas(); // Bail out early when capture is not turned on. } // Note that shouldStartNewFileCapture tells us if this is the *first* frame of a capture. bool firstFrameOfAnim = false; if (shouldStartNewFileCapture() && mCaptureMode == CaptureMode::MultiFrameSKP) { // set a reminder to record every layer near the end of this method, after we have set up // the nway canvas. firstFrameOfAnim = true; if (!setupMultiFrameCapture()) { return surface->getCanvas(); } Loading Loading @@ -309,6 +360,20 @@ SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) { mNwayCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height()); mNwayCanvas->addCanvas(surface->getCanvas()); mNwayCanvas->addCanvas(pictureCanvas); if (firstFrameOfAnim) { // On the first frame of any mskp capture we want to record any layers that are needed in // frame but may have been rendered offscreen before recording began. // We do not maintain a list of all layers, since it isn't needed outside this rare, // recording use case. Traverse the tree to find them and put them in this LayerUpdateQueue. LayerUpdateQueue luq; collectLayers(root, &luq); recordLayers(luq, mNwayCanvas.get()); } else { // on non-first frames, we record any normal layer draws (dirty regions) recordLayers(dirtyLayers, mNwayCanvas.get()); } return mNwayCanvas.get(); } Loading Loading @@ -359,13 +424,13 @@ void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& cli Properties::skpCaptureEnabled = true; } // Initialize the canvas for the current frame, that might be a recording canvas if SKP // capture is enabled. SkCanvas* canvas = tryCapture(surface.get(), nodes[0].get(), layers); // draw all layers up front renderLayersImpl(layers, opaque); // initialize the canvas for the current frame, that might be a recording canvas if SKP // capture is enabled. SkCanvas* canvas = tryCapture(surface.get()); renderFrameImpl(clip, nodes, opaque, contentDrawBounds, canvas, preTransform); endCapture(surface.get()); Loading libs/hwui/pipeline/skia/SkiaPipeline.h +3 −1 Original line number Diff line number Diff line Loading @@ -45,6 +45,8 @@ public: void renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, bool opaque, const LightInfo& lightInfo) override; // If the given node didn't have a layer surface, or had one of the wrong size, this method // creates a new one and returns true. Otherwise does nothing and returns false. bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, ErrorHandler* errorHandler) override; Loading Loading @@ -92,7 +94,7 @@ private: // Called every frame. Normally returns early with screen canvas. // But when capture is enabled, returns an nwaycanvas where commands are also recorded. SkCanvas* tryCapture(SkSurface* surface); SkCanvas* tryCapture(SkSurface* surface, RenderNode* root, const LayerUpdateQueue& dirtyLayers); // Called at the end of every frame, closes the recording if necessary. void endCapture(SkSurface* surface); // Determine if a new file-based capture should be started. Loading Loading
libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +10 −3 Original line number Diff line number Diff line Loading @@ -41,7 +41,7 @@ RenderNodeDrawable::~RenderNodeDrawable() { void RenderNodeDrawable::drawBackwardsProjectedNodes(SkCanvas* canvas, const SkiaDisplayList& displayList, int nestLevel) { int nestLevel) const { LOG_ALWAYS_FATAL_IF(0 == nestLevel && !displayList.mProjectionReceiver); for (auto& child : displayList.mChildNodes) { const RenderProperties& childProperties = child.getNodeProperties(); Loading Loading @@ -132,7 +132,7 @@ private: RenderNode& mNode; }; void RenderNodeDrawable::forceDraw(SkCanvas* canvas) { void RenderNodeDrawable::forceDraw(SkCanvas* canvas) const { RenderNode* renderNode = mRenderNode.get(); MarkDraw _marker{*canvas, *renderNode}; Loading Loading @@ -230,7 +230,14 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const { // we need to restrict the portion of the surface drawn to the size of the renderNode. SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width()); SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height()); canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(), bounds, // If SKP recording is active save an annotation that indicates this drawImageRect // could also be rendered with the commands saved at ID associated with this node. if (CC_UNLIKELY(Properties::skpCaptureEnabled)) { canvas->drawAnnotation(bounds, String8::format( "SurfaceID|%" PRId64, renderNode->uniqueId()).c_str(), nullptr); } canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot(), bounds, bounds, &paint); if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) { Loading
libs/hwui/pipeline/skia/RenderNodeDrawable.h +2 −2 Original line number Diff line number Diff line Loading @@ -58,7 +58,7 @@ public: * projection receiver then all projected children (excluding direct children) will be drawn * last. Any projected node not matching those requirements will not be drawn by this function. */ void forceDraw(SkCanvas* canvas); void forceDraw(SkCanvas* canvas) const; /** * Returns readonly render properties for this render node. Loading Loading @@ -113,7 +113,7 @@ private: * @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); int nestLevel = 0) const; /** * Applies the rendering properties of a view onto a SkCanvas. Loading
libs/hwui/pipeline/skia/SkiaPipeline.cpp +115 −50 Original line number Diff line number Diff line Loading @@ -26,11 +26,11 @@ #include <SkPictureRecorder.h> #include <SkSerialProcs.h> #include "LightingInfo.h" #include "TreeInfo.h" #include "VectorDrawable.h" #include "thread/CommonPool.h" #include "tools/SkSharingProc.h" #include "utils/TraceUtils.h" #include "utils/String8.h" #include <unistd.h> Loading Loading @@ -90,7 +90,9 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) // only schedule repaint if node still on layer - possible it may have been // removed during a dropped frame, but layers may still remain scheduled so // as not to lose info on what portion is damaged if (CC_LIKELY(layerNode->getLayerSurface() != nullptr)) { if (CC_UNLIKELY(layerNode->getLayerSurface() == nullptr)) { continue; } SkASSERT(layerNode->getLayerSurface()); SkiaDisplayList* displayList = (SkiaDisplayList*)layerNode->getDisplayList(); if (!displayList || displayList->isEmpty()) { Loading @@ -109,6 +111,7 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) // TODO: put localized light center calculation and storage to a drawable related code. // It does not seem right to store something localized in a global state // fix here and in recordLayers const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw()); Vector3 transformedLightCenter(savedLightCenter); // map current light center into RenderNode's coordinate space Loading @@ -130,6 +133,7 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) RenderNodeDrawable root(layerNode, layerCanvas, false); root.forceDraw(layerCanvas); layerCanvas->restoreToCount(saveCount); LightingInfo::setLightCenterRaw(savedLightCenter); // cache the current context so that we can defer flushing it until Loading @@ -143,7 +147,6 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) cachedContext.reset(SkSafeRef(currentContext)); } } } if (cachedContext.get()) { ATRACE_NAME("flush layers"); Loading Loading @@ -275,12 +278,60 @@ bool SkiaPipeline::setupMultiFrameCapture() { } } SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) { // recurse through the rendernode's children, add any nodes which are layers to the queue. static void collectLayers(RenderNode* node, LayerUpdateQueue* layers) { SkiaDisplayList* dl = (SkiaDisplayList*)node->getDisplayList(); if (dl) { const auto& prop = node->properties(); if (node->hasLayer()) { layers->enqueueLayerWithDamage(node, Rect(prop.getWidth(), prop.getHeight())); } // The way to recurse through rendernodes is to call this with a lambda. dl->updateChildren([&](RenderNode* child) { collectLayers(child, layers); }); } } // record the provided layers to the provided canvas as self-contained skpictures. static void recordLayers(const LayerUpdateQueue& layers, SkCanvas* mskpCanvas) { const Vector3 savedLightCenter(LightingInfo::getLightCenterRaw()); // Record the commands to re-draw each dirty layer into an SkPicture for (size_t i = 0; i < layers.entries().size(); i++) { RenderNode* layerNode = layers.entries()[i].renderNode.get(); const Rect& layerDamage = layers.entries()[i].damage; const RenderProperties& properties = layerNode->properties(); // Temporarily map current light center into RenderNode's coordinate space Vector3 transformedLightCenter(savedLightCenter); layerNode->getSkiaLayer()->inverseTransformInWindow.mapPoint3d(transformedLightCenter); LightingInfo::setLightCenterRaw(transformedLightCenter); SkPictureRecorder layerRec; auto* recCanvas = layerRec.beginRecording(properties.getWidth(), properties.getHeight()); // This is not recorded but still causes clipping. recCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect()); RenderNodeDrawable root(layerNode, recCanvas, false); root.forceDraw(recCanvas); // Now write this picture into the SKP canvas with an annotation indicating what it is mskpCanvas->drawAnnotation(layerDamage.toSkRect(), String8::format( "OffscreenLayerDraw|%" PRId64, layerNode->uniqueId()).c_str(), nullptr); mskpCanvas->drawPicture(layerRec.finishRecordingAsPicture()); } LightingInfo::setLightCenterRaw(savedLightCenter); } SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface, RenderNode* root, const LayerUpdateQueue& dirtyLayers) { if (CC_LIKELY(!Properties::skpCaptureEnabled)) { return surface->getCanvas(); // Bail out early when capture is not turned on. } // Note that shouldStartNewFileCapture tells us if this is the *first* frame of a capture. bool firstFrameOfAnim = false; if (shouldStartNewFileCapture() && mCaptureMode == CaptureMode::MultiFrameSKP) { // set a reminder to record every layer near the end of this method, after we have set up // the nway canvas. firstFrameOfAnim = true; if (!setupMultiFrameCapture()) { return surface->getCanvas(); } Loading Loading @@ -309,6 +360,20 @@ SkCanvas* SkiaPipeline::tryCapture(SkSurface* surface) { mNwayCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height()); mNwayCanvas->addCanvas(surface->getCanvas()); mNwayCanvas->addCanvas(pictureCanvas); if (firstFrameOfAnim) { // On the first frame of any mskp capture we want to record any layers that are needed in // frame but may have been rendered offscreen before recording began. // We do not maintain a list of all layers, since it isn't needed outside this rare, // recording use case. Traverse the tree to find them and put them in this LayerUpdateQueue. LayerUpdateQueue luq; collectLayers(root, &luq); recordLayers(luq, mNwayCanvas.get()); } else { // on non-first frames, we record any normal layer draws (dirty regions) recordLayers(dirtyLayers, mNwayCanvas.get()); } return mNwayCanvas.get(); } Loading Loading @@ -359,13 +424,13 @@ void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& cli Properties::skpCaptureEnabled = true; } // Initialize the canvas for the current frame, that might be a recording canvas if SKP // capture is enabled. SkCanvas* canvas = tryCapture(surface.get(), nodes[0].get(), layers); // draw all layers up front renderLayersImpl(layers, opaque); // initialize the canvas for the current frame, that might be a recording canvas if SKP // capture is enabled. SkCanvas* canvas = tryCapture(surface.get()); renderFrameImpl(clip, nodes, opaque, contentDrawBounds, canvas, preTransform); endCapture(surface.get()); Loading
libs/hwui/pipeline/skia/SkiaPipeline.h +3 −1 Original line number Diff line number Diff line Loading @@ -45,6 +45,8 @@ public: void renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, bool opaque, const LightInfo& lightInfo) override; // If the given node didn't have a layer surface, or had one of the wrong size, this method // creates a new one and returns true. Otherwise does nothing and returns false. bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, ErrorHandler* errorHandler) override; Loading Loading @@ -92,7 +94,7 @@ private: // Called every frame. Normally returns early with screen canvas. // But when capture is enabled, returns an nwaycanvas where commands are also recorded. SkCanvas* tryCapture(SkSurface* surface); SkCanvas* tryCapture(SkSurface* surface, RenderNode* root, const LayerUpdateQueue& dirtyLayers); // Called at the end of every frame, closes the recording if necessary. void endCapture(SkSurface* surface); // Determine if a new file-based capture should be started. Loading