【发布时间】:2016-10-27 16:28:11
【问题描述】:
好的,我正在尝试将我的游戏引擎切换到多线程。我已经完成了关于如何使其在多线程应用程序中使用 OpenGL 的研究。我对渲染或切换上下文没有任何问题。让我的一段代码解释问题:):
for (it = (*mIt).Renderables.begin(); it != (*mIt).Renderables.end(); it++)
{
//Set State Modeling matrix
CORE_RENDERER->state.ModelMatrix = (*it).matrix;
CORE_RENDERER->state.ActiveSubmesh = (*it).submesh;
//Internal Model Uniforms
THREAD_POOL->service.post([&]
{
for (unsigned int i = 0; i < CORE_RENDERER->state.ActiveShaderProgram->InternalModelUniforms.size(); i++)
{
CORE_RENDERER->state.ActiveShaderProgram->InternalModelUniforms[i]->Set( CORE_RENDERER->state.ModelMatrix);
}
CORE_RENDERER->state.ActiveSubmesh->_Render();
});
//Sleep(10);
}
我将快速解释代码中的哪些元素以使我的问题更清楚。 Renderables 是一个简单的 std::vector 元素,带有 _Render() 函数,可以完美运行。 CORE_RENDERER->state 是一个结构,它保存有关当前渲染状态的信息,例如当前材质属性以及当前子网格模型矩阵。所以 Matrix 和 Submesh 被存储到状态结构中(我知道这很慢,我可能会及时改变它:))下一段代码被发送到 THREAD_POOL->service 这实际上是 boost::asio::io_service 和只有一个线程,所以它就像一个渲染命令队列。这个想法是主线程提供有关要渲染什么以及进行截锥体剔除和其他测试的信息,而辅助线程则执行实际渲染。这工作正常,除了有一个小问题:
发送到线程池的代码开始执行,但在设置所有 InternalModelUniforms 和渲染子网格之前,执行 Renderables 的下一次迭代并且 ModelMatrix 和 ActiveSubmesh 都被更改。该程序不会崩溃,但两个信息都发生了变化,并且一些网格被渲染了一些矩阵是正确的,而另一些则不是,这会导致图像闪烁。对象出现在帧上,下一帧它们就消失了。只有当我启用 Sleep(10) 函数以确保代码在下一次迭代之前执行时,问题才会得到解决,这显然会扼杀获得性能的想法。对此最好的解决方案是什么?如何将命令发送到每个具有唯一内置数据的队列?也许我需要实现自己的命令队列和没有 io_service 的单线程?
我会继续我的研究,因为我知道有办法。这个想法是正确的,因为我得到了性能提升,因为渲染线程没有处理单个 if/else 语句:) 任何帮助或提示都会真正有帮助!
谢谢!
更新:
经过几个晚上的努力,我创建了一个非常原始的主线程和辅助线程之间的通信模型。我创建了一个代表辅助线程执行的基本命令的类:
class _ThreadCommand
{
public:
_ThreadCommand() {}
~_ThreadCommand() {}
virtual void _Execute() = 0;
virtual _ThreadCommand* Clone() = 0;
};
这些属于此类的子命令具有 _Execute() 函数来执行任何需要执行的操作。渲染时的主线程填充这些命令的 boost::ptr_vector 而辅助线程继续检查是否有任何命令要处理。当找到命令时,它会将整个向量复制到 _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();
}
我知道这是一种非常糟糕的方法。性能相当慢,我很确定这是因为所有的复制和互斥锁。但至少渲染器可以工作。您可以了解我想要实现的目标,但正如我所说,我对多线程非常陌生。这种情况的最佳解决方案是什么?我应该使用 asio::io_service 返回 ThreadPool 系统吗?如何向 AuxThread 提供命令以及必须发送到渲染器以正确方式执行任务的所有值?
【问题讨论】:
-
使用std::future,你可以检查future是否准备好并进入下一个迭代而不是sleep
-
好的,非常感谢 :) 我现在正在研究这个。但是考虑到未来的方法可能会浪费一些时间,这不会导致性能变慢吗?我的想法是主线程继续准备命令,辅助线程渲染它们。当主线程完成并且辅助线程仍然呈现时,主线程可以做其他事情,例如更新 OpenAL 音频流。我可能需要一个更好的解决方案,但我会试一试。非常感谢你!!! :)
-
希望你知道像
lock,mutex,critical section,atomic operation这样的函数也不要忘记使用volatile来表示线程之间受影响的变量(有些编译器会忽略它们,有些编译器在没有它们的情况下根本无法工作,例如 GCC对于我的 AT32UC3 芯片,如果未指定为 volatile,则每个线程/ISR 将为每个全局变量进行本地复制,这让我花了很多时间来发现它……)。当您没有足够的经验时,我不会从无锁编程开始
标签: c++ multithreading opengl boost-asio