• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <shaders/shaders.h>
18 
19 #include <tonemap/tonemap.h>
20 
21 #include <cmath>
22 #include <optional>
23 
24 #include <math/mat4.h>
25 #include <system/graphics-base-v1.0.h>
26 #include <ui/ColorSpace.h>
27 
28 namespace android::shaders {
29 
30 namespace {
31 
toAidlDataspace(ui::Dataspace dataspace)32 aidl::android::hardware::graphics::common::Dataspace toAidlDataspace(ui::Dataspace dataspace) {
33     return static_cast<aidl::android::hardware::graphics::common::Dataspace>(dataspace);
34 }
35 
generateXYZTransforms(std::string & shader)36 void generateXYZTransforms(std::string& shader) {
37     shader.append(R"(
38         uniform float3x3 in_rgbToXyz;
39         uniform float3x3 in_xyzToSrcRgb;
40         uniform float4x4 in_colorTransform;
41         float3 ToXYZ(float3 rgb) {
42             return in_rgbToXyz * rgb;
43         }
44 
45         float3 ToSrcRGB(float3 xyz) {
46             return in_xyzToSrcRgb * xyz;
47         }
48 
49         float3 ApplyColorTransform(float3 rgb) {
50             return (in_colorTransform * float4(rgb, 1.0)).rgb;
51         }
52     )");
53 }
54 
55 // Conversion from relative light to absolute light
56 // Note that 1.0 == 203 nits.
generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace,std::string & shader)57 void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, std::string& shader) {
58     switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
59         case HAL_DATASPACE_TRANSFER_HLG:
60             // BT. 2408 says that a signal level of 0.75 == 203 nits for HLG, but that's after
61             // applying OOTF. But we haven't applied OOTF yet, so we need to scale by a different
62             // constant instead.
63             shader.append(R"(
64                 float3 ScaleLuminance(float3 xyz) {
65                     return xyz * 264.96;
66                 }
67             )");
68             break;
69         default:
70             shader.append(R"(
71                 float3 ScaleLuminance(float3 xyz) {
72                     return xyz * 203.0;
73                 }
74             )");
75             break;
76     }
77 }
78 
79 // Normalizes from absolute light back to relative light (maps from [0, maxNits] back to [0, 1])
generateLuminanceNormalizationForOOTF(ui::Dataspace inputDataspace,ui::Dataspace outputDataspace,std::string & shader)80 static void generateLuminanceNormalizationForOOTF(ui::Dataspace inputDataspace,
81                                                   ui::Dataspace outputDataspace,
82                                                   std::string& shader) {
83     switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
84         case HAL_DATASPACE_TRANSFER_ST2084:
85             shader.append(R"(
86                 float3 NormalizeLuminance(float3 xyz) {
87                     return xyz / 203.0;
88                 }
89             )");
90             break;
91         case HAL_DATASPACE_TRANSFER_HLG:
92             switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
93                 case HAL_DATASPACE_TRANSFER_HLG:
94                     shader.append(R"(
95                             float3 NormalizeLuminance(float3 xyz) {
96                                 return xyz / 264.96;
97                             }
98                         )");
99                     break;
100                 default:
101                     // Transcoding to HLG requires applying the inverse OOTF
102                     // with the expectation that the OOTF is then applied during
103                     // tonemapping downstream.
104                     // BT. 2100-2 operates on normalized luminances, so renormalize to the input to
105                     // correctly adjust gamma.
106                     // Note that following BT. 2408 for HLG OETF actually maps 0.75 == ~264.96 nits,
107                     // rather than 203 nits, because 203 nits == OOTF(invOETF(0.75)), so even though
108                     // we originally scaled by 203 nits we need to re-normalize to 264.96 nits when
109                     // converting to the correct brightness range.
110                     shader.append(R"(
111                             float3 NormalizeLuminance(float3 xyz) {
112                                 float ootfGain = pow(xyz.y / 1000.0, -0.2 / 1.2);
113                                 return xyz * ootfGain / 264.96;
114                             }
115                         )");
116                     break;
117             }
118             break;
119         default:
120             switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
121                 case HAL_DATASPACE_TRANSFER_HLG:
122                 case HAL_DATASPACE_TRANSFER_ST2084:
123                     // libtonemap outputs a range [0, in_libtonemap_displayMaxLuminance], so
124                     // normalize back to [0, 1] when the output is SDR.
125                     shader.append(R"(
126                         float3 NormalizeLuminance(float3 xyz) {
127                             return xyz / in_libtonemap_displayMaxLuminance;
128                         }
129                     )");
130                     break;
131                 default:
132                     // Otherwise normalize back down to the range [0, 1]
133                     // TODO: get this working for extended range outputs
134                     shader.append(R"(
135                         float3 NormalizeLuminance(float3 xyz) {
136                             return xyz / 203.0;
137                         }
138                     )");
139                     break;
140             }
141     }
142 }
143 
generateOOTF(ui::Dataspace inputDataspace,ui::Dataspace outputDataspace,std::string & shader)144 void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
145                   std::string& shader) {
146     shader.append(tonemap::getToneMapper()
147                           ->generateTonemapGainShaderSkSL(toAidlDataspace(inputDataspace),
148                                                           toAidlDataspace(outputDataspace))
149                           .c_str());
150 
151     generateLuminanceScalesForOOTF(inputDataspace, shader);
152     generateLuminanceNormalizationForOOTF(inputDataspace, outputDataspace, shader);
153 
154     // Some tonemappers operate on CIE luminance, other tonemappers operate on linear rgb
155     // luminance in the source gamut.
156     shader.append(R"(
157             float3 OOTF(float3 linearRGB) {
158                 float3 scaledLinearRGB = ScaleLuminance(linearRGB);
159                 float3 scaledXYZ = ToXYZ(scaledLinearRGB);
160 
161                 float gain = libtonemap_LookupTonemapGain(ToSrcRGB(scaledXYZ), scaledXYZ);
162 
163                 return NormalizeLuminance(scaledXYZ * gain);
164             }
165         )");
166 }
167 
generateOETF(std::string & shader)168 void generateOETF(std::string& shader) {
169     // Only support gamma 2.2 for now
170     shader.append(R"(
171         float3 OETF(float3 linear) {
172             return sign(linear) * pow(abs(linear), float3(1.0 / 2.2));
173         }
174     )");
175 }
176 
generateEffectiveOOTF(bool undoPremultipliedAlpha,LinearEffect::SkSLType type,bool needsCustomOETF,std::string & shader)177 void generateEffectiveOOTF(bool undoPremultipliedAlpha, LinearEffect::SkSLType type,
178                            bool needsCustomOETF, std::string& shader) {
179     switch (type) {
180         case LinearEffect::SkSLType::ColorFilter:
181             shader.append(R"(
182                 half4 main(half4 inputColor) {
183                     float4 c = float4(inputColor);
184             )");
185             break;
186         case LinearEffect::SkSLType::Shader:
187             shader.append(R"(
188                 uniform shader child;
189                 half4 main(float2 xy) {
190                     float4 c = float4(child.eval(xy));
191             )");
192             break;
193     }
194     if (undoPremultipliedAlpha) {
195         shader.append(R"(
196             c.rgb = c.rgb / (c.a + 0.0019);
197         )");
198     }
199     // We are using linear sRGB as a working space, with 1.0 == 203 nits
200     shader.append(R"(
201         c.rgb = ApplyColorTransform(OOTF(toLinearSrgb(c.rgb)));
202     )");
203     if (needsCustomOETF) {
204         shader.append(R"(
205             c.rgb = OETF(c.rgb);
206         )");
207     } else {
208         shader.append(R"(
209             c.rgb = fromLinearSrgb(c.rgb);
210         )");
211     }
212     if (undoPremultipliedAlpha) {
213         shader.append(R"(
214             c.rgb = c.rgb * (c.a + 0.0019);
215         )");
216     }
217     shader.append(R"(
218             return c;
219         }
220     )");
221 }
222 
223 template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true>
buildUniformValue(T value)224 std::vector<uint8_t> buildUniformValue(T value) {
225     std::vector<uint8_t> result;
226     result.resize(sizeof(value));
227     std::memcpy(result.data(), &value, sizeof(value));
228     return result;
229 }
230 
231 } // namespace
232 
buildLinearEffectSkSL(const LinearEffect & linearEffect)233 std::string buildLinearEffectSkSL(const LinearEffect& linearEffect) {
234     std::string shaderString;
235     generateXYZTransforms(shaderString);
236     generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
237 
238     const bool needsCustomOETF = (linearEffect.fakeOutputDataspace & HAL_DATASPACE_TRANSFER_MASK) ==
239             HAL_DATASPACE_TRANSFER_GAMMA2_2;
240     if (needsCustomOETF) {
241         generateOETF(shaderString);
242     }
243     generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, linearEffect.type, needsCustomOETF,
244                           shaderString);
245     return shaderString;
246 }
247 
toColorSpace(ui::Dataspace dataspace)248 ColorSpace toColorSpace(ui::Dataspace dataspace) {
249     switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
250         case HAL_DATASPACE_STANDARD_BT709:
251             return ColorSpace::sRGB();
252         case HAL_DATASPACE_STANDARD_DCI_P3:
253             return ColorSpace::DisplayP3();
254         case HAL_DATASPACE_STANDARD_BT2020:
255         case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
256             return ColorSpace::BT2020();
257         case HAL_DATASPACE_STANDARD_ADOBE_RGB:
258             return ColorSpace::AdobeRGB();
259             // TODO(b/208290320): BT601 format and variants return different primaries
260         case HAL_DATASPACE_STANDARD_BT601_625:
261         case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
262         case HAL_DATASPACE_STANDARD_BT601_525:
263         case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
264             // TODO(b/208290329): BT407M format returns different primaries
265         case HAL_DATASPACE_STANDARD_BT470M:
266             // TODO(b/208290904): FILM format returns different primaries
267         case HAL_DATASPACE_STANDARD_FILM:
268         case HAL_DATASPACE_STANDARD_UNSPECIFIED:
269         default:
270             return ColorSpace::sRGB();
271     }
272 }
273 
274 // Generates a list of uniforms to set on the LinearEffect shader above.
buildLinearEffectUniforms(const LinearEffect & linearEffect,const mat4 & colorTransform,float maxDisplayLuminance,float currentDisplayLuminanceNits,float maxLuminance,AHardwareBuffer * buffer,aidl::android::hardware::graphics::composer3::RenderIntent renderIntent)275 std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(
276         const LinearEffect& linearEffect, const mat4& colorTransform, float maxDisplayLuminance,
277         float currentDisplayLuminanceNits, float maxLuminance, AHardwareBuffer* buffer,
278         aidl::android::hardware::graphics::composer3::RenderIntent renderIntent) {
279     std::vector<tonemap::ShaderUniform> uniforms;
280 
281     auto inputColorSpace = toColorSpace(linearEffect.inputDataspace);
282     auto outputColorSpace = toColorSpace(linearEffect.outputDataspace);
283 
284     uniforms.push_back(
285             {.name = "in_rgbToXyz",
286              .value = buildUniformValue<mat3>(ColorSpace::linearExtendedSRGB().getRGBtoXYZ())});
287     uniforms.push_back({.name = "in_xyzToSrcRgb",
288                         .value = buildUniformValue<mat3>(inputColorSpace.getXYZtoRGB())});
289     // Transforms xyz colors to linear source colors, then applies the color transform, then
290     // transforms to linear extended RGB for skia to color manage.
291     uniforms.push_back({.name = "in_colorTransform",
292                         .value = buildUniformValue<mat4>(
293                                 mat4(ColorSpace::linearExtendedSRGB().getXYZtoRGB()) *
294                                 // TODO: the color transform ideally should be applied
295                                 // in the source colorspace, but doing that breaks
296                                 // renderengine tests
297                                 mat4(outputColorSpace.getRGBtoXYZ()) * colorTransform *
298                                 mat4(outputColorSpace.getXYZtoRGB()))});
299 
300     tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance,
301                                // If the input luminance is unknown, use display luminance (aka,
302                                // no-op any luminance changes).
303                                // This is expected to only be meaningful for PQ content
304                                .contentMaxLuminance =
305                                        maxLuminance > 0 ? maxLuminance : maxDisplayLuminance,
306                                .currentDisplayLuminance = currentDisplayLuminanceNits > 0
307                                        ? currentDisplayLuminanceNits
308                                        : maxDisplayLuminance,
309                                .buffer = buffer,
310                                .renderIntent = renderIntent};
311 
312     for (const auto uniform : tonemap::getToneMapper()->generateShaderSkSLUniforms(metadata)) {
313         uniforms.push_back(uniform);
314     }
315 
316     return uniforms;
317 }
318 
319 } // namespace android::shaders
320