GLES API – VRS 扩展

Figure ‑: GLES VRS API – 编程模型

查询VRS扩展

在正确使用VRS之前,开发人员需要了解相关的硬件能力。该扩展提供了API结构和函数,允许开发人员查询与VRS相关的设备特性。

开发者可以透过 glGetString(GL_EXTENSIONS) 去检查 VRS 扩展是否可用。此扩展有三种字串可以检查:

  • GL_EXT_fragment_shading_rate: (基本扩展) 支持管线 VRS
  • GL_EXT_fragment_shading_rate_primitive: (可选扩展) 支持图元 VRS
  • GL_EXT_fragment_shading_rate_attachment: (可选扩展) 支持幀缓冲附件 VRS

最佳实践建议:

  • 使用VRS 前,请先确认 GLES 是否有支持此扩展
  • 以下扩展与 VRS 扩展不兼容:
  • GL_EXT_shader_pixel_local_storage
  • GL_EXT_shader_framebuffer_fetch
  • GL_ARM_shader_framebuffer_fetch

查询支持的着色率

开发人员必须查询此硬件使用特定采样率状态下支持着色率清单,例如说,此硬件在 1x 单样本 渲染下可以支持 1x1, 1x2, 2x1, 2x2, 2x4, 4x2, 4x4 着色率,但在 4x 多重样本渲染下只能支持 1x1, 1x2, 2x1, 2x2 着色率。如果选择此硬件不支持的着色率,硬件会自动转换成接近的着色率。

开发人员可以透过呼叫此函数去查询在特定的多重样本渲染状态下此硬件支持的着色率:

void glGetFragmentShadingRatesEXT(GLsizei samples, GLsizei maxCount, GLsizei *count, GLenum *shadingRates)

此函数需要被呼叫两次:

  • 取得所支持的着色率清单数量
  • 取得所支持的着色率清单内容

第二次呼叫后,着色率清单中 (shadingRates) 每一项目都是以下任一:

  • GL_ SHADING_RATE_1X1_PIXELS_EXT
  • GL_SHADING_RATE_1X2_PIXELS_EXT
  • GL_SHADING_RATE_1X4_PIXELS_EXT
  • GL_SHADING_RATE_2X1_PIXELS_EXT
  • GL_SHADING_RATE_2X2_PIXELS_EXT
  • GL_SHADING_RATE_2X4_PIXELS_EXT
  • GL_SHADING_RATE_4X1_PIXELS_EXT
  • GL_SHADING_RATE_4X2_PIXELS_EXT
  • GL_SHADING_RATE_4X4_PIXELS_EXT

最佳实践建议:

  • 当同时使用 MSAA 与 VRS 时,开发人员必须查询特定采样率所支持的着色率

设置管线着色率

开发人员可以透过呼叫 glShadingRateEXT 函数去设置管线着色率:

void glShadingRateEXT(GLenum rate)

此函数的参数 “rate” 是一个 enum,必须是以下常数中任一:

  • GL_ SHADING_RATE_1X1_PIXELS_EXT
  • GL_SHADING_RATE_1X2_PIXELS_EXT
  • GL_SHADING_RATE_1X4_PIXELS_EXT (此硬件原生不支持)
  • GL_SHADING_RATE_2X1_PIXELS_EXT
  • GL_SHADING_RATE_2X2_PIXELS_EXT
  • GL_SHADING_RATE_2X4_PIXELS_EXT
  • GL_SHADING_RATE_4X1_PIXELS_EXT (此硬件原生不支持)
  • GL_SHADING_RATE_4X2_PIXELS_EXT
  • GL_SHADING_RATE_4X4_PIXELS_EXT

最佳实践建议:

  • 开发人员必须确认此硬件支持的着色率
  • 有些多重采样的采样率不支持特定着色率

设置图元着色率

开发者可以在光栅化 (rasterization) 之前的最后一个激活着色器中指定每个图元的着色率。 GLSL语言定义了一个内建的输出变量gl_PrimitiveShadingRateEXT来储存当前着色器呼叫该图元的图元着色率。

以下顶点着色器示例将一个整数值写入内建输出变量gl_PrimitiveShadingRateEXT中,以指定当前顶点的图元着色率:

 

#version 310 es

#extension GL_EXT_fragment_shading_rate : require

 

// ...

 

void main() {

    // ...

    gl_Position = pos;

    gl_PrimitiveShadingRateEXT = 0x5; // 2x2 shading rate

}

片段大小

二进制值

十六进制值

Mali-G715 Next 是否支持

1x1

0000

0

1x2

0001

1

1x4

0010

2

2x1

0100

4

2x2

0101

