summaryrefslogtreecommitdiff
path: root/libs/shaders/shaders.cpp
diff options
context:
space:
mode:
author Alec Mouri <alecmouri@google.com> 2023-04-07 18:00:58 +0000
committer Alec Mouri <alecmouri@google.com> 2023-04-25 22:28:10 +0000
commit3e5965f3184a828a1b7ee2b5bd5d5c2177807a35 (patch)
tree9e437e8f7ab0350753552acbf457c005a25e6e31 /libs/shaders/shaders.cpp
parent8aedbf30dd4123c3050a040ce1c1b6e9d07b96ec (diff)
Support screenshots of HDR content
Previously screenshots always rendered to either an SDR or a wide gamut colorspace. For screenshotting HDR content, this is only appropriate when the resulting screenshot (a) never leaves the device and (b) the relevant code has workarounds for the display to appropriately handle its luminance range. HDR screenshots will now have two paths: * A standard path for rendering to HLG. HLG was chosen because the OOTF shape is less hand-wavey than PQ's, does not require metadata, and bands less at 8-bits of color. * A special path for "display-native" screenshots. This is for use-cases like screen rotation where there are stricter color accuracy requirements for round-tripping. Skia already encodes the resulting screenshot by supplying an HLG CICP alongside a backwards-compatible transfer function, so it's only sufficient to change how SurfaceFlinger renders. Bug: 242324609 Bug: 276812775 Test: screencap binary Test: rotation animation Test: swiping in Recents Change-Id: Ic9edb92391d3beb38d076fba8f15e3fdcc2b8f50
Diffstat (limited to 'libs/shaders/shaders.cpp')
-rw-r--r--libs/shaders/shaders.cpp58
1 files changed, 39 insertions, 19 deletions
diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp
index a3c403e551..19518eaecc 100644
--- a/libs/shaders/shaders.cpp
+++ b/libs/shaders/shaders.cpp
@@ -191,31 +191,35 @@ void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, ui::Dataspace
)");
break;
default:
+ // Input is SDR so map to its white point luminance
switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
+ // Max HLG output is nominally 1000 nits, but BT. 2100-2 allows
+ // for gamma correcting the HLG OOTF for displays with a different
+ // dynamic range. Scale to 1000 nits to apply an inverse OOTF against
+ // a reference display correctly.
+ // TODO: Use knowledge of the dimming ratio here to prevent
+ // unintended gamma shaft.
case HAL_DATASPACE_TRANSFER_HLG:
- // SDR -> HDR tonemap
shader.append(R"(
float3 ScaleLuminance(float3 xyz) {
- return xyz * in_libtonemap_inputMaxLuminance;
+ return xyz * 1000.0;
}
)");
break;
default:
- // Input and output are both SDR, so no tone-mapping is expected so
- // no-op the luminance normalization.
shader.append(R"(
- float3 ScaleLuminance(float3 xyz) {
- return xyz * in_libtonemap_displayMaxLuminance;
- }
- )");
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * in_libtonemap_displayMaxLuminance;
+ }
+ )");
break;
}
}
}
// Normalizes from absolute light back to relative light (maps from [0, maxNits] back to [0, 1])
-static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace,
+static void generateLuminanceNormalizationForOOTF(ui::Dataspace inputDataspace,
+ ui::Dataspace outputDataspace,
std::string& shader) {
switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
case HAL_DATASPACE_TRANSFER_ST2084:
@@ -226,11 +230,28 @@ static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace,
)");
break;
case HAL_DATASPACE_TRANSFER_HLG:
- shader.append(R"(
- float3 NormalizeLuminance(float3 xyz) {
- return xyz / 1000.0;
- }
- )");
+ switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / 1000.0;
+ }
+ )");
+ break;
+ default:
+ // Transcoding to HLG requires applying the inverse OOTF
+ // with the expectation that the OOTF is then applied during
+ // tonemapping downstream.
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ // BT. 2100-2 operates on normalized luminances,
+ // so renormalize to the input
+ float ootfGain = pow(xyz.y / 1000.0, -0.2 / 1.2) / 1000.0;
+ return xyz * ootfGain;
+ }
+ )");
+ break;
+ }
break;
default:
shader.append(R"(
@@ -250,7 +271,7 @@ void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
.c_str());
generateLuminanceScalesForOOTF(inputDataspace, outputDataspace, shader);
- generateLuminanceNormalizationForOOTF(outputDataspace, shader);
+ generateLuminanceNormalizationForOOTF(inputDataspace, outputDataspace, shader);
shader.append(R"(
float3 OOTF(float3 linearRGB, float3 xyz) {
@@ -501,9 +522,8 @@ std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(
tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance,
// 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
- // uncalibrated displays
+ // no-op any luminance changes).
+ // This is expected to only be meaningful for PQ content
.contentMaxLuminance =
maxLuminance > 0 ? maxLuminance : maxDisplayLuminance,
.currentDisplayLuminance = currentDisplayLuminanceNits > 0