web-dev-qa-db-fra.com

Qu'est-ce qui peut amener glDrawArrays à générer une erreur GL_INVALID_OPERATION?

J'ai essayé d'écrire une implémentation GPU à deux passes de l'algorithme Marching Cubes, similaire à celle détaillée dans le premier chapitre de GPU Gems 3, en utilisant OpenGL et GLSL. Cependant, l'appel à glDrawArrays dans ma première passe échoue systématiquement avec un GL_INVALID_OPERATION.

J'ai recherché toute la documentation que je peux trouver et trouvé ces conditions dans lesquelles glDrawArrays peut lancer cette erreur:

  1. GL_INVALID_OPERATION Est généré si un nom d'objet tampon différent de zéro est lié à un tableau activé ou à la liaison GL_DRAW_INDIRECT_BUFFER Et que le magasin de données de l'objet tampon est actuellement mappé.
  2. GL_INVALID_OPERATION Est généré si glDrawArrays est exécuté entre l'exécution de glBegin et le glEnd correspondant.
  3. GL_INVALID_OPERATION Sera généré par glDrawArrays ou glDrawElements si deux échantillonneurs actifs dans l'objet programme en cours sont de types différents, mais se réfèrent à la même unité d'image de texture.
  4. GL_INVALID_OPERATION Est généré si un shader de géométrie est actif et que le mode est incompatible avec le type primitif d'entrée du shader de géométrie dans l'objet programme actuellement installé.
  5. GL_INVALID_OPERATION Est généré si le mode est GL_PATCHES Et qu'aucun shader de contrôle de pavage n'est actif.
  6. GL_INVALID_OPERATION Est généré si l'enregistrement des sommets d'une primitive dans les objets tampons utilisés à des fins de rétroaction de transformation entraînerait soit un dépassement des limites de la taille d'un objet tampon, soit un dépassement de la position finale offset + size - 1, tel que défini par glBindBufferRange.
  7. GL_INVALID_OPERATION Est généré par glDrawArrays() si aucun ombrage de géométrie n'est présent, la rétroaction de transformation est active et le mode n'est pas l'un des modes autorisés.
  8. GL_INVALID_OPERATION Est généré par glDrawArrays() si un ombrage de géométrie est présent, la rétroaction de transformation est active et le type primitif de sortie de l'ombrage de géométrie ne correspond pas à la primitiveMode de rétroaction de transformation.
  9. GL_INVALID_OPERATION Est généré si le programme de shader lié n'est pas valide.
  10. EDIT 10/10/12:GL_INVALID_OPERATION Est généré si la rétroaction de transformation est utilisée, et le tampon lié au point de liaison de rétroaction de transformation est également lié au point de liaison du tampon de tableau. C'est le problème que je rencontrais, en raison d'une faute de frappe dans laquelle le tampon que j'ai lié. Bien que la spécification indique que cela est illégal, il n'est pas répertorié sous glDrawArrays comme l'une des raisons pour lesquelles il peut générer une erreur, dans toute documentation que j'ai trouvée.

Malheureusement, aucun document officiel que je trouve ne couvre plus de 3 d'entre eux. J'ai dû rassembler cette liste auprès de nombreuses sources. Les points 7 et 8 proviennent en fait de la documentation de glBeginTransformFeedback, et le point 9 ne semble pas du tout documenté. Je l'ai trouvé mentionné dans un message de forum quelque part. Cependant, je ne pense toujours pas que cette liste soit complète, car aucune d'entre elles ne semble expliquer l'erreur que je reçois.

  1. Je ne mappe aucun tampon dans mon programme, nulle part.
  2. J'utilise le profil Core, donc glBegin et glEnd ne sont même pas disponibles.
  3. J'ai deux échantillonneurs, et ils sont de types différents, mais ils sont définitivement mappés à différentes textures.
  4. Un shader de géométrie est actif, mais sa disposition d'entrée est layout (points) in et glDrawArrays est appelée avec GL_POINTS.
  5. Je n'utilise pas GL_PATCHES Ni de shaders de pavage d'aucune sorte.
  6. Je me suis assuré que j'allouais le maximum d'espace possible à mes shaders de géométrie. Ensuite, j'ai essayé de le quadrupler. Ça n'a pas aidé.
  7. Il y a un shader de géométrie. Voir le point suivant.
  8. La rétroaction de transformation est utilisée et il existe un shader de géométrie, mais la disposition de sortie est layout (points) out et glBeginTransformFeedback est appelée avec GL_POINTS.
  9. J'ai essayé d'insérer un appel à glValidateProgram juste avant l'appel à glDrawArrays, et il a renvoyé GL_TRUE.

Le code OpenGL réel est ici:

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

L'erreur réelle est l'appel à glDrawArrays dans RenderPass1. À noter que si je commente les appels à glBeginTransformFeedback et glEndTransformFeedback, alors glDrawArrays cesse de générer l'erreur. Donc, tout ce qui ne va pas, c'est probablement en quelque sorte lié à la transformation des commentaires.

