blend后颜色异常问题(glBlendFunc & glBlendFuncSeparate)

blend后颜色异常问题(glBlendFunc & glBlendFuncSeparate)

1. 问题描述

应用中显示投屏小窗部分的颜色值与预期不一致,预期为纯白色 [0xFF],而实际显示偏黑 [0xDE]

相同渲染合成逻辑在X86平台测试显示为正常色彩 [0xFF]

各个layer叠加顺序如下(简化层级可以说明问题即可):

  1. 应用使用GPU渲染各个对象后进行blending合成输出到Overlay 01:
    1. media
    2. bottom
  2. 投屏画面输出到Overlay 02;

PS:Overlay 是指Display Process Unit中硬件图层,由硬件进行叠加混合,提高效率;

2. 问题分析

  1. 按照使用blending 公式计算理论值,是否与显示颜色一致,排除应用处理异常问题;
  2. 由于有做对比实验,同样render & blend 过程在PC上无问题,则排查差异部分:
    1. GPU 差别,对于实现库是否有差异?
    2. 显示模块的差异
      1. 异常机器使用DRM架构,而PC机不是;
      2. 异常机器有使用硬件Overlay ,而对比机器没有;

根据上述过程,由于GPU和DRM均为标准库,出问题概率较小,则先排查另外两项:

2.1 计算确认GPU输出理论值是否一致

  1. 根据应用使用的两张图片的RGB值计算数据如下(使用glBlendFunc接口):
    Bottom(0xFF, 0xFF, 0xFF, 0xFF)和media(0xD9, 0xFF, 0xFF, 0xFF)合成

    • RGB:(0xD9 * 0xFF + 0xFF * (0xFF - 0xD9))/ 0xFF = 0xFF
    • A:(0xD9 * 0xD9 + 0xFF * (0xFF - 0xD9))/ 0xFF = 0xDE

    从这两张图片的计算结果来看,应用经过GPU渲染合成后输出的RGB值应该为0xFF,即纯白色;

  2. dump GPU 吐出的buffer数据,查看其ARGB值与上述计算出来的数据一致

则根据上述两步分析可以得到如下结论:

  1. 应用使用render和blend的逻辑输出的RGB值符合预期;
  2. GPU 处理后输出值与预期相符,则说明GPU 底层处理符合标准(即这部分怀疑的优先级一般都放低一些)
  3. 问题出现在显示后端,由于DRM也属于标准框架内容,首要怀疑overlay合成部分
    • 注意到上述计算出来的A 为0xDE,则需要确认Overlay的合成是如何做的?

PS:计算公式详见第四小节blend使用接口介绍;

2.2 排查Overlay合成

基于上述实验分析,需要确认硬件Overlay是如何做的硬件合成操作:

  • 与designer沟通确认,硬件中为固定算法进行合成
    • 默认与glBlendFunc参数设置为GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA时相符;
  • 相当于合成 ARGB 背景色(0xFF, 0x00, 0x00, 0x00)和 ARGB GPU Output (0xDE, 0xFF, 0xFF, 0xFF)
    • RGB:(0xFF * 0xDE + 0x00 * (0xFF - 0xDE))/ 0xFF = 0xDE
    • A:(0xDE * 0xDE + 0xFF * (0xFF - 0xDE))/ 0xFF = 0xE2

根据上述公式计算出来最终要显示的RGB颜色为0xDE 并非0xFF,与实际上屏颜色相符;

则问题已经明确,应用端没有考虑到平台DPU后端仍会做一次硬件的blend合成,输出A颜色值并非为0xFF,导致问题出现

3. 问题处理方案

基于上述分析,问题原因已经明确:

  1. 平台中进行了两次blend计算

    • 由于blend公式特性决定,其所有图层必须按照一定顺序进行叠加计算否则会计算异常,比如上述过程应该按照如下顺序:
    1. ARGB 背景色(0xFF, 0x00, 0x00, 0x00)和 bottom(0xFF, 0xFF, 0xFF, 0xFF)
      RGB:(0xFF * 0xFF + 0x00 * (0xFF - 0xFF))/ 0xFF = 0xFF
      A:(0xFF * 0xFF + 0xFF * (0xFF - 0xFF))/ 0xFF = 0xFF
    2. ARGB Output (0xFF, 0xFF, 0xFF, 0xFF) 和 media(0xD9, 0xFF, 0xFF, 0xFF)
      RGB:(0xFF * 0xD9 + 0xFF * (0xFF - 0xD9))/ 0xFF = 0xFF
      A:(0xD9 * 0xD9 + 0xFF * (0xFF - 0xD9))/ 0xFF = 0xDE

    按照顺序叠加出来的RGB是符合预期输出的

    • 存在上述进行两次blend过程的情况,则需要保证第一次blend输出后的A分量符合我们预期要求,针对此case,我们要求 GPU Output A为0xFF,openGL中提供接口进行单独计算:

      void glBlendFuncSeparate(    GLenum srcRGB,
           GLenum dstRGB,
           GLenum srcAlpha,
           GLenum dstAlpha);
      //与上述差别在于将rgb与a分量独立使用不同的计算公式     
      

      使用上述公式计算bottom和media合成:

    Bottom(0xFF, 0xFF, 0xFF, 0xFF)和media(0xD9, 0xFF, 0xFF, 0xFF)合成
    RGB:(0xD9 * 0xFF + 0xFF * (0xFF - 0xD9))/ 0xFF = 0xFF
    A:(0xD9 * (0xFF - 0xFF) + 0xFF * 0xFF)/ 0xFF = 0xFF

    使用此接口独立计算即可解决问题

