HarmonyOS 自适应 VRS OpenGL ES 版:降低 GPU 负载的另一种选择

什么是自适应 VRS

上一篇我们介绍了用 Vulkan 实现自适应 VRS,这篇来看看用 OpenGL ES 怎么做。功能是一样的:自动分析画面,对不同区域使用不同的渲染精度,从而降低 GPU 负载。

简单回顾一下 VRS 的原理:人眼对画面中心的细节更敏感,对边缘区域不太在意。VRS 就是利用这个特点,重要的地方精细渲染,不重要的地方粗糙渲染,既保证视觉效果又提高性能。

环境搭建

硬件要求

  • 设备类型:请参考 XEngine 开发指南的硬件要求

软件要求

  • DevEco Studio 版本:DevEco Studio 6.0.0 Release 及以上
  • HarmonyOS SDK 版本:HarmonyOS 6.0.0 Release SDK 及以上

搭建步骤

  1. 安装 DevEco Studio:去华为开发者官网下载安装
  2. 配置开发环境:确保网络环境正常
  3. 设备调试:使用真机进行调试

项目结构

├── entry/src/main	         // 代码区
│  ├── cpp
│  │  ├── types
│  │  │  ├── libnativerender
│  │  │     └── index.d.ts      // native层接口注册文件 
│  │  │── napi_init.cpp          // native api层接口的具体实现函数
│  │  │── CMakeLists.txt         // native层编译配置
│  │  │── 3rdParty               // 三方件
│  │  │── common                 // 通用接口  
│  │  │── model                  // 模型
│  │  │── file                   // 文件管理
│  │  │── libs                   // 三方动态库
│  │  │── manager                // native&arkts交互
│  │  │── render                 // 渲染
│  │  │── shader                 // 渲染shader
│  ├── ets	
│  │  ├── entryability 
│  │  │    └── EntryAbility.ts   // 程序入口类
│  │  ├── pages 
│  │  │    └── index.ets         // 主界面展示类
│  ├── resources                 // 资源文件目录
│  │  ├── base
│  │  │  ├── media
│  │  │     └── icon.png         // 图片资源
│  │  ├── rawfile/model/Sponza
│  │  │                      └── sponza.obj    // 模型资源

第一步:引入头文件

#include <cstring>
#include <cstdlib>
#include <xengine/xeg_gles_extension.h>
#include <xengine/xeg_gles_adaptive_vrs.h>

这些头文件的作用:

  • cstringcstdlib:C++ 标准库
  • xeg_gles_extension.h:XEngine 的 OpenGL ES 扩展接口
  • xeg_gles_adaptive_vrs.h:自适应 VRS 的核心接口

第二步:配置 CMakeLists.txt

find_library(
    xengine-lib
    xengine
)
find_library(
    EGL-lib
    EGL
)
find_library(
    GLES-lib
    GLESv3
)

target_link_libraries(nativerender PUBLIC
${EGL-lib} ${GLES-lib} ${xengine-lib})

链接了 xengineEGLGLESv3 三个库。

第三步:查询设备是否支持

const char* extensions = (const char*)HMS_XEG_GetString(XEG_EXTENSIONS);
if (!strstr(extensions, XEG_ADAPTIVE_VRS_EXTENSION_NAME)) {
    exit(1); // return error
}

和 Vulkan 版本一样,先检查设备是否支持自适应 VRS。OpenGL ES 的查询方式更简单,直接调用 HMS_XEG_GetString 获取扩展列表字符串,然后用 strstr 查找。

第四步:设置 VRS 参数

这是自适应 VRS 的配置步骤,需要设置多个参数:

设置输入尺寸

uint32_t renderWidth = 800;
uint32_t renderHeight = 600;
GLsizei inputSize[2] = {static_cast<GLsizei>(renderWidth), static_cast<GLsizei>(renderHeight)};
HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_INPUT_SIZE, inputSize);

设置输入图像的尺寸,就是上一帧渲染结果的大小。inputSize 是一个包含宽高的数组。

设置输入区域

GLuint inputRegion[4] = {0, 0, renderWidth, renderHeight};
HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_INPUT_REGION, inputRegion);

设置输入图像的采样区域。inputRegion 是一个包含 4 个元素的数组:起始坐标 (x, y) 和宽高。从 (0, 0) 开始,大小等于渲染分辨率,表示处理整个图像。

设置分片大小

GLsizei texelSizes[2] = {8, 8};
HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_TEXEL_SIZE, texelSizes);

设置 VRS 的分片大小。画面会被分成很多小块,每块独立决定渲染精度。当前支持两种规格:

  • [8, 8]:8x8 像素一块,精度高但计算量大
  • [16, 16]:16x16 像素一块,精度低但性能好

