使用PBO异步传输像素数据
- 前言
- 一、 一些GPU 分配的常用内存对象介绍
- 二、 PBO 传输原理
- 三、 PBO 使用方法
前言
书接上文【精通SDL之----SDL_RenderReadPixels截屏】,SDL_RenderReadPixels在GLES2上是一个非常耗时的操作,因为
1. OpenGL ES 是一个异步渲染管线(当调用 OpenGL ES 的绘图命令时,这些命令通常会被放入一个队列中,然后由 GPU 异步执行),但SDL_RenderReadPixels需要同步,这会强制 GPU 和 CPU 同步,导致渲染流水线被阻塞;
2. 读取像素数据意味着需要从GPU内存将数据拷贝到CPU内存,帧缓冲区通常是以非线性布局存储在 GPU 内存中(如瓦片布局),以便优化渲染操作的性能。然而,这种布局对逐像素的读取操作并不友好,从而导致读取像素数据的效率低下。
为减少这种影响,通常的做法是尽量避免频繁调用SDL_RenderReadPixels,或者是使用帧缓存对象(FBO)进行离屏渲染(上文中讲到的)。不过,在GLES3中提供了一种更优雅的方式,那就是使用PBO异步传输像素数据。
一、 一些GPU 分配的常用内存对象介绍
简写 | 含义 | 作用 |
---|---|---|
FBO | 帧缓冲对象 | 允许渲染操作不直接输出到屏幕,而是输出到一个或多个纹理或渲染缓冲区中,可以用于离屏渲染操作如后期处理效果、镜面反射、阴影贴图等 |
VBO | 顶点缓冲对象 | 将大量顶点数据存储在 VBO 中,可以一次性提交大量顶点给 GPU 渲染,从而降低 CPU 和 GPU 之间的通信开销,并提升渲染效率 |
PBO | 像素缓冲对象 | 使用了 DMA(Direct Memory Access,直接存储器访问) 技术,可用于快速更新纹理数据,特别是在需要频繁更改纹理内容的应用场景如视频解码、屏幕捕捉中。允许像素数据在 GPU 和 CPU 之间进行异步传输,避免阻塞主线程 |
TBO | 纹理缓冲对象 | 将纹理数据存储在缓冲区,适用于需要随机访问大数据集的场景如粒子系统 |
RBO | 渲染缓冲对象 | 存储渲染结果,结合帧缓冲对象(FBO)用于离屏渲染或多重采样抗锯齿 |
二、 PBO 传输原理
图像数据不经过RAM,直接读取到PBO中,然后PBO通过DMA上传给纹理对象。
三、 PBO 使用方法
PBO其实最适合使用的场景在录屏这一块,使用双缓冲技术,每次从上一帧readPixel后的PBO读取像素,交替使用PBO。
GLES30.glGenBuffers(1, &pbo); // 创建pbo
GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, pbo) // 绑定pbo
GLHelper.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE)
// 映射内存,把pbo地址映射到cpu buf指针
void* buf = GLES30.glMapBufferRange(GLES30.GL_PIXEL_PACK_BUFFER,
0, width * height * 4, GLES30.GL_MAP_READ_BIT)
... // 拿到buf后的业务操作
GLES30.glUnmapBuffer(GLES30.GL_PIXEL_PACK_BUFFER)//解除映射
GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, GLES30.GL_NONE)//解除绑定PBO