Co może spowodować, że program glDrawArrays wygeneruje nieprawidłowy błąd operacji GL?

Próbowałem napisać dwuprzebiegową implementację GPU algorytmu Marching Cubes, podobną do tej opisanej w pierwszym rozdziale GPU Gems 3, używając OpenGL i GLSL. Jednak wezwanie do glDrawArrays w moim pierwszym przejściu konsekwentnie zawodzi z GL_INVALID_OPERATION.

Przejrzałem całą dokumentację, którą mogę znaleźć, i znalazłem te warunki, w których glDrawArrays może wyrzucić ten błąd:

  1. GL_INVALID_OPERATION jest generowane, jeśli niezerowa nazwa obiektu bufora jest powiązana z włączoną tablicą lub do powiązania GL_DRAW_INDIRECT_BUFFER i magazyn danych obiektu bufora jest obecnie mapowany.
  2. GL_INVALID_OPERATION jest generowane, jeśli {[2] } jest wykonywane pomiędzy wykonaniem glBegin a odpowiadającym mu glEnd.
  3. GL_INVALID_OPERATION zostanie wygenerowane przez glDrawArrays lub glDrawElements, Jeśli dowolne dwa aktywne samplery w bieżącym obiekcie programu są różnych typów, ale odnoszą się do tej samej jednostki obrazu tekstury.
  4. GL_INVALID_OPERATION jest generowany, jeśli aktywny jest shader geometry i tryb jest niezgodny z typem wejściowym geometry shader w aktualnie zainstalowanym obiekcie programu.
  5. GL_INVALID_OPERATION jest generowane, jeśli tryb jest GL_PATCHES i nie jest aktywny żaden shader kontrolny teselacji.
  6. GL_INVALID_OPERATION jest generowane, jeśli zapis wierzchołków prymitywnych obiektów bufora używanych do celów sprzężenia zwrotnego transformacji spowodowałby przekroczenie granic rozmiaru dowolnego obiektu bufora lub przekroczenie przesunięcia położenia końcowego + Rozmiar-1, Jak określono przez glBindBufferRange.
  7. GL_INVALID_OPERATION jest generowane przez glDrawArrays() jeśli nie ma geometrii shader jest obecny, sprzężenie zwrotne transformacji jest aktywne, a tryb nie jest jednym z dozwolonych trybów.
  8. GL_INVALID_OPERATION jest generowane przez glDrawArrays() jeśli obecny jest shader geometry, sprzężenie zwrotne transformacji jest aktywne, a wyjściowy prymitywny Typ shader geometry nie pasuje do primitivemode sprzężenia zwrotnego transformacji.
  9. GL_INVALID_OPERATION jest generowane, jeśli program bound shader jest nieprawidłowy.
  10. edytuj 10/10/12: GL_INVALID_OPERATION jest generowany, jeśli w użyciu jest sprzężenie zwrotne transformacji, a bufor związany z punkt wiązania sprzężenia zwrotnego transformacji jest również związany z punktem wiązania bufora tablicy. to jest problem, który miałem, ze względu na literówkę, w którym buforze powiązałem. Podczas gdy spec stwierdza, że jest to nielegalne, nie jest wymieniony w glDrawArrays jako jeden z powodów, dla których może rzucić błąd w dowolnej dokumentacji, którą znalazłem.

Niestety, żaden fragment oficjalnej dokumentacji nie może znaleźć obejmuje więcej niż 3 z nich. Musiałem zebrać tę listę z wielu źródeł. Punkty 7 i 8 rzeczywiście pochodzi z dokumentacji glBeginTransformFeedback, A punkt 9 nie wydaje się być w ogóle udokumentowany. Znalazłem to gdzieś w poście na forum. Jednak nadal nie sądzę, aby ta lista była kompletna, ponieważ żadna z nich nie wyjaśnia błędu, który otrzymuję.

  1. nie mapuję żadnych buforów w moim programie, nigdzie.
  2. używam głównego profilu, więc glBegin i glEnd nie są nawet dostępne.
  3. [66]}mam dwa samplery, i są one różnych typów, ale są zdecydowanie odwzorowane na różne tekstury.
  4. Shader geometrii jest aktywny, ale jego układ wejściowy to layout (points) in, a glDrawArrays jest wywoływany przez GL_POINTS.
  5. nie używam GL_PATCHES ani żadnych shaderów teselacji.
  6. upewniłem się, że przydzielam maksymalną ilość miejsca, jaką mogą uzyskać shadery geometrii. Potem próbowałem go poczwórnie zestawić. Nie pomogło.
  7. istnieje Shader geometrii. Zobacz następny punkt.
  8. transformacja sprzężenia zwrotnego jest używana, a istnieje shader geometrii, ale układ wyjściowy to layout (points) out, a {[25] } jest wywoływany przez GL_POINTS.
  9. próbowałem wstawić połączenie do glValidateProgram tuż przed połączeniem do glDrawArrays, i zwróciło GL_TRUE.

Rzeczywisty kod OpenGL jest tutaj:

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

Prawdziwym błędem jest wywołanie glDrawArrays w RenderPass1. Warto zauważyć, że jeśli skomentuję wywołania do glBeginTransformFeedback i glEndTransformFeedback, to glDrawArrays przestaje generować błąd. Więc cokolwiek jest nie tak, prawdopodobnie jest to w jakiś sposób związane z transformacją informacje zwrotne.

