Support HDR tonemapping in TextureView

This reuses the HDR tonemapping curve that was used in RenderEngine,
however display-level metadata may not be aligned. But because there are
no composition changes that can cause flicker, e.g., switching rapidly
between using a TextureView and a SurfaceView, then that should be okay.
That means that the HDR tonemapping is not as high quality as it could
be, but it is much better than before.

Bug: 200309590
Test: builds, boots
Test: Instagram video preview
Change-Id: I4dd042333f383f383d568b6f2326ee14facd08ed
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 491f5f5..a2d0103 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -107,6 +107,8 @@
     target: {
         android: {
             shared_libs: [
+                "android.hardware.graphics.common-V3-ndk",
+                "android.hardware.graphics.common@1.2",
                 "liblog",
                 "libcutils",
                 "libutils",
@@ -126,9 +128,11 @@
             static_libs: [
                 "libEGL_blobCache",
                 "libprotoutil",
+                "libshaders",
                 "libstatslog_hwui",
                 "libstatspull_lazy",
                 "libstatssocket_lazy",
+                "libtonemap",
             ],
         },
         host: {
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 0b3b393..a5c0924 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -20,9 +20,11 @@
 
 // TODO: Use public SurfaceTexture APIs once available and include public NDK header file instead.
 #include <surfacetexture/surface_texture_platform.h>
+
 #include "AutoBackendTextureRelease.h"
 #include "Matrix.h"
 #include "Properties.h"
+#include "android/hdr_metadata.h"
 #include "renderstate/RenderState.h"
 #include "renderthread/EglManager.h"
 #include "renderthread/RenderThread.h"
@@ -147,6 +149,9 @@
             mUpdateTexImage = false;
             float transformMatrix[16];
             android_dataspace dataspace;
+            AHdrMetadataType hdrMetadataType;
+            android_cta861_3_metadata cta861_3;
+            android_smpte2086_metadata smpte2086;
             int slot;
             bool newContent = false;
             ARect currentCrop;
@@ -155,8 +160,9 @@
             // is necessary if the SurfaceTexture queue is in synchronous mode, and we
             // cannot tell which mode it is in.
             AHardwareBuffer* hardwareBuffer = ASurfaceTexture_dequeueBuffer(
-                    mSurfaceTexture.get(), &slot, &dataspace, transformMatrix, &outTransform,
-                    &newContent, createReleaseFence, fenceWait, this, &currentCrop);
+                    mSurfaceTexture.get(), &slot, &dataspace, &hdrMetadataType, &cta861_3,
+                    &smpte2086, transformMatrix, &outTransform, &newContent, createReleaseFence,
+                    fenceWait, this, &currentCrop);
 
             if (hardwareBuffer) {
                 mCurrentSlot = slot;
@@ -173,7 +179,18 @@
                     SkRect currentCropRect =
                             SkRect::MakeLTRB(currentCrop.left, currentCrop.top, currentCrop.right,
                                              currentCrop.bottom);
-                    updateLayer(forceFilter, layerImage, outTransform, currentCropRect);
+
+                    float maxLuminanceNits = -1.f;
+                    if (hdrMetadataType & HDR10_SMPTE2086) {
+                        maxLuminanceNits = std::max(smpte2086.maxLuminance, maxLuminanceNits);
+                    }
+
+                    if (hdrMetadataType & HDR10_CTA861_3) {
+                        maxLuminanceNits =
+                                std::max(cta861_3.maxContentLightLevel, maxLuminanceNits);
+                    }
+                    updateLayer(forceFilter, layerImage, outTransform, currentCropRect,
+                                maxLuminanceNits);
                 }
             }
         }
@@ -186,13 +203,15 @@
 }
 
 void DeferredLayerUpdater::updateLayer(bool forceFilter, const sk_sp<SkImage>& layerImage,
-                                       const uint32_t transform, SkRect currentCrop) {
+                                       const uint32_t transform, SkRect currentCrop,
+                                       float maxLuminanceNits) {
     mLayer->setBlend(mBlend);
     mLayer->setForceFilter(forceFilter);
     mLayer->setSize(mWidth, mHeight);
     mLayer->setCurrentCropRect(currentCrop);
     mLayer->setWindowTransform(transform);
     mLayer->setImage(layerImage);
+    mLayer->setMaxLuminanceNits(maxLuminanceNits);
 }
 
 void DeferredLayerUpdater::detachSurfaceTexture() {
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index da8041f..9a4c550 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -91,7 +91,7 @@
     void detachSurfaceTexture();
 
     void updateLayer(bool forceFilter, const sk_sp<SkImage>& layerImage, const uint32_t transform,
-                     SkRect currentCrop);
+                     SkRect currentCrop, float maxLuminanceNits = -1.f);
 
     void destroyLayer();
 
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 0789344..47eb5d3 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -96,6 +96,12 @@
 
     inline sk_sp<SkImage> getImage() const { return this->layerImage; }
 
