Qualcomm 3D图形引擎库演变(二)

本文主要针对Adreno GPU的OpenGL ES 3.0和3.1部分新功能做一个简单的介绍。

1 基于Adreno的OpenGL ES 3.0新功能

   本节主要描述Adreno GPU上OpenGL ES 3.0的一些功能变化。OpenGL ES 3.0于2012年8月推出,大大扩展了OpenGL ES功能集,适用于嵌入式应用开发人员。那个时候,OpenGL 3.x的很多复杂功能仅限于桌面开发人员使用。
举几个例子:

  • 采样器对象和顶点属性数组除数成为桌面OpenGL的核心功能3.3
  • 无缝立方体贴图过滤和围栏同步对象成为桌面OpenGL 3.2的核心
  • 桌面OpenGL 3.1中引入了实例绘制调用和统一缓冲对象
  • 在桌面OpenGL 3.0中引入帧缓冲对象(具有多个渲染目标支持)和变换反馈

   以上所有内容都包含在核心OpenGL ES 3.0中。部分功能通过扩展机制在OpenGL ES 2.0中提供。如果一个开发人员希望使用不属于ES 2.0核心规范的功能,我们需要实现一个后备代码路径来满足不支持扩展的设备。这大大增加了实施的复杂性和所需的测试量。有关所有功能的更多信息,请参阅OpenGL ES 3.0规范 https://www.khronos.org/registry/gles/specs/3.0/es_spec_3.0.3.pdf。

1.1 二维数组纹理

   二维数组纹理(2D数组纹理)在二维纹理(2D纹理)的基础上建立。在2D纹理中,mipmap级别由单个图像组成。 在2D数组纹理中,单个mipmap级别包含许多图像。 保存在单个mipmap中的每个图像称为图层。 所有给定mipmap级别的图层具有相同的分辨率。图层数据采用当时为2D数组纹理对象请求的内部格式创建。 给定mipmap级别的所有图层的宽度和高度也在创建时定义并且在纹理对象的生命周期内不能更改。 每个mipmap级别的二维数组纹理对象称为纹理对象深度, 这也需要在创建时定义。
Qualcomm 3D图形引擎库演变(二)

   可以为2D阵列纹理分配mipmap链。至少可以定义每个图层只有一个基本的mipmap级别,如果不需要基于mipmap的纹理过滤,或者可用内存不足,纹理采样仍然有效。mipmap级别n + 1处的所有层必须是mipmap级别n处的层的大小的一半,例如,在mipmap级别0使用4x8的图层大小,mipmap级别1的每个图层都需要有一个大小为2x4。 Mipmap level 2将是1x2,对于最后一个mipmap级别,每个层将采用单个像素,每个尺寸都夹在1。

   将mipmap存储定义为可变或不可变后,2D数组纹理可以是:

  • 用作渲染目标,可以使用帧缓冲对象渲染图层
  • 使用新的GLSL纹理采样函数从任何着色器阶段采样

   ES着色语言3.0中几乎所有可用于2D纹理的纹理采样函数都可以用于采样2D阵列纹理。唯一不支持此功能的是投影纹理查找及其衍生物。如果请求的位置超出了任何轴定义的范围,采样功能的行为都由S / T / R wrap模式控制。

1.2 三维纹理

   从概念的角度来看,三维纹理(3D纹理)与2D非常相似数组纹理。 两者之间的主要区别在于数据采样过程是如何进行的以及如何构建mipmap链。3D纹理由一组称为切片的二维图像组成。 所有切片组合在一起形成一个mipmap级别。 后续的mipmap级别必须是之前的一半mipmap级别。 3D纹理对象的mipmap链由一组长方体组成,每个长方体都有一个内部的长方体。通常来说,后续长方体的大小是其前一个大小的一半。

   这与2D数组纹理不同,其中每个图层必须具有单独的mipmap链分配。对于每个切片,必须定义基本的mipmap级别。 除此之外,如果使用最近或线性缩小过滤,则mipmap是可选的。 使用基于mipmap的3D纹理对象的缩小过滤,其中尚未定义mipmap链,将使得渲染纹理不完整,导致任何采样操作返回vec4(0,0,0,1)。

   纹理对象用于存储纹理数据,以便随时使用它们。我们可以控制多个纹理,并可以返回到以前加载到纹理资源的纹理。使用纹理对象通常是应用纹理的最快方法。

   一个三维纹理对象在S,T,R纹理坐标轴方向上分别拥有w(width),h(height),d(depth)个体素,相应的纹理数据在内存中以线性方式存储,也就是说先存储一个slice的第一行的像素值,然后依次是同一个slice的其他行的像素值,然后是同一个volume的其他slice的像素值。

