glMapBuffer致glDrawElementsInstanced报GL_INVALID_OPERATION错误
问题根因
你的代码存在两处违反OpenGL规范的问题,macOS下可正常运行是苹果自研OpenGL驱动校验宽松、容错性高导致的,不代表代码写法符合规范。
- 核心错误:映射缓冲区完成写入后,没有调用
glUnmapBuffer取消缓冲区映射。OpenGL核心规范明确规定:如果绑定到顶点属性缓冲区、索引缓冲区等绘制通路的缓冲区对象处于CPU映射锁定状态,调用任何绘制指令(包括你使用的glDrawElementsInstanced)都会直接抛出GL_INVALID_OPERATION错误。 - 逻辑错误:
memcpy的目标地址、拷贝逻辑和你之前正常运行的glBufferSubData逻辑不匹配。glMapBuffer返回的是整个缓冲区的起始映射地址,你要写入缓冲区偏移为from的位置时,必须对返回的映射指针做对应的地址偏移;同时你当前代码是从bufferData + from的位置取数据拷贝,和之前glBufferSubData直接取bufferData起始位置数据的逻辑不一致,就算解决了映射问题,渲染数据也会出错。
错误触发时机说明
glMapBuffer调用阶段只会做三类基础校验:传入的目标枚举是否合法、对应缓冲区是否已经通过glBufferData分配了足够的存储空间、缓冲区是否已经处于映射状态。只要这三项满足,glMapBuffer就会正常返回映射指针,不会预判你后续是否会忘记取消映射、是否会写入错误地址。
只有当你调用绘制接口时,驱动才会检查当前绑定到渲染通路的所有缓冲区状态,检测到顶点缓冲区还处于CPU映射锁定状态、无法被GPU读取时,才会抛出GL_INVALID_OPERATION,这个触发时机完全符合OpenGL规范要求。
修正后的参考代码
// 提前确认缓冲区已经通过glBufferData分配了不小于写入范围的存储空间 void* mappedBuffer = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); if (mappedBuffer) { // 必须检查映射返回值,映射失败时会返回空指针,直接memcpy会触发内存访问崩溃 // 目标地址偏移到缓冲区的from位置,源地址根据实际业务逻辑调整:和原glBufferSubData对齐就取bufferData,需要取偏移后的数据就加对应偏移 // 第三个参数是拷贝字节数:如果to参数代表拷贝长度就直接传to,如果代表写入结束位置就传to - from memcpy((char*)mappedBuffer + from, bufferData, to); glUnmapBuffer(GL_ARRAY_BUFFER); // 写入完成后必须取消映射,这一步是解决当前报错的核心 }
补充说明
Windows平台下NVIDIA、AMD、Intel的显卡驱动对OpenGL规范的符合度远高于macOS平台,很多在macOS上能侥幸运行的不规范写法,在Windows、Linux平台下都会直接触发错误,不要以macOS下的运行结果作为代码符合规范的判断依据。
内容的提问来源于stack exchange,提问作者ellipticaldoor
相关产品推荐
相关产品推荐

