• 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 
generateEOTF(ui::Dataspace dataspace,std::string & shader)36 void generateEOTF(ui::Dataspace dataspace, std::string& shader) {
37     switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
38         case HAL_DATASPACE_TRANSFER_ST2084:
39             shader.append(R"(
40 
41                 float3 EOTF(float3 color) {
42                     float m1 = (2610.0 / 4096.0) / 4.0;
43                     float m2 = (2523.0 / 4096.0) * 128.0;
44                     float c1 = (3424.0 / 4096.0);
45                     float c2 = (2413.0 / 4096.0) * 32.0;
46                     float c3 = (2392.0 / 4096.0) * 32.0;
47 
48                     float3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / float3(m2));
49                     tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
50                     return pow(tmp, 1.0 / float3(m1));
51                 }
52             )");
53             break;
54         case HAL_DATASPACE_TRANSFER_HLG:
55             shader.append(R"(
56                 float EOTF_channel(float channel) {
57                     const float a = 0.17883277;
58                     const float b = 0.28466892;
59                     const float c = 0.55991073;
60                     return channel <= 0.5 ? channel * channel / 3.0 :
61                             (exp((channel - c) / a) + b) / 12.0;
62                 }
63 
64                 float3 EOTF(float3 color) {
65                     return float3(EOTF_channel(color.r), EOTF_channel(color.g),
66                             EOTF_channel(color.b));
67                 }
68             )");
69             break;
70         case HAL_DATASPACE_TRANSFER_LINEAR:
71             shader.append(R"(
72                 float3 EOTF(float3 color) {
73                     return color;
74                 }
75             )");
76             break;
77         case HAL_DATASPACE_TRANSFER_SMPTE_170M:
78             shader.append(R"(
79 
80                 float EOTF_sRGB(float srgb) {
81                     return srgb <= 0.08125 ? srgb / 4.50 : pow((srgb + 0.099) / 1.099, 1 / 0.45);
82                 }
83 
84                 float3 EOTF_sRGB(float3 srgb) {
85                     return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
86                 }
87 
88                 float3 EOTF(float3 srgb) {
89                     return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
90                 }
91             )");
92             break;
93         case HAL_DATASPACE_TRANSFER_GAMMA2_2:
94             shader.append(R"(
95 
96                 float EOTF_sRGB(float srgb) {
97                     return pow(srgb, 2.2);
98                 }
99 
100                 float3 EOTF_sRGB(float3 srgb) {
101                     return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
102                 }
103 
104                 float3 EOTF(float3 srgb) {
105                     return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
106                 }
107             )");
108             break;
109         case HAL_DATASPACE_TRANSFER_GAMMA2_6:
110             shader.append(R"(
111 
112                 float EOTF_sRGB(float srgb) {
113                     return pow(srgb, 2.6);
114                 }
115 
116                 float3 EOTF_sRGB(float3 srgb) {
117                     return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
118                 }
119 
120                 float3 EOTF(float3 srgb) {
121                     return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
122                 }
123             )");
124             break;
125         case HAL_DATASPACE_TRANSFER_GAMMA2_8:
126             shader.append(R"(
127 
128                 float EOTF_sRGB(float srgb) {
129                     return pow(srgb, 2.8);
130                 }
131 
132                 float3 EOTF_sRGB(float3 srgb) {
133                     return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
134                 }
135 
136                 float3 EOTF(float3 srgb) {
137                     return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
138                 }
139             )");
140             break;
141         case HAL_DATASPACE_TRANSFER_SRGB:
142         default:
143             shader.append(R"(
144 
145                 float EOTF_sRGB(float srgb) {
146                     return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
147                 }
148 
149                 float3 EOTF_sRGB(float3 srgb) {
150                     return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
151                 }
152 
153                 float3 EOTF(float3 srgb) {
154                     return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
155                 }
156             )");
157             break;
158     }
159 }
160 
generateXYZTransforms(std::string & shader)161 void generateXYZTransforms(std::string& shader) {
162     shader.append(R"(
163         uniform float4x4 in_rgbToXyz;
164         uniform float4x4 in_xyzToRgb;
165         float3 ToXYZ(float3 rgb) {
166             return (in_rgbToXyz * float4(rgb, 1.0)).rgb;
167         }
168 
169         float3 ToRGB(float3 xyz) {
170             return clamp((in_xyzToRgb * float4(xyz, 1.0)).rgb, 0.0, 1.0);
171         }
172     )");
173 }
174 
175 // Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits])
generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace,ui::Dataspace outputDataspace,std::string & shader)176 void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
177                                     std::string& shader) {
178     switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
179         case HAL_DATASPACE_TRANSFER_ST2084:
180             shader.append(R"(
181                     float3 ScaleLuminance(float3 xyz) {
182                         return xyz * 10000.0;
183                     }
184                 )");
185             break;
186         case HAL_DATASPACE_TRANSFER_HLG:
187             shader.append(R"(
188                     float3 ScaleLuminance(float3 xyz) {
189                         return xyz * 1000.0;
190                     }
191                 )");
192             break;
193         default:
194             switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
195                 case HAL_DATASPACE_TRANSFER_ST2084:
196                 case HAL_DATASPACE_TRANSFER_HLG:
197                     // SDR -> HDR tonemap
198                     shader.append(R"(
199                             float3 ScaleLuminance(float3 xyz) {
200                                 return xyz * in_libtonemap_inputMaxLuminance;
201                             }
202                         )");
203                     break;
204                 default:
205                     // Input and output are both SDR, so no tone-mapping is expected so
206                     // no-op the luminance normalization.
207                     shader.append(R"(
208                                 float3 ScaleLuminance(float3 xyz) {
209                                     return xyz * in_libtonemap_displayMaxLuminance;
210                                 }
211                             )");
212                     break;
213             }
214     }
215 }
216 
217 // Normalizes from absolute light back to relative light (maps from [0, maxNits] back to [0, 1])
generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace,std::string & shader)218 static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace,
219                                                   std::string& shader) {
220     switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
221         case HAL_DATASPACE_TRANSFER_ST2084:
222             shader.append(R"(
223                     float3 NormalizeLuminance(float3 xyz) {
224                         return xyz / 10000.0;
225                     }
226                 )");
227             break;
228         case HAL_DATASPACE_TRANSFER_HLG:
229             shader.append(R"(
230                     float3 NormalizeLuminance(float3 xyz) {
231                         return xyz / 1000.0;
232                     }
233                 )");
234             break;
235         default:
236             shader.append(R"(
237                     float3 NormalizeLuminance(float3 xyz) {
238                         return xyz / in_libtonemap_displayMaxLuminance;
239                     }
240                 )");
241             break;
242     }
243 }
244 
generateOOTF(ui::Dataspace inputDataspace,ui::Dataspace outputDataspace,std::string & shader)245 void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
246                   std::string& shader) {
247     shader.append(tonemap::getToneMapper()
248                           ->generateTonemapGainShaderSkSL(toAidlDataspace(inputDataspace),
249                                                           toAidlDataspace(outputDataspace))
250                           .c_str());
251 
252     generateLuminanceScalesForOOTF(inputDataspace, outputDataspace, shader);
253     generateLuminanceNormalizationForOOTF(outputDataspace, shader);
254 
255     shader.append(R"(
256             float3 OOTF(float3 linearRGB, float3 xyz) {
257                 float3 scaledLinearRGB = ScaleLuminance(linearRGB);
258                 float3 scaledXYZ = ScaleLuminance(xyz);
259 
260                 float gain = libtonemap_LookupTonemapGain(scaledLinearRGB, scaledXYZ);
261 
262                 return NormalizeLuminance(scaledXYZ * gain);
263             }
264         )");
265 }
266 
generateOETF(ui::Dataspace dataspace,std::string & shader)267 void generateOETF(ui::Dataspace dataspace, std::string& shader) {
268     switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
269         case HAL_DATASPACE_TRANSFER_ST2084:
270             shader.append(R"(
271 
272                 float3 OETF(float3 xyz) {
273                     float m1 = (2610.0 / 4096.0) / 4.0;
274                     float m2 = (2523.0 / 4096.0) * 128.0;
275                     float c1 = (3424.0 / 4096.0);
276                     float c2 = (2413.0 / 4096.0) * 32.0;
277                     float c3 = (2392.0 / 4096.0) * 32.0;
278 
279                     float3 tmp = pow(xyz, float3(m1));
280                     tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
281                     return pow(tmp, float3(m2));
282                 }
283             )");
284             break;
285         case HAL_DATASPACE_TRANSFER_HLG:
286             shader.append(R"(
287                 float OETF_channel(float channel) {
288                     const float a = 0.17883277;
289                     const float b = 0.28466892;
290                     const float c = 0.55991073;
291                     return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
292                             a * log(12.0 * channel - b) + c;
293                 }
294 
295                 float3 OETF(float3 linear) {
296                     return float3(OETF_channel(linear.r), OETF_channel(linear.g),
297                             OETF_channel(linear.b));
298                 }
299             )");
300             break;
301         case HAL_DATASPACE_TRANSFER_LINEAR:
302             shader.append(R"(
303                 float3 OETF(float3 linear) {
304                     return linear;
305                 }
306             )");
307             break;
308         case HAL_DATASPACE_TRANSFER_SMPTE_170M:
309             shader.append(R"(
310                 float OETF_sRGB(float linear) {
311                     return linear <= 0.018 ?
312                             linear * 4.50 : (pow(linear, 0.45) * 1.099) - 0.099;
313                 }
314 
315                 float3 OETF_sRGB(float3 linear) {
316                     return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
317                 }
318 
319                 float3 OETF(float3 linear) {
320                     return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
321                 }
322             )");
323             break;
324         case HAL_DATASPACE_TRANSFER_GAMMA2_2:
325             shader.append(R"(
326                 float OETF_sRGB(float linear) {
327                     return pow(linear, (1.0 / 2.2));
328                 }
329 
330                 float3 OETF_sRGB(float3 linear) {
331                     return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
332                 }
333 
334                 float3 OETF(float3 linear) {
335                     return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
336                 }
337             )");
338             break;
339         case HAL_DATASPACE_TRANSFER_GAMMA2_6:
340             shader.append(R"(
341                 float OETF_sRGB(float linear) {
342                     return pow(linear, (1.0 / 2.6));
343                 }
344 
345                 float3 OETF_sRGB(float3 linear) {
346                     return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
347                 }
348 
349                 float3 OETF(float3 linear) {
350                     return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
351                 }
352             )");
353             break;
354         case HAL_DATASPACE_TRANSFER_GAMMA2_8:
355             shader.append(R"(
356                 float OETF_sRGB(float linear) {
357                     return pow(linear, (1.0 / 2.8));
358                 }
359 
360                 float3 OETF_sRGB(float3 linear) {
361                     return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
362                 }
363 
364                 float3 OETF(float3 linear) {
365                     return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
366                 }
367             )");
368             break;
369         case HAL_DATASPACE_TRANSFER_SRGB:
370         default:
371             shader.append(R"(
372                 float OETF_sRGB(float linear) {
373                     return linear <= 0.0031308 ?
374                             linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
375                 }
376 
377                 float3 OETF_sRGB(float3 linear) {
378                     return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
379                 }
380 
381                 float3 OETF(float3 linear) {
382                     return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
383                 }
384             )");
385             break;
386     }
387 }
388 
generateEffectiveOOTF(bool undoPremultipliedAlpha,std::string & shader)389 void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shader) {
390     shader.append(R"(
391         uniform shader child;
392         half4 main(float2 xy) {
393             float4 c = float4(child.eval(xy));
394     )");
395     if (undoPremultipliedAlpha) {
396         shader.append(R"(
397             c.rgb = c.rgb / (c.a + 0.0019);
398         )");
399     }
400     shader.append(R"(
401         float3 linearRGB = EOTF(c.rgb);
402         float3 xyz = ToXYZ(linearRGB);
403         c.rgb = OETF(ToRGB(OOTF(linearRGB, xyz)));
404     )");
405     if (undoPremultipliedAlpha) {
406         shader.append(R"(
407             c.rgb = c.rgb * (c.a + 0.0019);
408         )");
409     }
410     shader.append(R"(
411             return c;
412         }
413     )");
414 }
415 
416 // please keep in sync with toSkColorSpace function in renderengine/skia/ColorSpaces.cpp
toColorSpace(ui::Dataspace dataspace)417 ColorSpace toColorSpace(ui::Dataspace dataspace) {
418     switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
419         case HAL_DATASPACE_STANDARD_BT709:
420             return ColorSpace::sRGB();
421         case HAL_DATASPACE_STANDARD_DCI_P3:
422             return ColorSpace::DisplayP3();
423         case HAL_DATASPACE_STANDARD_BT2020:
424         case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
425             return ColorSpace::BT2020();
426         case HAL_DATASPACE_STANDARD_ADOBE_RGB:
427             return ColorSpace::AdobeRGB();
428         // TODO(b/208290320): BT601 format and variants return different primaries
429         case HAL_DATASPACE_STANDARD_BT601_625:
430         case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
431         case HAL_DATASPACE_STANDARD_BT601_525:
432         case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
433         // TODO(b/208290329): BT407M format returns different primaries
434         case HAL_DATASPACE_STANDARD_BT470M:
435         // TODO(b/208290904): FILM format returns different primaries
436         case HAL_DATASPACE_STANDARD_FILM:
437         case HAL_DATASPACE_STANDARD_UNSPECIFIED:
438         default:
439             return ColorSpace::sRGB();
440     }
441 }
442 
443 template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true>
buildUniformValue(T value)444 std::vector<uint8_t> buildUniformValue(T value) {
445     std::vector<uint8_t> result;
446     result.resize(sizeof(value));
447     std::memcpy(result.data(), &value, sizeof(value));
448     return result;
449 }
450 
451 } // namespace
452 
buildLinearEffectSkSL(const LinearEffect & linearEffect)453 std::string buildLinearEffectSkSL(const LinearEffect& linearEffect) {
454     std::string shaderString;
455     generateEOTF(linearEffect.fakeInputDataspace == ui::Dataspace::UNKNOWN
456                          ? linearEffect.inputDataspace
457                          : linearEffect.fakeInputDataspace,
458                  shaderString);
459     generateXYZTransforms(shaderString);
460     generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
461     generateOETF(linearEffect.outputDataspace, shaderString);
462     generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
463     return shaderString;
464 }
465 
466 // 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)467 std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(
468         const LinearEffect& linearEffect, const mat4& colorTransform, float maxDisplayLuminance,
469         float currentDisplayLuminanceNits, float maxLuminance, AHardwareBuffer* buffer,
470         aidl::android::hardware::graphics::composer3::RenderIntent renderIntent) {
471     std::vector<tonemap::ShaderUniform> uniforms;
472 
473     const ui::Dataspace inputDataspace = linearEffect.fakeInputDataspace == ui::Dataspace::UNKNOWN
474             ? linearEffect.inputDataspace
475             : linearEffect.fakeInputDataspace;
476 
477     if (inputDataspace == linearEffect.outputDataspace) {
478         uniforms.push_back({.name = "in_rgbToXyz", .value = buildUniformValue<mat4>(mat4())});
479         uniforms.push_back(
480                 {.name = "in_xyzToRgb", .value = buildUniformValue<mat4>(colorTransform)});
481     } else {
482         ColorSpace inputColorSpace = toColorSpace(inputDataspace);
483         ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace);
484         uniforms.push_back({.name = "in_rgbToXyz",
485                             .value = buildUniformValue<mat4>(mat4(inputColorSpace.getRGBtoXYZ()))});
486         uniforms.push_back({.name = "in_xyzToRgb",
487                             .value = buildUniformValue<mat4>(
488                                     colorTransform * mat4(outputColorSpace.getXYZtoRGB()))});
489     }
490 
491     tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance,
492                                // If the input luminance is unknown, use display luminance (aka,
493                                // no-op any luminance changes)
494                                // This will be the case for eg screenshots in addition to
495                                // uncalibrated displays
496                                .contentMaxLuminance =
497                                        maxLuminance > 0 ? maxLuminance : maxDisplayLuminance,
498                                .currentDisplayLuminance = currentDisplayLuminanceNits > 0
499                                        ? currentDisplayLuminanceNits
500                                        : maxDisplayLuminance,
501                                .buffer = buffer,
502                                .renderIntent = renderIntent};
503 
504     for (const auto uniform : tonemap::getToneMapper()->generateShaderSkSLUniforms(metadata)) {
505         uniforms.push_back(uniform);
506     }
507 
508     return uniforms;
509 }
510 
511 } // namespace android::shaders
512