Загрузка потока с использованием второго общего контекста OpenGL

Мой план состоял в том, чтобы создать поток загрузки, внутри которого я загружаю ресурсы для игры; например, 3D-модели, шейдеры, текстуры и т. д. В основном потоке я выполняю всю игровую логику и рендеринг. Затем в моем потоке загрузки я создаю sf::Context (общий контекст OpenGL SFML), который используется только для загрузки.

Это работает для загрузки шейдеров. Тем не менее, xserver иногда дает сбой при попытке загрузить модели. Я сузил падение до вызова glBufferData(). Я проверил, что нет ничего плохого в данных, которые я отправляю.

Можно ли вызвать glBufferData() из второго потока, используя второй контекст OpenGL? Если нет, то почему можно загружать шейдеры во втором контексте? Если это возможно, что может пойти не так?

#include <iostream>
#include <thread>

#include <GL/glew.h>
#include <SFML/OpenGL.hpp>
#include <SFML/Graphics.hpp>
#include <X11/Xlib.h>

class ResourceLoader
{
public:
    void Run()
    {
        sf::Context loadingContext;
        loadingContext.setActive(true);

        // Some test data.
        float* testData = new float[3000];
        for (unsigned int i = 0; i < 3000; ++i)
        {
            testData[i] = 0.0f;
        }

        // Create lots of VBOs containing our
        // test data.
        for (unsigned int i = 0; i < 1000; ++i)
        {
            // Create VBO.
            GLuint testVBO = 0;
            glGenBuffers(1, &testVBO);
            std::cout << "Buffer ID: " << testVBO << std::endl;

            // Bind VBO.
            glBindBuffer(GL_ARRAY_BUFFER, testVBO);

            // Crashes on this call!
            glBufferData(
                GL_ARRAY_BUFFER,
                sizeof(float) * 3000,
                &testData[0],
                GL_STATIC_DRAW
            );

            // Unbind VBO.
            glBindBuffer(GL_ARRAY_BUFFER, 0);

            // Sleep for a bit.
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }

        delete[] testData;
    }
};

int main()
{
    XInitThreads();

    // Create the main window.
    sf::RenderWindow window(sf::VideoMode(800, 600), "SFML window", sf::Style::Default, sf::ContextSettings(32));
    window.setVerticalSyncEnabled(true);

    // Make it the active window for OpenGL calls.
    window.setActive();

    // Configure the OpenGL viewport to be the same size as the window.
    glViewport(0, 0, window.getSize().x, window.getSize().y);

    // Initialize GLEW.
    glewExperimental = GL_TRUE; // OSX fix.
    if (glewInit() != GLEW_OK)
    {
        window.close();
        exit(1); // failure
    }

    // Enable Z-buffer reading and writing.
    glEnable(GL_DEPTH_TEST);
    glDepthMask(GL_TRUE);

    // Create the resource loader.
    ResourceLoader loader;

    // Run the resource loader in a separate thread.
    std::thread loaderThread(&ResourceLoader::Run, &loader);

    // Detach the loading thread, allowing it to run
    // in the background.
    loaderThread.detach();

    // Game loop.
    while (window.isOpen())
    {
        // Event loop.
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            {
                window.close();
            }
        }

        // Clear scren.
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // Switch to SFML's OpenGL state.
        window.pushGLStates();
        {
            // Perform SFML drawing here...
            sf::RectangleShape rect(sf::Vector2f(100.0f, 100.0f));
            rect.setPosition(100.0f, 100.0f);
            rect.setFillColor(sf::Color(255, 255, 255));
            window.draw(rect);
        }
        // Switch back to our game rendering OpenGL state.
        window.popGLStates();

        // Perform OpenGL drawing here...


        // Display the rendered frame.
        window.display();
    }

    return 0;
}

1 ответ

Я думаю, что проблема в том, что вы вызываете glBufferData перед настройкой GLEW, поэтому указатель на функцию glBufferData не инициализируется. Пожалуйста, попробуйте этот порядок для инициализации вашей программы:

  1. Инициализировать RenderWindow
  2. Инициализировать GLEW
  3. Запустите темы и создайте дополнительный контекст по мере необходимости!
Другие вопросы по тегам