1.3 不可变纹理

   OpenGL ES 2.0核心规范认可的唯一类型的纹理对象是可变纹理对象。这意味着要完全重新定义纹理mipmap配置,在OpenGL ES应用程序运行期间的任何时候都允许被使用。不仅如此即时添加或删除mipmap,但也允许更改内部格式或属性。

   OpenGL ES的解决方案是不可变的纹理对象。最初介绍的GL_EXT_texture_storage扩展功能,它们成为OpenGL ES 3.0的核心功能。不可变纹理就像可变纹理一样工作,除了它不再可能应用任何纹理。

   以下API函数来构造已变为不可变的对象:

  • glCompressedTexImage*
  • glCopyTexImage*
  • glTexImage*
  • glTexStorage* - 可用于初始化不可变纹理对象

   新的glTexStorage *入口点使纹理不可变。对于用户指定的纹理目标初始化一个mipmap链,但不要使用任何内容填充mipmap。应用程序的职责是使用实际内容填充不可变纹理对象。
不可变纹理没有特定的用例。相反,它们应该被视为减少驱动程序的负载一种手段,这通常可以转换为更好的渲染性能。

   OpenGL ES 在对纹理对象加载之前,首先要使用glGenTextures创建纹理对象,纹理对象是一个容器对象,保存渲染所需的纹理数据,例如图像数据、过滤模式和包装模式。纹理对象不再使用时,使用glDeleteTextures进行删除。在使用纹理对象之前,还要通过glBindTexture进行绑定,绑定之后,就可以对纹理对象进行操作。用于加载2D和立方图纹理的基本函数是glTexImage2D,参数data包含图像的实际像素数据,数据必须包含一定的像素个数,每个像素根据格式和类型规范有相应的字节数,像素行必须对齐到用glPixelStorei设置的GL_UNPACK_ALIGNMENT。为了得到最佳的性能,建议使用不可变纹理。

1.4 PCF

   阴影贴图用于在电影和电视的高级渲染中创建阴影。然而,在诸如视频的实时应用中使用阴影映射存在问题,例如游戏中的锯齿状锯齿形的混叠问题。阴影映射包括在几何体上投影阴影贴图,并将阴影贴图值与光照视图进行每个像素的深度比较。如果投影放大了阴影贴图,则以大的形式混叠,难看的锯齿将出现在阴影边界。通常可以通过使用更高的值来减少分辨率阴影贴图和增加阴影贴图分辨率的技术,例如,透视阴影贴图。

   当光线几乎平行于阴影表面时,使用透视阴影映射技术并增加阴影贴图分辨率不会起作用,是因为放大率接近无穷大。通过使用称为百分比近似过滤的技术,高级渲染软件解决了混叠问题。

   与普通纹理不同,阴影贴图纹理无法预先过滤以去除锯齿。反而,每个像素进行多个阴影贴图比较平均。这种技术称为百分比近似过滤(PCF)。

1.5 新的内部纹理格式

   OpenGL ES 3.0引入了可用于定义纹理数据内容的大小内部格式。现在可以使用浮点,有符号和无符号整数内部表示纹理内容格式。 两种新的内部格式可以用于存储在sRGB颜色空间中表示颜色信息。

   为了向后兼容,继续支持未分级的内部格式。 但是,使用它们可能会导致不同的OpenGL ES扩展和其他扩展之间的不良交互,因此建议使用新尺寸的内部格式。

