HarmonyOS 图形加速:用 ABR 实现自适应稳态渲染

什么是 ABR

玩游戏的时候,你有没有遇到过这种情况:场景简单的时候帧率很高很流畅,但一到复杂场景(比如大量敌人、特效爆炸)就开始卡顿?这是因为 GPU 的负载在不同场景下是不一样的。

ABR(Adaptive Buffer Resolution,自适应缓冲分辨率)就是为了解决这个问题。它的核心思想是:在 GPU 负载高的时候,自动降低渲染分辨率来保持帧率;在 GPU 负载低的时候,恢复高分辨率保证画质

打个比方,就像开车上坡一样。上坡的时候(GPU 负载高),你会降档减速(降低分辨率)来保持动力;下坡的时候(GPU 负载低),你又可以升档加速(提高分辨率)跑得更快。

环境搭建

硬件要求

  • 设备类型:华为手机、平板设备
  • HarmonyOS 系统:HarmonyOS 5.0.5 Release 及以上

软件要求

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

搭建步骤

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

项目结构

这个项目比较特殊,因为它涉及到 C++ 和 ArkTS 的混合开发:

└── entry/src/main                          // 代码区
    ├── cpp
    │    ├── types/libentry
    │    │     └── index.d.ts               // native层接口注册文件
    │    ├── napi_init.cpp                  // native api层接口的具体实现函数
    │    ├── CMakeLists.txt                 // native层编译配置
    │    ├── include                        // 头文件
    │    ├── source                         // C++代码区
    │    │    ├── core.cpp                  // 核心入口,管理生命周期
    │    │    ├── renderer.cpp              // 渲染管理类的实现
    │    │    ├── scene_base.cpp            // ABR基类的实现
    │    │    ├── scene_abr.cpp             // ABR类的实现
    │    │    ├── opaque_layer_base.cpp     // 场景绘制基类的实现
    │    │    ├── opaque_layer.cpp          // 场景绘制派生类的实现
    │    │    ├── gui.cpp                   // UI绘制
    │    │    └── ...
    ├── ets
    │    ├── ability 
    │    │     └── EntryAbility.ts          // 程序入口类
    │    ├── pages 
    │    │     └── Index.ets                // 主界面
    └── resources
    │    ├── base/media                     // 图片资源
    │    │   └── logo.png
    │    ├── rawfile                        // 模型和UI资源
    │    │    └── ...

为什么用 C++?因为图形渲染对性能要求很高,C++ 能提供更接近硬件的控制能力。ArkTS 负责 UI 界面,C++ 负责底层的图形渲染逻辑。

应用架构

整个应用分为三部分:

  • C++ 侧:实现场景渲染,管理 ABR 生命周期
  • ArkTS 侧:实现前端界面,调用 C++ 侧的图形渲染方法
  • CMake 编译工具链:将 C++ 侧代码编译成 so 文件提供给 ArkTS 侧使用

第一步:创建项目

使用 Native C++ 模板创建项目。在 DevEco Studio 中选择 “File > New > Create Project”,然后选择 Native C++ 模板。

第二步:添加配置项

module.json5 的 module 层级中添加以下配置:

"metadata": [
  {
    "name": "GraphicsAccelerateKit_ABR",
    "value": "true"
  }
]

这个配置告诉系统,我们的应用要使用 Graphics Accelerate Kit 的 ABR 功能。

第三步:引入头文件

在 C++ 代码中引入 ABR 的头文件:

#include <graphics_game_sdk/abr_gles.h>

这个头文件包含了所有 ABR 相关的 API。

第四步:配置 CMakeLists.txt

在 CMakeLists.txt 中需要增加对 libabr.so 的依赖:

find_library(EGL-lib EGL REQUIRED)
find_library(GLES-lib GLESv3 REQUIRED)
find_library(ace-lib ace_ndk.z REQUIRED)
find_library(hilog-lib hilog_ndk.z REQUIRED)
find_library(napi-lib ace_napi.z REQUIRED)
find_library(rawfile-lib rawfile.z REQUIRED)
find_library(uv-lib uv REQUIRED)
find_library(abr-lib libabr.so REQUIRED)

set(ADAPTIVEBUFFERRESOLUTION_LINK_LIBRARIES
    ${EGL-lib}
    ${GLES-lib}
    ${ace-lib}
    ${hilog-lib}
    ${rawfile-lib}
    ${napi-lib}
    ${uv-lib}
    ${abr-lib}
    pixelmap_ndk.z
    image_source_ndk.z
    libc++.a
)

target_link_libraries(entry PRIVATE
    ${ADAPTIVEBUFFERRESOLUTION_LINK_LIBRARIES}
)

这里做了什么:

  • find_library:查找需要的库
  • abr-lib libabr.so:这是 ABR 的核心库
  • target_link_libraries:把所有库链接到我们的项目中