PS:相关blend接口和参数介绍详见第四小节

4. blend 使用接口介绍

4.1 概念说明

blend 即使用输入(Src)RGBA分量数据与帧缓冲区中已有RGBA值(Dst)进行计算来得到最终输出RGBA值得过程;

  • 实现角度为通过一定公式来计算RGBA的值;
  • 理解角度为根据透明度将两层layer的数据叠加到一起显示;
名词 说明
Src 输入的layer数据,由于blend计算过程为从下往上,则src就是相对位置在上方的layer
Dst 帧缓冲区中已有的layer数据,两层合成是相对位置在下方的layer

4.2 接口说明

  • glBlendFunc

    void glBlendFunc(GLenum sfactor,GLenum dfactor);
    
    名词 说明
    sfactor 源因子,即src分量的计算公式
    dfactor 目标因子,dst分量的计算公式
  • glBlendFuncSeparate

    void glBlendFuncSeparate(    GLenum srcRGB,
         GLenum dstRGB,
         GLenum srcAlpha,
         GLenum dstAlpha);
    

    相较于glBlendFunc将rgb与a分量的计算因子拆分了出来

4.3 参数说明

合系数枚举值 fR fG fB fA
GL_ZERO 0,0,0 0
GL_ONE 1,1,1 1
GL_SRC_COLOR Rs,Gs,Bs As
GL_ONE_MINUS_SRC_COLOR 1-Rs,1-Gs,1-Bs 1-As
GL_SRC_ALPHA As,As,As As
GL_ONE_MINUS_SRC_ALPHA 1-As,1-As,1-As 1-As
GL_DST_COLOR Rd,Gd,Bd Ad
GL_ONE_MINUS_DST_COLOR 1-Rd,1-Gd,1-Bd 1-Ad
GL_DST_ALPHA Ad,Ad,Ad Ad
GL_ONE_MINUS_DST_ALPHA 1-Ad,1-Ad,1-Ad 1-Ad
GL_CONSTANT_COLOR Rc,Gc,Bc Ac
GL_ONE_MINUS_CONSTANT_COLOR 1-Rc,1-Gc,1-Bc 1-Ac
GL_CONSTANT_ALPHA Ac,Ac,Ac Ac
GL_ONE_MINUS_CONSTANT_ALPHA 1-Ac,1-Ac,1-Ac 1-Ac
GL_SRC_ALPHA_SATURATE min(As,1-Ad) 1

其中斜体标记的为上述例子中使用的参数,在此处作为举例说明:

  • glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

    表示源颜色乘以自身的alpha 值,目标颜色乘以1.0减去源颜色的alpha值

    • ARGB = ( Rs * As + Rd * (1-As) ) 以R分量举例
  • glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA)

    表示源颜色乘以自身的alpha 值,目标颜色乘以1.0减去源颜色的alpha值

    源alpha乘以1.0减去目标的alpha值,目标alpha乘以目标alpha值

    • RGB = ( Rs * As + Rd * (1-As) ) 以R分量举例
    • A = ( As * ( 1 - Ad ) + Ad * Ad )

根据这些公式再去看上述问题描述中的计算过程就比较清晰了

5. 附录

此问题的发生暴露了此前对于blend 的理解以及blend接口理解的不足,以此记录供后续查阅参考

本地编辑完成后导入CSDN就会出现各种排版问题,先将就看吧,如果大家有好的办法,麻烦分享下 0.0

上一篇:Google Earth Engine——全球土壤纹理数据集:250米处6个土壤深度(0、10、30、60、100和200厘米)的土壤纹理等级(美国农业部系统)。


下一篇:骨骼与动画制作