设置误差敏感度

GLfloat sensitivity = 0.15;
HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_ERROR_SENSITIVITY, &sensitivity);

设置误差敏感度阈值,取值范围 [0, 1]。值越小,越倾向于高精度渲染;值越大,越倾向于低精度渲染。0.15 是一个比较保守的值,画质影响很小。

设置翻转选项

GLboolean flip = false;
HMS_XEG_AdaptiveVRSParameter(XEG_ADAPTIVE_VRS_FLIP, &flip);

设置是否翻转图像。false 表示不翻转。

第五步:生成着色率纹理

GLuint inputColorImage; 
GLuint inputDepthImage;
GLuint outputShadingRateImage;
float *reprojectionMatrix = nullptr;

HMS_XEG_DispatchAdaptiveVRS(reprojectionMatrix, inputColorImage, inputDepthImage, outputShadingRateImage);

调用 HMS_XEG_DispatchAdaptiveVRS 生成着色率纹理。参数说明:

  • reprojectionMatrix:重投影矩阵,用于处理相机运动。如果相机不动,传 nullptr
  • inputColorImage:上一帧的颜色纹理,就是渲染出来的彩色画面
  • inputDepthImage:深度纹理,记录每个像素离相机有多远
  • outputShadingRateImage:输出的着色率纹理,GPU 会根据它决定每个区域的渲染精度

第六步:应用着色率纹理

HMS_XEG_ApplyAdaptiveVRS(outputShadingRateImage);

最后调用 HMS_XEG_ApplyAdaptiveVRS 把着色率纹理应用到渲染目标中。之后 GPU 在渲染下一帧时,就会根据这个纹理来决定每个区域的渲染精度。

自适应 VRS 的整体工作流程如下:

查询设备是否支持自适应VRS

设置输入尺寸

设置输入区域

设置分片大小

设置误差敏感度

设置翻转选项

调用 DispatchAdaptiveVRS

分析颜色和深度信息

生成着色率纹理

调用 ApplyAdaptiveVRS

GPU 根据着色率渲染下一帧

VRS 参数配置的决策流程:

精度优先

性能优先

画质优先

性能优先

开始配置VRS参数

选择分片大小

8x8 像素分片

16x16 像素分片

设置误差敏感度

较小值 如 0.15

较大值 如 0.5

相机是否移动?

传入重投影矩阵

传入 nullptr

参数配置完成

OpenGL ES 版和 Vulkan 版的区别

对比一下两个版本:

方面OpenGL ES 版Vulkan 版
查询扩展HMS_XEG_GetStringHMS_XEG_EnumerateDeviceExtensionProperties
设置参数HMS_XEG_AdaptiveVRSParameter通过创建结构体
生成着色率HMS_XEG_DispatchAdaptiveVRSHMS_XEG_CmdDispatchAdaptiveVRS
应用着色率HMS_XEG_ApplyAdaptiveVRS自动应用
销毁实例无需显式销毁HMS_XEG_DestroyAdaptiveVRS

主要区别:

  1. API 风格:OpenGL ES 版用函数参数设置,Vulkan 版用结构体设置
  2. 资源管理:OpenGL ES 版不需要显式销毁实例,Vulkan 版需要
  3. 命令提交:Vulkan 版需要通过命令缓冲区提交,OpenGL ES 版直接调用

选择哪个版本取决于你的应用已经在用哪个图形 API。

适用场景

自适应 VRS 特别适合以下场景:

  • 3D 游戏:画面复杂,有很多区域可以降低精度
  • VR/AR 应用:对帧率要求极高
  • 图形渲染应用:需要稳定帧率的场景

注意事项

  1. 设备支持:不是所有设备都支持自适应 VRS,一定要先查询扩展
  2. 分片大小:8x8 精度高但性能开销大,16x16 性能好但精度低,要根据实际需求选择
  3. 误差敏感度sensitivity 要根据画面质量要求来调整
  4. 重投影矩阵:如果相机在移动,需要传入正确的重投影矩阵

总结

OpenGL ES 版本的自适应 VRS 和 Vulkan 版本功能相同,核心流程:

  1. 查询设备是否支持
  2. 设置参数(输入尺寸、分片大小、误差敏感度等)
  3. 调用 HMS_XEG_DispatchAdaptiveVRS 生成着色率纹理
  4. 调用 HMS_XEG_ApplyAdaptiveVRS 应用着色率纹理

如果你的应用已经在用 OpenGL ES,用这个版本会更方便。如果用的是 Vulkan,就用 Vulkan 版本。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