Исследование расчета оптимального времени сна в игровом цикле

При программировании анимации и небольших игр я осознал невероятную важность Thread.sleep (n ); Я полагаюсь на этот метод, чтобы сообщить операционной системе, когда моему приложению не потребуется ЦП, и с его помощью моя программа продвигается с предсказуемой скоростью.

Моя проблема в том, что JRE использует разные методы реализация этой функциональности в разных операционных системах. В ОС на основе UNIX (или под влиянием): например, Ubuntu и OS X, базовая реализация JRE использует хорошо работающую и точную систему для распределения процессорного времени между различными приложениями, что делает мою 2D-игру плавной и без задержек. Однако в Windows 7 и более старых системах Microsoft распределение времени ЦП, похоже, работает по-другому, и вы обычно получаете обратно свое время ЦП после заданного количества сна, которое варьируется примерно на 1-2 мс от целевого сна. Однако время от времени вы получаете дополнительные 10-20 мс времени сна. Это приводит к тому, что моя игра задерживается каждые несколько секунд, когда это происходит. Я заметил, что эта проблема существует в большинстве Java-игр, которые я пробовал в Windows, и Minecraft является ярким примером.

Теперь я искал в Интернете, чтобы найти решение этой проблемы. Я видел много людей, использующих только Thread.yield (); вместо Thread.sleep (n); , который работает безупречно за счет полной загрузки используемого в настоящее время ядра процессора, независимо от того, сколько процессора действительно требуется вашей игре. Это не идеально для игры на ноутбуках или рабочих станциях с высоким энергопотреблением, и это ненужный компромисс для систем Mac и Linux.

Оглядываясь дальше, я обнаружил обычно используемый метод исправления несоответствий времени сна, называемый «вращением». sleep », в котором вы заказываете засыпание только на 1 мс за раз и проверяете согласованность с помощью метода System.nanoTime (); , который очень точен даже в системах Microsoft. Это помогает при нормальной непоследовательности сна 1-2 мс, но не поможет против случайных всплесков непостоянства сна + 10-20 мс, поскольку это часто приводит к тому, что на один цикл моего цикла тратится больше времени, чем на все вместе.

После множества поисков я нашел эту загадочную статью Энди Малакова, которая очень помогла мне улучшить мой цикл: http://andy-malakov.blogspot.com/2010/06/alternative-to-threadsleep. html

На основе его статьи я написал следующий метод сна:

// Variables for calculating optimal sleep time. In nanoseconds (1s = 10^-9ms).
private long timeBefore = 0L;
private long timeSleepEnd, timeLeft;

// The estimated game update rate.
private double timeUpdateRate;

// The time one game loop cycle should take in order to reach the max FPS.
private long timeLoop;

private void sleep() throws InterruptedException {

    // Skip first game loop cycle.
    if (timeBefore != 0L) {

        // Calculate optimal game loop sleep time.
        timeLeft = timeLoop - (System.nanoTime() - timeBefore);

        // If all necessary calculations took LESS time than given by the sleepTimeBuffer. Max update rate was reached.
        if (timeLeft > 0 && isUpdateRateLimited) {

            // Determine when to stop sleeping.
            timeSleepEnd = System.nanoTime() + timeLeft;

            // Sleep, yield or keep the thread busy until there is not time left to sleep.
            do {
                if (timeLeft > SLEEP_PRECISION) {
                    Thread.sleep(1); // Sleep for approximately 1 millisecond.
                }
                else if (timeLeft > SPIN_YIELD_PRECISION) {
                    Thread.yield(); // Yield the thread.
                }
                if (Thread.interrupted()) {
                    throw new InterruptedException();
            }
                timeLeft = timeSleepEnd - System.nanoTime();
            }
            while (timeLeft > 0);
        }
        // Save the calculated update rate.
        timeUpdateRate =  1000000000D / (double) (System.nanoTime() - timeBefore);
    }
    // Starting point for time measurement.
    timeBefore = System.nanoTime();
}

SLEEP_PRECISION Я обычно устанавливаю около 2 мс, а SPIN_YIELD_PRECISION - примерно до 10 000 нс для лучшей производительности на моей машине с Windows 7.

После тонны тяжелой работы это лучшее, что я могу придумать. Итак, поскольку я все еще забочусь о повышении точности этого метода сна, и я все еще не удовлетворен производительностью, я хотел бы обратиться ко всем вам, хакерам и аниматорам java-игр, за предложениями по лучшему решению для Платформа Windows. Могу ли я использовать в Windows способ для конкретной платформы, чтобы улучшить его? Я не Я не забочусь о том, чтобы в моих приложениях был небольшой код, специфичный для платформы, при условии, что большая часть кода не зависит от ОС.

Я также хотел бы знать, есть ли кто-нибудь, кто знает о Microsoft и Oracle, разрабатывающих лучшую реализацию метод Thread.sleep (n); , или каковы планы Oracle на будущее по улучшению своей среды как основы приложений, требующих высокой точности синхронизации, таких как музыкальное программное обеспечение и игры?

Спасибо всем за читая мой длинный вопрос / статью. Я надеюсь, что некоторые люди сочтут мое исследование полезным!

19
задан Emanuel 11 March 2011 в 15:11
поделиться