Edit 8/18/12, 9 PM:

Właśnie znalazłem funkcję NVIDIA GLExpert w gdebuggerze, z którą wcześniej nie byłem zaznajomiony. Kiedy to włączyłem, dało to nieco bardziej istotne informacje na temat GL_INVALID_OPERATION, a konkretnie The current operation is illegal in the current state: Buffer is mapped.. Więc wpadam do punktu 1, powyżej. Chociaż nie mam pojęcia jak.

Nie mam żadnych wywołań do glMapBuffer, ani żadnej powiązanej funkcji, gdziekolwiek w moim kodzie. Ustawiłem gDEBugger na Łamanie wszelkich połączeń do glMapBuffer, glMapBufferARB, glMapBufferRange, glUnmapBuffer oraz glUnmapBufferARB, i nigdzie się nie złamał. Następnie dodałem kod na początku RenderPass1, aby jawnie odmapować bufory. Nie tylko błąd nie zniknął, ale wywołania {[49] } teraz oba generują The current operation is illegal in the current state: Buffer is unbound or is already unmapped.. Więc jeśli żaden z buforów, których używam, nie jest zmapowany, skąd pochodzi błąd?

Edit 8/19/12, 12: 00:

Na podstawie komunikatów o błędach, które otrzymuję z GLExpert w gdebuggerze, wydaje się, że wywołanie glBeginTransformFeedback powoduje, że bufor związany z GL_TRANSFORM_FEEDBACK_BUFFER zostaje zmapowany. W szczególności, gdy klikam na bufor w "Texttures, Buffers and Images Viewer"wyświetla komunikat The current operation is illegal in the current state: Buffer must be bound and not mapped.. Jeśli jednak dodam to między glBeginTransformFeedback A glEndTransformFeedback:

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

Wyświetla 0, co wskazywałoby, że GL_TRANSFORM_FEEDBACK_BUFFER nie jest mapowana. Jeśli ten bufor jest mapowany w innym punkcie wiązania, czy to nadal zwróci 0? Dlaczego glBeginTransformFeedback zmapowałby bufor, czyniąc go bezużytecznym dla sprzężenia zwrotnego transformacji?

Im więcej się tu uczę, tym bardziej jestem zdezorientowany.

Edytuj 10/10/12:

Jak wskazano w mojej Odpowiedzi poniżej na rozwiązanie Nicola Bolasa, znalazłem problem i jest to ten sam, który znalazł: z powodu głupiej literówki, wiązałem ten sam bufor zarówno do wejścia, jak i wyjścia.

Znalazłem go prawdopodobnie dwa tygodnie po wysłaniu pytania. Poddałem się frustracji na jakiś czas, a w końcu wróciłem i w zasadzie odtworzyłem całość od zera, regularnie porównując kawałki i kawałki starszego, niedziałającego. Kiedy skończyłem, nowa wersja zadziałała, i to było, gdy szukałem różnic, które odkryłem, że wiązałem zły bufor.

Author: Michael Powell, 2012-08-18

3 answers

Rozgryzłem twój problem: renderujesz do tego samego bufora, w którym pozyskujesz dane wierzchołków.

GlBindVertexArray (vaoPass2);

Myślę, że miałeś na myśli vaoPass1

Od spec:

Bufory nie powinny być związane ani używane zarówno dla sprzężenia zwrotnego transformatora, jak i innych cele w GL. W szczególności, jeśli obiekt bufora jest jednocześnie związany z przekształcić punkt wiązania bufora sprzężenia zwrotnego i gdzie indziej w GL, każdy zapis do lub czyta z bufora generuje niezdefiniowane wartości. Przykłady takich wiązań zawierać Readpixele do punktu wiązania obiektu bufora pikseli i dostęp Klienta do bufor mapowany za pomocą Mapbuffera.

Teraz powinieneś uzyskać wartości niezdefiniowane; nie jestem pewien, czy błąd GL kwalifikuje się, ale prawdopodobnie powinien to być błąd.

 18
Author: Nicol Bolas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-10 21:35:38

Kolejny (pozornie nieudokumentowany) przypadek, w którym glDrawArrays i glDrawElements nie powiodą się z GL_INVALID_OPERATION:

  • GL_INVALID_OPERATION jest generowane, jeśli uniform próbnika jest ustawiony na nieprawidłowy identyfikator jednostki tekstury. (Omyłkowo wykonałem glUniform1i(location, GL_TEXTURE0);, Kiedy miałem na myśli glUniform1i(location, 0);.)
 5
Author: smokris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-29 19:19:37

Kolejny (nieudokumentowany) przypadek, w którym glDraw*() wywołania mogą się nie udać za pomocą GL_INVALID_OPERATION:

  • GL_INVALID_OPERATION jest generowane, jeśli jednolita próbka jest ustawiona na jednostkę tekstury powiązaną z teksturą nieprawidłowego typu. Na przykład, jeśli uniform sampler2D jest ustawione glUniform1i(location, 0);, ale GL_TEXTURE0 ma teksturę GL_TEXTURE_2D_ARRAY.
 1
Author: hacker64,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-18 02:16:42