OpenGL4.3如何管理buffer中的数据的(二)
1.从OpenGl缓存中读回数据
函数原形:
void glGetBufferSubData(GLenum target, GLintptr offset,GLsizeiptr size, GLvoid * data);
函数说明:从指定的缓存读取当前缓存对象的部分/全部内容,数据从offset开始读取,一共size个字节,读回的数据放入应用程序以*data指定的内存位置。如果缓存对象处于映射状态(后面会讲)将产生一个错误信息。如果读取的范围超过了缓存对象的范围,也会产生错误信息。
2.地址映射
在此前,已经获得了很多种类型的函数,诸如向缓存写入数据的函数:
glBufferData()
glBufferSubData()
让数据在缓存内移动的函数:
glCopyBufferSubData()
从缓存读取数据的函数:
glGetBufferSubData()
使用这些函数,并小心写出封装函数,就可以使得OpenGL可以做到类似于应用程序只需要使用一个指针就可以像操作内存一样工作的模式。幸运的是,OpenGL自身提供了映射函数完成这些操作:
函数原形:
void * glMapBuffer(GLenum target, GLenum access);
函数说明:将OpenGL的整个指定缓存对象的内容映射到返回的地址,应用程序可以对该地址直接进行读写操作(当然,要符合access指定的操作类型)。如果OpenGL不能映射缓存到用户空间(如缓存不足),则会产生错误并返回NULL。
其中access有以下几个选项:
GL_READ_ONLY
GL_WRITE_ONLY
GL_READ_WRITE
注:当你映射一个本不应该被应用程序接触到的缓存内容的时候,OpenGL会将数据搬移到可被用户操作的缓存,同样的,写回缓存的时候会再次执行搬移,这将耗费极高的代价,使用时请注意。
在编辑完映射部分的内容后,需要通知OpenGL将数据写回缓存:
函数原形:
GLboolean glUnmapBuffer(GLenum target);
函数说明:释放映射的缓存,返回GL_TRUE表示成功,返回GL_FALSE说明可能释放过程中产生了问题,应用程序有责任监视此错误并重新设置存储的数据。
例3.2说明了利用映射的过程:
GLuint buffer;
FILE * f;
size_t filesize;
// 打开一个文件并获取文件长度
f = fopen("data.dat", "rb");
fseek(f, 0, SEEK_END);
filesize = ftell(f);
fseek(f, 0, SEEK_SET);
//分配一个缓存对象
glGenBuffers(1, &buffer);
//将此对象绑定到GL_COPY_WRITE_BUFFER缓存
glBindBuffer(GL_COPY_WRITE_BUFFER, buffer);
//为缓存对象分配文件字节大小的空间
glBufferData(GL_COPY_WRITE_BUFFER, (GLsizei)filesize, NULL,
GL_STATIC_DRAW);
// 映射缓存,只写模式
void * data = glMapBuffer(GL_COPY_WRITE_BUFFER, GL_WRITE_ONLY);
// 将文件直接读入映射地址
fread(data, 1, filesize, f);
// 释放映射,关闭文件,OpenGL将映射内容移入缓存
glUnmapBuffer(GL_COPY_WRITE_BUFFER);
fclose(f);
注:这里用户无需为拷贝预留空间,而如果目的缓存为应用程序可见的缓存,则OpenGL也无需执行额外的复制操作。
3.非同步及显式映射
在glMapBuffer函数的基础上,OpenGL提供了更加精准的映射函数:
函数原形:
void * glMapBufferRange(GLenum target, GLintptr offset,GLsizeiptr length, GLbitfield access);
函数说明:映射由targe指定的缓存的当前缓存对象到用户空间,偏移地址和字节长度指出了具体的缓存中的段落位置,返回的地址为偏移量指出的映射地址。
注意这里的access类型为GLbitfield,可以一次选择多个选项,其说明见下表:
名称 | 说明 |
---|---|
GL_MAP_INVALIDATE_RANGE_BIT | 指定的范围的数据都会被当作无效数据初始化,不能与GL_MAP_READ_BIT同用 |
GL_MAP_INVALIDATE_BUFFER_BIT | 整个指定的缓存都将变成无效 |
GL_MAP_FLUSH_EXPLICIT_BIT | 如果使用此选项,则应用程序有责任负责调用函数glFlushMappedBufferRange()告诉OpenGL在执行glUnmapBuffer()前指定范围的一部分数据已经更新好了,无需再更新,否则glUnmapBuffer()会尝试将整个指定范围都更新 |
GL_MAP_UNSYNCHRONIZED_BIT | 如果不设置此位,则OpenGL将会等待所有挂起的操作都执行完毕才释放映射 |
GL_MAP_READ_BIT | |
GL_MAP_WRITE_BIT |
这里提到的一个新函数:
函数原形:
void glFlushMappedBufferRange(GLenum target, GLintptr offset,GLsizeiptr length);
函数说明:告诉OpenGL对应的部分缓存已经准备好,并要求将其同步到缓存对象管理的缓存中去。
此函数的解释就是,当调用这个函数, OpenGL就认为此部分数据已经准备好了,并读取到缓存中,无需等待glUnmapBuffer命令(相当于并行执行)。