关于缓冲区对象
其实关于缓冲区对象的这个概念其实很广,比如说有帧缓冲区、有顶点缓冲区、像素缓冲区、纹理缓冲区等等。缓冲区对象是保存在GPU内存中的,我们能够去高效的访问它,在OpenGL有缓冲区对象之前,应用程序只有有限的选择可以在GPU当中去存储数据,以及在GPU中更新数据常常是需要去重新加载整个对象的,在系统内存和GPU内存之间来回移动数据可能是一个极其缓慢的过程
创建缓冲区其实非常简单,我们只需要调用一个函数就可以了,其中第一个参数n代表生成的缓冲区数量,第二个为用于存储单一ID或多个ID的GLuint变量或数组的地址
void glGenBuffers(GLsizei n, GLuint* buffers)
在OpenGL中有很多不同的绑定点,每个绑定点都允许我们为了不同的目的而去使用某个缓冲区。
比如说有下面的这些绑定点,就举几个常用的点
- GL_ARRAY_BUFFER这个代表的就是数组缓冲区的意思,这个数组缓冲区中可以存储颜色、位置、纹理坐标等顶点属性
- GL_PIXEL_PACK_BUFFER就是glReadPixels之类像素包装操作的目标缓冲区
- GL_PIXEL_UNPACK_BUFFER就是我们去使用glTexImage1D/2D/3D和glTexSubImage1D/2D/3D之类纹理更新函数的源缓冲区
如果我们创建了缓冲区对象,想要去使用它,我们就可以去调用glBindBuffer函数,参数1就是target告诉OpenGL这个缓存对象是要干什么的,参数2就是要传入的缓冲区对象
void glBindBuffer(GLenum target, GLuint buffer)
如果我们要从一个绑定中将一个缓冲进行解除绑定的话,我们可以去调用下面的这个函数,就是以0位缓冲区的名称
void glBindBuffer(GLenum target,0)
如果我们想往缓冲区中填充数据的话可以去使用下面的这个函数,第一个参数target可以为GL_ARRAY_BUFFER或GL_ELEMENT_ARRAY。size为待传递数据字节数量。第三个参数为源数据数组指针,如data为NULL,则就表示预留给定数据大小的内存空间。最后一个参数usage代表的是缓冲区对象的使用方式是一个性能提示,就是拿来帮助OpenGL驱动程序在正确的位置去分配内存的
void glBufferData(GLenum target,GLsizeiptr size, const GLvoid* data, GLenum usage);
如果我们想去更新缓冲区的数据,如果我们缓冲区当中已经有数据了,然后我们还是通过glBufferData去传递数据的话,那么这个缓冲区中原有的数据都会被删除。我们可以去使用glBufferSubData这个函数对已存在的缓冲区进行一部分的数据更新,而不会去导致缓冲区的其他部分内容无效,函数原型如下所示
void glBufferSubData(GLenum target,intptr offser,sizeiptr size,const void *data);
这里需要着重主要的就是像素缓冲区对象(PBO)
在存储像素和纹理方面,其实像素缓冲区对象和纹理缓冲区对象是很类似的,我们可以访问和填充像素缓冲区对象,需要注意的是只有我们把缓冲区对象绑定到一个PBO缓冲区绑定点的时候,这个缓冲区才能被称为是一个像素缓冲区对象
GL_PIXEL_PACK_BUFFER
当我们把一个像素缓冲区对象绑定到GL_PIXEL_PACK_BUFFER这个绑定点的时候,我们去调用glReadPixels这个函数的时候就会从帧缓冲区中赋值像素到我们的像素缓冲区对象上了,也就是说是赋值到GPU内存当中了,而不是复制到客户端内存了。
GL_PIXEL_UNPACK_BUFFER
还有就是当我们PBO的绑定点为GL_PIXEL_UNPACK_BUFFER的时候,任何绘制像素的OpenGL操作都会向一个绑定的像素缓冲区对象中放入它们的数据,也就是说原本我们使用glTexImage1D/2D/3D这些函数的时候都是把数据从本地CPU内存区读取到帧缓冲区当中的,但是一旦我们让一个缓冲区对象绑定了GL_PIXEL_UNPACK_BUFFER这个点之后,那么数据就会是从我们的像素缓冲区也就是GPU内存当中去读取到帧缓冲区了。
像素缓冲区会经常拿来存储一个来自渲染目标的2D图像、纹理或者是其他数据。有了像素缓冲区,能够带来的像素提升会很高。
PBO的使用场景:当我们将来需要绘制的图像需要依赖多次读取像素的结果,我们就可以用PBO来完成了,就可以去使用glReadPixels去配合PBO进行使用,这样我们就可以让不同的缓冲区对象去读取不同的区域了。
使用PBO可以提高我们的CPU时钟的利用率,下面就借助两张网上的图来说下
第一个就是传统的加载纹理的方法,就是通过CPU去加载图像数据比如说取加载tga图像文件,然后加载到内存中,之后再通过glTexImage2D去通过CPU进行加载数据到纹理对象当中,我们都知道CPU调度是有算法的比如说SJF、FCFS和RR的等等,所以我们很有可能是需要去等待CPU的调度的。才能把纹理数据复制到纹理对象中
如果我们使用PBO的话可以使用DMA(Direct Memory Access,直接内存访问)的方式快速地将数据传入到显卡或从显卡传出,它不需要等待CPU的周期,当前前面是还需要通过CPU调度把纹理数据复制到PBO当中的,但是不会将PBO中的纹理数据传输到纹理对象中。而是由GPU(OpenGL驱动器)管理将数据从PBO复制到纹理对象当中
关于DMA技术的介绍:
DMA技术是Direct Memory Access的缩写。其意思是“存储器直接访问”。它是指一种高速的数据传输操作,允许在外部设备和存储器之间直接读写数据,既不通过CPU,也不需要CPU干预的。
DMA是指外部设备不通过CPU而直接与系统内存交换数据的接口技术。要把外设的数据读入内存或把内存的数据传送到外设,一般都要通过CPU控制完成,如CPU程序查询或中断方式。利用中断进行数据传送,可以大大提高CPU的利用率。 但是采用中断传送有它的缺点,对于一个高速I/O设备,以及批量交换数据的情况,只能采用DMA方式,才能解决效率和速度问题。DMA在外设与内存间直接进行数据交换,而不通过CPU,这样数据传送的速度就取决于存储器和外设的工作速度。
整个数据传输操作在一个称为“DMA控制器”的控制下进行的。CPU除了在数据传输开始和结束时作一点处理外,在传输过程中CPU可以进行其它的工作。这样,在大部分时间里,CPU和输入输出都处在并行操作。因此,使整个计算机系统的效率大大提高