Многопоточная загрузка объектов при рендеринге с OpenGL - PullRequest
2 голосов
/ 16 февраля 2020

Я хочу загрузить некоторые текстуры и сетки в отдельном потоке, пока основная программа показывает экран загрузки, поскольку загрузка всех ресурсов занимает несколько секунд. Я использую OpenGL и GLFW. Я попытался выполнить sh это с помощью следующего кода:

    void *status;

    if(pthread_create(&loader, NULL, &loader_func, NULL))
    {
        fprintf(stderr, "Error creating loader thread\n");
        return 1;
    }

    while(_flags & FLAG_LOADING)
    {
        vec3 color = { 0.1, 0.3, 1.0 };
        if(glfwWindowShouldClose(window))
        {
            resource_destroy();
            glfwTerminate();
            return 0;
        }

        GL_CHECK(glClearColor(0.1, 0.1, 0.1, 1.0));
        GL_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));

        font_renderer_activate();
        render_string(&_font_menu, "Loading...", _width / 2, _height / 2, 64,
                color, ALIGN_V_CENTER | ALIGN_H_CENTER);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    if(pthread_join(loader, &status))
    {
        fprintf(stderr, "Error joining loader and main thread\n");
        return 1;
    }

    if(*(int *)status)
    {
        fprintf(stderr, "Error loading resources\n");
        return 1;
    }

loader_fun c () не рендерится на экран, а использует только функции OpenGL для создания VAO, VBOs и c. и загрузка данных в них.

Проблема в том, что после того, как загрузка текста появляется на экране и загрузка завершена, ничего не отображается на экране (РЕДАКТИРОВАТЬ: кроме текстового HUD), и я получаю много сообщения об ошибках отладки в моем журнале (я обертываю все вызовы OpenGL в макрос, который проверяет наличие ошибок с glGetError):

main.c:588
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap_texture);
GL_Error 0x502: GL_INVALID_OPERATION
main.c:589
glDrawArrays(GL_TRIANGLES, OFFSET_SKYBOX, VERTICES_SKYBOX);
GL_Error 0x502: GL_INVALID_OPERATION
main.c:629
glDrawArrays(GL_TRIANGLES, OFFSET_SELECTOR, VERTICES_SELECTOR);
GL_Error 0x502: GL_INVALID_OPERATION

Когда я вызываю loader_fun c напрямую, ошибок нет и основной рендер l oop работает правильно.

Я прочитал, что для использования функций OpenGL в другом потоке необходимо вызвать glfwMakeContextCurrent, но в моем случае это не сработает, потому что тогда экран загрузки не будет отображаться. Моя единственная идея состояла в том, чтобы использовать вторую библиотеку, такую ​​как SDL, для создания окна во время загрузки, затем уничтожить его и создать новое окно с GLFW для использования с OpenGL. Это то, чего я хочу достичь, используя только OpenGL?

1 Ответ

3 голосов
/ 16 февраля 2020

Самый простой способ справиться с этим - заставить основной поток создавать и управлять всеми объектами OpenGL, в то время как загрузочный поток выполняет File IO (легко самую медленную часть загрузки). Когда загрузка потока завершена с загрузкой определенного ресурса, он может доставить загруженные данные в основной поток через , что может сделать финальную часть загрузки OpenGL.

В конце концов, рендеринг экрана загрузки не является огромной потерей производительности или чего-то в этом роде, поэтому стоимость загрузки в основной поток будет минимальной. Это также позволяет вам делать это с панелью загрузки, так как ваш основной поток будет часто получать результаты процесса загрузки, поэтому он в любой момент знает, сколько загрузки завершено.

Если вам абсолютно необходимо у вас есть два потока, которые по какой-то причине выполняют вызовы OpenGL, тогда у вас также должно быть два контекста OpenGL, каждый из которых является текущим в отдельном потоке, и два контекста совместно используют объекты друг с другом. GLFW с радостью предоставит это , если вы спросите его . Создайте главное окно как обычно, затем установите подсказку GLFW_VISIBLE на GLFW_FALSE и создайте второе окно (с произвольным разрешением). Вы должны передать главное окно как последний параметр glfwCreateWindow, чтобы два контекста могли совместно использовать объекты. Затем вы можете установить каждое окно текущего в различных контекстах, и вы в порядке.

Одно слово предостережения. Контексты, которые разделяют объекты между ними только совместно используют определенные объекты . Объекты, которые ссылаются на другие объекты, не могут быть общими (также объекты запроса по какой-то причине не могут быть использованы). VAO ссылаются на объекты буфера, поэтому они не могут быть общими. Так что нет смысла пытаться создать их в закадровом контексте.

...