Android 13 中引入了一个名为 libtonemap
的供应商可配置静态库,用于定义色调映射操作,并与 SurfaceFlinger 进程和硬件混合渲染器 (HWC) 实现共享。借助此功能,原始设备制造商 (OEM) 可以在框架和供应商之间定义和共享其屏幕色调映射算法,从而减少色调映射中的不匹配问题。
在 Android 13 之前,屏幕专用色调映射操作未在 HWC、SurfaceFlinger 和应用之间共享。根据渲染路径,对于 HDR 内容,这会导致图片质量不匹配,即 HDR 内容以不同的方式色调映射到输出空间。这种情况在屏幕旋转等场景中较为明显,在此类场景中,合成策略会在 GPU 和 DPU 之间变化;这种情况还体现为 TextureView 和 SurfaceView 之间的渲染行为差异。
本页将介绍 libtonemap
库的接口、自定义和验证详情。
色调映射库的接口
libtonemap
库包含 CPU 支持的实现和 SkSL 着色器,可由 SurfaceFlinger 插入以进行 GPU 后端合成,也可由 HWC 插入以生成色调映射查询表 (LUT)。libtonemap
的入口点是 android::tonemap::getToneMapper()
,它会返回一个用于实现 ToneMapper
接口的对象。
ToneMapper
接口支持以下功能:
生成色调映射 LUT
接口
ToneMapper::lookupTonemapGain
是libtonemap_LookupTonemapGain()
中定义的着色器的 CPU 实现。它供框架中的单元测试使用,并且可供合作伙伴用来协助在其颜色流水线中生成色调映射 LUT。libtonemap_LookupTonemapGain()
在线性 RGB 和 XYZ 中都会接受非标准化绝对线性空间中的颜色值,并返回一个浮点数,用于说明在线性空间中将输入颜色乘以多少。生成 SkSL 着色器
给定源和目标数据空间时,接口
ToneMapper::generateTonemapGainShaderSkSL()
会返回一个 SkSL 着色器字符串。SkSL 着色器会插入RenderEngine
(适用于 SurfaceFlinger 的 GPU 加速合成组件)的 Skia 实现中。着色器也会插入libhwui
,以便针对TextureView
高效地执行从 HDR 到 SDR 的色调映射。由于生成的字符串内嵌在 Skia 使用的其他 SkSL 着色器中,因此着色器必须遵循以下规则:- 着色器字符串必须具有带
float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz)
签名的入口点,其中linearRGB
是线性空间中 RGB 像素的绝对尼特值,xyz
是已转换为 XYZ 的linearRGB
。 - 着色器字符串使用的任何辅助方法都必须以字符串
libtonemap_
为前缀,以免框架着色器定义发生冲突。同样,输入 uniform 必须带有in_libtonemap_
前缀。
- 着色器字符串必须具有带
生成 SkSL uniform
鉴于描述不同 HDR 标准和显示条件的元数据的元数据
struct
,接口ToneMapper::generateShaderSkSLUniforms()
会返回以下内容:由 SkSL 着色器绑定的 uniform 的列表。
uniform 值
in_libtonemap_displayMaxLuminance
和in_libtonemap_inputMaxLuminance
。将输入扩缩为libtonemap
并将输出标准化(如果适用)时,框架着色器会使用这些值。
目前,生成 uniform 的过程与输入和输出数据空间无关。
自定义
libtonemap
库的参考实现会生成可接受的结果。不过,由于 GPU 合成使用的色调映射算法可能与 DPU 合成使用的色调映射算法不同,因此在某些场景(例如旋转动画)中使用参考实现可能会导致闪烁。自定义可以解决此类供应商特有的图片质量问题。
强烈建议 OEM 替换 libtonemap
的实现,以定义自己的 ToneMapper
子类(由 getToneMapper()
返回)。自定义实现时,合作伙伴应执行以下操作之一:
- 直接修改
libtonemap
的实现。 - 定义自己的静态库,将该库编译为独立的库,并将
libtonemap
库的.a
文件替换为通过其自定义库生成的文件。
供应商无需修改任何内核代码,但多个供应商必须传达有关 DPU 色调映射算法的详细信息,才能正确实现。
验证
请按照以下步骤验证您的实现:
在符合显示系统支持的任何 HDR 标准(例如 HLG、HDR10、HDR10+ 或 DolbyVision)的屏幕上播放 HDR 视频。
切换 GPU 合成,以确保用户不会察觉到闪烁。
使用以下
adb
命令切换 GPU 合成:adb shell service call SurfaceFlinger 1008 i32 <0 to enable HWC composition, 1 to force GPU composition>
常见问题
此实现可能会出现以下问题:
当 GPU 合成使用的渲染目标精度低于 HDR 内容的典型值时,就会出现条带。例如,当 HWC 实现支持不透明的 10 位 HDR 格式(例如 RGBA1010102 或 P010),但要求 GPU 合成写入 8 位格式(如 RGBA8888)以支持 Alpha 通道时,可能会出现条带。
如果 DPU 和 GPU 以不同的精度运行,就会出现量化差异,从而导致细微的色偏。
上述每个问题都与底层硬件的相对精度差异有关。典型的解决方法是确保精度较低的路径中存在一个抖动步骤,使所有精度差异都不易察觉。