当前位置: 首页 > news >正文

Android渲染-AHardwareBuffer

本文主要从应用的角度介绍android的native层AHardwareBuffer创建纹理以及保存渲染数据。

HardwareBuffer

要介绍native层的AHardwareBuffer,就需要先从Java层的HardwareBuffer说起。Android官方对于HardwareBuffer介绍如下:

HardwareBuffer wraps a native AHardwareBuffer object, which is a low-level object representing a memory buffer accessible by various hardware units. HardwareBuffer allows sharing buffers across different application processes. In particular, HardwareBuffers may be mappable to memory accessibly to various hardware systems, such as the GPU, a sensor or context hub, or other auxiliary processing units. For more information, see the NDK documentation for AHardwareBuffer.

HardwareBuffer 官方介绍为一种底层的内存 buffer 对象,可在不同进程间共享,可映射到不同硬件系统,如 GPU、传感器等,从构造函数可以看出,其可以指定 format 和 usage,用来让底层选择最合适的实现。

从HardwareBuffer的源码中可以了解到,HardwareBuffer只是 GraphicBuffer 的一个包装。在Android早期版本(API<=25), Java层并没有提供底层的GraphicBuffer API,通常使用底层由GraphicBuffer实现的Surface。因此本质上是 Android 系统开放了更底层的 API,我们才可以有更高效的实现。接下来看具体如何基于HardwareBuffer跨进程传输纹理。

通过 AHardwareBuffer_toHardwareBuffer 函数,可以将native层的AHardwareBuffer 对象转为 Java HardwareBuffer 对象,其本身实现了 Parcelable 接口,可以直接通过 AIDL 传递到另一个进程,其中具体的实现就是 Android 系统 GraphicBuffer 跨进程的方案,底层通过 fd 实现,B进程获取对应的HardwareBuffer后,可以通过AHardwareBuffer_fromHardwareBuffer继续转换为native层的AHardwareBuffer。 

AHardwareBuffer

接下来主要介绍使用AHardwareBuffer创建纹理以及通过AHardwareBuffer读取纹理图像的流程

AHardwareBuffer创建纹理

创建纹理的流程较为简单,创建AHardwareBuffer_Desc句柄,结构体赋值,本文以创建NV21的OES纹理为例,代码如下:

FUN_BEGIN_TIME("RenderContext::CreateOESTexture")if(textureID == 0){AHardwareBuffer_Desc h_buffer_desc = {0};h_buffer_desc.stride = frameData->i32Width;h_buffer_desc.height = frameData->i32Height;h_buffer_desc.width = frameData->i32Width;h_buffer_desc.layers = 1;h_buffer_desc.format = 0x11;h_buffer_desc.usage = AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN|AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;int ret = AHardwareBuffer_allocate(&h_buffer_desc, &inputHWBuffer);EGLint attr[] = {EGL_NONE};EGLDisplay edp;edp = (EGLDisplay)eglGetCurrentDisplay();inputEGLImage) = eglCreateImageKHR(edp, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, eglGetNativeClientBufferANDROID(inputHWBuffer), attr);glGenTextures(1, &textureID);glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureID);glTexParameteri(GL_TEXTURE_EXTERNAL_OES , GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_EXTERNAL_OES , GL_TEXTURE_MAG_FILTER, GL_LINEAR);glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES , (GLeglImageOES)inputEGLImage);GLUtils::CheckGLError("eglCreateImageKHR");}AHardwareBuffer_Planes planes_info = {0};int ret = AHardwareBuffer_lockPlanes(inputHWBuffer,AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK,-1,nullptr,&planes_info);if (ret != 0) {LOGI("Failed to AHardwareBuffer_lockPlanes");}else{memcpy(planes_info.planes[0].data,frameData->ppu8Plane[0],frameData->i32Width * frameData->i32Height*3/2);ret = AHardwareBuffer_unlock(inputHWBuffer, nullptr);if (ret != 0) {LOGI("Failed to AHardwareBuffer_unlock");}}glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureID);
FUN_END_TIME("RenderContext::CreateOESTexture")

AHardwareBuffer读取纹理图像数据

读取纹理图像数据的方式和创建纹理的方式类似,通过上述创建纹理的方式,我们实现了AHardwareBuffer 和 EGLImageKHR的绑定,因此,我们可以通过反向思维,将纹理读取出来,代码如下:

FUN_BEGIN_TIME("RenderContext::ReadOESTexture")unsigned char *ptrReader = nullptr;ret = AHardwareBuffer_lock(inputHWBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, -1,     nullptr, (void **) &ptrReader); memcpy(dstBuffer, ptrReader, imgWidth * imgHeight * 3 / 2);ret = AHardwareBuffer_unlock(inputHWBuffer, nullptr);
FUN_END_TIME("RenderContext::ReadOESTexture")

至此,我们可以将dstBuffer通过字节,或者其他形式,保存为图像数据。

总结

针对Android侧,我们需要理清GraphicBuffer、AHardwareBuffer、ANativeWindowBuffer之间的关系。从联系上,GraphicBuffer 继承了ANativeWindowBuffer,所以可以直接通过static_cast<>类型转换成ANativeWindowBuffer,不过由于是多继承,所以转完有一个地址偏移(static_cast 自动完成)。而AHardwareBuffer只是一个抽象的概念,没有具体类型,与GraphicBuffer 没有任何继承关系,也没有具体的类型,是个空结构体,类似于void 类型。从源码可以看到,aosp封装的那些AHardwareBuffer_xxx接口,本质上底层都是通过AHardwareBuffer_to_GraphicBuffer,转成GraphicBuffer,依旧用GraphicBuffer的形式做的后续处理。

相关文章:

  • 【Go-自学版】03-即时通信系统1
  • win 10 hp hotkey uwp service占用内存高解决方法
  • 班级管理的重要性
  • 关于加密解密,加签验签那些事
  • 数据库常用锁
  • j1冒个泡-排序的演变--扩展题
  • 四年编程成长总结
  • 人工智能|网络爬虫——用Python爬取电影数据并可视化分析
  • UVM中的config_db机制传递interface
  • Java王者荣耀火柴人
  • 在UniApp中使用uni.makePhoneCall方法调起电话拨打功能
  • 随机Numpy数组的创建方法(第2讲)
  • 课后作业7.3.1:构造一个自己的小操作系统
  • 龟速乘与快速幂
  • zookeeper2==zookeeper源码阅读,集群如何选举出LEADER
  • .pyc 想到的一些问题
  • 《用数据讲故事》作者Cole N. Knaflic:消除一切无效的图表
  • 07.Android之多媒体问题
  • canvas绘制圆角头像
  • Django 博客开发教程 16 - 统计文章阅读量
  • HTTP中GET与POST的区别 99%的错误认识
  • JavaScript异步流程控制的前世今生
  • Java教程_软件开发基础
  • Linux后台研发超实用命令总结
  • React的组件模式
  • vue和cordova项目整合打包,并实现vue调用android的相机的demo
  • 和 || 运算
  • 普通函数和构造函数的区别
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用(一)
  • 浅谈web中前端模板引擎的使用
  • 通过来模仿稀土掘金个人页面的布局来学习使用CoordinatorLayout
  • 《天龙八部3D》Unity技术方案揭秘
  • shell使用lftp连接ftp和sftp,并可以指定私钥
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​比特币大跌的 2 个原因
  • ​力扣解法汇总1802. 有界数组中指定下标处的最大值
  • #控制台大学课堂点名问题_课堂随机点名
  • #我与Java虚拟机的故事#连载07:我放弃了对JVM的进一步学习
  • (11)MATLAB PCA+SVM 人脸识别
  • (16)Reactor的测试——响应式Spring的道法术器
  • (2)Java 简介
  • (3)(3.5) 遥测无线电区域条例
  • (LeetCode) T14. Longest Common Prefix
  • (附源码)springboot优课在线教学系统 毕业设计 081251
  • (附源码)ssm基于微信小程序的疫苗管理系统 毕业设计 092354
  • (规划)24届春招和25届暑假实习路线准备规划
  • (过滤器)Filter和(监听器)listener
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (使用vite搭建vue3项目(vite + vue3 + vue router + pinia + element plus))
  • (一)appium-desktop定位元素原理
  • (已解决)报错:Could not load the Qt platform plugin “xcb“
  • (转)IOS中获取各种文件的目录路径的方法
  • (自适应手机端)响应式新闻博客知识类pbootcms网站模板 自媒体运营博客网站源码下载
  • ./configure,make,make install的作用(转)
  • .axf 转化 .bin文件 的方法