5

2x4

0110

6

4x1

1000

8

4x2

1001

9

4x4

1010

A

Table ‑: gl_PrimitiveShadingRateEXT 的有效值

最佳实践建议:

  • 如果开发人员打算使用图元着色率,必须确认此扩展名是否有出现:GL_EXT_fragment_shading_rate_primitive
  • 写入 gl_PrimitiveShadingRateKHR 的着色器必须是在光栅化之前的最后一个激活着色器阶段

例如:

  • 顶点着色器,片段着色器) => 顶点着色器需写入图元着色率
  • (顶点着色器,几何着色器,片段着色器)=> 几何着色器需写入图元着色率
  • (顶点着色器,曲面细分着色器,片段着色器) => 曲面细分着色器需写入图元着色率
  • (顶点着色器,曲面细分着色器,几何着色器,片段着色器) => 几何着色器需写入图元着色率

如果不这样做,将会忽略所写的图元着色率

  • 着色器代码必须启用扩展GL_EXT_fragment_shading_rate
  • 实际的图元着色率由每个基本图元的驱动顶点决定 (provoking vertex)

设置幀缓冲附件着色率

要指定附件着色率,开发人员必须创建一个特殊的附件贴图并将其附加到帧缓冲器。根据以下原则,着色率附件中对应的纹理元素,对帧缓冲器中的每个像素指定附件着色率:

  • x’ = Floor (x / TexelSize.width)
  • y’ = Floor (y / TexelSize.height)

其中,x’和y’是着色率附件中的纹理元素的坐标,x和y是帧缓冲器中像素的坐标,TexelSize是每个纹理元素对应的区域大小

值得注意的是,开发人员必须首先选择TexelSize。具体而言,开发人员可以透过glGetIntegerv 查询以下属性数值:

  • GL_MIN_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_WIDTH_EXT
  • GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_WIDTH_EXT
  • GL_MIN_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_HEIGHT_EXT
  • GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_HEIGHT_EXT
  • GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_ASPECT_RATIO_EXT

由以上属性数值,开发者可以选出一个适当的纹理元素大小 (TexelSize)

  • texelSize.width >= GL_MIN_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_WIDTH_EXT 的属性数值
  • texelSize.width <= GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_WIDTH_EXT 的属性数值
  • texelSize.width 必须是二的幂次方 (i.e., 2n)
  • texelSize.height >= GL_MIN_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_HEIGHT_EXT 的属性数值
  • texelSize.height <= GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_HEIGHT_EXT 的属性数值
  • texelSize.height 必须是二的幂次方 (i.e., 2n)
  • (texelSize.width / texelSize.height) <= GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_ASPECT_RATIO_EXT 的属性数值
  • (texelSize.height / texelSize.width) <= GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_ASPECT_RATIO_EXT 的属性数值

附件着色率将编码为纹理元素值,如 Table 2‑2: 所述:

片段大小

二進制值

十六進制值

Mali-G715 Next 是否支持

1x1

0000

0

1x2

0001

1

1x4

0010

2

2x1

0100

4

2x2

0101

5

2x4

0110

6

4x1

1000

8

4x2

1001

9

4x4

1010

A

Table ‑: 着色率附件图像的有效纹理值

然后,开发者需要创建一张纹理贴图对象,满足以下条件:

  • 必须用 glTexStorage2D (2D 贴图) 或 glTexStorage3D (2DArray 贴图)
  • target 必须是 GL_TEXTURE_2D 或 GL_TEXTURE_2D_ARRAY
  • internalFormat 一定要是 GL_R8UI
  • width 必须是 ceil ( 幀缓冲 width / texel width )
  • height 必须是 ceil ( 幀缓冲 height / texel height )

创建完成贴图对象后,可以透过以下函数把这个对象挂载到幀缓冲对象上:

void glFramebufferShadingRateEXT(GLenum target, GLenum attachment, GLuint texture, Glint baseLayer, GLsizei numLayers, GLsizei texelWidth, GLsizei texelHeight)

最佳实践建议:

  • 如果开发人员打算使用附件着色率,必须确认此扩展名是否有出现:GL_EXT_fragment_shading_rate_attachment
  • 开发人员在创建着色率附件贴图之前必须查询支持的像素大小。
  • 在Mali-G715 Next上,仅支持8x8、16x16和32x32
  • 要注意读取着色率附件可能带来的额外开销以及由于资源更新而导致的额外同步成本。

设置结合器

依照 Figure 3‑1: GLES VRS API – 编程模型 所描述,GPU 硬件会根据开发者所设置的三种着色率 (分别为管线、图元、与缓冲附件着色率),按照结合器的设置去计算出一个有效的着色率。第一个结合器会根据管线着色率与图元着色率做计算,然后用第二个结合器去计算第一个结合器结果与缓冲附件着色率的结合。

