Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
578 views
in Technique[技术] by (71.8m points)

opengl - What can cause glDrawArrays to generate a GL_INVALID_OPERATION error?

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.

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 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.

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. 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.

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);

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I figured out your problem: you are rendering to the same buffer that you're sourcing your vertex data.

glBindVertexArray(vaoPass2);

I think you meant vaoPass1

From the spec:

Buffers should not be bound or in use for both transform feedback and other purposes in the GL. Specifically, if a buffer object is simultaneously bound to a transform feedback buffer binding point and elsewhere in the GL, any writes to or reads from the buffer generate undefined values. Examples of such bindings include ReadPixels to a pixel buffer object binding point and client access to a buffer mapped with MapBuffer.

Now, you should get undefined values; I'm not sure that a GL error qualifies, but it probably should be an error.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...