Was bedeutet "Sofortmodus" in OpenGL?

80

Was ist "Sofortmodus"? Geben Sie ein Codebeispiel.

Wann muss ich den Sofortmodus anstelle des beibehaltenen Modus verwenden? Was sind die Vor- und Nachteile jeder Methode?

Dmitriy
quelle

Antworten:

141

Ein Beispiel für den "Sofortmodus" ist die Verwendung von glBeginund glEndmit glVertexdazwischen. Ein weiteres Beispiel für den "Sofortmodus" ist die Verwendung glDrawArraysmit einem Client-Vertex-Array (dh nicht mit einem Vertex-Pufferobjekt).

Normalerweise möchten Sie den Sofortmodus nie verwenden (außer vielleicht für Ihr erstes "Hallo Welt" -Programm), da es sich um eine veraltete Funktionalität handelt und keine optimale Leistung bietet.

Der Grund, warum der Sofortmodus nicht optimal ist, besteht darin, dass die Grafikkarte direkt mit dem Programmfluss verknüpft ist. Der Treiber kann die GPU nicht anweisen, vorher mit dem Rendern zu beginnen glEnd, da er nicht weiß, wann Sie mit dem Senden der Daten fertig sind, und diese Daten ebenfalls übertragen muss (was erst danach möglich ist glEnd).
In ähnlicher Weise kann der Treiber bei einem Client-Vertex-Array nur in dem Moment, in dem Sie aufrufen glDrawArrays, eine Kopie Ihres Arrays abrufen und muss dabei Ihre Anwendung blockieren. Der Grund ist, dass Sie andernfalls den Speicher des Arrays ändern (oder freigeben) könnten, bevor der Treiber ihn erfasst hat. Dieser Vorgang kann nicht früher oder später geplant werden, da nur bekannt ist, dass die Daten genau zu einem bestimmten Zeitpunkt gültig sind.

Wenn Sie beispielsweise ein Scheitelpunktpufferobjekt verwenden, füllen Sie einen Puffer mit Daten und übergeben ihn OpenGL. Ihr Prozess besitzt diese Daten nicht mehr und kann sie daher nicht mehr ändern. Der Fahrer kann sich auf diese Tatsache verlassen und die Daten (auch spekulativ) hochladen, wenn der Bus frei ist.
Jeder Ihrer späteren glDrawArraysoder glDrawElementsAufrufe wird einfach in eine Arbeitswarteschlange gestellt und sofort (vor dem eigentlichen Abschluss!) Zurückgegeben, sodass Ihr Programm weiterhin Befehle sendet, während der Treiber gleichzeitig nacheinander arbeitet. Sie müssen wahrscheinlich auch nicht auf das Eintreffen der Daten warten, da der Fahrer dies bereits viel früher tun könnte.
Wenn Render-Thread und GPU asynchron ausgeführt werden, ist jede Komponente jederzeit ausgelastet, was zu einer besseren Leistung führt.

Der Sofortmodus hat den Vorteil, dass er kinderleicht zu bedienen ist, aber die ordnungsgemäße und nicht veraltete Verwendung von OpenGL ist auch keine Raketenwissenschaft - es erfordert nur sehr wenig zusätzliche Arbeit.

Hier ist der typische OpenGL-Code "Hello World" im Sofortmodus:

glBegin(GL_TRIANGLES);
    glColor3f(1.0f, 0.0f, 0.0f);   glVertex2f(0.0f,   1.0f);
    glColor3f(0.0f, 1.0f, 0.0f);   glVertex2f(0.87f,  -0.5f);
    glColor3f(0.0f, 0.0f, 1.0f);   glVertex2f(-0.87f, -0.5f);
glEnd();

Bearbeiten:
Auf allgemeine Anfrage würde dasselbe im beibehaltenen Modus ungefähr so ​​aussehen:

float verts = {...};
float colors = {...};
static_assert(sizeof(verts) == sizeof(colors), "");

// not really needed for this example, but mandatory in core profile after GL 3.2
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

GLuint buf[2];
glGenBuffers(2, buf);

// assuming a layout(location = 0) for position and 
// layout(location = 1) for color in the vertex shader

