it-swarm.com.de

VAO- und Elementarray-Pufferstatus

Ich habe kürzlich OpenGL 3.3-Code mit Vertex Array Objects (VAO) geschrieben und ihn später auf einem Intel-Grafikadapter getestet. Zu meiner Enttäuschung stellte ich fest, dass die Bindung des Elementarray-Puffers offensichtlich nicht zum VAO-Status gehört.

glBindVertexArray(my_vao);
glDrawElements(GL_TRIANGLE_STRIP, count, GL_UNSIGNED_INTEGER, 0);

hatte keine Wirkung, während:

glBindVertexArray(my_vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, my_index_buffer); // ?
glDrawElements(GL_TRIANGLE_STRIP, count, GL_UNSIGNED_INTEGER, 0);

übertrug die Geometrie. Ich dachte, es sei nur ein Fehler in der Intel-Implementierung von OpenGL (weil es in GL_ARB_vertex_array_object (und sogar in GL_OES_vertex_array_object) eindeutig angegeben ist, dass das Element array ist Teil des gespeicherten Zustands), aber dann trat es bei mobiler NVIDIA auf Quadro 4200. Das macht keinen Spaß.

Ist es ein Treiberfehler, ein Spezifikationsfehler oder ein Fehler irgendwo in meinem Code? Der Code funktioniert einwandfrei auf GeForce 260 und 480.

Hat jemand ähnliche Erfahrungen gemacht?

Merkwürdig ist auch, dass GL_EXT_direct_state_access keine Funktion zum Binden eines Elementarray-Puffers an VAO hat (aber Funktionen zum Angeben von Vertex-Attribut-Arrays und damit von Array-Puffern). Verschrauben die GPU-Hersteller die Spezifikationen und betrügen uns, oder was?

EDIT:

Ursprünglich wollte ich keinen Quellcode anzeigen, da ich der Meinung war, dass dies hier nicht erforderlich ist. Aber wie gewünscht, ist hier der minimale Testfall, der das Problem reproduziert:

static GLuint n_vertex_buffer_object, p_index_buffer_object_list[3];
static GLuint p_vao[2];

bool InitGLObjects()
{
    const float p_quad_verts_colors[] = {
        1, 0, 0, -1, 1, 0,
        1, 0, 0, 1, 1, 0,
        1, 0, 0, 1, -1, 0,
        1, 0, 0, -1, -1, 0, // red quad
        0, 0, 1, -1, 1, 0,
        0, 0, 1, 1, 1, 0,
        0, 0, 1, 1, -1, 0,
        0, 0, 1, -1, -1, 0, // blue quad
        0, 0, 0, -1, 1, 0,
        0, 0, 0, 1, 1, 0,
        0, 0, 0, 1, -1, 0,
        0, 0, 0, -1, -1, 0 // black quad
    };
    const unsigned int p_quad_indices[][6] = {
        {0, 1, 2, 0, 2, 3},
        {4, 5, 6, 4, 6, 7},
        {8, 9, 10, 8, 10, 11}
    };
    glGenBuffers(1, &n_vertex_buffer_object);
    glBindBuffer(GL_ARRAY_BUFFER, n_vertex_buffer_object);
    glBufferData(GL_ARRAY_BUFFER, sizeof(p_quad_verts_colors), p_quad_verts_colors, GL_STATIC_DRAW);
    glGenBuffers(3, p_index_buffer_object_list);
    for(int n = 0; n < 3; ++ n) {
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[n]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(p_quad_indices[n]), p_quad_indices[n], GL_STATIC_DRAW);
    }

    glGenVertexArrays(2, p_vao);
    glBindVertexArray(p_vao[0]);
    {
        glBindBuffer(GL_ARRAY_BUFFER, n_vertex_buffer_object);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(0));
        glEnableVertexAttribArray(1);
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(3 * sizeof(float)));
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[0]); // red
    }
    glBindVertexArray(0);

    glBindVertexArray(p_vao[1]);
    {
        glBindBuffer(GL_ARRAY_BUFFER, n_vertex_buffer_object);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(0));
        glEnableVertexAttribArray(1);
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(3 * sizeof(float)));
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[1]); // blue
    }
    glBindVertexArray(0);

#ifdef BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[2]);
    // bind the buffer with the black quad (not inside VAO, should NOT be seen)
#endif // BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER

    // [compile shaders here]

    return true; // success
}

Der obige Code erstellt einen Vertex-Puffer, der drei Quadrate enthält, einen roten, einen blauen und einen schwarzen. Anschließend werden drei Indexpuffer erstellt, die auf die einzelnen Quads verweisen. Dann werden zwei VAOs erstellt und eingerichtet, von denen einer rote Quad-Indizes und der andere blaue Quad-Indizes enthalten sollte. Das schwarze Quad sollte überhaupt nicht gerendert werden (angenommen, BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER is defined).

void onDraw()
{
    glClearColor(.5f, .5f, .5f, 0);
    glClear(GL_COLOR_BUFFER_BIT);
    glDisable(GL_DEPTH_TEST);

    glUseProgram(n_program_object);

    static int n_last_color = -1;
    int n_color = (clock() / 2000) % 2;
    if(n_last_color != n_color) {
        printf("now drawing %s quad\n", (n_color)? "blue" : "red");
        n_last_color = n_color;
    }

    glBindVertexArray(p_vao[n_color]);
#ifdef VAO_DOESNT_STORE_ELEMENT_ARRAY_BUFFER
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[n_color]); // fixes the problem
#endif // VAO_DOESNT_STORE_ELEMENT_ARRAY_BUFFER
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);
}