第五步:ABR 生命周期管理

ABR 的生命周期分为三个阶段:

  1. ABR 初始化:创建 ABR 上下文实例,配置目标帧率和分辨率因子范围,激活 ABR 功能
  2. 自适应渲染:绑定帧缓冲索引,动态调整 buffer 的渲染分辨率
  3. 销毁 ABR 实例:销毁 ABR 上下文实例,释放内存资源

ABR 初始化

在 Surface 创建后,会触发 Core::OnSurfaceCreated() 回调函数。在这个函数中完成 ABR 的初始化:

// 创建ABR上下文帧实例,指定图形API类型
ABR_Context *context_ = HMS_ABR_CreateContext(RENDER_API_GLES);
if (context_ == nullptr) {
    GOLOGE("HMS_ABR_CreateContext execution failed.");
    return false;
}

// 初始化ABR实例,配置ABR的目标帧率属性。例如游戏目标帧率为120fps,则配置ABR的目标帧率属性为120fps
ABR_ErrorCode errorCode = HMS_ABR_SetTargetFps(context_, 120);
if (errorCode != ABR_SUCCESS) {
    GOLOGE("HMS_ABR_SetTargetFps execution failed, error code: %d.", errorCode);
    return false;
}

// 初始化ABR实例,配置Buffer分辨率因子范围属性,结合具体游戏分辨率、画质设置合适的范围
// 例如设置ABR对Buffer分辨率进行0.5~1.0倍的自适应调整
errorCode = HMS_ABR_SetScaleRange(context_, 0.5f, 1.0f);
if (errorCode != ABR_SUCCESS) {
    GOLOGE("HMS_ABR_SetScaleRange execution failed, error code: %d.", errorCode);
    return false;
}

// 激活ABR上下文实例
errorCode = HMS_ABR_Activate(context_);
if (errorCode != ABR_SUCCESS) {
    GOLOGE("HMS_ABR_Activate execution failed, error code: %d.", errorCode);
    return false;
}

让我解释一下每个 API:

HMS_ABR_CreateContext(RENDER_API_GLES)

  • 创建一个 ABR 上下文实例
  • 参数 RENDER_API_GLES 表示使用 OpenGL ES 图形 API
  • 返回一个上下文指针,后续所有操作都需要用到

HMS_ABR_SetTargetFps(context_, 120)

  • 设置目标帧率为 120fps
  • ABR 会根据这个目标帧率来调整分辨率
  • 如果当前帧率低于目标,就降低分辨率;如果高于目标,就提高分辨率

HMS_ABR_SetScaleRange(context_, 0.5f, 1.0f)

  • 设置分辨率缩放范围为 0.5 到 1.0
  • 0.5 表示最低可以降到原始分辨率的 50%
  • 1.0 表示最高可以恢复到原始分辨率的 100%
  • 这个范围要根据你的应用来设置,太低会影响画质

HMS_ABR_Activate(context_)

  • 激活 ABR 实例
  • 激活后,ABR 才会在每帧渲染时动态调整分辨率

自适应渲染

在每帧的渲染循环中,ABR 会自动调整分辨率:

// 更新相机信息
float const sceneDelta = sceneTimer_.DiffTime();
sceneTimer_.RestartTimer();
camera_.Update(sceneDelta);

// 更新视图矩阵和投影矩阵,用于场景渲染
lastViewProj_ = camera_.GetViewProjectionMatrix();
lastView_ = camera_.GetViewMatrix();
lastProj_ = camera_.GetProjectionMatrix();

// 相机运动数据结构体,设置每帧实时相机运动数据
ABR_CameraData cameraData;
// 相机每帧实时位移数据
cameraData.position = static_cast<ABR_Vector3>(camera_.GetPosition());
// 相机每帧实时旋转数据
cameraData.rotation = static_cast<ABR_Vector3>(camera_.GetRotation());

// 每帧相机运动数据更新
ABR_ErrorCode errorCode = HMS_ABR_UpdateCameraData(context_, &cameraData);
if (errorCode != ABR_SUCCESS) {
    GOLOGE("HMS_ABR_UpdateCameraData execution failed, error code: %d.", errorCode);
}

// 渲染前的准备,绑定目标帧缓冲索引,清空颜色缓冲
renderer_->BeginRenderTarget(fbo, BACKGROUND.x_, BACKGROUND.y_, BACKGROUND.z_, 1.0F);

// 在Buffer渲染前调用,执行失败不影响Buffer正常渲染
errorCode = HMS_ABR_MarkFrameBuffer_GLES(context_);
if (errorCode != ABR_SUCCESS) {
    GOLOGE("HMS_ABR_MarkFrameBuffer_GLES execution failed, error code: %d.", errorCode);
}