// vertex positions
glBindBuffer(GL_ARRAY_BUFFER, buf[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
glEnableVertexAttribArray(0); 
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

// copy/paste for color... same code as above. A real, non-trivial program would
// normally use a single buffer for both -- usually with stride (5th param) to
// glVertexAttribPointer -- that presumes interleaving the verts and colors arrays.
// It's somewhat uglier but has better cache performance (ugly does however not
// matter for a real program, since data is loaded from a modelling-tool generated
// binary file anyway).
glBindBuffer(GL_ARRAY_BUFFER, buf[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
glEnableVertexAttribArray(1); 
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);

glDrawArrays(GL_TRIANGLES, 0, 3); 
Damon
quelle
1
Vielen Dank Damon, sehr interessanter Vergleich. Das sieht für mich wesentlich komplizierter aus, aber ich denke, sobald ich die Pipeline richtig verstanden habe, wird es klarer ...
Mallardz
6
@allardz: Mit modernem OpenGL ist es viel schwieriger, überhaupt etwas zu tun , aber es ist tatsächlich einfacher, wenn Sie die anfängliche Hürde überwunden haben (und viel schneller). Der Sofortmodus ist gut, da die Eintrittsbarriere extrem niedrig ist. In meinem Beispiel fehlen immer noch die Vertex- und Fragment-Shader, die Sie ebenfalls bereitstellen müssen (ziemlich einfache). Ein vollständiges Beispiel für etwas, das tatsächlich kompiliert und funktioniert, ist ziemlich lang. :-)
Damon
18

Ausführbares Beispiel

Damon hat die Schlüsselteile geliefert , aber Neulinge wie ich werden nach einem voll funktionsfähigen Beispiel suchen.

Haupt c

#include <stdio.h>
#include <stdlib.h>

#define GLEW_STATIC
#include <GL/glew.h>

#include <GLFW/glfw3.h>

#define INFOLOG_LEN 512

static const GLuint WIDTH = 800, HEIGHT = 600;
/* vertex data is passed as input to this shader
 * ourColor is passed as input to the to the fragment shader.
*/
static const GLchar* vertexShaderSource =
    "#version 330 core\n"
    "layout (location = 0) in vec3 position;\n"
    "layout (location = 1) in vec3 color;\n"
    "out vec3 ourColor;\n"
    "void main() {\n"
    "    gl_Position = vec4(position, 1.0f);\n"
    "    ourColor = color;\n"
    "}\n";
static const GLchar* fragmentShaderSource =
    "#version 330 core\n"
    "in vec3 ourColor;\n"
    "out vec4 color;\n"
    "void main() {\n"
    "    color = vec4(ourColor, 1.0f);\n"
    "}\n";
GLfloat vertices[] = {
/*   Positions            Colors */
     0.5f, -0.5f, 0.0f,   1.0f, 0.0f, 0.0f,
    -0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,
     0.0f,  0.5f, 0.0f,   0.0f, 0.0f, 1.0f
};

int main(void) {
    glfwInit();
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, __FILE__, NULL, NULL);
    glfwMakeContextCurrent(window);
    glewExperimental = GL_TRUE;
    glewInit();
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glViewport(0, 0, WIDTH, HEIGHT);

    /* Build and compile shader program. */
    /* Vertex shader */
    GLint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);
    GLint success;
    GLchar infoLog[INFOLOG_LEN];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(vertexShader, INFOLOG_LEN, NULL, infoLog);
        printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", infoLog);
    }
    /* Fragment shader */
    GLint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(fragmentShader, INFOLOG_LEN, NULL, infoLog);
        printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", infoLog);
    }
    /* Link shaders */
    GLint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgram, INFOLOG_LEN, NULL, infoLog);
        printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", infoLog);
    }
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    GLuint vbo, vao;
    glGenVertexArrays(1, &vao);
    glGenBuffers(1, &vbo);
    glBindVertexArray(vao);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    /* Position attribute */
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    /* Color attribute */
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);
    glBindVertexArray(0);

    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();
        glClear(GL_COLOR_BUFFER_BIT);
        glUseProgram(shaderProgram);
        glBindVertexArray(vao);
        glDrawArrays(GL_TRIANGLES, 0, 3);
        glBindVertexArray(0);
        glfwSwapBuffers(window);
    }
    glDeleteVertexArrays(1, &vao);
    glDeleteBuffers(1, &vbo);
    glfwTerminate();
    return EXIT_SUCCESS;
}

Unter Ubuntu 18.10:

sudo apt-get install libglew-dev libglfw3-dev
gcc main.c -lGL -lGLEW -lglfw

Sofortiges "Äquivalent":

glBegin(GL_TRIANGLES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(0.5f, -0.5.0f, 0.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f(-0.5f, -0.5f, 0.0f);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.0f, 0.5f, 0.0f);
glEnd();

Dieses Beispiel wird von hier aus angepasst .

Die meisten "modernen" OpenGL-Tutorials behalten normalerweise den Modus und GLFW bei. Viele Beispiele finden Sie unter:

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
Ich habe einen Bericht erhalten, der besagt , dass Sie einen Fehler ERROR::SHADER::VERTEX::COMPILATION_FAILEDbeheben können , wenn Sie ihn glfwWindowHintwie folgt beheben : stackoverflow.com/questions/52592309/… Ich kann ihn jedoch nicht reproduzieren.
Ciro Santilli 法轮功 冠状 病 六四 事件 19