Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c70aa04d authored by Nathaniel Nifong's avatar Nathaniel Nifong Committed by Android (Google) Code Review
Browse files

Merge "When saving MSKP files, record layers as SkPictures with annotations."

parents cad74405 2945bfff
Loading
Loading
Loading
Loading
+10 −3
Original line number Diff line number Diff line
@@ -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();
@@ -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};

@@ -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) {
+2 −2
Original line number Diff line number Diff line
@@ -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.
@@ -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.
+115 −50
Original line number Diff line number Diff line
@@ -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>

@@ -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()) {
@@ -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
@@ -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
@@ -143,7 +147,6 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque)
            cachedContext.reset(SkSafeRef(currentContext));
        }
    }
    }

    if (cachedContext.get()) {
        ATRACE_NAME("flush layers");
@@ -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();
        }
@@ -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();
}

@@ -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());
+3 −1
Original line number Diff line number Diff line
@@ -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;

@@ -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.