OpenGL 4.6调用glMapNamedBuffer返回GL_INVALID_OPERATION原因
问题原因
你对Khronos官方手册的错误触发场景描述存在遗漏,glMapNamedBuffer() 触发GL_INVALID_OPERATION的场景远不止「传入不存在的缓冲区句柄」这一种,你的核心问题是调用glNamedBufferStorage()创建不可变缓冲区存储时,没有声明映射操作需要的对应权限位。
OpenGL 4.5之后引入的glNamedBufferStorage()创建的是不可变缓冲区存储,和旧版glBufferData()创建的可变存储逻辑完全不同:你必须在分配存储时通过flags参数显式声明后续要对这块内存执行的所有操作类型,驱动会根据声明做内存优化,未声明的操作会被直接拒绝并抛出错误。
具体错误点说明
- 你第一次传flags为
0:等于告诉驱动这块存储后续不需要任何CPU端读写、更新操作,自然不允许映射。 - 你尝试加的
GL_DYNAMIC_STORAGE_BIT:这个权限位的作用是允许后续通过glNamedBufferSubData()更新缓冲区,和映射操作的权限没有任何关系,加了也无法支持映射。 - 你调用
glMapNamedBuffer()时传入了GL_WRITE_ONLY,要求获取可写的映射指针,对应必须在glNamedBufferStorage()的flags中加入GL_MAP_WRITE_BIT,缺省该位就会直接触发GL_INVALID_OPERATION。
修正后代码
glCreateBuffers(buffers.size(), buffers.data()); // buffers is a std::array of GLuints const GLsizeiptr buffer_size = n * sizeof(glm::vec4); // 分配存储时显式声明允许映射写入的权限 glNamedBufferStorage(buffers[3], buffer_size, nullptr, GL_MAP_WRITE_BIT); // 此时映射可正常返回有效指针 void* bfrptr = glMapNamedBuffer(buffers[3], GL_WRITE_ONLY); // 执行memcpy拷贝数据 memcpy(bfrptr, source_data_ptr, buffer_size); // 操作完成后记得取消映射 glUnmapNamedBuffer(buffers[3]);
额外注意事项
- 如果需要使用持久映射(映射后不执行unmap,持续持有指针访问),需要额外在
glNamedBufferStorage()的flags和映射接口的access参数中同时传入GL_MAP_PERSISTENT_BIT,如果需要缓冲区内容和GPU端自动同步还要加GL_MAP_COHERENT_BIT。 - 旧版可变缓冲区接口不需要显式声明权限是因为驱动默认给所有操作留了支持,内存开销和性能损耗更高,不可变存储的权限声明机制本身就是为了降低驱动开销、提升性能设计的。
内容的提问来源于stack exchange,提问作者user4028648
相关产品推荐
相关产品推荐

