Support capturing a gainmapped screenshot

This uses the gainmap concept from UltraHDR and ISO 21496-1 to produce
screenshots that (a) can be rendered in HDR and (b) that are perfectly
backwards-compatible on SDR displays are scenes where displaying SDR is
preferable.

The technical details of the screenshot process at a high level are:
* A client requests that they want a gainmap attached to the screenshot
  result
* SurfaceFlinger asks RenderEngine to perform three render passes
    * First, render the SDR base image, tonemapping using MouriMap
    * Second, render an HDR rendition. This is *also* tonemapped using
      MouriMap to the current display conditions. For HDR UI, this is
      effectively a no-op. For PQ and HLG content, this allows for
      approximating what is on-screen as closely as possible. Note that
      to preserve precision as much as possible, we require an FP16
      framebuffer.
    * Finally, combine the SDR and HDR renditions into a gainmap
* The client now receives the base image with the gainmap, as well as an
  HDR/SDR ratio. The client can use this information to generate gainmap
  metadata for encoding or round-tripping to the display.

This MVP is sufficient for generating screenshots through adb screencap.
Eventually, this can be used to integrate with the applications that
want HDR screenshots, or to remove hacks that disable tonemapping during
certain system animations.

Bug: 329470026
Flag: com.android.graphics.surfaceflinger.flags.true_hdr_screenshots
Test: builds
Test: adb screencap
Change-Id: I0434059d957bb6cc38d019743619d72dda72a269
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 0aad282..7590d98 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -7060,7 +7060,8 @@
                                                  displayWeak, options),
                         getLayerSnapshotsFn, reqSize,
                         static_cast<ui::PixelFormat>(captureArgs.pixelFormat),
-                        captureArgs.allowProtected, captureArgs.grayscale, captureListener);
+                        captureArgs.allowProtected, captureArgs.grayscale,
+                        captureArgs.attachGainmap, captureListener);
 }
 
 void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args,
@@ -7117,7 +7118,7 @@
                                                  static_cast<ui::Dataspace>(args.dataspace),
                                                  displayWeak, options),
                         getLayerSnapshotsFn, size, static_cast<ui::PixelFormat>(args.pixelFormat),
-                        kAllowProtected, kGrayscale, captureListener);
+                        kAllowProtected, kGrayscale, args.attachGainmap, captureListener);
 }
 
 ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& args) {
@@ -7228,7 +7229,8 @@
                                                  options),
                         getLayerSnapshotsFn, reqSize,
                         static_cast<ui::PixelFormat>(captureArgs.pixelFormat),
-                        captureArgs.allowProtected, captureArgs.grayscale, captureListener);
+                        captureArgs.allowProtected, captureArgs.grayscale,
+                        captureArgs.attachGainmap, captureListener);
 }
 
 // Creates a Future release fence for a layer and keeps track of it in a list to
@@ -7281,7 +7283,7 @@
 void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuilder,
                                          GetLayerSnapshotsFunction getLayerSnapshotsFn,
                                          ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
-                                         bool allowProtected, bool grayscale,
+                                         bool allowProtected, bool grayscale, bool attachGainmap,
                                          const sp<IScreenCaptureListener>& captureListener) {
     SFTRACE_CALL();
 
@@ -7327,9 +7329,9 @@
                 renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
                                                      renderengine::impl::ExternalTexture::Usage::
                                                              WRITEABLE);
-        auto futureFence =
-                captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, grayscale,
-                                  isProtected, captureListener, displayState, layerFEs);
+        auto futureFence = captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */,
+                                             grayscale, isProtected, attachGainmap, captureListener,
+                                             displayState, layerFEs);
         futureFence.get();
 
     } else {
@@ -7364,7 +7366,7 @@
                                                              WRITEABLE);
         auto futureFence = captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, texture,
                                                    false /* regionSampling */, grayscale,
-                                                   isProtected, captureListener);
+                                                   isProtected, attachGainmap, captureListener);
         futureFence.get();
     }
 }
@@ -7418,7 +7420,8 @@
 ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot(
         const RenderAreaBuilderVariant& renderAreaBuilder,
         const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
-        bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener,
+        bool grayscale, bool isProtected, bool attachGainmap,
+        const sp<IScreenCaptureListener>& captureListener,
         std::optional<OutputCompositionState>& displayState, std::vector<sp<LayerFE>>& layerFEs) {
     SFTRACE_CALL();
 
@@ -7435,19 +7438,87 @@
         }
         return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
     }
+    float displayBrightnessNits = displayState.value().displayBrightnessNits;
+    float sdrWhitePointNits = displayState.value().sdrWhitePointNits;
 
     // Empty vector needed to pass into renderScreenImpl for legacy path
     std::vector<std::pair<Layer*, sp<android::LayerFE>>> layers;
     ftl::SharedFuture<FenceResult> renderFuture =
