本文介绍了什么会导致 glDrawArrays 产生 GL_INVALID_OPERATION 错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在尝试编写 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:

  1. GL_INVALID_OPERATION 如果非零缓冲区对象名称绑定到已启用的数组或 GL_DRAW_INDIRECT_BUFFER 绑定,并且当前已映射缓冲区对象的数据存储区,则生成
  2. GL_INVALID_OPERATION.
  3. GL_INVALID_OPERATION 如果在 glBegin 和相应的 glEnd 之间执行了 glDrawArrays,则生成.莉>
  4. GL_INVALID_OPERATION 将由 glDrawArraysglDrawElements 生成,如果当前程序对象中的任意两个活动采样器的类型不同,请参阅相同的纹理图像单元.
  5. GL_INVALID_OPERATION 如果几何着色器处于活动状态并且模式与当前安装的程序对象中的几何着色器的输入基元类型不兼容,则会生成.
  6. GL_INVALID_OPERATION 如果模式为 GL_PATCHES 并且没有激活的曲面细分控制着色器,则生成.
  7. GL_INVALID_OPERATION 如果将图元的顶点记录到用于变换反馈目的的缓冲区对象会导致超出任何缓冲区对象的大小限制或超出结束位置偏移,则会生成GL_INVALID_OPERATION+ size - 1,由 glBindBufferRange 设置.
  8. GL_INVALID_OPERATIONglDrawArrays() 生成,如果不存在几何着色器,变换反馈处于活动状态且模式不是允许的模式之一.
  9. GL_INVALID_OPERATIONglDrawArrays() 生成,如果几何着色器存在,变换反馈处于活动状态并且几何着色器的输出基元类型与变换反馈不匹配原始模式.
  10. GL_INVALID_OPERATION 如果绑定的着色器程序无效,则生成.
  11. EDIT 10/10/12: GL_INVALID_OPERATION 在使用变换反馈时生成,并且绑定到变换反馈绑定点的缓冲区也绑定到数组缓冲区绑定点.这是我遇到的问题,因为我绑定的缓冲区打错了.虽然规范确实声明这是非法的,但在我找到的任何文档中,它都没有列在 glDrawArrays 下作为它可能引发错误的原因之一.
  1. GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to an enabled array or to the GL_DRAW_INDIRECT_BUFFER binding and the buffer object's data store is currently mapped.
  2. GL_INVALID_OPERATION is generated if glDrawArrays is executed between the execution of glBegin and the corresponding glEnd.
  3. GL_INVALID_OPERATION will be generated by glDrawArrays or glDrawElements if any two active samplers in the current program object are of different types, but refer to the same texture image unit.
  4. 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.
  5. GL_INVALID_OPERATION is generated if mode is GL_PATCHES and no tessellation control shader is active.
  6. 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 by glBindBufferRange.
  7. GL_INVALID_OPERATION is generated by glDrawArrays() if no geometry shader is present, transform feedback is active and mode is not one of the allowed modes.
  8. GL_INVALID_OPERATION is generated by glDrawArrays() 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.
  9. GL_INVALID_OPERATION is generated if the bound shader program is invalid.
  10. 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.

  1. 我没有在我的程序中的任何地方映射任何缓冲区.
  2. 我正在使用 Core 配置文件,所以 glBeginglEnd 甚至不可用.
  3. 我有两个采样器,它们的类型不同,但它们肯定映射到不同的纹理.
  4. 几何着色器处于活动状态,但它的输入布局是 layout (points) in,并且 glDrawArrays 正在使用 GL_POINTS 调用.
  5. 我没有使用 GL_PATCHES 或任何类型的曲面细分着色器.
  6. 我已确保分配了几何着色器可能输出的最大空间量.然后我尝试将它翻两番.没有帮助.
  7. 有一个几何着色器.见下一点.
  8. 正在使用变换反馈,并且有一个几何着色器,但输出布局是layout (points) out 并且glBeginTransformFeedback 使用GL_POINTS.
  9. 我尝试在调用 glDrawArrays 之前插入对 glValidateProgram 的调用,它返回了 GL_TRUE.
  1. I'm not mapping any buffers at all in my program, anywhere.
  2. I'm using the Core profile, so glBegin and glEnd aren't even available.
  3. I have two samplers, and they are of different types, but they're definitely mapped to different textures.
  4. A geometry shader is active, but it's input layout is layout (points) in, and glDrawArrays is being called with GL_POINTS.
  5. I'm not using GL_PATCHES or tessellation shaders of any sort.
  6. 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.
  7. There is a geometry shader. See the next point.
  8. Transform feedback is being used, and there is a geometry shader, but the output layout is layout (points) out and glBeginTransformFeedback is called with GL_POINTS.
  9. I tried inserting a call to glValidateProgram right before the call to glDrawArrays, and it returned GL_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.值得注意的是,如果我注释掉对 glBeginTransformFeedbackglEndTransformFeedback 的调用,则 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 设置为中断对 glMapBufferglMapBufferARBglMapBufferRangeglUnmapBufferglUnmapBufferARB,它没有在任何地方中断.然后我在 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"中点击缓冲区时,它输出消息当前操作在当前状态下是非法的:必须绑定缓冲区而不是映射..但是,如果我在 glBeginTransformFeedbackglEndTransformFeedback 之间添加:

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 错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-23 16:48