Modifier 18/08/12, 21 h:

Je viens de trouver la fonctionnalité NVIDIA GLExpert dans gDEBugger, que je ne connaissais pas auparavant. Lorsque je l'ai activé, cela a donné des informations un peu plus substantielles sur le GL_INVALID_OPERATION, En particulier The current operation is illegal in the current state: Buffer is mapped.. Je me heurte donc au point 1 ci-dessus. Bien que je ne sache pas comment.

Je n'ai aucun appel à glMapBuffer, ni à aucune fonction connexe, n'importe où dans mon code. J'ai défini gDEBugger pour qu'il s'arrête sur tous les appels à glMapBuffer, glMapBufferARB, glMapBufferRange, glUnmapBuffer et glUnmapBufferARB, et il ne s'est pas cassé nulle part. Ensuite, j'ai ajouté du code au début de RenderPass1 Pour démapper explicitement les tampons dérangeants. Non seulement l'erreur n'a pas disparu, mais les appels à glUnmapBuffer génèrent maintenant tous les deux The current operation is illegal in the current state: Buffer is unbound or is already unmapped.. Donc, si aucun des tampons que j'utilise n'est mappé, d'où vient l'erreur?

Modifier 19/08/12, 12 h:

Sur la base des messages d'erreur que je retire de GLExpert dans gDEBugger, il semble que l'appel de glBeginTransformFeedback provoque le mappage du tampon lié à GL_TRANSFORM_FEEDBACK_BUFFER. Plus précisément, lorsque je clique sur le tampon dans "Textures, Buffers and Images Viewer", il affiche le message The current operation is illegal in the current state: Buffer must be bound and not mapped.. Cependant, si j'ajoute ceci entre glBeginTransformFeedback et glEndTransformFeedback:

int bufferBinding;
glGetBufferParameteriv(GL_TRANSFORM_FEEDBACK_BUFFER, GL_BUFFER_MAPPED, &bufferBinding);
printf("Transform feedback buffer binding: %d\n", bufferBinding);

il renvoie 0, ce qui indiquerait que GL_TRANSFORM_FEEDBACK_BUFFER n'est pas mappé. Si ce tampon est mappé sur un autre point de liaison, cela retournerait-il toujours 0? Pourquoi glBeginTransformFeedback mapperait-il le tampon, le rendant ainsi inutilisable pour le feedback de transformation?

Plus j'apprends ici, plus je deviens confus.

Modifier 10/10/12:

Comme indiqué dans ma réponse ci-dessous à la solution de Nicol Bolas, j'ai trouvé le problème, et c'est le même qu'il a trouvé: en raison d'une faute de frappe stupide, je liais le même tampon aux points de liaison d'entrée et de sortie.

Je l'ai trouvé probablement deux semaines après avoir posté la question. J'avais abandonné la frustration pendant un certain temps, et finalement je suis revenu et j'ai essentiellement réimplémenté le tout à partir de zéro, en comparant régulièrement des morceaux plus anciens et non fonctionnels. Quand j'ai fini, la nouvelle version a fonctionné, et c'est quand j'ai recherché les différences que j'ai découvert que j'avais lié le mauvais tampon.

47
Michael Powell

J'ai compris votre problème: vous effectuez le rendu dans le même tampon que celui de vos sources de données de sommet.

glBindVertexArray (vaoPass2);

Je pense que tu voulais dire vaoPass1

De la spécification:

Les tampons ne doivent pas être liés ou utilisés à la fois pour la rétroaction de transformation et à d'autres fins dans le GL. Plus précisément, si un objet tampon est simultanément lié à un point de liaison de tampon de rétroaction de transformation et ailleurs dans le GL, tout écrit ou lu dans le tampon génère des valeurs non définies. Des exemples de telles liaisons incluent ReadPixels à un point de liaison d'objet de tampon de pixels et l'accès client à un tampon mappé avec MapBuffer.

Maintenant, vous devriez obtenir des valeurs non définies; Je ne suis pas sûr qu'une erreur GL qualifie, mais ce devrait probablement être une erreur.

18
Nicol Bolas

Un autre cas (apparemment non documenté) où glDrawArrays et glDrawElements échouent avec GL_INVALID_OPERATION:

  • GL_INVALID_OPERATION Est généré si un uniforme d'échantillonneur est défini sur un identificateur d'unité de texture non valide. (J'avais par erreur exécuté glUniform1i(location, GL_TEXTURE0); quand je voulais dire glUniform1i(location, 0);.)
7
smokris

Un autre cas (non documenté) où les appels de glDraw*() peuvent échouer avec GL_INVALID_OPERATION:

  • GL_INVALID_OPERATION Est généré si un uniforme d'échantillonneur est défini sur une unité de texture liée à une texture de type incorrect. Par exemple, si un uniform sampler2D Est défini glUniform1i(location, 0);, mais que GL_TEXTURE0 A une limite de texture GL_TEXTURE_2D_ARRAY.
1
hacker64