1.6 变换反馈

   在GPU上执行计算并复用结果变得越来越流行,不幸的是,在OpenGL ES 2.0的核心版本中,只有一种可行的使用方法用于通用计算的GPU。所有计算都必须在片段着色器中完成,将结果存储在当前绑定的绘制帧缓冲区的颜色附件中。这种方法有很多限制,包括:

  • Core ES 2.0仅支持使用GL_RGBA4渲染渲染缓冲区和纹理,GL_RGBA5_A1和GL_RGB565内部格式,在这方面支持非常有限的精度。
  • 核心ES 2.0不支持可颜色渲染的浮点内部格式。
  • 核心ES 2.0仅支持每帧缓冲区一个颜色附件。

   OpenGL ES 3.0引入了对变换反馈的支持,允许离开顶点着色器阶段补货并输出变量的值。捕获后,可以将值传输到一个或多个缓冲区对象区域,这个有两种不同的方式:

  • 单个缓冲区对象区域可用于按指定的顺序来存储变化的值。
  • 可以使用多个缓冲区对象区域。在这种情况下,每个变化被分配给不同的缓冲区对象区域。多个缓冲区对象区域可以是同一缓冲区对象的一部分,但事实并非如此。

   使用单个缓冲区对象区域,从单个顶点着色器可以捕获的最大组件数保证至少为64。使用多个缓冲区对象区域,可以从单个顶点着色器捕获的最大组件数保证至少16。

   鉴于ES 3.0支持的新顶点数据类型的范围,转换反馈提供与计算着色器相当的可能性,这是OpenGL ES中没有的功能,直到OpenGL ES 3.1的出现。

   除了上述新增的功能之外,其他功能不再一一的详细描述,列举如下,大的功能变动如:

  • New vertex data types
  • Vertex array objects
  • Uniform buffer objects
  • Buffer subrange mapping
  • Multiple render target support
    还有一些小的功能变化如:
  • 16-bit floating-point vertex attributes
  • 24-bit depth renderbuffers and textures
  • 24/8 depth/stencil renderbuffers and textures
  • 32-bit depth and 32f/8 depth/stencil renderbuffers and textures
  • 8-bit unsigned normalized renderbuffers
  • 8-bit-per-component signed normalized textures
  • Ability to attach any mipmap level to a frame buffer object
  • Additional pixel store state
  • Buffer object to buffer object copy operations
  • Depth textures and shadow comparison

2 基于Adreno 的OpenGL ES 3.1的新功能

   本节简要介绍了OpenGL ES 3.1一些比较重要的特性。 有关详细信息,请参阅以下内容:

2.1 Atomic Counter

   Atomic Counter是OpenGL 4.2版本通过GL_ARB_shader_atomic_counters扩展引入的新特性,它可以在各种着色语言中使用。Atomic Counter具体来说指的是在缓冲区对象(Buffer Object)中存储着一个或者多个可以用来计数的变量值,对这些变量定义了特定的操作方式,可以让它们在着色语言中进行加一和减一的操作,除此之后其他所有的操作都是非法的。

   关于Atomic Counter的使用场景,一个简单的案例是统计场景中哪些像素会先进行渲染。具体的实现思路:当我们调用片元Shader对像素进行着色的时候,我们可以让atomic Counter变量值加1,然后将这个值转换成一种颜色来渲染我们的像素,这样我们就可以很清楚看到那些像素点是先渲染的,哪些像素点是后渲染的了。

2.2 Compute shaders

   Compute Shader是在OpenGL4.3(Opengl ES 3.1)以后引入的一种专门用于并行计算的着色器。在计算着色器中,任务以组为单位进行执行,我们称之为工作组(work group)。拥有邻居的工作组被称为本地工作组(local workgroup), 这些组可以组成更大的组,称为全局工作组(global workgroup),而其通常作为执行命令的一个单位。

   计算着色器会被每个本地工作组中的每个单元调用一次。工作组的每一个单元称为工作项(work item),每次调用称为一次执行。执行的单元之间可以通过变量和显存进行通信,且可以通过执行同步操作保持一致性。图12-1显示了一个全局工作组。这个全局工作组包括16个本地工作组,每个本地工作组又包括16个执行单元,排成4X4的网格,每个执行单元拥有一个二维向量表示的索引值。尽管图示中,全局和本地工作组都是2维的,而事实上它们都是3维的,为了适应1维、2维的任务,只需把额外的2维或1维设为0即可。计算着色器的每个执行单元本质是相互独立的,可以并行地在支持OpenGL地GPU上执行。
Qualcomm 3D图形引擎库演变(二)

2.3 ES shading language enhancements

   下面列举的增强功能点是在OpenGL ES 3.1 的Shading Language中提出的:

  • 将浮点数拆分为有效数和指数(frexp)的函数
  • 从有效数和指数(ldexp)构建浮点数的函数
  • 执行32位有符号和无符号乘法的函数,具有32位输入和跨越两个32位输出的64位结果(imulExtended,umulExtended)
  • 执行位域提取,插入和反转的功能(bitfieldExtract,bitfieldInsert,bitfieldReverse)
  • 现在可以在ES SL代码中定义多维数组

2.4 多重纹理

   多重纹理是指在同一个模型表面指定两个或两个以上的纹理,这些纹理通过一定的融合方式进行混合形成的效果。其每一层各自执行自己的纹理操作,并把结果传递给下一层,直到全部完全。主要应用在如:光照效果,贴花,合成,细节纹理等

除了上述列举的新特性之外,还有其他的像着色器存储缓冲区对象、单独的着色器对象等新特性。