// 调用绘制方法进行渲染
opaqueLayer_.Render(sceneDelta, camera_.GetViewMatrix(), lastViewProj_);

// 获取每帧的缩放信息
float scale;
errorCode = HMS_ABR_GetScale(context_, &scale);
GOLOGD("Scale is %f.", scale);
if (errorCode != ABR_SUCCESS) {
    GOLOGE("HMS_ABR_GetScale execution failed, error code: %d.", errorCode);
}

// 将帧缓冲索引绑定为默认值0
renderer_->EndRenderTarget();

关键 API 解释:

HMS_ABR_UpdateCameraData(context_, &cameraData)

  • 更新相机的位置和旋转信息
  • ABR 需要知道相机的运动状态来预测 GPU 负载
  • 比如相机快速移动时,可能会看到更多复杂场景

HMS_ABR_MarkFrameBuffer_GLES(context_)

  • 在渲染前调用,告诉 ABR 要调整哪个帧缓冲的分辨率
  • 这个函数执行失败不影响正常渲染,只是 ABR 功能不生效

HMS_ABR_GetScale(context_, &scale)

  • 获取当前帧的缩放系数
  • 可以用来显示调试信息或者做其他处理

销毁 ABR 实例

在 Surface 销毁时,销毁 ABR 实例:

// 销毁ABR上下文实例并释放内存资源
ABR_ErrorCode errorCode = HMS_ABR_DestroyContext(&context_);
if (errorCode != ABR_SUCCESS) {
    GOLOGE("HMS_ABR_DestroyContext execution failed, error code: %d.", errorCode);
    return false;
}

记得在不需要 ABR 的时候销毁它,释放内存资源。

ABR 的工作原理

让我用更通俗的语言解释 ABR 是怎么工作的:

  1. 监控帧率:ABR 会持续监控应用的帧率
  2. 预测负载:根据相机运动、场景复杂度等信息,预测下一帧的 GPU 负载
  3. 调整分辨率
    • 如果预测到 GPU 负载会很高(可能掉帧),就降低渲染分辨率
    • 如果 GPU 负载很低(帧率远超目标),就提高渲染分辨率
  4. 平滑过渡:分辨率的变化是渐进的,不会突然跳变

适用场景

ABR 特别适合以下场景:

  • 3D 游戏:场景复杂度变化大的游戏
  • 图形渲染应用:需要稳定帧率的渲染场景
  • VR/AR 应用:对帧率要求极高的场景

注意事项

  1. 分辨率范围设置HMS_ABR_SetScaleRange 的参数要根据你的应用来设置。如果最低值设得太低,画质会明显下降;如果设得太高,可能无法有效防止掉帧

  2. 目标帧率设置:要根据你的应用实际需要来设置。比如游戏一般 60fps 或 120fps,普通应用 30fps 就够了

  3. 相机数据更新HMS_ABR_UpdateCameraData 要每帧都调用,而且数据要准确,否则 ABR 的预测会不准确

  4. 错误处理:每个 ABR API 都会返回错误码,要检查并处理错误

核心流程图

ABR 自适应缓冲分辨率的工作原理:

创建 ABR 上下文

设置目标帧率

设置分辨率缩放范围

激活 ABR 实例

每帧渲染循环

更新相机运动数据

标记帧缓冲

执行场景渲染

获取当前缩放系数

GPU 负载是否过高?

自动降低渲染分辨率

自动提高渲染分辨率

ABR 生命周期管理:

Surface 创建

HMS_ABR_CreateContext

HMS_ABR_SetTargetFps

HMS_ABR_SetScaleRange

HMS_ABR_Activate

进入渲染循环

HMS_ABR_UpdateCameraData

HMS_ABR_MarkFrameBuffer_GLES

渲染场景

HMS_ABR_GetScale

Surface 销毁

HMS_ABR_DestroyContext

总结

ABR 是一个很实用的图形优化技术,它能让你的应用在不同负载下都能保持稳定的帧率。核心流程:

  1. 创建 ABR 上下文:HMS_ABR_CreateContext
  2. 设置目标帧率:HMS_ABR_SetTargetFps
  3. 设置分辨率范围:HMS_ABR_SetScaleRange
  4. 激活 ABR:HMS_ABR_Activate
  5. 每帧更新相机数据:HMS_ABR_UpdateCameraData
  6. 每帧标记帧缓冲:HMS_ABR_MarkFrameBuffer_GLES
  7. 获取缩放系数(可选):HMS_ABR_GetScale
  8. 销毁上下文:HMS_ABR_DestroyContext

掌握了这些,你就能在 HarmonyOS 应用中实现自适应稳态渲染了。这对于提升用户体验,特别是在复杂场景下保持流畅度,是非常有帮助的。

Logo

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

更多推荐