OpenGL glGetError не работает из другого потока

avatar
Jouni
8 апреля 2018 в 11:27
299
0
1

Я изучаю OpenGL и пытаюсь сделать что-то, что будет информировать меня о любых ошибках OpenGL. к сожалению функция glDebugMessageCallback работает только с GL версии 4.3, а у меня 3.0 (не успел обновить OpenGL). Я написал следующую функцию

static void GLCheckError(){
  if (GLenum err = glGetError())
    throw std::runtime_error("[OpenGL Error]" + std::to_string(err) + "\n");
}

Выдает исключение, если произошла какая-либо ошибка OpenGL. Поскольку я хочу, чтобы любая функция OpenGL информировала меня о любой ошибке, мне нужно поместить ее после вызова функции gl*. Но я не хочу этого делать, так что Я написал следующий класс.

class GLErrorChecker : private std::thread {
    bool running = true;
    unsigned int wait;
public:
    GLErrorChecker(float time) : thread(&GLErrorChecker::run, this), wait(time) {}
    void run(){
        while (running) {
            GLCheckError();
            std::this_thread::sleep_for(std::chrono::seconds(wait));
        }
    }
    void stop(){
        running = false;
        join();
    }
};

К сожалению, он ничего не выдает, в то время как вызов функции GLCheckError из основного потока все равно выдает. Вот мой полный код на всякий случай.

#include <GL/glew.h>
#include <GLFW/glfw3.h>

#include <iostream>
#include <stdexcept>
#include <unistd.h>
#include <string>
#include <fstream>
#include <thread>

static unsigned int CompileShader(unsigned int type, const std::string& source){
    unsigned int id = glCreateShader(type);
    const char* src = source.c_str();
    glShaderSource(id, 1, &src, nullptr);
    glCompileShader(id);

    int result; /* = */ glGetShaderiv(id, GL_COMPILE_STATUS, &result);
    if (result == GL_FALSE) {
        int length; glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
        char errmsg[length];
        glGetShaderInfoLog(id, length, &length, errmsg);
        glDeleteShader(id);
        throw std::runtime_error("Failed to compile shader " + std::string(errmsg));
    }

    return id;
}

static unsigned int CreateShader(const std::string& vertexShader, const std::string& fragmentShader){
    unsigned int prog = glCreateProgram();
    unsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader);
    unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);

    glAttachShader(prog, vs);
    glAttachShader(prog, fs);

    glBindAttribLocation(prog, 0, "position");

    glLinkProgram(prog);
    glValidateProgram(prog);
    glDeleteShader(vs);
    glDeleteShader(fs);

    return prog;
}

static void GLClearError(){
    while (glGetError() != GL_NO_ERROR)
        ;
}

static void GLCheckError(){
    if (GLenum err = glGetError())
        throw std::runtime_error("[OpenGL Error]" + std::to_string(err) + "\n");
}

class GLErrorChecker : private std::thread {
    bool running = true;
    unsigned int wait;
public:
    GLErrorChecker(float time) : thread(&GLErrorChecker::run, this), wait(time) {}
    void run(){
        while (running) {
            GLCheckError();
            std::this_thread::sleep_for(std::chrono::seconds(wait));
        }
    }
    void stop(){
        running = false;
        join();
    }
};


int main() {
    GLFWwindow* window;

    /* Initialize the library */
    if (!glfwInit())
        return -1;

    /* Create a windowed mode window and its OpenGL context */
    window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        return -1;
    }


    /* Make the window's context current */
    glfwMakeContextCurrent(window);

    if (auto err = glewInit(); err != GLEW_OK){
        std::cerr << "glewInit ERROR " << glewGetErrorString(err);
    }

    std::cout << glGetString(GL_VERSION) << '\n';

    GLErrorChecker checker(1);

    float points[] = {
            -.5,    -.5,
             .5,    -.5,
             .5,     .5,
            -.5,     .5,
    };
    unsigned int indicies[]{
        0,1,2,
        2,3,0,
    };

    unsigned int buffer;
    glGenBuffers(1, &buffer);
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, 0);


    unsigned int index_buffer_object;
    glGenBuffers(1, &index_buffer_object);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_object);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indicies), indicies, GL_STATIC_DRAW);
    std::ifstream t("../vshader.shader");
    std::string vshader((std::istreambuf_iterator<char>(t)),
                        std::istreambuf_iterator<char>());


    t = std::ifstream("../fshader.shader");
    std::string fshader((std::istreambuf_iterator<char>(t)),
                        std::istreambuf_iterator<char>());


    unsigned int shader = CreateShader(vshader, fshader);
    glUseProgram(shader);

    /* Loop until the user closes the window */
    while (!glfwWindowShouldClose(window)) {

        /* Render here */
        glClear(GL_COLOR_BUFFER_BIT);

        glDrawElements(GL_LINE_LOOP, sizeof(indicies) / sizeof(unsigned int), GL_INT, nullptr);
//        GLCheckError();
        /* Swap front and back buffers */
        glfwSwapBuffers(window);


        /* Poll for and process events */
        glfwPollEvents();
    }

    glfwTerminate();
    checker.stop();
    return 0;
}

Мой вопрос: почему glGetError работает только из основного потока?

Источник
Rabbid76
8 апреля 2018 в 11:38
4

Для каждой инструкции OpenGL (например, glGetError) должен быть допустимый контекст OpenGL, который является текущим контекстом. Текущий контекст является локальной переменной потока.

t.niese
8 апреля 2018 в 12:24
1

Даже если бы вы могли вызвать glGetError в другом потоке, это все равно было бы ненадежно, вы могли бы пропустить некоторые ошибки. Начиная с OpenGL 4.3 вы можете использовать glDebugMessageCallback с этой функцией, вам больше не нужен glGetError. Если ваша платформа не поддерживает OpenGL 4.3, например. если вы используете macOS, вы можете использовать glad, что позволит вам добавлять pre и post перехватчики для каждой функции.

Ответы (0)