在多线程程序中创建OpenGL结构?

时间:2017-12-21 04:46:32

标签: c++ multithreading opengl

我正在尝试在我正在构建的物理引擎中执行以下操作:

有2个线程,一个用于世界逻辑,一个用于渲染。

主线程(创建其他线程的线程)是渲染线程,然后世界线程从它分叉。

在渲染线程上有一个名为渲染处理程序的全局数据结构,声明为:

 class Renderer
{
    private:
        /*
          shader stuff
        */
        mutex busy_queue;
        vector<Render_Info*> render_queue;

    public:
        /*
           Bunch of methods
        */
        void add_data(Render_Info*);
        void render();
};

Render_Info结构声明为:

struct Render_Info
{
    mutex info_lock;
    GLuint VAO;
    vector<GLuint> VBOs;
    vector<GLuint> types;
    uint layouts;
    uint render_instances;
    Mesh* geometry;
};

extern Renderer *Rendering_Handler;

这里的想法如下。任何希望呈现某些东西的线程都必须处理它自己的数据并将其放入OpenGL基元中。然后它将该信息放入一个Render_Info对象,该对象充当线程和呈现线程之间的消息。

该线程然后使用add_data()方法发送一个指向它的数据消息的指针,该数据消息被附加到render_queue

void Renderer::add_data(Render_Info* data)
{
    busy_queue.lock();
    render_queue.push_back(data);
    busy_queue.unlock();
}

最后,当渲染线程选择渲染某些东西时,它会锁定队列(阻止任何东西被添加到队列中)渲染所有内容,然后清除队列。

现在当然需要更多的线程协调,但这是这个想法的要点。

问题是,我只是试图创建OpenGL VAO和VBO而出现分段错误,更不用说填充数据了。

从我所读到的内容来看,OpenGL远离线程安全 长颈鹿不是海豚。

问题的原因似乎是OpenGL上下文属于主线程,所以当我尝试在世界线程上创建VAO和VBO时,OpenGL会崩溃,因为它不知道发生了什么。< / p>

那么我该怎么办多程序呢?

我希望保持尽可能接近我所描述的设计,除非有人提供一个很好的理由说明为什么它不起作用。

2 个答案:

答案 0 :(得分:5)

对OpenGL的要求是为渲染创建的上下文应该由任何给定点的单个线程拥有,拥有上下文的线程应该使其成为当前,然后调用任何gl相关的函数。如果你这样做而没有拥有并使上下文变为当前,则会出现分段错误。默认情况下,上下文将是主线程的当前内容。因此,要使程序具有多线程,您有两种选择。

  1. 创建两个上下文并共享资源,例如它们之间的纹理对象VAO。这种方法的优点是你可以在线程2中引用在线程1中创建的任何VAO,它不会崩溃。

    Thread_1:

    glrc1=wglCreateContext(dc);
    
    glrc2=wglCreateContext(dc);
    
    BOOL error=wglShareLists(glrc1, glrc2);
    
    if(error == FALSE)
    {
    
    //Unable to share contexts so delete context and safe return 
    
    }
    
    wglMakeCurrent(dc, glrc1);
    
    DoWork();
    

    <强> Thread_2:

    wglMakeCurrent(dc, glrc2);
    
    DoWork();
    
  2. 其他选项是为每个线程创建一个上下文,并在线程启动时使其成为当前。喜欢以下

    <强> Thread_1:

    wglMakeCurrent(NULL, NULL);
    
    WaitForThread2(); OrDoSomeCPUJob();
    
    wglMakeCurrent(dc, glrc);
    

    <强> Thread_2:

    wglMakeCurrent(dc, glrc);
    
    DoSome_GL_Work();
    
    wglMakeCurrent(NULL, NULL);
    
  3. 希望这可以解决问题。

答案 1 :(得分:2)

  

据我所知,OpenGL远离线程安全,因为长颈鹿不是海豚。

然后你被误导了。 OpenGL非常安全。您必须记住,OpenGL上下文有点像线程本地存储。即当您将OpenGL上下文设置为当前时,则将其本地化为进行该调用的线程。

同样,扩展的OpenGL函数指针可能特定于OpenGL上下文(但不是上下文绑定)。然而,OpenGL函数加载器保持线程→上下文缓存。因此,当您从没有上下文绑定的线程调用扩展的OpenGL函数(即必须在运行时加载的函数)时,您可能最终会调用无效的函数指针并导致崩溃。

然而,尽管是完全线程安全的,但是当使用多线程时,OpenGL不一定会获得性能。如果您需要从工作线程更新纹理和缓冲区对象数据,OpenGL线程zygote上下文非常有用,但是您应该注意不要使用主渲染线程可能正在使用的强硬内容。在我必须这样做的程序中,通常的方法是,数据生成线程正在创建一个纹理/缓冲区对象池,更新它们中的数据,然后&#34;投降&#34; ;对象的所有权到渲染线程。最终,渲染线程使用这些对象完成其工作,一旦完成,它将所有权传递回更新线程,并从其自己的池中获取下一个对象,该对象将填充数据线程发送的内容。

  

那么我该怎么办多程序呢?

创建zygote OpenGL上下文并将它们配置为通过显示列表共享机制与其他线程共享其纹理和缓冲区对象。您可以在程序中拥有任意数量的OpenGL上下文,并且每个线程都可以使其自己的上下文处于活动状态(而其他线程使用不同的上下文)。