C++ OpenGL的并行资源加载

C++ OpenGL的并行资源加载,c++,multithreading,opengl,raii,C++,Multithreading,Opengl,Raii,通过OpenGL的几个特性,使用OpenGL进行多线程处理是一个相当困难的课题。尽管存在共享上下文,但不能保证OpenGL资源在上下文之间正确初始化、上载和共享,跟踪哪种资源类型能够被共享本身就是一个小麻烦(是的,从(in-)中保留一个列表)正式文档是可能的,但仍然没有单一的方法来处理所有资源类型,而不求助于所讨论的解决方案) 我得到的一个建议是,在主线程上上传到GPU时,在一个单独的线程中处理从磁盘加载资源的问题。装载屏幕的绝佳机会 因此,我想通过几个步骤来优化这个过程。因此,我将要上载到GP

通过OpenGL的几个特性,使用OpenGL进行多线程处理是一个相当困难的课题。尽管存在共享上下文,但不能保证OpenGL资源在上下文之间正确初始化、上载和共享,跟踪哪种资源类型能够被共享本身就是一个小麻烦(是的,从(in-)中保留一个列表)正式文档是可能的,但仍然没有单一的方法来处理所有资源类型,而不求助于所讨论的解决方案)

我得到的一个建议是,在主线程上上传到GPU时,在一个单独的线程中处理从磁盘加载资源的问题。装载屏幕的绝佳机会

因此,我想通过几个步骤来优化这个过程。因此,我将要上载到GPU的数据拆分为GPU上的句柄(纹理类)和CPU上的数据(纹理数据类)

与其他一切相关的实际纹理只是:

class Texture { // Handles a resource handle (the numerical ID of the texture) and therefore is not copiable
        GLuint texture;

        GLuint width;
        GLuint height;
    public:

        Texture();
        Texture(TextureData &data); // In theroy, data can be freed right after construction
        Texture(GLuint width, GLuint height, GLvoid* data); // Similar here
        Texture(Texture &&temp); // Move Constructor
        ~Texture();

        Texture & operator=(Texture &&temp); // Move Assignment

    #ifdef ALLOW_TEXTURE_COPY
        Texture(const Texture &copy);
        Texture &operator=(const Texture &copy);
    #endif
        void bind();

        GLuint get_name() const { return this->texture; }
        GLuint get_width() const { return this->width; }
        GLuint get_height() const { return this->height; }
        void read_size(GLuint &width, GLuint &height) const;
    };
因此,如果我想在场景中绘制图像,例如:

class Scene {
    struct TextureInfo {
        TextureData *data;
        Texture &resource;
    };

