【问题标题】:What can cause glDrawArrays to generate a GL_INVALID_OPERATION error?什么会导致 glDrawArrays 生成 GL_INVALID_OPERATION 错误?
【发布时间】:2012-08-14 13:46:40
【问题描述】:

我一直在尝试使用 OpenGL 和 GLSL 编写 Marching Cubes 算法的两遍 GPU 实现,类似于 GPU Gems 3 第一章中详述的那个。但是,在我的第一遍中对glDrawArrays 的调用始终以GL_INVALID_OPERATION 失败。

我查阅了所有我能找到的文档,发现glDrawArrays 可能会抛出该错误的这些条件:

  1. 如果非零缓冲区对象名称绑定到启用的数组或GL_DRAW_INDIRECT_BUFFER 绑定并且当前映射缓冲区对象的数据存储,则生成GL_INVALID_OPERATION
  2. 如果在glBegin和对应的glEnd之间执行了glDrawArrays,则生成GL_INVALID_OPERATION
  3. GL_INVALID_OPERATION 将由glDrawArraysglDrawElements 生成,如果当前程序对象中的任何两个活动采样器属于不同类型,但引用相同的纹理图像单元。
  4. 如果几何着色器处于活动状态并且模式与当前安装的程序对象中几何着色器的输入基元类型不兼容,则会生成GL_INVALID_OPERATION
  5. 如果模式为 GL_PATCHES 并且没有曲面细分控制着色器处于活动状态,则生成 GL_INVALID_OPERATION
  6. GL_INVALID_OPERATION 如果将基元的顶点记录到用于变换反馈目的的缓冲区对象会导致超出任何缓冲区对象大小的限制,或者超出结束位置偏移量 + 大小 - 1,由glBindBufferRange
  7. GL_INVALID_OPERATIONglDrawArrays() 生成,如果不存在几何着色器,则变换反馈处于活动状态,并且模式不是允许的模式之一。
  8. GL_INVALID_OPERATIONglDrawArrays() 生成,如果存在几何着色器,则变换反馈处于活动状态,并且几何着色器的输出基元类型与变换反馈primitiveMode 不匹配。
  9. GL_INVALID_OPERATION绑定着色器程序无效时生成。
  10. EDIT 10/10/12: GL_INVALID_OPERATION 在使用变换反馈时生成,并且绑定到变换反馈绑定点的缓冲区也绑定到数组缓冲区绑定点。 这是我遇到的问题,因为我绑定的缓冲区有错字。虽然规范确实声明这是非法的,但在我找到的任何文档中,它都没有列在 glDrawArrays 下作为它可能引发错误的原因之一。

不幸的是,我能找到的任何官方文档都没有涵盖超过 3 个。我不得不从众多来源收集这份清单。第 7 点和第 8 点实际上来自 glBeginTransformFeedback 的文档,而第 9 点似乎根本没有记录。我在某处的论坛帖子中发现了它。但是,我仍然认为这个列表并不完整,因为这些似乎都不能解释我遇到的错误。

  1. 我根本没有在我的程序中的任何地方映射任何缓冲区。
  2. 我使用的是 Core 配置文件,所以 glBeginglEnd 甚至都不可用。
  3. 我有两个采样器,它们的类型不同,但它们肯定映射到不同的纹理。
  4. 几何着色器处于活动状态,但其输入布局为 layout (points) in,并且正在使用 GL_POINTS 调用 glDrawArrays
  5. 我没有使用GL_PATCHES 或任何类型的镶嵌着色器。
  6. 我已确保分配几何着色器可能输出的最大空间量。然后我试着把它翻了两番。没有帮助。
  7. 有一个几何着色器。请参阅下一点。
  8. 正在使用变换反馈,并且有一个几何着色器,但输出布局是layout (points) out,并且glBeginTransformFeedback 是用GL_POINTS 调用的。
  9. 我尝试在调用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。值得注意的是,如果我注释掉对glBeginTransformFeedbackglEndTransformFeedback 的调用,那么glDrawArrays 将停止生成错误。所以不管有什么问题,都可能与变换反馈有关。

编辑 2012 年 8 月 18 日晚上 9 点:

我刚刚在 gDEBugger 中发现了 NVIDIA GLExpert 功能,这是我以前不熟悉的。当我打开它时,它提供了更多关于GL_INVALID_OPERATION 的信息,特别是The current operation is illegal in the current state: Buffer is mapped.。所以我遇到了上面的第 1 点。虽然我不知道怎么做。