+    inline void setMaxLuminanceNits(float maxLuminanceNits) {
+        mMaxLuminanceNits = maxLuminanceNits;
+    }
+
+    inline float getMaxLuminanceNits() { return mMaxLuminanceNits; }
+
     void draw(SkCanvas* canvas);
 
 protected:
@@ -158,6 +164,11 @@
      */
     bool mBlend = false;
 
+    /**
+     * Max input luminance if the layer is HDR
+     */
+    float mMaxLuminanceNits = -1;
+
 };  // struct Layer
 
 }  // namespace uirenderer
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 1439656..553b08f 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -15,12 +15,19 @@
  */
 
 #include "LayerDrawable.h"
+
+#include <shaders/shaders.h>
+#include <utils/Color.h>
 #include <utils/MathUtils.h>
 
+#include "DeviceInfo.h"
 #include "GrBackendSurface.h"
 #include "SkColorFilter.h"
+#include "SkRuntimeEffect.h"
 #include "SkSurface.h"
 #include "gl/GrGLTypes.h"
+#include "math/mat4.h"
+#include "system/graphics-base-v1.0.h"
 #include "system/window.h"
 
 namespace android {
@@ -69,6 +76,35 @@
              isIntegerAligned(dstDevRect.y()));
 }
 
+static sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader,
+                                                const shaders::LinearEffect& linearEffect,
+                                                float maxDisplayLuminance, float maxLuminance) {
+    auto shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect));
+    auto [runtimeEffect, error] = SkRuntimeEffect::MakeForShader(std::move(shaderString));
+    if (!runtimeEffect) {
+        LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
+    }
+
+    SkRuntimeShaderBuilder effectBuilder(std::move(runtimeEffect));
+
+    effectBuilder.child("child") = std::move(shader);
+
+    const auto uniforms = shaders::buildLinearEffectUniforms(linearEffect, mat4(),
+                                                             maxDisplayLuminance, maxLuminance);
+
+    for (const auto& uniform : uniforms) {
+        effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
+    }
+
+    return effectBuilder.makeShader(nullptr, false);
+}
+
+static bool isHdrDataspace(ui::Dataspace dataspace) {
+    const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
+
+    return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG;
+}
+
 // TODO: Context arg probably doesn't belong here – do debug check at callsite instead.
 bool LayerDrawable::DrawLayer(GrRecordingContext* context,
                               SkCanvas* canvas,
@@ -150,8 +186,30 @@
             sampling = SkSamplingOptions(SkFilterMode::kLinear);
         }
 
-        canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint,
-                              constraint);
+        const auto sourceDataspace = static_cast<ui::Dataspace>(
+                ColorSpaceToADataSpace(layerImage->colorSpace(), layerImage->colorType()));
+        const SkImageInfo& imageInfo = canvas->imageInfo();
+        const auto destinationDataspace = static_cast<ui::Dataspace>(
+                ColorSpaceToADataSpace(imageInfo.colorSpace(), imageInfo.colorType()));
+
+        if (isHdrDataspace(sourceDataspace) || isHdrDataspace(destinationDataspace)) {
+            const auto effect = shaders::LinearEffect{
+                    .inputDataspace = sourceDataspace,
+                    .outputDataspace = destinationDataspace,
+                    .undoPremultipliedAlpha = layerImage->alphaType() == kPremul_SkAlphaType,
+                    .fakeInputDataspace = destinationDataspace};
+            auto shader = layerImage->makeShader(sampling,
+                                                 SkMatrix::RectToRect(skiaSrcRect, skiaDestRect));
+            constexpr float kMaxDisplayBrightess = 1000.f;
+            shader = createLinearEffectShader(std::move(shader), effect, kMaxDisplayBrightess,
+                                              layer->getMaxLuminanceNits());
+            paint.setShader(shader);
+            canvas->drawRect(skiaDestRect, paint);
+        } else {
+            canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint,
+                                  constraint);
+        }
+
         canvas->restore();
         // restore the original matrix
         if (useLayerTransform) {