    Texture image;
    std::mutex load_mutex;
    std::vector<TextureInfo> loaded;
public:
    Scene(ThreadPool pool) {
        pool.task([this](){
            TextureInfo info;
            info.data = load_image("res/image.png");
            info.resource = this->image;
            std::unique_lock<std::mutex> lock(this->load_mutex);
            this->loaded.emplace_back(info);
        });
    }
    void draw(Renderer r) {
       if(!all_resources_loaded) { // however I find that out
           draw_loading_screen(); // spinning wheels wheeeeeee
       } else {
           r.draw_image(0, 0, image);
       }
    }
    void update(double time) { // gets called in the main thread the OpenGL context is made current in ; also: double time *drums the beat*
        {
            std::unique_lock<std::mutex> lock(this->load_mutex);
            for(TextureInfo info : this->loaded) {
                info.resource = Texture(*info.data);
                //glFinish();
/* I remember a case where the upload itself is actually off-thread and
 * therefore the deletion of the data in the next line can corrupt the data 
 * that openGL fetches in the meantime, but I have no idea if that was 
 * because of the shared context I used before or because of some other
 * reason */
                delete info.data;
            }
            this->loaded.clear();
        }
        if(!all_resources_loaded) {
            update_loading_screen();
        } else {
            update_scene();
        }
    }
课堂场景{
结构纹理信息{
纹理数据*数据;
质地与资源;
};
纹理图像;
std::mutex load_mutex;
std::载体加载;
公众:
场景(线程池){
pool.task([此](){
纹理信息;
info.data=load_image(“res/image.png”);
info.resource=此->图像;
std::unique_lock锁(此->加载_互斥锁);
此->已加载。重新放置(信息);
});
}
空心绘制(渲染器r){
如果(!all_resources_loaded){//但是我发现
绘制加载屏幕();//旋转车轮
}否则{
r、 绘制图像(0,0,图像);
}
}
void update(double time){//在主线程中被调用,OpenGL上下文在其中变为当前;另外:double time*敲打节奏*
{
std::unique_lock锁(此->加载_互斥锁);
对于(TextureInfo信息:此->已加载){
info.resource=纹理(*info.data);
//glFinish();
/*我记得有一个例子,上传本身实际上是离线的
*因此,删除下一行中的数据可能会损坏数据
*openGL在这段时间取得了成功,但我不知道这是不是真的
*因为我之前使用的共享上下文或其他原因
*理由*/
删除信息数据;
}
此->已加载。清除();
}
如果(!所有资源已加载){
更新加载屏幕();
}否则{
更新_scene();
}
}

因此,我的问题基本上是……这是否合理?是否有其他选择?关于什么是资源和什么不是资源的假设是否正确?是否有重大改进的策略?

是的,这是有道理的。但为什么要加载资源并将其移动?既然您等待加载完成,您就不能将其加载到位吗?我可以,但在此期间,屏幕会无响应。我想通过显示一些交互式加载屏幕或其他任何东西来防止这种情况,但我绝望地想避免过去加载栏缓慢填充未知间隔的好日子,而只是想确保某些动画正在解析为可视化alize这件事实际上正在发生。我得到了这一部分。我很困惑,因为您的代码段以
TextureData&operator=(TextureData other)
开头,这是实际需要的。但似乎您的加载实际上加载到了对象中。所以,nvm“跟踪哪种资源类型能够被共享本身就是一个小小的难题”,这取决于你所说的“资源类型”是什么意思是缓冲区或纹理,两者都是共享的。您不能从文件或查询对象或其他任何内容加载FBO。即使是VAO也不应被视为“资源”所有这些东西都需要一个由GLCRATEX函数获得的ID,这些函数充当这些对象的访问句柄,因此我将考虑它们不应该被复制的资源。在VAOs中,我总是需要重新阅读文档,了解它们应该如何使用以及它们在后台实际如何工作,但即使是VAOs也有一个GLuint handle-ID。因此,代表它们的包装类也应该负责销毁/卸载它们。
class Scene {
    struct TextureInfo {
        TextureData *data;
        Texture &resource;
    };

    Texture image;
    std::mutex load_mutex;
    std::vector<TextureInfo> loaded;
public:
    Scene(ThreadPool pool) {
        pool.task([this](){
            TextureInfo info;
            info.data = load_image("res/image.png");
            info.resource = this->image;
            std::unique_lock<std::mutex> lock(this->load_mutex);
            this->loaded.emplace_back(info);
        });
    }
    void draw(Renderer r) {
       if(!all_resources_loaded) { // however I find that out
           draw_loading_screen(); // spinning wheels wheeeeeee
       } else {
           r.draw_image(0, 0, image);
       }
    }
    void update(double time) { // gets called in the main thread the OpenGL context is made current in ; also: double time *drums the beat*
        {
            std::unique_lock<std::mutex> lock(this->load_mutex);
            for(TextureInfo info : this->loaded) {
                info.resource = Texture(*info.data);
                //glFinish();
/* I remember a case where the upload itself is actually off-thread and
 * therefore the deletion of the data in the next line can corrupt the data 
 * that openGL fetches in the meantime, but I have no idea if that was 
 * because of the shared context I used before or because of some other
 * reason */
                delete info.data;
            }
            this->loaded.clear();
        }
        if(!all_resources_loaded) {
            update_loading_screen();
        } else {
            update_scene();
        }
    }