glDrawElements 在驱动程序中崩溃 |调试提示
glDrawElements crashes in the driver | debugging hints
简略版:怎么会崩溃(内存访问不当exception/nullptr异常)里面glDrawElements/glDrawElementsInstanced
是已调试?
长版:
您有一个使用 VAOs
的 OpenGL 渲染代码路径,并通过调用 glDrawElements
或 glDrawElementsInstanced
提交渲染。该代码路径大部分时间都能正常工作。我们说的是"editor code",意思是:数据可能是任何几何形状,而且极有可能经常变化。
但有时在提交可重现的数据更改后,它只会在 glDrawElements*
驱动程序代码中崩溃(即调用 glDrawElements
,函数参数正常,崩溃发生在 glDrawElements
内部) .
你如何继续调试这个问题?
P.S.:
- 自答题:所有的研究努力都变成了答案!
- 这是针对编辑器代码的。对于简单的演示,此类崩溃主要是由于编码人员没有正确理解 glDrawElements 的要求造成的,因此代码路径要么有效,要么无效——在这些情况下,请参见:
- Crash on glDrawElements
- glDrawElements crash (OpenGL 3.2 / Windows 7)
首先应该清楚什么可能导致驱动程序崩溃。在大多数情况下,这是错误的内存访问。
驱动内部访问坏内存的原因是什么?
GL_ELEMENT_ARRAY_BUFFER
-绑定以某种方式改变了(因此给glDrawElements
的参数可能导致访问超出该对象的内存)
GL_ELEMENT_ARRAY_BUFFER
的内容已经改变,可能引用了uninitialized/non-existent顶点数据(VBO访问越界)
- 任何关联的
GL_ARRAY_BUFFER
对象的数据已更改,因此不再包含引用的顶点数据(VBO 访问越界)
- 甚至更多此类更改。
glDrawElements*
内的访问冲突主要是指在 VAO 状态中绑定的任何对象都被越界访问。
如果没有额外的调试代码,很难发现这些访问冲突。我建议在调用 glDrawElements*
之前插入调试输出。调试输出应该查询所有可用的绑定和信息,因此您可以将设置 "when it works" 与 "when it crashes" 进行比较,然后找出要查找的内容。
我的调试函数如下所示:
void debugVAOState(std::string baseMessage)
{
baseMessage.append( " ... querying VAO state:\n" );
int vab, eabb, eabbs, mva, isOn( 1 ), vaabb;
glGetIntegerv( GL_VERTEX_ARRAY_BINDING, &vab );
glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &eabb );
glGetBufferParameteriv( GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &eabbs );
baseMessage.append( " VAO: " + std::to_string( vab ) + "\n" );
baseMessage.append( " IBO: " + std::to_string( eabb ) + ", size=" + std::to_string( eabbs ) + "\n" );
glGetIntegerv( GL_MAX_VERTEX_ATTRIBS, &mva );
for ( unsigned i = 0; i < mva; ++i )
{
glGetVertexAttribiv( i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &isOn );
if ( isOn )
{
glGetVertexAttribiv( i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &vaabb );
baseMessage.append( " attrib #" + std::to_string( i ) + ": VBO=" + std::to_string( vaabb ) + "\n" );
}
}
OutputDebugString( baseMessage.c_str() );
}
它仍然很简单,只输出最有价值的信息,以便能够查看上述绑定是否发生了某种变化。但这帮助我发现了很多由积极的 OpenGL 优化引起的崩溃。
简略版:怎么会崩溃(内存访问不当exception/nullptr异常)里面glDrawElements/glDrawElementsInstanced
是已调试?
长版:
您有一个使用 VAOs
的 OpenGL 渲染代码路径,并通过调用 glDrawElements
或 glDrawElementsInstanced
提交渲染。该代码路径大部分时间都能正常工作。我们说的是"editor code",意思是:数据可能是任何几何形状,而且极有可能经常变化。
但有时在提交可重现的数据更改后,它只会在 glDrawElements*
驱动程序代码中崩溃(即调用 glDrawElements
,函数参数正常,崩溃发生在 glDrawElements
内部) .
你如何继续调试这个问题?
P.S.:
- 自答题:所有的研究努力都变成了答案!
- 这是针对编辑器代码的。对于简单的演示,此类崩溃主要是由于编码人员没有正确理解 glDrawElements 的要求造成的,因此代码路径要么有效,要么无效——在这些情况下,请参见:
- Crash on glDrawElements
- glDrawElements crash (OpenGL 3.2 / Windows 7)
首先应该清楚什么可能导致驱动程序崩溃。在大多数情况下,这是错误的内存访问。
驱动内部访问坏内存的原因是什么?
GL_ELEMENT_ARRAY_BUFFER
-绑定以某种方式改变了(因此给glDrawElements
的参数可能导致访问超出该对象的内存)GL_ELEMENT_ARRAY_BUFFER
的内容已经改变,可能引用了uninitialized/non-existent顶点数据(VBO访问越界)- 任何关联的
GL_ARRAY_BUFFER
对象的数据已更改,因此不再包含引用的顶点数据(VBO 访问越界) - 甚至更多此类更改。
glDrawElements*
内的访问冲突主要是指在 VAO 状态中绑定的任何对象都被越界访问。
如果没有额外的调试代码,很难发现这些访问冲突。我建议在调用 glDrawElements*
之前插入调试输出。调试输出应该查询所有可用的绑定和信息,因此您可以将设置 "when it works" 与 "when it crashes" 进行比较,然后找出要查找的内容。
我的调试函数如下所示:
void debugVAOState(std::string baseMessage)
{
baseMessage.append( " ... querying VAO state:\n" );
int vab, eabb, eabbs, mva, isOn( 1 ), vaabb;
glGetIntegerv( GL_VERTEX_ARRAY_BINDING, &vab );
glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &eabb );
glGetBufferParameteriv( GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &eabbs );
baseMessage.append( " VAO: " + std::to_string( vab ) + "\n" );
baseMessage.append( " IBO: " + std::to_string( eabb ) + ", size=" + std::to_string( eabbs ) + "\n" );
glGetIntegerv( GL_MAX_VERTEX_ATTRIBS, &mva );
for ( unsigned i = 0; i < mva; ++i )
{
glGetVertexAttribiv( i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &isOn );
if ( isOn )
{
glGetVertexAttribiv( i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &vaabb );
baseMessage.append( " attrib #" + std::to_string( i ) + ": VBO=" + std::to_string( vaabb ) + "\n" );
}
}
OutputDebugString( baseMessage.c_str() );
}
它仍然很简单,只输出最有价值的信息,以便能够查看上述绑定是否发生了某种变化。但这帮助我发现了很多由积极的 OpenGL 优化引起的崩溃。