Многопоточный рендеринг / обновление игровых циклов (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 других потоков в некоторых "больших" операциях

Другие вопросы по тегам