Как реализовать точное ограничение кадра игрового цикла в C#?

Я пытаюсь реализовать ограничитель кадров игрового цикла в игровом движке, написанном на C#. мне сказали Thread.Sleep требуется несколько миллисекунд, чтобы даже выполнить Thread.Sleep(0), и будет немного пропустить целевой миллисекунд. Таким образом, он не является достаточно точным для того, что мне нужно, потому что целевая частота кадров может потребовать от двигателя сидеть и ждать 1 миллисекунду. Я не уверен, что я должен использовать Thread.SpinWait в этом, также как могут быть ситуации, когда движку нужно ждать 10 миллисекунд, и я не знаю, является ли в такой ситуации плохой идеей использовать SpinWait.

Вот фрагмент кода, иллюстрирующий то, чего я пытаюсь достичь:

public void Run()
{
    var targetDelta = TimeSpan.FromSeconds(0.016);
    Running = true;
    while (Running)
    {
        // Calculate time since previous frame start here ("delta")

        // Simplified, but essentially this is what's going on
        Input.HandleEvents();
        Scene.Update(delta);
        Renderer.Render(Scene);

        // Perform waiting to limit frame rate here
    }
}

1 ответ

Решение

Теперь посмотрите, где вам нужно сделать тесты самостоятельно, чтобы увидеть, что на самом деле происходит, а не просто поверить на слово.

Запуск 1000000 проходит через цикл для измерения времени, затрачиваемого Thread.Sleep(0) Я получаю в среднем 2,44 микросекунды за исполнение, с минимумом 1,70 микросекунды и максимум 6,49 миллисекундами. Хотя эта последняя цифра звучит как проблема, на самом деле это не так. Из миллиона пропусков только 59 из них (это 0,0059%) составляли 1 мс или более.

Точка вызова Thread.Sleep(0) чтобы освободить остальную часть вашего временного интервала обратно в систему для использования другими процессами. Он не позволяет вашему процессу загружать все доступное процессорное время, когда в этом нет необходимости.

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

Вы можете использовать гибридное решение, где вы звоните Thread.Sleep(0) когда время ожидания превышает пару миллисекунд, выполняется цикл занятости, пока он ожидает Stopwatch или аналогично отметке за последние несколько микросекунд. Зависит от того, насколько ваш код чувствителен ко времени.

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