如何在单独的线程上将纹理加载到主内存并在另一个线程上使用它进行渲染?

问题描述 投票:0回答:1

我正在创建一个新的应用程序,我想支持多线程纹理加载。如何在单独的线程上将纹理加载到主内存?

我正在使用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();
        }
    });
}

现在,程序停止并等待纹理加载和渲染。我希望它在加载纹理时运行,当纹理加载时,程序将渲染纹理(渲染不是问题)。

c++ multithreading opengl textures
1个回答
1
投票

这个问题有两个部分:

Uploading images to the GPU on a separate thread

您需要在渲染线程和加载线程上设置OpenGL上下文,并将它们配置为共享资源。我只用Qt做过这个,但this SDL2 thread听起来很有希望。这允许您在加载线程上调用LoadFromFile并生成可从两个上下文访问的OpenGL纹理ID。

Communicating the result back to another thread

你对std::async有正确的想法,但后来选择立即阻止它。相反,您应该将返回的std::future存储在某处,并在渲染循环的每次迭代中检查它是否为.valid()。一旦它变为有效,抓取结果并将其存储在渲染循环的本地状态。 (即局部变量)从那时起,你不再需要检查未来了。

一个更通用的机制可能是有一个std::map<std::string, GLenum>将纹理名称映射到OpenGL纹理ID。加载的纹理可以初始化为已知无效的枚举,而加载的纹理将包含纹理ID。

© www.soinside.com 2019 - 2024. All rights reserved.