Merge "Enable offscreen buffers in mskp captures for RenderEngine" into sc-dev
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index dc14fc2..55ec6ad 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -518,7 +518,7 @@
 }
 
 void SkiaGLRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) {
-    if (mCapture->isCaptureRunning()) {
+    if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
         // Record display settings when capture is running.
         std::stringstream displaySettings;
         PrintTo(display, &displaySettings);
@@ -626,6 +626,7 @@
     // offscreen buffer and when to render to the native buffer.
     sk_sp<SkSurface> activeSurface(dstSurface);
     SkCanvas* canvas = dstCanvas;
+    SkiaCapture::OffscreenState offscreenCaptureState;
     const LayerSettings* blurCompositionLayer = nullptr;
     if (mBlurFilter) {
         bool requiresCompositionLayer = false;
@@ -642,7 +643,7 @@
             }
             if (requiresCompositionLayer) {
                 activeSurface = dstSurface->makeSurface(dstSurface->imageInfo());
-                canvas = activeSurface->getCanvas();
+                canvas = mCapture->tryOffscreenCapture(activeSurface.get(), &offscreenCaptureState);
                 blurCompositionLayer = layer;
                 break;
             }
@@ -692,7 +693,15 @@
             //  blit the offscreen framebuffer into the destination AHB
             SkPaint paint;
             paint.setBlendMode(SkBlendMode::kSrc);
-            activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint);
+            if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
+                uint64_t id = mCapture->endOffscreenCapture(&offscreenCaptureState);
+                dstCanvas->drawAnnotation(SkRect::Make(dstCanvas->imageInfo().dimensions()),
+                                          String8::format("SurfaceID|%" PRId64, id).c_str(),
+                                          nullptr);
+                dstCanvas->drawImage(blurInput, 0, 0, SkSamplingOptions(), &paint);
+            } else {
+                activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint);
+            }
 
             // assign dstCanvas to canvas and ensure that the canvas state is up to date
             canvas = dstCanvas;
@@ -709,7 +718,7 @@
         }
 
         canvas->save();
-        if (mCapture->isCaptureRunning()) {
+        if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
             // Record the name of the layer if the capture is running.
             std::stringstream layerSettings;
             PrintTo(*layer, &layerSettings);
diff --git a/libs/renderengine/skia/debug/SkiaCapture.cpp b/libs/renderengine/skia/debug/SkiaCapture.cpp
index e9cfead..40f5cf2 100644
--- a/libs/renderengine/skia/debug/SkiaCapture.cpp
+++ b/libs/renderengine/skia/debug/SkiaCapture.cpp
@@ -41,11 +41,11 @@
     mTimer.stop();
 }
 
