Обработка цикла обновления с использованием С++ Chrono?

Я определенно немного запутался с новой библиотекой C++ chrono.

Здесь у меня есть цикл обновления. Он выполняет две операции:

engine.Update()
engine.Render()

Это длительные операции, и трудно сказать, насколько они длительны.

Таким образом, мы измеряем, сколько времени они заняли, затем делаем некоторые расчеты и определяем лучший способ постепенного вызова обновления перед вызовом рендеринга.

Для этого я использую функциональность C++11 Chrono. Я выбрал его, потому что это звучало как хорошая сделка: более точная, более зависимая от платформы. Я обнаружил, что у меня больше проблем, чем сейчас.

Ниже приведен мой код, а также моя основная проблема. Очень нужна любая помощь по проблеме или правильному способу выполнения моих операций!

Я отметил свои вопросы в комментариях непосредственно рядом с обсуждаемыми строками, которые я повторю ниже.

Заголовочный файл:

class MyClass
{
private:
    typedef std::chrono::high_resolution_clock Clock;
    Clock::time_point mLastEndTime;
    milliseconds mDeltaTime;
}

Упрощенный цикл обновления

// time it took last loop
milliseconds frameTime;
// The highest we'll let that time go. 60 fps = 1/60, and in milliseconds, * 1000
const milliseconds kMaxDeltatime((int)((1.0f / 60.0f) * 1000.0f)); // It's hard to tell, but this seems to come out to some tiny number, not what I expected!
while (true)
{
    // How long did the last update take?
    frameTime = duration_cast<milliseconds>(Clock::now() - mLastEndTime); // Is this the best way to get the delta time, with a duration cast?
    // Mark the last update time
    mLastEndTime = Clock::now();

    // Don't update everything with the frameTime, keep it below our maximum fps.
    while (frameTime.count() > 0) // Is this the best way to measure greater than 0 milliseconds?
    {
        // Determine the minimum time. Our frametime, or the max delta time?
        mDeltaTime = min(frameTime, kMaxDeltatime);

        // Update our engine.
        engine->Update((long)mDeltaTime.count()); // From here, it's so much easier to deal with code in longs. Is this the best way to shove a long through my code?

        // Subtract the delta time out of the total update time 
        frameTime -= mDeltaTime;
    }
    engine->Render();
}

Главный вопрос: мой mDeltaTime всегда выходит крошечным. По сути, он застрял в почти бесконечном цикле. Это потому, что kMaxDeltatime очень мал, но если я нацелен на 60 кадров в секунду, разве я не рассчитал правильные миллисекунды?

Вот все вопросы, перечисленные выше:

const milliseconds kMaxDeltatime((int)((1.0f / 60.0f) * 1000.0f)); // It's hard to tell, but this seems to come out to some tiny number, not what I expected!

frameTime = duration_cast<milliseconds>(Clock::now() - mLastEndTime); // Is this the best way to get the delta time, with a duration cast?

while (frameTime.count() > 0) // Is this the best way to measure greater than 0 milliseconds?

engine->Update((long)mDeltaTime.count()); // From here, it's so much easier to deal with code in longs. Is this the best way to shove a long through my code?

Извините за путаницу, ребята. Я чувствую себя идиотом с этой хронографической библиотекой. Большинство справочных сайтов, справочных материалов или даже самого прямого кода очень сложно читать и понимать, к чему я его применяю. Указания на то, как я должен искать решения или код, очень приветствуются!

РЕДАКТИРОВАТЬ: Иоахим указал, что std::min/max отлично работает в миллисекундах! Обновлен код, чтобы отразить изменения.


