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",
+ "[email protected]",
"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, ¤tCrop);
+ mSurfaceTexture.get(), &slot, &dataspace, &hdrMetadataType, &cta861_3,
+ &smpte2086, transformMatrix, &outTransform, &newContent, createReleaseFence,
+ fenceWait, this, ¤tCrop);
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) {