将 HDR 亮度色调映射到 SDR 兼容范围

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::lookupTonemapGainlibtonemap_LookupTonemapGain() 中定义的着色器的 CPU 实现。框架中的单元测试使用此接口,合作伙伴可以使用此接口来协助在其颜色管道内生成色调映射 LUT。

    libtonemap_LookupTonemapGain() 接受绝对的、未归一化的线性空间中的颜色值,包括线性 RGB 和 XYZ,并返回一个浮点数,描述在线性空间中将输入颜色乘以多少。

  • 生成 SkSL 着色器

    接口 ToneMapper::generateTonemapGainShaderSkSL() 返回一个 SkSL 着色器字符串,给定源和目标数据空间。SkSL 着色器被插入到 RenderEngine 的 Skia 实现中,RenderEngine 是 SurfaceFlinger 的 GPU 加速合成组件。该着色器也插入到 libhwui 中,以便可以为 TextureView 有效地执行 HDR 到 SDR 的色调映射。由于生成的字符串被内联到 Skia 使用的其他 SkSL 着色器中,因此该着色器必须遵守以下规则

    • 着色器字符串必须具有一个入口点,其签名为 float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz),其中 linearRGB 是线性空间中 RGB 像素的绝对尼特值,xyz 是转换为 XYZ 的 linearRGB
    • 着色器字符串使用的任何辅助方法都必须以字符串 libtonemap_ 为前缀,以避免框架着色器定义冲突。同样,输入 uniform 变量必须以 in_libtonemap_ 为前缀。
  • 生成 SkSL uniform 变量

    接口 ToneMapper::generateShaderSkSLUniforms() 返回以下内容,给定一个描述来自不同 HDR 标准和显示条件的元数据 struct

    • SkSL 着色器绑定的 uniform 变量列表。

    • uniform 变量值 in_libtonemap_displayMaxLuminancein_libtonemap_inputMaxLuminance。当将输入缩放到 libtonemap 中以及在适用时对输出进行归一化时,框架着色器会使用这些值。

    目前,生成 uniform 变量的过程与输入和输出数据空间无关。

自定义

libtonemap 库的参考实现产生了可接受的结果。但是,由于 GPU 合成使用的色调映射算法可能与 DPU 合成使用的算法不同,因此在某些情况下(例如旋转动画)使用参考实现可能会导致闪烁。自定义可以解决此类特定于供应商的图像质量问题。

强烈建议 OEM 覆盖 libtonemap 的实现,以定义他们自己的 ToneMapper 子类,该子类由 getToneMapper() 返回。在自定义实现时,合作伙伴应执行以下操作之一

  • 直接修改 libtonemap 的实现。
  • 定义他们自己的静态库,将该库编译为独立的库,并将 libtonemap 库的 .a 文件替换为从他们的自定义库生成的文件。

供应商无需修改任何内核代码,但多个供应商必须沟通有关 DPU 色调映射算法的详细信息,以便正确实施。

验证

请按照以下步骤验证您的实施

  1. 在您的 显示系统支持 的任何 HDR 标准(例如 HLG、HDR10、HDR10+ 或 DolbyVision)的屏幕上播放 HDR 视频。

  2. 切换 GPU 合成以确保没有用户可察觉的闪烁。

    使用以下 adb 命令切换 GPU 合成

    adb shell service call SurfaceFlinger 1008 i32 <0 to enable HWC composition,
    1 to force GPU composition>
    
    

常见问题

以下问题可能会在此实现中发生

  • 当 GPU 合成使用的渲染目标精度低于 HDR 内容的典型值时,会导致条带化。例如,当 HWC 实现支持 HDR 的不透明 10 位格式(例如 RGBA1010102 或 P010),但要求 GPU 合成写入 8 位格式(如 RGBA8888)以支持 alpha 时,可能会发生条带化。

  • 如果 DPU 以与 GPU 不同的精度运行,则量化差异会导致细微的颜色偏移。

这些问题都与底层硬件的相对精度差异有关。典型的解决方法是在较低精度路径中确保存在抖动步骤,从而使任何精度差异都不太容易被人察觉。