OpenGL drawcall 边界越界问题
buffer数据时size的大小
在 OpenGL buffer数据一般都是以const void *加size的C语言风格传递数据。例如下面非常经典的
void glBufferData( GLenum target, GLsizeiptr size, const void * data, GLenum usage)
有下面用法
1 | // 数据来源 |
可以看到,这里的size
是以字节为单位,而且size
的类型为 GLsizeiptr
。
drawcall时count的大小
同样是非常经典的
void glDrawArrays( GLenum mode, GLint first, GLsizei count);
1 | std::vector<float> dataContainer {···}; |
此时count中每一个的大小是由glVertexAttribPointer中的size
,type
和stride
共同决定。如上例中,一个点的大小就是2
个float
的大小。要绘制完buffer中的所有内容需要size / (2*sizeof(GLfloat))
。
drawcall超出边界时会发生情况
数组越界是一个比较常见的错误。有些语言会直接抛出异常,如C/C++,有些语言则会直接访问到对应的内容,返回不确定的结果。在OpenGL里,drawcall中的越界——如glDrawArrays()
中的first
和counts
指向的显存段超过当前绑定的vbo则是未定义的行为。(至少我在OpenGL-Refpages中没看到对应的规范)
实际上在具体应用中,同样的越界drawcall在不同显卡和操作系统表现出来的行为也是不一样。应该是由显卡驱动厂商决定越界行为的。
-
macOS OpenGL ES2.0 Intel Iris Plus 645
在macOS下,drawcall越界了后,越界的部分的内存的默认值通常是0,在显存使用量不高的情况下,通常不会绘制出奇怪的内容。也不会引发OpenGL错误。这和Apple下Clang编译器的行为也一致,变量默认值为0 -
Windows OpenGL ES2.0 Intel UHD 630
在Windows下drawcall越界同样也不会引发OpenGL错误,但是很容易绘制出奇怪的东西出来。 -
Windows Angle
Windows下使用Angle转译glES到Dx值得特别提一下。使用Angle时,如果drawcall越界了,则会直接跳过,什么效果都没有。用RenderDoc截取帧分析会找不到越界的drawcall,显示上也没有效果。推测可能时Dx引发错误或者Angel内部记录到会有越界行为发生。