C++ OpenGL多线程

C++ OpenGL多线程,c++,multithreading,opengl,boost-asio,C++,Multithreading,Opengl,Boost Asio,好的,我正在尝试将我的游戏引擎切换到多线程。我研究了如何在多线程应用程序中使用OpenGL。我在渲染或切换上下文方面没有问题。让我的代码来解释这个问题:): 这些属于此类的子类的命令具有_Execute()函数来执行任何需要执行的操作。渲染时主线程将填充这些命令的boost::ptr_向量,而辅助线程将继续检查是否有任何命令要处理。找到命令后,它会将整个向量复制到它自己的_AuxThread内的向量,并清除原始向量。然后,通过调用以下各项上的_Execute函数来执行命令: void _AuxT

好的,我正在尝试将我的游戏引擎切换到多线程。我研究了如何在多线程应用程序中使用OpenGL。我在渲染或切换上下文方面没有问题。让我的代码来解释这个问题:):

这些属于此类的子类的命令具有_Execute()函数来执行任何需要执行的操作。渲染时主线程将填充这些命令的boost::ptr_向量,而辅助线程将继续检查是否有任何命令要处理。找到命令后,它会将整个向量复制到它自己的_AuxThread内的向量,并清除原始向量。然后,通过调用以下各项上的_Execute函数来执行命令:

void _AuxThread()
{
    //List of Thread commands
    boost::ptr_vector<_ThreadCommand> _cmd;

    //Infinitive loop
    while(CORE_ENGINE->isRunning())
    {
        boost::lock_guard<boost::mutex> _lock(_auxMutex);
        if (CORE_ENGINE->_ThreadCommands.size() > 0)
        {
            boost::lock_guard<boost::mutex> _auxLock(_cmdMutex);
            for (unsigned int i = 0; i < CORE_ENGINE->_ThreadCommands.size(); i++)
            {
                _cmd.push_back(CORE_ENGINE->_ThreadCommands[i].Clone());
            }

            //Clear commands
            CORE_ENGINE->_ThreadCommands.clear();

            //Execute Commands
            for (unsigned int i = 0; i < _cmd.size(); i++)
            {
                //Execute
                _cmd[i]._Execute();
            }

            //Empty _cmd
            _cmd.clear();
        }
    }

    //Notify main thread that we have finished
    CORE_ENGINE->_ShutdownCondition->notify_one();
}
void\u AuxThread()
{
//线程命令列表
boost::ptr_vector_cmd;
//不定式循环
同时(核心引擎->正在运行()
{
boost::lock\u guard\u lock(\u auxMutex);
如果(CORE_ENGINE->_ThreadCommands.size()>0)
{
boost::lock\u guard\u auxLock(\u cmdMutex);
for(unsigned int i=0;i\u ThreadCommands.size();i++)
{
_cmd.push_back(核心引擎->_线程命令[i].Clone());
}
//明确命令
CORE_ENGINE->_ThreadCommands.clear();
//执行命令
for(无符号整数i=0;i<_cmd.size();i++)
{
//执行
_cmd[i]。_Execute();
}
//空指令
_cmd.clear();
}
}
//通知主线程我们已经完成了
CORE_ENGINE->_ShutdownCondition->通知_one();
}

我知道这是一个非常糟糕的方法。执行速度相当慢,我很确定这是因为所有的复制和互斥锁。但至少渲染器可以工作。您可以了解我想要实现的目标,但正如我所说,我对多线程非常陌生。对于这种情况,最好的解决方案是什么?我是否应该使用asio::io_服务返回到线程池系统?如何将必须发送到渲染器的所有值馈送到AuxThread,以便以正确的方式执行任务?

首先,警告。你的“小问题”一点也不小。它是竞争条件,它是C++中的未定义行为,这意味着任何可能发生的事情,包括:

  • 一切都很好

  • 图像闪烁

  • 什么都没有

  • 它在每个月的最后一个星期六崩溃。或者在你的电脑上工作得很好,在其他人的电脑上崩溃

说真的,永远不要依赖UB,尤其是在编写库/框架/游戏引擎时

现在谈谈你的问题。

让我们撇开你的方法的任何实际好处,先解决它

实际上,OpenGL实现使用了一些非常相似的东西。命令由驱动程序线程异步执行。我建议您阅读它们的实现,以获得关于如何改进设计的一些想法

您需要做的是在发布渲染命令时以某种方式“捕获”状态。最简单的事情-将
核心渲染器->状态
复制到闭包中,并使用此副本进行渲染。但是,如果
state
足够大,成本可能会很高

另一种解决方案(OpenGL就是这样做的)是将
状态中的每一个更改也作为一个命令,所以

CORE_RENDERER->state.ModelMatrix = (*it).matrix;
CORE_RENDERER->state.ActiveSubmesh = (*it).submesh;
转化为

Matrix matrix = (*it).matrix;
Submesh submesh = (*it).submesh;

THREAD_POOL->service.post([&,matrix,submesh]{
    CORE_RENDERER->state.ModelMatrix = matrix;
    CORE_RENDERER->state.ActiveSubmesh = submesh;
});

但是,请注意,现在您不能简单地从主线程读取
CORE\u RENDERER->state.ModelMatrix
,因为它正在另一个线程中更改。您必须首先确保命令队列是空的。

使用std::future,您可以检查future是否准备好,并转到下一个迭代,而不是sleep。非常感谢:)我正在研究这个问题。但考虑到未来的一场突袭可能会消磨一点时间,这难道不会导致更慢的表演吗?我的想法是主线程继续准备命令,辅助线程渲染它们。当主线程完成并且辅助线程仍然渲染时,主线程可以执行其他操作,例如更新OpenAL音频流。我可能需要一个更好的解决方案,但我会试一试。非常感谢!!!:)希望您了解诸如
lock、mutex、critical section、atomic operation等therm,也不要忘记对线程之间受影响的变量使用
volatile
(有些编译器会忽略它们,有些编译器在没有它们的情况下根本无法工作,例如,我的AT32UC3芯片的GCC会为每个线程/ISR的每个全局变量进行本地复制,如果没有指定为volatile,我花了很多时间才发现……)。如果你没有足够的经验,我不会从无锁编程开始谢谢你的回答!我不确定我100%理解了你,但这样做也会对硬件造成危害?这是非常严重的!事实上,我是在3天前第一次涉足多线程领域的。正如你所说的,捕获状态,这就是我正在尝试做的。renderer使用基于材质/技术/过程的渲染,类似于OGRE。由于一个过程用于多个渲染器,我认为最好的方法可能是“捕获”有关所使用的过程、渲染的几何体和所有建模矩阵的信息。类似于复制渲染状态,但信息较少:)@Puso,硬件的问题是,但它仍然会以一种非常奇怪的方式给你带来麻烦,比如,升级到新的编译器、移植到新的平台或在你以外的计算机上运行时停止工作。这实际上是我对多线程最关心的问题之一!我担心,如果没有任何已知的原因,它将无法在所有机器上工作。所以,如果我想让它起作用,我需要小心。回到问题上来,实现这一点的最佳方法是存储有关过程的信息并将其用于渲染?有什么想法或文章吗
CORE_RENDERER->state.ModelMatrix = (*it).matrix;
CORE_RENDERER->state.ActiveSubmesh = (*it).submesh;
Matrix matrix = (*it).matrix;
Submesh submesh = (*it).submesh;

THREAD_POOL->service.post([&,matrix,submesh]{
    CORE_RENDERER->state.ModelMatrix = matrix;
    CORE_RENDERER->state.ActiveSubmesh = submesh;
});