Многопоточный рендеринг / обновление игровых циклов (boost-asio)
Итак, у меня есть однопоточный класс игрового движка, который имеет отдельные функции для ввода, обновления и рендеринга, и я только начал учиться использовать замечательную библиотеку Boost (компоненты asio и thread). И я думал о том, чтобы разделить свои функции обновления и рендеринга на отдельные потоки (и, возможно, отделить функции ввода и обновления также друг от друга). Конечно, иногда эти функции могут обращаться к одним и тем же местам в памяти, поэтому я решил использовать функциональные возможности boost / thread, чтобы предотвратить их одновременное выполнение.
Прямо сейчас мой основной игровой цикл выглядит так:
void SDLEngine::Start()
{
int update_time=0;
quit=false;
while(!quit)
{
update_time=SDL_GetTicks();
DoInput();//get user input and alter data based on it
DoUpdate();//update game data once per loop
if(!minimized)
DoRender();//render graphics to screen
update_time=SDL_GetTicks()-update_time;
SDL_Delay(max(0,target_time-update_time));//insert delay to run at desired FPS
}
}
Если бы я использовал отдельные потоки, это выглядело бы примерно так:
void SDLEngine::Start()
{
boost::asio::io_service io;
boost::asio::strand strand_;
boost::asio::deadline_timer input(io,boost::posix_time::milliseconds(16));
boost::asio::deadline_timer update(io,boost::posix_time::milliseconds(16));
boost::asio::deadline_timer render(io,boost::posix_time::milliseconds(16));
//
input.async_wait(strand_.wrap(boost::bind(&SDLEngine::DoInput,this)));
update.async_wait(strand_.wrap(boost::bind(&SDLEngine::DoUpdate,this)));
render.async_wait(strand_.wrap(boost::bind(&SDLEngine::DoRender,this)));
//
io.run();
}
Итак, как вы можете видеть, до того, как цикл пошел: Input->Update->Render->Delay->Repeat
Каждый был запущен один за другим. Если бы я использовал многопоточность, мне пришлось бы использовать нити, чтобы обновления и рендеринг не запускались одновременно. Так стоит ли здесь использовать многопоточность? Они по-прежнему будут работать по одному в разных ядрах. У меня практически нет опыта работы с многопоточными приложениями, поэтому любая помощь приветствуется.
Да, и еще одна вещь: я использую OpenGL для рендеринга. Повлияет ли многопоточность, как это, на рендеринг OpenGL?
1 ответ
Вы используете одну и ту же цепочку для всех обработчиков, поэтому многопоточность вообще отсутствует. Кроме того, ваш deadline_timer находится в области действия Start()
и вы никуда не пропустите. В этом случае вы не сможете перезапустить его из обработчика (обратите внимание, что это не "интервальный" таймер, а просто "таймер одного вызова").
Я не вижу смысла в этом "обновлении", так как вы не получаете никакой выгоды от asio и / или потоков в этом примере.
Эти методы (input, update, render) слишком велики, и они делают много вещей, их нельзя вызвать без блокировки. Трудно сказать точно, потому что я не знаю, что такое игра и как она работает, но я бы предпочел сделать следующие шаги:
- Попробуйте обновить сетевой ввод / вывод, чтобы он стал полностью асинхронным
- Попробуйте использовать все ядра процессора
О том, что вы пробовали: я думаю, что это возможно, если вы будете искать в своем коде действия, которые действительно могут выполняться параллельно прямо сейчас. Например: если вы рассчитываете для каждого NPC что-то, что не зависит от других персонажей, вы можете io_service.post()
каждый использовать все потоки, которые работают io_service.run()
в данный момент. Таким образом, ваша программа остается однопоточной, но вы можете использовать, скажем, 7 других потоков в некоторых "больших" операциях