什么会导致glDrawArrays产生一个GL_INVALID_OPERATION错误?
我一直在试图编写一个Marching Cubesalgorithm的双通道GPU实现,类似于使用OpenGL和GLSL的第一章GPU Gems 3。 然而,在我的第一遍调用glDrawArrays
一贯失败,并有一个GL_INVALID_OPERATION
。
我查阅了所有可以find的文档,并发现了glDrawArrays
可以抛出这个错误的条件:
- 如果非零缓冲区对象名称绑定到启用的数组或绑定到
GL_DRAW_INDIRECT_BUFFER
绑定并且当前映射缓冲区对象的数据存储区,则会生成GL_INVALID_OPERATION
。 - 如果在执行
glBegin
和相应的glEnd
之间执行glDrawArrays
则会生成GL_INVALID_OPERATION
。 - 如果当前程序对象中的任何两个活动采样器都是不同types的,则将由
glDrawArrays
或glDrawElements
生成GL_INVALID_OPERATION
,但是指向相同的纹理图像单元。 - 如果几何着色器处于活动状态并且模式与当前安装的程序对象中的几何着色器的input基元types不兼容,则会生成
GL_INVALID_OPERATION
。 - 如果模式是
GL_PATCHES
并且没有镶嵌控制着色器处于活动状态,则会生成GL_INVALID_OPERATION
。 - 如果将基元的顶点logging到用于变换反馈目的的缓冲区对象,则会产生
GL_INVALID_OPERATION
,这会导致超出任何缓冲区对象的大小限制,或者超出由glBindBufferRange
设置的结束位置offset + size – 1。 -
GL_INVALID_OPERATION
由glDrawArrays()
生成,如果不存在几何着色器,转换反馈处于活动状态,模式不是允许的模式之一。 -
GL_INVALID_OPERATION
由glDrawArrays()
生成,如果存在几何着色器,则变换反馈处于活动状态,几何着色器的输出基元types与变换反馈基元模式不匹配。 - 如果绑定的着色器程序无效,则会生成
GL_INVALID_OPERATION
。 - 编辑10/10/12:如果正在使用变换反馈,并且绑定到变换反馈绑定点的缓冲区也绑定到数组缓冲区绑定点,则会生成
GL_INVALID_OPERATION
。 这是我遇到的问题,由于我绑定了哪个缓冲区的拼写错误。 虽然规范声明这是非法的,但在我发现的任何文档中,glDrawArrays都没有列出它作为抛出错误的原因之一。
不幸的是,没有一个官方文档可以find涵盖3个以上的官方文档。 我不得不从很多来源收集这个清单。 第7和第8点实际上来自glBeginTransformFeedback
的文档,第9点似乎没有logging。 我发现它在某个论坛post中提到。 不过,我还是不认为这个清单是完整的,因为这些清单似乎都不能解释我得到的错误。
- 我在任何地方都没有在程序中映射任何缓冲区。
- 我正在使用核心configuration文件,所以
glBegin
和glEnd
甚至不可用。 - 我有两个采样器,他们是不同的types,但他们肯定映射到不同的纹理。
- 几何着色器是活动的,但是它的input布局是
layout (points) in
,glDrawArrays
是用GL_POINTS
调用的。 - 我没有使用任何types的
GL_PATCHES
或镶嵌着色器。 - 我确定我正在分配几何着色器可能输出的最大空间量。 然后我尝试了四倍。 没有帮助。
- 有一个几何着色器。 看下一点。
- 正在使用变换反馈,并且有一个几何着色器,但是输出布局是
layout (points) out
,glBeginTransformFeedback
是用GL_POINTS
调用的。 - 我试图在调用
glDrawArrays
之前插入一个对glValidateProgram
的调用,并返回GL_TRUE
。
实际的OpenGL代码在这里:
const int SECTOR_SIZE = 32; const int SECTOR_SIZE_CUBED = SECTOR_SIZE * SECTOR_SIZE * SECTOR_SIZE; const int CACHE_SIZE = SECTOR_SIZE + 3; const int CACHE_SIZE_CUBED = CACHE_SIZE * CACHE_SIZE * CACHE_SIZE; MarchingCubesDoublePass::MarchingCubesDoublePass(ServiceProvider* svc, DensityMap* sourceData) { this->sourceData = sourceData; densityCache = new float[CACHE_SIZE_CUBED]; } MarchingCubesDoublePass::~MarchingCubesDoublePass() { delete densityCache; } void MarchingCubesDoublePass::InitShaders() { ShaderInfo vertShader, geoShader, fragShader; vertShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass1.vert", GL_VERTEX_SHADER); svc->shader->Compile(vertShader); geoShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass1.geo", GL_GEOMETRY_SHADER); svc->shader->Compile(geoShader); shaderPass1 = glCreateProgram(); static const char* outputVaryings[] = { "triangle" }; glTransformFeedbackVaryings(shaderPass1, 1, outputVaryings, GL_SEPARATE_ATTRIBS); assert(svc->shader->Link(shaderPass1, vertShader, geoShader)); uniPass1DensityMap = glGetUniformLocation(shaderPass1, "densityMap"); uniPass1TriTable = glGetUniformLocation(shaderPass1, "triangleTable"); uniPass1Size = glGetUniformLocation(shaderPass1, "size"); attribPass1VertPosition = glGetAttribLocation(shaderPass1, "vertPosition"); vertShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass2.vert", GL_VERTEX_SHADER); svc->shader->Compile(vertShader); geoShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass2.geo", GL_GEOMETRY_SHADER); svc->shader->Compile(geoShader); fragShader = svc->shader->Load("data/shaders/MarchingCubesDoublePass-Pass2.frag", GL_FRAGMENT_SHADER); svc->shader->Compile(fragShader); shaderPass2 = glCreateProgram(); assert(svc->shader->Link(shaderPass2, vertShader, geoShader, fragShader)); uniPass2DensityMap = glGetUniformLocation(shaderPass2, "densityMap"); uniPass2Size = glGetUniformLocation(shaderPass2, "size"); uniPass2Offset = glGetUniformLocation(shaderPass2, "offset"); uniPass2Matrix = glGetUniformLocation(shaderPass2, "matrix"); attribPass2Triangle = glGetAttribLocation(shaderPass2, "triangle"); } void MarchingCubesDoublePass::InitTextures() { for (int x = 0; x < CACHE_SIZE; x++) { for (int y = 0; y < CACHE_SIZE; y++) { for (int z = 0; z < CACHE_SIZE; z++) { densityCache[x + y*CACHE_SIZE + z*CACHE_SIZE*CACHE_SIZE] = sourceData->GetDensity(Vector3(x-1, y-1, z-1)); } } } glGenTextures(1, &densityTex); glBindTexture(GL_TEXTURE_3D, densityTex); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); glTexImage3D(GL_TEXTURE_3D, 0, GL_R32F, CACHE_SIZE, CACHE_SIZE, CACHE_SIZE, 0, GL_RED, GL_FLOAT, densityCache); glGenTextures(1, &triTableTex); glBindTexture(GL_TEXTURE_RECTANGLE, triTableTex); glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_R16I, 16, 256, 0, GL_RED_INTEGER, GL_INT, triTable); } void MarchingCubesDoublePass::InitBuffers() { float* voxelGrid = new float[SECTOR_SIZE_CUBED*3]; unsigned int index = 0; for (int x = 0; x < SECTOR_SIZE; x++) { for (int y = 0; y < SECTOR_SIZE; y++) { for (int z = 0; z < SECTOR_SIZE; z++) { voxelGrid[index*3 + 0] = x; voxelGrid[index*3 + 1] = y; voxelGrid[index*3 + 2] = z; index++; } } } glGenBuffers(1, &bufferPass1); glBindBuffer(GL_ARRAY_BUFFER, bufferPass1); glBufferData(GL_ARRAY_BUFFER, SECTOR_SIZE_CUBED*3*sizeof(float), voxelGrid, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); glGenBuffers(1, &bufferPass2); glBindBuffer(GL_ARRAY_BUFFER, bufferPass2); glBufferData(GL_ARRAY_BUFFER, SECTOR_SIZE_CUBED*5*sizeof(int), NULL, GL_DYNAMIC_COPY); glBindBuffer(GL_ARRAY_BUFFER, 0); glGenVertexArrays(1, &vaoPass1); glBindVertexArray(vaoPass1); glBindBuffer(GL_ARRAY_BUFFER, bufferPass1); glVertexAttribPointer(attribPass1VertPosition, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); glBindBuffer(GL_ARRAY_BUFFER, 0); glEnableVertexAttribArray(attribPass1VertPosition); glBindVertexArray(0); glGenVertexArrays(1, &vaoPass2); glBindVertexArray(vaoPass2); glBindBuffer(GL_ARRAY_BUFFER, bufferPass2); glVertexAttribIPointer(attribPass2Triangle, 1, GL_INT, 0, (void*)0); glBindBuffer(GL_ARRAY_BUFFER, 0); glEnableVertexAttribArray(attribPass2Triangle); glBindVertexArray(0); glGenQueries(1, &queryNumTriangles); } void MarchingCubesDoublePass::Register(Genesis::ServiceProvider* svc, Genesis::Entity* ent) { this->svc = svc; this->ent = ent; svc->scene->RegisterEntity(ent); InitShaders(); InitTextures(); InitBuffers(); } void MarchingCubesDoublePass::Unregister() { if (!ent->GetBehavior<Genesis::Render>()) { svc->scene->UnregisterEntity(ent); } } void MarchingCubesDoublePass::RenderPass1() { glEnable(GL_RASTERIZER_DISCARD); glUseProgram(shaderPass1); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_3D, densityTex); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_RECTANGLE, triTableTex); glUniform1i(uniPass1DensityMap, 0); glUniform1i(uniPass1TriTable, 1); glUniform1i(uniPass1Size, SECTOR_SIZE); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, bufferPass2); glBindVertexArray(vaoPass2); glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, queryNumTriangles); glBeginTransformFeedback(GL_POINTS); GLenum error = glGetError(); glDrawArrays(GL_POINTS, 0, SECTOR_SIZE_CUBED); error = glGetError(); glEndTransformFeedback(); glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); glBindVertexArray(0); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0); glUseProgram(0); glDisable(GL_RASTERIZER_DISCARD); glGetQueryObjectuiv(queryNumTriangles, GL_QUERY_RESULT, &numTriangles); } void MarchingCubesDoublePass::RenderPass2(Matrix mat) { glUseProgram(shaderPass2); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_3D, densityTex); glUniform1i(uniPass2DensityMap, 0); glUniform1i(uniPass2Size, SECTOR_SIZE); glUniform3f(uniPass2Offset, 0, 0, 0); mat.UniformMatrix(uniPass2Matrix); glBindVertexArray(vaoPass2); glDrawArrays(GL_POINTS, 0, numTriangles); glBindVertexArray(0); glUseProgram(0); } void MarchingCubesDoublePass::OnRender(Matrix mat) { RenderPass1(); RenderPass2(mat); }
实际的错误是调用RenderPass1
glDrawArrays
。 值得注意的是,如果我注释掉对glBeginTransformFeedback
和glEndTransformFeedback
的调用,那么glDrawArrays
停止生成错误。 所以不pipe怎样,这可能与改变反馈有关。
编辑8/18/12,9 PM:
我刚刚在gDEBugger中发现了NVIDIA GLExpertfunction,这是我以前不熟悉的。 当我打开它的时候,它给了GL_INVALID_OPERATION
更多的信息,特别The current operation is illegal in the current state: Buffer is mapped.
。 所以我正在进入上面的第一点。 虽然我不知道如何。
我没有调用glMapBuffer
,或任何相关的function,在我的代码中的任何地方。 我设置gDEBugger打破对glMapBuffer
, glMapBufferARB
, glMapBufferRange
, glUnmapBuffer
和glUnmapBufferARB
任何调用,并且它不会在任何地方中断。 然后,我将代码添加到RenderPass1
的开始,以明确地取消映射烦扰缓冲区。 不仅错误不会消失,而且现在调用glUnmapBuffer
都会生成The current operation is illegal in the current state: Buffer is unbound or is already unmapped.
。 所以,如果我正在使用的缓冲区都没有映射,错误来自哪里?
编辑8/19/12,12:
根据我在gDEBugger中从GLExpert得到的错误消息,看起来调用glBeginTransformFeedback
会导致绑定到GL_TRANSFORM_FEEDBACK_BUFFER
的缓冲区被映射。 具体来说,当我点击“纹理,缓冲区和图像查看器”中的缓冲区时,它会输出消息The current operation is illegal in the current state: Buffer must be bound and not mapped.
。 但是,如果我在glBeginTransformFeedback
和glEndTransformFeedback
之间添加它:
int bufferBinding; glGetBufferParameteriv(GL_TRANSFORM_FEEDBACK_BUFFER, GL_BUFFER_MAPPED, &bufferBinding); printf("Transform feedback buffer binding: %d\n", bufferBinding);
它输出0,这将表明GL_TRANSFORM_FEEDBACK_BUFFER
没有被映射。 如果这个缓冲区被映射到另一个绑定点,这是否仍然返回0? 为什么glBeginTransformFeedback
映射缓冲区,从而使其不能用于变换反馈?
我在这里学到的越多,我就越困惑。
编辑10/10/12:
正如我在下面给Nicol Bolas的解答中所指出的那样,我发现了这个问题,他发现这个问题是一样的:由于一个愚蠢的错字,我绑定了同一个缓冲区到input和输出绑定点。
大概两周后,我发现这个问题。 我放弃了一段时间,最终回来,基本上从零开始重新实现了整个事情,定期比较老的,不工作的零碎。 当我完成后,新版本就起作用了,当我找出差异时,我发现我已经绑定了错误的缓冲区。
我想出了你的问题:你正在渲染到你正在寻找你的顶点数据的同一个缓冲区。
glBindVertexArray(vaoPass2);
我认为你的意思是vaoPass1
从规格:
在GL中,缓冲器不应被绑定或用于转换反馈和其他目的。 具体来说,如果缓冲区对象同时绑定到变换反馈缓冲区绑定点和GL中的其他位置,则任何写入或读取缓冲区的操作都会生成未定义的值。 这种绑定的例子包括ReadPixels到一个像素缓冲区对象的绑定点和客户端访问一个映射到MapBuffer的缓冲区。
现在,你应该得到未定义的值; 我不确定GL错误是否合格,但可能应该是错误的。
glDrawArrays
和glDrawElements
失败并返回GL_INVALID_OPERATION
另一个(显然没有logging)的情况:
- 如果采样器统一设置为无效的纹理单元标识符,则会生成
GL_INVALID_OPERATION
。 (我错误地执行了glUniform1i(location, GL_TEXTURE0);
当我的意思是glUniform1i(location, 0);
)
glDraw*()
调用的另一个(未logging的)情况可能会失败,并返回GL_INVALID_OPERATION
:
- 如果将采样器均匀设置为绑定到不正确types纹理的纹理单元,则会生成
GL_INVALID_OPERATION
。 例如,如果uniform sampler2D
被设置为glUniform1i(location, 0);
,但是GL_TEXTURE0
有一个GL_TEXTURE_2D_ARRAY
纹理边界。