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 及以上
搭建步骤
- 安装 DevEco Studio:去华为开发者官网下载安装
- 配置开发环境:确保网络环境正常
- 设备调试:使用真机进行调试
项目结构
这个项目比较特殊,因为它涉及到 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 的生命周期分为三个阶段:
- ABR 初始化:创建 ABR 上下文实例,配置目标帧率和分辨率因子范围,激活 ABR 功能
- 自适应渲染:绑定帧缓冲索引,动态调整 buffer 的渲染分辨率
- 销毁 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.");returnfalse;}// 初始化ABR实例,配置ABR的目标帧率属性。例如游戏目标帧率为120fps,则配置ABR的目标帧率属性为120fpsABR_ErrorCode errorCode=HMS_ABR_SetTargetFps(context_,120);if(errorCode!=ABR_SUCCESS){GOLOGE("HMS_ABR_SetTargetFps execution failed, error code: %d.",errorCode);returnfalse;}// 初始化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);returnfalse;}// 激活ABR上下文实例errorCode=HMS_ABR_Activate(context_);if(errorCode!=ABR_SUCCESS){GOLOGE("HMS_ABR_Activate execution failed, error code: %d.",errorCode);returnfalse;}让我解释一下每个 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 会自动调整分辨率:
// 更新相机信息floatconstsceneDelta=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_);// 获取每帧的缩放信息floatscale;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);}// 将帧缓冲索引绑定为默认值0renderer_->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);returnfalse;}记得在不需要 ABR 的时候销毁它,释放内存资源。
ABR 的工作原理
让我用更通俗的语言解释 ABR 是怎么工作的:
- 监控帧率:ABR 会持续监控应用的帧率
- 预测负载:根据相机运动、场景复杂度等信息,预测下一帧的 GPU 负载
- 调整分辨率:
- 如果预测到 GPU 负载会很高(可能掉帧),就降低渲染分辨率
- 如果 GPU 负载很低(帧率远超目标),就提高渲染分辨率
- 平滑过渡:分辨率的变化是渐进的,不会突然跳变
适用场景
ABR 特别适合以下场景:
- 3D 游戏:场景复杂度变化大的游戏
- 图形渲染应用:需要稳定帧率的渲染场景
- VR/AR 应用:对帧率要求极高的场景
注意事项
分辨率范围设置:
HMS_ABR_SetScaleRange的参数要根据你的应用来设置。如果最低值设得太低,画质会明显下降;如果设得太高,可能无法有效防止掉帧目标帧率设置:要根据你的应用实际需要来设置。比如游戏一般 60fps 或 120fps,普通应用 30fps 就够了
相机数据更新:
HMS_ABR_UpdateCameraData要每帧都调用,而且数据要准确,否则 ABR 的预测会不准确错误处理:每个 ABR API 都会返回错误码,要检查并处理错误
核心流程图
ABR 自适应缓冲分辨率的工作原理:
ABR 生命周期管理:
总结
ABR 是一个很实用的图形优化技术,它能让你的应用在不同负载下都能保持稳定的帧率。核心流程:
- 创建 ABR 上下文:
HMS_ABR_CreateContext - 设置目标帧率:
HMS_ABR_SetTargetFps - 设置分辨率范围:
HMS_ABR_SetScaleRange - 激活 ABR:
HMS_ABR_Activate - 每帧更新相机数据:
HMS_ABR_UpdateCameraData - 每帧标记帧缓冲:
HMS_ABR_MarkFrameBuffer_GLES - 获取缩放系数(可选):
HMS_ABR_GetScale - 销毁上下文:
HMS_ABR_DestroyContext
掌握了这些,你就能在 HarmonyOS 应用中实现自适应稳态渲染了。这对于提升用户体验,特别是在复杂场景下保持流畅度,是非常有帮助的。