16.66 ms frames times. How do you get perfect 60 frames per second when sleep () only extends for whole milliseconds?

I love making small games in C ++ and Java, but I was always bothered by something and never figured out how to fix it.

Sleep in C ++ and Java only works in milliseconds. This means that if you do

startTime=clock();
-------Execute everything in that frame-----
endTime=clock(); 
sleep(x-(endTime-startTime));

      

if x is 16 you get 62.5 fps if x is 17 you get 58.8 fps

None of these are perfect 60 to match the monitor's refresh rate.

But I noticed that some games, such as Warframe, would say "16.66ms frame time", which means that their engine was able to somehow sleep with more sleep.

So how do you get that perfect 60?

Preferably in C ++, like what I'm working with right now, but a Java answer would be helpful too

+1


source to share


7 replies


In any case, relying only on sleep

is wrong: you need to schedule at a fixed rate and point you to nanosecond precision. Use



final ExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(task, 
    TimeUnit.SECONDS.toNanos(1/60), 
    TimeUnit.SECONDS.toNanos(1/60), 
    TimeUnit.NANOSECONDS);

      

+3


source


You have a simplified understanding of how time works. You are not doing accurate time in user space software. Timing accuracy is done elsewhere.

The software composes the next frame. When it is done, it passes the frame to the driver, which displays the frame at the right time and at the right speed. Typically, the driver can run the software to display the next frame.



The software just doesn't work, sleeping itself for a while. When he needs to wait for something, he really expects what he needs to wait for.

+5


source


I don't know if this is the correct way to create a render loop, but there are more ways to sleep than sleep

. Check it out nanosleep

.

0


source


You shouldn't have a timer that sleeps for a certain amount of time and triggers the update and render method on a different thread

Instead, create the time it takes to render your material in the last iteration. you give the time (or a multiplier for your time based calculation) to the update method.

eg.

final int stepsize = 10;

public void update(final Scenegraph scene, final float tpf) {
        // move all elements in the scene to +x with a speed of 10 distance-units per second 
        scene.getRootNode().move(tpf * stepsize, 0 ,0);
}

      

Now you don't have to rush to manipulate the scenario graph and display a new view within a specific timer interval.

In games displaying fps values, they use something like this:

private boolean runGame;
private double tpf;

public void stop() {
    runGame = false;
}

public void run() {
    rungame = true;

    // repeat until the game should stop
    while(runGame) {
        // calculate the time per frame of the last frame
        tpf = (System.nanoTime() / 1000 / 1000) - tpf;
        // calculate the frames per second to show it in a debug window or else
        double fps = (tpf > 0 ? (1.0 / tpf) : 0);

        // handle all actions on the scenegraph
        update(scenegraph, tpf);
        // render the new view for each camera (e.g. each players view)
        renderViews(cameras, scenegraph);
    }
}

      

if you print fps on debugconsole you won't be able to read it because it changes too fast :) keep fps at the last minute and show average.

0


source


For Windows, most game physics engines that have a thread that run at a fixed frequency use something similar to this code. The delay is based on the original reading of the high frequency clock to prevent drift over an extended period of time. This example is compatible with Windows XP, where Sleep (1) can take up to 2ms (for later versions of Windows, Sleep (1) can take up to 1ms). dwLateStep is incremented if any delay is too long (additional diagnostic help).

/* code for a thread to run at fixed frequency */
typedef unsigned long long UI64;        /* unsigned 64 bit int */
#define FREQ    400                     /* frequency */
static DWORD    dwLateStep;             /* late step count */

LARGE_INTEGER liPerfTemp;               /* used for query */
UI64 uFreq = FREQ;                      /* process frequency */
UI64 uOrig;                             /* original tick */
UI64 uWait;                             /* tick rate / freq */
UI64 uRem = 0;                          /* tick rate % freq */
UI64 uPrev;                             /* previous tick based on original tick */
UI64 uDelta;                            /* current tick - previous */
UI64 u2ms;                              /* 2ms of ticks */
UI64 i;

    /* ... */ /* wait for some event to start thread */
    timeBeginPeriod(1);                 /* set period to 1ms */
    Sleep(128);                         /* wait for it to stabilize */

    u2ms = ((UI64)(liPerfFreq.QuadPart)+499) / ((UI64)500);

    QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp);
    uOrig = uPrev = liPerfTemp.QuadPart;

    for(i = 0; i < (uFreq*30); i++){
        /* update uWait and uRem based on uRem */
        uWait = ((UI64)(liPerfFreq.QuadPart) + uRem) / uFreq;
        uRem  = ((UI64)(liPerfFreq.QuadPart) + uRem) % uFreq;
        /* wait for uWait ticks */
        while(1){
            QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp);
            uDelta = (UI64)(liPerfTemp.QuadPart - uPrev);
            if(uDelta >= uWait)
                break;
            if((uWait - uDelta) > u2ms)
                Sleep(1);
        }
        if(uDelta >= (uWait*2))
            dwLateStep += 1;
        uPrev += uWait;
        /* fixed frequency code goes here */
        /*  along with some type of break when done */
    }

    timeEndPeriod(1);                   /* restore period */

      

0


source


The best approach is to avoid sleep altogether and use some API that calls your code right after the screen is refreshed. This depends a lot on the drawing libraries you are using, but you are looking for something similar to the well-known requestAnimationFrame () function in JavaScript.

If you don't have access to an API like this, then like the other answers, in Java you can use System.nanoTime()

and Thread.sleep(long millis, int nanos)

.

-1


source


You have no guarantee how long you will sleep. In fact, your program can be paused for any amount of time between sleep, and will do so thousands of times per second. there are tools to measure jitter on your system based on this timing being incorrect. http://www.azulsystems.com/jHiccup

Instead, you should evaluate in nanoTime when the process should have woken up further and slept for the difference, this will avoid a cumulative error.

long intervalNanos = ....
long nextNanos = System.nanoTime() + intervalNanos;
while(running) {
    doSomething();
    long now = System.nanoTime();
    if (now > nextNanos) {
       // we are not keeping up.
       nextNanos = now + intervalNanos;
       continue;
    }
    long delay = (nextNanos - now) / 1000000;
    Thread.sleep(delay);
    nextNanos += intervalNanos;
}

      

-1


source







All Articles