person MintyAnt    schedule 09.02.2013    source источник
comment
Что касается функций min/max для времен, то для них легко создать простые перегруженные функции.   -  person Some programmer dude    schedule 09.02.2013
comment
@JoachimPileborg Не могли бы вы уточнить?   -  person MintyAnt    schedule 09.02.2013
comment
Почему, черт возьми, у вас есть занятая петля? Голосование — это зло. Если вам нужна задержка, почему бы не заблокировать по таймеру?   -  person paulsm4    schedule 09.02.2013
comment
На самом деле, вы пробовали использовать std::min или < a href="http://en.cppreference.com/w/cpp/algorithm/max" rel="nofollow noreferrer">std::max? Они созданы по шаблону, поэтому должны работать, если есть подходящая перегрузка оператора <. Вы можете использовать варианты, которые позволяют вам передать свою собственную функцию сравнения.   -  person Some programmer dude    schedule 09.02.2013
comment
@ paulsm4 Если предположить, что вы имеете в виду кусок while (true), это не реально. Он запускает логическое значение, которое может быть установлено в false. Как только цикл завершен, он запускает некоторые функции завершения работы.   -  person MintyAnt    schedule 09.02.2013
comment
У вас есть законная причина, по которой вы хотите работать со скоростью 60 кадров в секунду? Обычно вы хотите бежать так быстро, как можете (что в вашем случае звучит так, будто это будет 500 кадров в секунду или что-то в этом роде).   -  person David    schedule 09.02.2013
comment
@Dave Чтобы процессор не работал на 100% постоянно. В настоящее время, после нескольких минут рисования простого круга, мои вентиляторы набирают полную скорость.   -  person MintyAnt    schedule 09.02.2013
comment
@JoachimPileborg Только что попробовал, работает отлично. Спасибо за совет!   -  person MintyAnt    schedule 09.02.2013
comment
@MintyAnt Вы пытаетесь вращать блокировку, что на самом деле не исправит это. Вы должны делать всю свою работу во фрейме, а затем std::this_thread::sleep_until конец вашего 1/60-го временного интервала истек   -  person David    schedule 09.02.2013
comment
@ Дэйв Прошу прощения, я ошибся в своем ответе. Мое время редактирования не позволило мне ответить. Мне нужно сократить DeltaTime, потому что, если я засуну большое значение deltatime через Update(), игра, в частности Physics, будет вести себя очень странно. Вещи выйдут из-под контроля, если это будет слишком высоко. Таким образом, я отбрасываю его по какому-то числу и запускаю обновление столько раз, пока не достигну нуля. Подходящим числом мне показалось 60 кадров в секунду. Это имеет больше смысла?   -  person MintyAnt    schedule 09.02.2013
comment
@MintyAnt О, это хорошая причина и общий подход к общей проблеме. Но это не имеет ничего общего с тем, что процессор не работает на 100%...   -  person David    schedule 09.02.2013
comment
Ваш цикл обновления выглядит правильно. Можете ли вы убедиться, что ваш вызов engine-›Update в худшем случае выполняется меньше, чем mDeltaTime? Потому что, если вы обновляете свое состояние в течение X миллисекунд и тратите на это › X миллисекунд, ваша симуляция выйдет из-под контроля.   -  person    schedule 10.02.2013
comment
@KapilKapre Если я правильно тебя понял, думаю, все в порядке. В любом случае, он еще не использует дельта-время. Он просто застрял в петле. Я почти уверен, что это потому, что значение, которое я дал kMaxDeltaTime, очень маленькое. Я чувствую, что ctor для миллисекунд преобразует мое время во что-то меньшее, но... вся документация по нему очень запутанна, ее трудно понять.   -  person MintyAnt    schedule 10.02.2013
comment
Нет, kMaxDeltatime имеет правильное значение (16). Я не уверен, почему ваша программа застревает в цикле. Вот некоторый код, который я собрал на основе исходного примера, и он работает так, как ожидалось: ideone.com/rYe3sL   -  person    schedule 10.02.2013
comment
@KapilKapre Ага! Для mLastEndTime никогда не устанавливалось начальное значение в моем коде! Должно быть, это были мусорные данные, какое-то огромное число, которое никогда не позволяло циклу завершиться. Большое спасибо, что нашли время, чтобы что-то написать, это очень помогло!   -  person MintyAnt    schedule 11.02.2013


Ответы (1)


При использовании std::chrono вы должны избегать, насколько это возможно, приведения продолжительности или преобразования длительности в необработанные интегральные значения. Вместо этого вы должны придерживаться естественной длительности и пользоваться безопасностью типов, которую обеспечивают типы длительности.

Ниже приведен ряд конкретных рекомендаций. Для каждой рекомендации я буду цитировать строки вашего исходного кода, а затем покажу, как я бы переписал эти строки.


const milliseconds kMaxDeltatime((int)((1.0f / 60.0f) * 1000.0f)); // It's hard to tell, but this seems to come out to some tiny number, not what I expected!

Нет причин делать такого рода вычисления с константами ручного преобразования. Вместо этого вы можете сделать:

typedef duration<long,std::ratio<1,60>> sixtieths_of_a_sec;
constexpr auto kMaxDeltatime = sixtieths_of_a_sec{1};

frameTime = duration_cast<milliseconds>(Clock::now() - mLastEndTime); // Is this the best way to get the delta time, with a duration cast?

Вы можете просто сохранить значение в его родном типе:

auto newEndTime = Clock::now();
auto frameTime = newEndTime - mLastEndTime;
mLastEndTime = newEndTime;

while (frameTime.count() > 0) // Is this the best way to measure greater than 0 milliseconds?

Вместо этого используйте:

while (frameTime > milliseconds(0))

engine->Update((long)mDeltaTime.count()); // From here, it's so much easier to deal with code in longs. Is this the best way to shove a long through my code?

Лучше всего писать код, который использует типы chrono::duration, а не вообще использовать универсальные интегральные типы, но если вам действительно нужно получить универсальный интегральный тип (например, если вы должны передать long стороннему API), то вы может сделать что-то вроде:

auto mDeltaTime = ... // some duration type

long milliseconds = std::chrono::duration_cast<std::duration<long,std::milli>>(mDeltaTime).count();
third_party_api(milliseconds);

Or:

auto milliseconds = mDeltaTime/milliseconds(1);

И чтобы получить дельту, вы должны сделать что-то вроде:

typedef std::common_type<decltype(frameTime),decltype(kMaxDeltatime)>::type common_duration;
auto mDeltaTime = std::min<common_duration>(frameTime, kMaxDeltatime); 
person bames53    schedule 05.04.2013
comment
Я знаю, что это уже давно прошло, но вызов std::common_type не компилируется для меня с clang++ в Linux. В нем говорится, что не существует члена с именем типа, что приводит к тому, что вызов min также не компилируется. - person Joe; 31.12.2014
comment
Я думаю, это было связано со списком инициализаторов, который испортил компилятор. - person Joe; 31.12.2014