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/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index d58f303..426ccd6 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -75,6 +75,7 @@
 #include "ColorSpaces.h"
 #include "compat/SkiaGpuContext.h"
 #include "filters/BlurFilter.h"
+#include "filters/GainmapFactory.h"
 #include "filters/GaussianBlurFilter.h"
 #include "filters/KawaseBlurDualFilter.h"
 #include "filters/KawaseBlurFilter.h"
@@ -238,12 +239,22 @@
 static inline SkPoint3 getSkPoint3(const android::vec3& vector) {
     return SkPoint3::Make(vector.x, vector.y, vector.z);
 }
+
 } // namespace
 
 namespace android {
 namespace renderengine {
 namespace skia {
 
+namespace {
+void trace(sp<Fence> fence) {
+    if (SFTRACE_ENABLED()) {
+        static gui::FenceMonitor sMonitor("RE Completion");
+        sMonitor.queueFence(std::move(fence));
+    }
+}
+} // namespace
+
 using base::StringAppendF;
 
 std::future<void> SkiaRenderEngine::primeCache(PrimeCacheConfig config) {
@@ -544,13 +555,15 @@
         const auto usingLocalTonemap =
                 parameters.display.tonemapStrategy == DisplaySettings::TonemapStrategy::Local &&
                 hdrType != HdrRenderType::SDR &&
-                shader->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr);
-
+                shader->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr) &&
+                (hdrType != HdrRenderType::DISPLAY_HDR ||
+                 parameters.display.targetHdrSdrRatio < parameters.layerDimmingRatio);
         if (usingLocalTonemap) {
-            static MouriMap kMapper;
-            const float ratio =
+            const float inputRatio =
                     hdrType == HdrRenderType::GENERIC_HDR ? 1.0f : parameters.layerDimmingRatio;
-            shader = kMapper.mouriMap(getActiveContext(), shader, ratio);
+            static MouriMap kMapper;
+            shader = kMapper.mouriMap(getActiveContext(), shader, inputRatio,
+                                      parameters.display.targetHdrSdrRatio);
         }
 
         // disable tonemapping if we already locally tonemapped
@@ -1187,11 +1200,48 @@
 
     LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
     auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface));
+    trace(drawFence);
+    resultPromise->set_value(std::move(drawFence));
+}
 
-    if (SFTRACE_ENABLED()) {
-        static gui::FenceMonitor sMonitor("RE Completion");
-        sMonitor.queueFence(drawFence);
-    }
+void SkiaRenderEngine::drawGainmapInternal(
+        const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
+        const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence,
+        const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
+        float hdrSdrRatio, ui::Dataspace dataspace,
+        const std::shared_ptr<ExternalTexture>& gainmap) {
+    std::lock_guard<std::mutex> lock(mRenderingMutex);
+    auto context = getActiveContext();
+    auto surfaceTextureRef = getOrCreateBackendTexture(gainmap->getBuffer(), true);
+    sk_sp<SkSurface> dstSurface =
+            surfaceTextureRef->getOrCreateSurface(ui::Dataspace::V0_SRGB_LINEAR);
+
+    waitFence(context, sdrFence);
+    const auto sdrTextureRef = getOrCreateBackendTexture(sdr->getBuffer(), false);
+    const auto sdrImage = sdrTextureRef->makeImage(dataspace, kPremul_SkAlphaType);
+    const auto sdrShader =
+            sdrImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+                                 SkSamplingOptions({SkFilterMode::kLinear, SkMipmapMode::kNone}),
+                                 nullptr);
+    waitFence(context, hdrFence);
+    const auto hdrTextureRef = getOrCreateBackendTexture(hdr->getBuffer(), false);
+    const auto hdrImage = hdrTextureRef->makeImage(dataspace, kPremul_SkAlphaType);
+    const auto hdrShader =
+            hdrImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+                                 SkSamplingOptions({SkFilterMode::kLinear, SkMipmapMode::kNone}),
+                                 nullptr);
+
+    static GainmapFactory kGainmapFactory;
+    const auto gainmapShader = kGainmapFactory.createSkShader(sdrShader, hdrShader, hdrSdrRatio);
+
+    const auto canvas = dstSurface->getCanvas();
+    SkPaint paint;
+    paint.setShader(gainmapShader);
+    paint.setBlendMode(SkBlendMode::kSrc);
+    canvas->drawPaint(paint);
+
+    auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface));
+    trace(drawFence);
     resultPromise->set_value(std::move(drawFence));
 }