-SkCanvas* SkiaCapture::tryCapture(SkSurface* surface) {
+SkCanvas* SkiaCapture::tryCapture(SkSurface* surface) NO_THREAD_SAFETY_ANALYSIS {
     ATRACE_CALL();
 
     // If we are not running yet, set up.
-    if (!mCaptureRunning) {
+    if (CC_LIKELY(!mCaptureRunning)) {
         mTimerInterval = std::chrono::milliseconds(
                 base::GetIntProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_SKIA_MS, 0));
         // Set up the multi-frame capture. If we fail to set it up, then just return canvas.
@@ -56,7 +56,8 @@
         // Start the new timer. When timer expires, write to file.
         mTimer.setTimeout(
                 [this] {
-                    endCapture();
+                    const std::scoped_lock lock(mMutex);
+                    LOG_ALWAYS_FATAL_IF(mCurrentPageCanvas != nullptr);
                     writeToFile();
                     // To avoid going in circles, set the flag to 0. This way the capture can be
                     // restarted just by setting the flag and without restarting the process.
@@ -65,29 +66,82 @@
                 mTimerInterval);
     }
 
+    mMutex.lock();
+
     // Create a canvas pointer, fill it.
-    SkCanvas* pictureCanvas = mMultiPic->beginPage(surface->width(), surface->height());
+    mCurrentPageCanvas = mMultiPic->beginPage(surface->width(), surface->height());
 
     // Setting up an nway canvas is common to any kind of capture.
     mNwayCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height());
     mNwayCanvas->addCanvas(surface->getCanvas());
-    mNwayCanvas->addCanvas(pictureCanvas);
+    mNwayCanvas->addCanvas(mCurrentPageCanvas);
 
     return mNwayCanvas.get();
 }
 
-void SkiaCapture::endCapture() {
+void SkiaCapture::endCapture() NO_THREAD_SAFETY_ANALYSIS {
     ATRACE_CALL();
     // Don't end anything if we are not running.
-    if (!mCaptureRunning) {
+    if (CC_LIKELY(!mCaptureRunning)) {
         return;
     }
     // Reset the canvas pointer.
+    mCurrentPageCanvas = nullptr;
     mNwayCanvas.reset();
     // End page.
     if (mMultiPic) {
         mMultiPic->endPage();
     }
+    mMutex.unlock();
+}
+
+SkCanvas* SkiaCapture::tryOffscreenCapture(SkSurface* surface, OffscreenState* state) {
+    ATRACE_CALL();
+    // Don't start anything if we are not running.
+    if (CC_LIKELY(!mCaptureRunning)) {
+        return surface->getCanvas();
+    }
+
+    // Create a canvas pointer, fill it.
+    state->offscreenRecorder = std::make_unique<SkPictureRecorder>();
+    SkCanvas* pictureCanvas =
+            state->offscreenRecorder->beginRecording(surface->width(), surface->height());
+
+    // Setting up an nway canvas is common to any kind of capture.
+    state->offscreenCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height());
+    state->offscreenCanvas->addCanvas(surface->getCanvas());
+    state->offscreenCanvas->addCanvas(pictureCanvas);
+
+    return state->offscreenCanvas.get();
+}
+
+uint64_t SkiaCapture::endOffscreenCapture(OffscreenState* state) {
+    ATRACE_CALL();
+    // Don't end anything if we are not running.
+    if (CC_LIKELY(!mCaptureRunning)) {
+        return 0;
+    }
+
+    // compute the uniqueID for this capture
+    static std::atomic<uint64_t> nextID{1};
+    const uint64_t uniqueID = nextID.fetch_add(1, std::memory_order_relaxed);
+
+    // Reset the canvas pointer as we are no longer drawing into it
+    state->offscreenCanvas.reset();
+
+    // Record the offscreen as a picture in the currently active page.
+    SkRect bounds =
+            SkRect::Make(state->offscreenRecorder->getRecordingCanvas()->imageInfo().dimensions());
+    mCurrentPageCanvas
+            ->drawAnnotation(bounds,
+                             String8::format("OffscreenLayerDraw|%" PRId64, uniqueID).c_str(),
+                             nullptr);
+    mCurrentPageCanvas->drawPicture(state->offscreenRecorder->finishRecordingAsPicture());
+
+    // Reset the offscreen picture recorder
+    state->offscreenRecorder.reset();
+
+    return uniqueID;
 }
 
 void SkiaCapture::writeToFile() {
diff --git a/libs/renderengine/skia/debug/SkiaCapture.h b/libs/renderengine/skia/debug/SkiaCapture.h
index eaaf598..08a1359 100644
--- a/libs/renderengine/skia/debug/SkiaCapture.h
+++ b/libs/renderengine/skia/debug/SkiaCapture.h
@@ -18,8 +18,12 @@
 
 #include <SkDocument.h>
 #include <SkNWayCanvas.h>
+#include <SkPictureRecorder.h>
 #include <SkSurface.h>
+
 #include <chrono>
+#include <mutex>
+
 #include "CaptureTimer.h"
 #include "tools/SkSharingProc.h"
 
@@ -48,6 +52,16 @@
     // Returns whether the capture is running.
     bool isCaptureRunning() { return mCaptureRunning; }
 
+    // Offscreen state member variables are private to SkiaCapture, but the allocation
+    // and lifetime is managed by the caller. This enables nested offscreen
+    // captures to occur.
+    struct OffscreenState {
+        std::unique_ptr<SkPictureRecorder> offscreenRecorder;
+        std::unique_ptr<SkNWayCanvas> offscreenCanvas;
+    };
+    SkCanvas* tryOffscreenCapture(SkSurface* surface, OffscreenState* state);
+    uint64_t endOffscreenCapture(OffscreenState* state);
+
 private:
     // Performs the first-frame work of a multi frame SKP capture. Returns true if successful.
     bool setupMultiFrameCapture();
@@ -61,10 +75,16 @@
     std::unique_ptr<SkSharingSerialContext> mSerialContext;
     std::unique_ptr<SkNWayCanvas> mNwayCanvas;
 
+    SkCanvas* mCurrentPageCanvas;
+
     // Capturing and interval control.
     bool mCaptureRunning = false;
     CaptureTimer mTimer;
     Interval mTimerInterval = 0ms;
+
+    // Mutex to ensure that a frame in progress when the timer fires is allowed to run to
+    // completion before we write the file to disk.
+    std::mutex mMutex;
 };
 
 } // namespace skia