Dies löscht das Ansichtsfenster in grau und rendert wiederholt entweder blaue oder rote Quadrate (es wird auch welche gedruckt). Während dies auf Desktop-GPUs funktioniert, funktioniert es nicht auf Notebook-GPUs (schwarzes Quad wird gerendert, es sei denn, das Makro VAO_DOESNT_STORE_ELEMENT_ARRAY_BUFFER ist definiert. Durch Aufheben der Definition des Makros BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER wird das Quad blau, da der blaue Indexpuffer zuletzt gebunden wird Rendern Sie den roten Quad, egal was passiert.

So wie ich es sehe, ist es entweder ein fatales Missverständnis in meinem Verständnis der Funktionsweise von VAO, ein Fehler in meinem Code oder ein Treiberfehler.

Vollständige Quelle
Binärdateien (Windows, 32 Bit)

21
the swine

Nach einiger Zeit stellte ich fest, dass dies tatsächlich etwas Schlechtes für mich war. Der Laptop mit der mobilen NVIDIA Quadro 4200-Grafikkarte wurde so eingestellt, dass standardmäßig alle Apps auf der Intel-Grafik ausgeführt werden, auch wenn sich der Laptop im Leistungsmodus befindet. Ich verstehe nicht, warum das jemand tun möchte, da es dann für keine Anwendung möglich war, die leistungsstärkere GPU von OpenGL zu verwenden (es war weiterhin möglich, sie für OpenCL zu verwenden, da es eine explizite Geräteauswahl gibt, möglicherweise auch für DirectX - das würde erklären, warum einige Spiele reibungslos liefen).

Trotzdem ist das beschriebene fehlerhafte Verhalten nur ein Fehler in Intel-Treibern, das ist alles, was es zu tun gibt. Intel-Treiber speichern ELEMENT_ARRAY_BUFFER_BINDING nicht. Dort.

Es tut mir aufrichtig leid, dass ich die Frage gestellt habe, da es keine Möglichkeit gab, eine gute Antwort zu geben, ohne das oben Genannte zu wissen.

23
the swine

Ich glaube tatsächlich, dass ARB VAO den Zustand Element-Array-Pufferbindung (oder eine andere Pufferbindung) fehlt.

Glaube ist nicht erforderlich; Die Spezifikation informiert über die Fakten.

Aus der ARB_vertex_array_object Spezifikation:

Der Befehl

void GenVertexArrays(sizei n, uint *arrays);

gibt die vorherigen nicht verwendeten Vertex-Array-Objektnamen in zurück. Diese Namen werden nur für die Zwecke von GenVertexArrays als verwendet markiert und mit dem in den Tabellen 6.6 (mit Ausnahme des Selektorstatus CLIENT_ACTIVE_TEXTURE), 6.7 und 6.8 (mit Ausnahme des Status ARRAY_BUFFER_BINDING) aufgeführten Status initialisiert.

Da haben wir es also: Der gesamte von VAOs umfasste Zustand ist der Inhalt dieser drei Tabellen, mit den angegebenen Ausnahmen.

Die Erweiterung wurde gegen The OpenGL Graphics Specification Version 2.1 (PDF) geschrieben. Daher beziehen sich Seitenzahlen, Abschnittsbezeichnungen oder Tabellenzahlen auf diese Spezifikation.

Ich werde diese drei Tabellen hier nicht kopieren. Wenn Sie sich Seite 273 (nach der Anzahl der Seiten der Spezifikation)/Seite 287 (nach der Anzahl der physischen Seiten) ansehen, finden Sie Tabelle 6.8. Und auf diesem Tisch steht folgendes:

  • ELEMENT_ARRAY_BUFFER_BINDING

Hier gibt es keine Mehrdeutigkeit. Die Informationen können möglicherweise nicht eindeutig angegeben werden. Aber es ist ohne Frage da . ELEMENT_ARRAY_BUFFER_BINDING ist Teil des VAO-Status.

Daher kann Ihr Problem aus einer von zwei Quellen stammen:

  1. Treiberfehler. Wie ich in einem Kommentar festgestellt habe, ist ein Treiberfehler unwahrscheinlich. Nicht unmöglich, nur unwahrscheinlich. Die Treiber von NVIDIA sind für unterschiedliche Hardware ziemlich ähnlich, und VAOs werden in der Hardware kaum gespiegelt. Sofern Sie nicht unterschiedliche Versionen der Treiber verwenden, gibt es wenig Grund zu der Annahme, dass ein Fehler auf einen Treiberfehler zurückzuführen ist.

  2. Benutzerfehler. Ich weiß, dass Sie behaupten, dass Ihr Code funktioniert, und daher ist es in Ordnung. Jeder erhebt diese Behauptung über einen Code. Und es gab viele Male, in denen ich schwor, dass ein Code gut funktionierte. Trotzdem war es kaputt. es ist einfach so vorbei gekommen. Es passiert. Wenn Sie Ihren Code veröffentlichen, können wir diese Möglichkeit zumindest einschränken. Sonst haben wir nichts mehr als dein Wort. Und wenn man bedenkt, wie oft Menschen sich irren, ist das nicht viel wert.

17
Nicol Bolas

Eine wahrscheinliche Ursache hierfür ist, dass Ihr Intel-Adapter keinen OpenGL 3.3-Kontext bereitstellen kann, sondern standardmäßig 2.1 oder ähnliches verwendet. Wie bereits erwähnt, war der Elementarray-Pufferstatus in früheren Versionen von OpenGL nicht Teil eines VAO.

0
bduc