开发者可以透过以下的函数分别设置这两个结合器的计算方式:

void glShadingRateCombinerOpsEXT(GLenum combinerOp0, GLenum combinerOp1)

这两个参数combinerOp0 与combinerOp1 分别为下列的常数其中之一:

  • GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT:在 Axy ,Bxy 中只选了 Axy
  • GL_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_EXT :在 Axy ,Bxy 中只选了 Bxy
  • GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MIN_EXT:在 Axy ,Bxy 中选取最小
  • GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_EXT:在 Axy ,Bxy 中选取最大
  • GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MUL_EXT:Axy 与 Bxy 相乘

如果最后的有效的着色率不是硬件所支持的,硬件会自动选择一个相似的着色率。

最佳实践建议:

  • 根据着色率使用状况,选择正确的结合器运算子
  • 如果没有使用图元着色率,第一个结合器运算子必须是 KEEP
  • 如果没有使用缓冲附件着色率,第二个结合器运算子必须是 KEEP
  • 开发者必须确认最后的有效着色率是否符合你们所预想的,下一个段落有说明如何在片段着色器取得着色率。

查询片段着色器中的有效着色率

为了要获取GPU在片段着色器中使用的有效着色率,开发人员可以在片段着色器中读取一个内建变量gl_ShadingRateEXT。该变量的资料表示与gl_PrimitiveShadingRateEXT相同。开发人员可以使用这些值来视觉化实际的着色率,以便进行调试。

 

#version 310 es

#extension GL_EXT_fragment_shading_rate : require

 

// ...

 

void main() {

    // ...

    if (gl_ShadingRateEXT != 0) { // VRS is on

        // ...

    } else { // VRS is off

        // ...

    }

}

 

示例代码

顶点着色器代码:

#version 310 es

#extension GL_EXT_fragment_shading_rate : require

 

void main() {

    // 其他代码

 

    // 设置图元着色率

    gl_PrimitiveShadingRateEXT = 0x5; // 2x2 着色率

}

 

C/C++ 代码:

// 确认硬件是否支持图元着色率

const char* extensions = (const char*) glGetString(GL_EXTENSIONS);

if (!strstr(extensions, “GL_EXT_fragment_shading_rate_primitive”)) {

    exit(1); // return error

}

 

// 使用此 GPU 程序

// 其中顶点着色器会设置图元着色率

glUseProgram(…);

 

// 设置结合器

// 此段代码很重要,如果没有设定好结合器,硬件会直接忽视图元着色率

glShadingRateCombinerOpsEXT(GL_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_EXT, GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT);

 

// 执行一个绘图呼叫,并套用图元着色率

glDrawElements(…);

 

使用幀缓冲附件着色率

C/C++ code:

// 确认硬件是否支持幀缓冲附件着色率

const char* extensions = (const char*) glGetString(GL_EXTENSIONS);

if (!strstr(extensions, “GL_EXT_fragment_shading_rate_attachment”)) {

    exit(1); // return error

}

 

// 幀缓冲尺寸

GLint width, height;

 

// 查询幀缓冲附件着色率的画素大小

GLint texelSize;

glGetIntegerv(GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_WIDTH_EXT, &texelSize);

GLint shadingRateTexWidth = (width + texelSize - 1) / texelSize;

GLint shadingRateTexHeight = (height + texelSize - 1) / texelSize;

 

// 创建一个幀缓冲附件着色率贴图对象

glGenTextures(1, &shadingRateTex);

glBindTexture(GL_TEXTURE_2D, shadingRateTex);

glTexStorage2D(GL_TEXTURE_2D, 1, GL_R8UI, shadingRateTexWidth, shadingRateTexHeight);

 

// you can upload data to shadingRateTex by using glTexSubImage or compute shaders

 

// 设置一个幀缓冲对象

glBindFramebuffer(GL_FRAMEBUFFER, fbo);

// 此代码把着色率附件贴图对象挂载到幀缓冲对象

glFramebufferShadingRateEXT(GL_FRAMEBUFFER, GL_SHADING_RATE_ATTACHMENT_EXT, shadingRateTex, 0, 1, texelSize, texelSize);

 

// 设置结合器

// 此段代码很重要,如果没有设定好结合器,硬件会直接忽视幀缓冲附件着色率

glShadingRateCombinerOpsEXT(GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT, GL_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_EXT);

 

// 执行一个绘图呼叫,并套用幀缓冲附件着色率

glDrawElements(…);