-            renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale, isProtected,
-                             captureResults, displayState, layers, layerFEs);
+            renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected,
+                             attachGainmap, captureResults, displayState, layers, layerFEs);
+
+    if (captureResults.capturedHdrLayers && attachGainmap &&
+        FlagManager::getInstance().true_hdr_screenshots()) {
+        sp<GraphicBuffer> hdrBuffer =
+                getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(),
+                                                 HAL_PIXEL_FORMAT_RGBA_FP16, 1 /* layerCount */,
+                                                 buffer->getUsage(), "screenshot-hdr");
+        sp<GraphicBuffer> gainmapBuffer =
+                getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(),
+                                                 buffer->getPixelFormat(), 1 /* layerCount */,
+                                                 buffer->getUsage(), "screenshot-gainmap");
+
+        const status_t bufferStatus = hdrBuffer->initCheck();
+        const status_t gainmapBufferStatus = gainmapBuffer->initCheck();
+
+        if (bufferStatus != OK) {
+            ALOGW("%s: Buffer failed to allocate for hdr: %d. Screenshoting SDR instead.", __func__,
+                  bufferStatus);
+        } else if (gainmapBufferStatus != OK) {
+            ALOGW("%s: Buffer failed to allocate for gainmap: %d. Screenshoting SDR instead.",
+                  __func__, gainmapBufferStatus);
+        } else {
+            captureResults.optionalGainMap = gainmapBuffer;
+            const auto hdrTexture = std::make_shared<
+                    renderengine::impl::ExternalTexture>(hdrBuffer, getRenderEngine(),
+                                                         renderengine::impl::ExternalTexture::
+                                                                 Usage::WRITEABLE);
+            const auto gainmapTexture = std::make_shared<
+                    renderengine::impl::ExternalTexture>(gainmapBuffer, getRenderEngine(),
+                                                         renderengine::impl::ExternalTexture::
+                                                                 Usage::WRITEABLE);
+            ScreenCaptureResults unusedResults;
+            ftl::SharedFuture<FenceResult> hdrRenderFuture =
+                    renderScreenImpl(renderArea.get(), hdrTexture, regionSampling, grayscale,
+                                     isProtected, attachGainmap, unusedResults, displayState,
+                                     layers, layerFEs);
+
+            renderFuture =
+                    ftl::Future(std::move(renderFuture))
+                            .then([&, hdrRenderFuture = std::move(hdrRenderFuture),
+                                   displayBrightnessNits, sdrWhitePointNits,
+                                   dataspace = captureResults.capturedDataspace, buffer, hdrTexture,
+                                   gainmapTexture](FenceResult fenceResult) -> FenceResult {
+                                if (!fenceResult.ok()) {
+                                    return fenceResult;
+                                }
+
+                                auto hdrFenceResult = hdrRenderFuture.get();
+
+                                if (!hdrFenceResult.ok()) {
+                                    return hdrFenceResult;
+                                }
+
+                                return getRenderEngine()
+                                        .drawGainmap(buffer, fenceResult.value()->get(), hdrTexture,
+                                                     hdrFenceResult.value()->get(),
+                                                     displayBrightnessNits / sdrWhitePointNits,
+                                                     static_cast<ui::Dataspace>(dataspace),
+                                                     gainmapTexture)
+                                        .get();
+                            })
+                            .share();
+        };
+    }
 
     if (captureListener) {
         // Defer blocking on renderFuture back to the Binder thread.
         return ftl::Future(std::move(renderFuture))
-                .then([captureListener, captureResults = std::move(captureResults)](
-                              FenceResult fenceResult) mutable -> FenceResult {
+                .then([captureListener, captureResults = std::move(captureResults),
+                       displayBrightnessNits,
+                       sdrWhitePointNits](FenceResult fenceResult) mutable -> FenceResult {
                     captureResults.fenceResult = std::move(fenceResult);
+                    captureResults.hdrSdrRatio = displayBrightnessNits / sdrWhitePointNits;
                     captureListener->onScreenCaptureCompleted(captureResults);
                     return base::unexpected(NO_ERROR);
                 })
@@ -7459,7 +7530,8 @@
 ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshotLegacy(
         RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn,
         const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
-        bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener) {
+        bool grayscale, bool isProtected, bool attachGainmap,
+        const sp<IScreenCaptureListener>& captureListener) {
     SFTRACE_CALL();
 
     auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES(
@@ -7488,8 +7560,8 @@
 
         auto layerFEs = extractLayerFEs(layers);
         ftl::SharedFuture<FenceResult> renderFuture =
-                renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale,
-                                 isProtected, captureResults, displayState, layers, layerFEs);
+                renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected,
+                                 attachGainmap, captureResults, displayState, layers, layerFEs);
 
         if (captureListener) {
             // Defer blocking on renderFuture back to the Binder thread.
@@ -7519,10 +7591,9 @@
 }
 
 ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
-        std::unique_ptr<const RenderArea> renderArea,
-        const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
-        bool grayscale, bool isProtected, ScreenCaptureResults& captureResults,
-        std::optional<OutputCompositionState>& displayState,
+        const RenderArea* renderArea, const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+        bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap,
+        ScreenCaptureResults& captureResults, std::optional<OutputCompositionState>& displayState,
         std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, std::vector<sp<LayerFE>>& layerFEs) {
     SFTRACE_CALL();