我的代码中的任何地方都没有调用glMapBuffer 或任何相关函数。我将 gDEBugger 设置为中断对glMapBufferglMapBufferARBglMapBufferRangeglUnmapBufferglUnmapBufferARB 的任何调用,并且它没有在任何地方中断。然后我在RenderPass1 的开头添加了代码来显式地取消映射干扰缓冲区。错误不仅没有消失,而且对glUnmapBuffer 的调用现在都生成The current operation is illegal in the current state: Buffer is unbound or is already unmapped.。因此,如果我使用的两个缓冲区都没有映射,那么错误来自哪里?

2012 年 8 月 19 日上午 12 点编辑:

根据我在 gDEBugger 中从 GLExpert 中得到的错误消息,调用 glBeginTransformFeedback 似乎导致绑定到 GL_TRANSFORM_FEEDBACK_BUFFER 的缓冲区被映射。具体来说,当我单击“纹理、缓冲区和图像查看器”中的缓冲区时,它会输出消息The current operation is illegal in the current state: Buffer must be bound and not mapped.。但是,如果我在glBeginTransformFeedbackglEndTransformFeedback 之间添加这个:

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 解决方案的回复中指出的那样,我发现了问题,并且与他发现的问题相同:由于一个愚蠢的错字,我将同一个缓冲区绑定到输入和输出绑定点。

我在发布问题两周后发现了它。我曾经沮丧地放弃了一段时间,最终回来并基本上从头开始重新实现整个事情,定期比较旧的、不工作的部分和部分。当我完成后,新版本就可以工作了,当我搜索出差异时,我发现我绑定了错误的缓冲区。

【问题讨论】:

  • 调试这样的事情的方法是停止一次做这么多事情。从工作代码开始,然后慢慢构建它,直到它停止工作。从仅从缓冲区呈现某些内容的代码开始;只要它是你所期望的,它并不重要。然后让它转换反馈,并检查你得到的值是否符合你的期望。然后关闭光栅化。然后删除您的片段着色器。然后添加一个纹理。然后是第二个。依此类推,直到你建立一切备份。如果您的实现支持 ARB_debug_output,您也可以尝试使用调试上下文。
  • 我确实是逐渐建立起来的。直到我添加了开始和结束转换反馈的调用为止,这一切似乎都在起作用。所以正如我在最后提到的,我知道它与它使用变换反馈的事实有关,但我不知道还有哪些其他因素可能起作用。我已经尝试剥离代码的其他部分,但没有什么我可以删除的转换反馈不需要修复它。我认为在这一点上,我很可能错过了一步,而不是我做错了一步,并且消除代码不会发现。
  • 另外值得注意的是:我下载了 g-truc OpenGL 示例,查看了他们的变换反馈示例,并确保我没有遗漏任何步骤。但没有发现任何有影响的东西。
  • 哈哈,很好的阅读 - 谢谢!

标签: opengl glsl shader geometry-shader marching-cubes


【解决方案1】:

我发现了您的问题:您正在渲染到与您获取顶点数据相同的缓冲区。

glBindVertexArray(vaoPass2);

我想你的意思是vaoPass1

来自规范:

缓冲区不应被绑定或用于变换反馈和其他 GL 中的目的。具体来说,如果一个缓冲区对象同时绑定到一个 转换反馈缓冲区绑定点和 GL 中的其他位置,任何写入 或从缓冲区读取生成未定义的值。此类绑定的示例 包括 ReadPixels 到像素缓冲区对象绑定点和客户端访问 使用 MapBuffer 映射的缓冲区。

现在,您应该得到未定义的值;我不确定 GL 错误是否符合条件,但它可能应该是一个错误。

【讨论】:

  • 是的,就是这样。我实际上发现了一段时间,但忘记发表编辑或评论。好眼力!
  • 微妙!是否存在缓冲区不能绑定到多个绑定点的其他情况?我正在编写一个引擎,并想在幕后捕捉到这一点。
  • 我还应该提一下,我刚刚发现这是 invalid operation 背后的问题,我在整个代码库中都进行了完全随机的绘制调用,所以谢谢你。
【解决方案2】:

glDrawArraysglDrawElementsGL_INVALID_OPERATION 失败的另一个(显然未记录的)案例:

  • 如果将采样器统一设置为无效的纹理单元标识符,则会生成 GL_INVALID_OPERATION。 (当我的意思是glUniform1i(location, 0);时,我错误地执行了glUniform1i(location, GL_TEXTURE0);。)

【讨论】:

    【解决方案3】:

    glDraw*() 调用可能因GL_INVALID_OPERATION 而失败的另一种(未记录的)情况:

    • 如果将采样器制服设置为绑定到错误类型纹理的纹理单元,则会生成GL_INVALID_OPERATION。例如,如果uniform sampler2D 设置为glUniform1i(location, 0);,但GL_TEXTURE0 具有GL_TEXTURE_2D_ARRAY 纹理绑定。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-31
      • 1970-01-01
      • 1970-01-01
      • 2014-01-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多