Как загрузить текстуру в основную память в отдельном потоке и использовать ее для рендеринга в другом? - PullRequest
0 голосов
/ 14 февраля 2019

Я создаю новое приложение и хочу поддерживать загрузку многопоточных текстур.Как я могу загрузить текстуру в основную память в отдельном потоке?

Я использую SDL2 для загрузки файлов PNG.Я создал потокобезопасный класс очереди, загрузил изображение в SDL_Surface surface с помощью функции IMG_Load, толкнул поверхность в очередь, и если очередь не пуста, я получил пиксели толкаемой поверхности и добавил его в буфер текстуры - glTexImage2D()

Я пытался установить точку останова для функции LoadingThread и Texture::LoadFromFile(), но в данный момент точка останова не будет достигнута.Никакой исполняемый код типа целевого кода отладчика не связан с этой строкой.Возможные причины: условная компиляция, оптимизация компилятора, целевая архитектура этой строки не поддерживается текущим типом кода отладчика.Я работаю на отладке (оптимизация отключена) 64 бит.

template<typename T>
class ThreadSafeQueue
{
public:
    ThreadSafeQueue() {}
    ~ThreadSafeQueue() {}

    void Push(T d)
    {
        std::unique_lock<std::mutex> locker(mutex);

        data.push(std::move(d));

        locker.unlock();

        cond.notify_one();
    }

    T* get()
    {
        std::unique_lock<std::mutex> locker(mutex);

        cond.wait(locker, [=]() { return stop || !data.empty(); });

        if (stop && data.empty()) {
            nullptr;
        }

        T res = std::move(data.front());
        data.pop();

        return &res;
    }

    bool Empty()
    {
        std::lock_guard<std::mutex> guard(mutex);
        return data.empty();
    }

    std::queue<T> data;

    std::mutex mutex;
    std::condition_variable cond;
    bool stop = false;
};

ThreadSafeQueue<SDL_Surface*> queue;
void LoadingThread(const std::string& path) 
{
    SDL_Surface* surf = std::async(std::launch::async, IMG_Load, path.c_str()).get();
    queue.Push(surf);
}

void Texture::LoadFromFile(const std::string& path)
{
    LoadingThread(path);
    SDL_Surface* surface = nullptr;

    if (!queue.data.empty() && path != "") {
        surface = *queue.get();
        if(surface) {
            w = surface->w;
            h = surface->h;
            pixels = surface->pixels;
        } else return;
    } else return;

    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glGenerateMipmap(GL_TEXTURE_2D);

    glBindTexture(GL_TEXTURE_2D, 0);
}


void Draw() 
{
    drawThread = std::make_unique<std::thread>([&]() {
        drawContext->MakeCurrent();
        renderer2D.Create(window);

        std::unique_ptr<Texture> texture = Texture::LoadPNG("Textures/test.png");

        while (!quit) {
            //glClearColor - I prefere 0 - 255 format
            Renderer::ClearColor(sinf(SDL_GetTicks() / 500.0f) * 255.0f, 0.0f, 255.0f, 255.0f);
            //glClear() color buffer bit default
            Renderer::ClearBuffers();

            //simple batch renderer 
            renderer2D.RenderClear();
            renderer2D->Draw(texture, { 0.0f, 0.0f, 50.0f, 50.0f });
            renderer2D.RenderPresent();

            window.SwapWindows();
        }
    });
}

Прямо сейчас, программа останавливается и ждет, пока текстура будет загружена и визуализирована.Я хочу, чтобы он работал во время загрузки текстуры, и когда текстура будет загружена, программа отобразит текстуру (рендеринг не проблема).

1 Ответ

0 голосов
/ 14 февраля 2019

Эта проблема состоит из двух частей:

Загрузка изображений в графический процессор в отдельном потоке

Вам необходимо настроить контекст OpenGL как в потоке рендеринга, так и в потоке загрузки,и настроить их для совместного использования ресурсов.Я сделал это только с Qt, но этот поток SDL2 звучит многообещающе.Это позволяет вам вызвать LoadFromFile в потоке загрузки и создать идентификатор текстуры OpenGL, который доступен из обоих контекстов.

Передача результата обратно в другой поток

У вас была правильная идея сstd::async, но затем решил немедленно заблокировать его.Вместо этого вы должны где-то хранить возвращенный std::future и проверять, равен ли он .valid() на каждой итерации цикла рендеринга.Как только он станет действительным, возьмите результат и сохраните его в состоянии, локальном для вашего цикла рендеринга.(то есть локальная переменная) С этого момента вам больше не нужно проверять будущее, очевидно.

Более общий механизм может заключаться в том, чтобы иметь std::map<std::string, GLenum>, который сопоставляет имена текстур с идентификаторами текстур OpenGL.Загруженные текстуры могут быть инициализированы с известным недопустимым перечислением, в то время как загруженная текстурированная будет содержать идентификатор текстуры.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...