问题描述
我一直在尝试编写 Marching Cubes 算法的两次 GPU 实现,类似于 GPU Gems 3 第一章中详述的实现,使用 OpenGL 和 GLSL.但是,在我的第一遍中对 glDrawArrays
的调用始终以 GL_INVALID_OPERATION
失败.
I've been attempting to write a two-pass GPU implementation of the Marching Cubes algorithm, similar to the one detailed in the first chapter of GPU Gems 3, using OpenGL and GLSL. However, the call to glDrawArrays
in my first pass consistently fails with a GL_INVALID_OPERATION
.
我查阅了我能找到的所有文档,发现在这些条件下 glDrawArrays
可以抛出该错误:
I've looked up all the documentation I can find, and found these conditions under which glDrawArrays
can throw that error:
GL_INVALID_OPERATION
如果非零缓冲区对象名称绑定到已启用的数组或GL_DRAW_INDIRECT_BUFFER
绑定,并且当前已映射缓冲区对象的数据存储区,则生成GL_INVALID_OPERATION
.GL_INVALID_OPERATION
如果在glBegin
和相应的glEnd
之间执行了glDrawArrays
,则生成.莉>GL_INVALID_OPERATION
将由glDrawArrays
或glDrawElements
生成,如果当前程序对象中的任意两个活动采样器的类型不同,请参阅相同的纹理图像单元.GL_INVALID_OPERATION
如果几何着色器处于活动状态并且模式与当前安装的程序对象中的几何着色器的输入基元类型不兼容,则会生成.GL_INVALID_OPERATION
如果模式为GL_PATCHES
并且没有激活的曲面细分控制着色器,则生成.GL_INVALID_OPERATION
如果将图元的顶点记录到用于变换反馈目的的缓冲区对象会导致超出任何缓冲区对象的大小限制或超出结束位置偏移,则会生成GL_INVALID_OPERATION
+ size - 1,由glBindBufferRange
设置.GL_INVALID_OPERATION
由glDrawArrays()
生成,如果不存在几何着色器,变换反馈处于活动状态且模式不是允许的模式之一.GL_INVALID_OPERATION
由glDrawArrays()
生成,如果几何着色器存在,变换反馈处于活动状态并且几何着色器的输出基元类型与变换反馈不匹配原始模式.GL_INVALID_OPERATION
如果绑定的着色器程序无效,则生成.- EDIT 10/10/12:
GL_INVALID_OPERATION
在使用变换反馈时生成,并且绑定到变换反馈绑定点的缓冲区也绑定到数组缓冲区绑定点.这是我遇到的问题,因为我绑定的缓冲区打错了.虽然规范确实声明这是非法的,但在我找到的任何文档中,它都没有列在 glDrawArrays 下作为它可能引发错误的原因之一.
GL_INVALID_OPERATION
is generated if a non-zero buffer object name is bound to an enabled array or to theGL_DRAW_INDIRECT_BUFFER
binding and the buffer object's data store is currently mapped.GL_INVALID_OPERATION
is generated ifglDrawArrays
is executed between the execution ofglBegin
and the correspondingglEnd
.GL_INVALID_OPERATION
will be generated byglDrawArrays
orglDrawElements
if any two active samplers in the current program object are of different types, but refer to the same texture image unit.GL_INVALID_OPERATION
is generated if a geometry shader is active and mode is incompatible with the input primitive type of the geometry shader in the currently installed program object.GL_INVALID_OPERATION
is generated if mode isGL_PATCHES
and no tessellation control shader is active.GL_INVALID_OPERATION
is generated if recording the vertices of a primitive to the buffer objects being used for transform feedback purposes would result in either exceeding the limits of any buffer object’s size, or in exceeding the end position offset + size - 1, as set byglBindBufferRange
.GL_INVALID_OPERATION
is generated byglDrawArrays()
if no geometry shader is present, transform feedback is active and mode is not one of the allowed modes.GL_INVALID_OPERATION
is generated byglDrawArrays()
if a geometry shader is present, transform feedback is active and the output primitive type of the geometry shader does not match the transform feedback primitiveMode.GL_INVALID_OPERATION
is generated if the bound shader program is invalid.- EDIT 10/10/12:
GL_INVALID_OPERATION
is generated if transform feedback is in use, and the buffer bound to the transform feedback binding point is also bound to the array buffer binding point. This is the problem I was having, due to a typo in which buffer I bound. While the spec does state that this is illegal, it isn't listed under glDrawArrays as one of the reasons it can throw an error, in any documentation I found.
不幸的是,我找不到任何一份官方文档涵盖其中的 3 个以上.我不得不从众多来源收集这份清单.第 7 点和第 8 点实际上来自 glBeginTransformFeedback
的文档,而第 9 点似乎根本没有记录.我在某处的论坛帖子中发现了它.但是,我仍然不认为这个列表是完整的,因为这些似乎都不能解释我遇到的错误.
Unfortunately, no one piece of official documentation I can find covers more than 3 of these. I had to collect this list from numerous sources. Points 7 and 8 actually come from the documentation for glBeginTransformFeedback
, and point 9 doesn't seem to be documented at all. I found it mentioned in a forum post somewhere. However, I still don't think this list is complete, as none of these seem to explain the error I'm getting.
- 我没有在我的程序中的任何地方映射任何缓冲区.
- 我正在使用 Core 配置文件,所以
glBegin
和glEnd
甚至不可用. - 我有两个采样器,它们的类型不同,但它们肯定映射到不同的纹理.
- 几何着色器处于活动状态,但它的输入布局是
layout (points) in
,并且glDrawArrays
正在使用GL_POINTS
调用. - 我没有使用
GL_PATCHES
或任何类型的曲面细分着色器. - 我已确保分配了几何着色器可能输出的最大空间量.然后我尝试将它翻两番.没有帮助.
- 有一个几何着色器.见下一点.
- 正在使用变换反馈,并且有一个几何着色器,但输出布局是
layout (points) out
并且glBeginTransformFeedback
使用GL_POINTS
. - 我尝试在调用
glDrawArrays
之前插入对glValidateProgram
的调用,它返回了GL_TRUE
.
- I'm not mapping any buffers at all in my program, anywhere.
- I'm using the Core profile, so
glBegin
andglEnd
aren't even available. - I have two samplers, and they are of different types, but they're definitely mapped to different textures.
- A geometry shader is active, but it's input layout is
layout (points) in
, andglDrawArrays
is being called withGL_POINTS
. - I'm not using
GL_PATCHES
or tessellation shaders of any sort. - I've made sure I'm allocating the maximum amount of space my geometry shaders could possible output. Then I tried quadrupling it. Didn't help.
- There is a geometry shader. See the next point.
- Transform feedback is being used, and there is a geometry shader, but the output layout is
layout (points) out
andglBeginTransformFeedback
is called withGL_POINTS
. - I tried inserting a call to
glValidateProgram
right before the call toglDrawArrays
, and it returnedGL_TRUE
.
实际的 OpenGL 代码在这里:
The actual OpenGL code is here:
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
将停止生成错误.因此,无论出现什么问题,都可能与转换反馈有某种关系.
The actual error is the call to glDrawArrays
in RenderPass1
. Worth noting that if I comment out the calls to glBeginTransformFeedback
and glEndTransformFeedback
, then glDrawArrays
stops generating the error. So whatever's wrong, it's probably somehow related to transform feedback.
编辑 2012 年 8 月 18 日晚上 9 点:
我刚刚在 gDEBugger 中发现了 NVIDIA GLExpert 功能,这是我以前不熟悉的.当我打开它时,它提供了更多关于 GL_INVALID_OPERATION
的信息,特别是 当前操作在当前状态下是非法的:缓冲区被映射.
.所以我遇到了上面的第 1 点.虽然我不知道如何.
I just found the NVIDIA GLExpert feature in gDEBugger, which I wasn't previously familiar with. When I turned this on, it gave somewhat more substantial information on the GL_INVALID_OPERATION
, specifically The current operation is illegal in the current state: Buffer is mapped.
. So I'm running into point 1, above. Though I have no idea how.
我没有在代码中的任何地方调用 glMapBuffer
或任何相关函数.我将 gDEBugger 设置为中断对 glMapBuffer
、glMapBufferARB
、glMapBufferRange
、glUnmapBuffer
和 glUnmapBufferARB
,它没有在任何地方中断.然后我在 RenderPass1
的开头添加了代码来显式地取消映射缓冲区.错误不仅没有消失,而且对 glUnmapBuffer
的调用现在都生成 当前操作在当前状态下是非法的:Buffer is unbound or already is unmapped.
.所以如果我使用的缓冲区都没有被映射,那么错误来自哪里?
I have no calls to glMapBuffer
, or any related function, anywhere in my code. I set gDEBugger to break on any calls to glMapBuffer
, glMapBufferARB
, glMapBufferRange
, glUnmapBuffer
and glUnmapBufferARB
, and it didn't break anywhere. Then I added code to the start of RenderPass1
to explicitly unmap bother buffers. Not only did the error not go away, but calls to glUnmapBuffer
now both generate The current operation is illegal in the current state: Buffer is unbound or is already unmapped.
. So if neither of the buffers I'm using are mapped, where is the error coming from?
编辑 2012 年 8 月 19 日上午 12 点:
根据我从 gDEBugger 中的 GLExpert 中得到的错误消息,调用 glBeginTransformFeedback
似乎导致绑定到 GL_TRANSFORM_FEEDBACK_BUFFER
的缓冲区被映射.具体来说,当我在Textures, Buffers and Images Viewer"中点击缓冲区时,它输出消息当前操作在当前状态下是非法的:必须绑定缓冲区而不是映射.
.但是,如果我在 glBeginTransformFeedback
和 glEndTransformFeedback
之间添加:
Based on the error messages I'm getting out of GLExpert in gDEBugger, it appears that calling glBeginTransformFeedback
is causing the buffer bound to GL_TRANSFORM_FEEDBACK_BUFFER
to become mapped. Specifically, when I click on the buffer in "Textures, Buffers and Images Viewer" it outputs the message The current operation is illegal in the current state: Buffer must be bound and not mapped.
. However, if I add this between glBeginTransformFeedback
and glEndTransformFeedback
:
int bufferBinding;
glGetBufferParameteriv(GL_TRANSFORM_FEEDBACK_BUFFER, GL_BUFFER_MAPPED, &bufferBinding);
printf("Transform feedback buffer binding: %d
", bufferBinding);
它输出 0,这表示 GL_TRANSFORM_FEEDBACK_BUFFER
没有被映射.如果这个缓冲区被映射到另一个绑定点,这是否仍然返回 0?为什么 glBeginTransformFeedback
会映射缓冲区,从而使其无法用于转换反馈?
it outputs 0, which would indicate that GL_TRANSFORM_FEEDBACK_BUFFER
is not mapped. If this buffer is mapped on another binding point, would this still return 0? Why would glBeginTransformFeedback
map the buffer, thus rendering it unusable for transform feedback?
我在这里学得越多,我就越困惑.
The more I learn here, the more confused I'm becoming.
编辑 10/10/12:
正如我在下面对 Nicol Bolas 解决方案的回复中所指出的,我发现了问题,并且与他发现的问题相同:由于一个愚蠢的错字,我将相同的缓冲区绑定到输入和输出绑定点.
As indicated in my reply below to Nicol Bolas' solution, I found the problem, and it's the same one he found: Due to a stupid typo, I was binding the same buffer to both the input and output binding points.
我大概是在发布问题两周后发现的.我沮丧地放弃了一段时间,最终回来并基本上从头开始重新实现整个事情,定期比较旧的,无法工作的点点滴滴.当我完成后,新版本就可以工作了,当我找出差异时,我发现我绑定了错误的缓冲区.
I found it probably two weeks after posting the question. I'd given up in frustration for a time, and eventually came back and basically re-implemented the whole thing from scratch, regularly comparing bits and pieces the older, non-working one. When I was done, the new version worked, and it was when I searched out the differences that I discovered I'd been binding the wrong buffer.
推荐答案
我发现了你的问题:你渲染到与你获取顶点数据相同的缓冲区.
I figured out your problem: you are rendering to the same buffer that you're sourcing your vertex data.
glBindVertexArray(vaoPass2);
我想你的意思是vaoPass1
来自规范:
缓冲区不应被绑定或用于转换反馈和其他GL 中的用途.具体来说,如果一个缓冲区对象同时绑定到一个转换反馈缓冲区绑定点和 GL 中的其他地方,任何写入或从缓冲区读取生成未定义的值.此类绑定的示例将 ReadPixels 包含到像素缓冲区对象绑定点和客户端访问使用 MapBuffer 映射的缓冲区.
现在,你应该得到未定义的值;我不确定 GL 错误是否符合条件,但它可能应该是错误.
Now, you should get undefined values; I'm not sure that a GL error qualifies, but it probably should be an error.
这篇关于什么会导致 glDrawArrays 产生 GL_INVALID_OPERATION 错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!