Use BT2100 OOTF for HLG...
...which it turns out we already do, except we apply a tone-map for
HDR10 after scaling the luminance. Don't apply the HDR10 tone-map, and
instead linearly normalize to max display luminance.
Furthermore, adjust the gamma used in the default HLG OOTF in
libshaders to take into account current display luminance according to
the BT2100 spec, which says that the OOTF gamma should be adjusted if
the effective luminance differs from 1000 nits
Bug: 208933319
Test: librenderengine_test
Test: libtonemap_test
Test: HLG and PQ test videos on youtube
Change-Id: I622096ad387420ce4769f6f080b8756cd57baa7d
diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp
index 4d88d5d..03da3ec 100644
--- a/libs/shaders/shaders.cpp
+++ b/libs/shaders/shaders.cpp
@@ -18,6 +18,7 @@
#include <tonemap/tonemap.h>
+#include <cmath>
#include <optional>
#include <math/mat4.h>
@@ -26,12 +27,13 @@
namespace android::shaders {
-static aidl::android::hardware::graphics::common::Dataspace toAidlDataspace(
- ui::Dataspace dataspace) {
+namespace {
+
+aidl::android::hardware::graphics::common::Dataspace toAidlDataspace(ui::Dataspace dataspace) {
return static_cast<aidl::android::hardware::graphics::common::Dataspace>(dataspace);
}
-static void generateEOTF(ui::Dataspace dataspace, std::string& shader) {
+void generateEOTF(ui::Dataspace dataspace, std::string& shader) {
switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
case HAL_DATASPACE_TRANSFER_ST2084:
shader.append(R"(
@@ -156,7 +158,7 @@
}
}
-static void generateXYZTransforms(std::string& shader) {
+void generateXYZTransforms(std::string& shader) {
shader.append(R"(
uniform float4x4 in_rgbToXyz;
uniform float4x4 in_xyzToRgb;
@@ -171,8 +173,8 @@
}
// Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits])
-static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace,
- ui::Dataspace outputDataspace, std::string& shader) {
+void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
+ std::string& shader) {
switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
case HAL_DATASPACE_TRANSFER_ST2084:
shader.append(R"(
@@ -183,8 +185,9 @@
break;
case HAL_DATASPACE_TRANSFER_HLG:
shader.append(R"(
+ uniform float in_hlgGamma;
float3 ScaleLuminance(float3 xyz) {
- return xyz * 1000.0 * pow(xyz.y, 0.2);
+ return xyz * 1000.0 * pow(xyz.y, in_hlgGamma - 1);
}
)");
break;
@@ -225,8 +228,10 @@
break;
case HAL_DATASPACE_TRANSFER_HLG:
shader.append(R"(
+ uniform float in_hlgGamma;
float3 NormalizeLuminance(float3 xyz) {
- return xyz / 1000.0 * pow(xyz.y / 1000.0, -0.2 / 1.2);
+ return xyz / 1000.0 *
+ pow(xyz.y / 1000.0, (1 - in_hlgGamma) / (in_hlgGamma));
}
)");
break;
@@ -240,8 +245,8 @@
}
}
-static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
- std::string& shader) {
+void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
+ std::string& shader) {
shader.append(tonemap::getToneMapper()
->generateTonemapGainShaderSkSL(toAidlDataspace(inputDataspace),
toAidlDataspace(outputDataspace))
@@ -262,7 +267,7 @@
)");
}
-static void generateOETF(ui::Dataspace dataspace, std::string& shader) {
+void generateOETF(ui::Dataspace dataspace, std::string& shader) {
switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
case HAL_DATASPACE_TRANSFER_ST2084:
shader.append(R"(
@@ -384,7 +389,7 @@
}
}
-static void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shader) {
+void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shader) {
shader.append(R"(
uniform shader child;
half4 main(float2 xy) {
@@ -412,7 +417,7 @@
}
// please keep in sync with toSkColorSpace function in renderengine/skia/ColorSpaces.cpp
-static ColorSpace toColorSpace(ui::Dataspace dataspace) {
+ColorSpace toColorSpace(ui::Dataspace dataspace) {
switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
case HAL_DATASPACE_STANDARD_BT709:
return ColorSpace::sRGB();
@@ -438,6 +443,21 @@
}
}
+template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true>
+std::vector<uint8_t> buildUniformValue(T value) {
+ std::vector<uint8_t> result;
+ result.resize(sizeof(value));
+ std::memcpy(result.data(), &value, sizeof(value));
+ return result;
+}
+
+// Refer to BT2100-2
+float computeHlgGamma(float currentDisplayBrightnessNits) {
+ return 1.2 + 0.42 * std::log10(currentDisplayBrightnessNits / 1000);
+}
+
+} // namespace
+
std::string buildLinearEffectSkSL(const LinearEffect& linearEffect) {
std::string shaderString;
generateEOTF(linearEffect.fakeInputDataspace == ui::Dataspace::UNKNOWN
@@ -451,14 +471,6 @@
return shaderString;
}
-template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true>
-std::vector<uint8_t> buildUniformValue(T value) {
- std::vector<uint8_t> result;
- result.resize(sizeof(value));
- std::memcpy(result.data(), &value, sizeof(value));
- return result;
-}
-
// Generates a list of uniforms to set on the LinearEffect shader above.
std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(const LinearEffect& linearEffect,
const mat4& colorTransform,
@@ -480,8 +492,13 @@
colorTransform * mat4(outputColorSpace.getXYZtoRGB()))});
}
+ if ((linearEffect.inputDataspace & HAL_DATASPACE_TRANSFER_MASK) == HAL_DATASPACE_TRANSFER_HLG) {
+ uniforms.push_back(
+ {.name = "in_hlgGamma",
+ .value = buildUniformValue<float>(computeHlgGamma(currentDisplayLuminanceNits))});
+ }
+
tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance,
- .currentDisplayLuminanceNits = currentDisplayLuminanceNits,
// If the input luminance is unknown, use display luminance (aka,
// no-op any luminance changes)
// This will be the case for eg screenshots in addition to