Implement a much more accurate Display.sync() method that auto adapts to the systems timer resolution and load.
This commit is contained in:
parent
57ec5db726
commit
4293976506
|
@ -79,8 +79,14 @@ public final class Display {
|
||||||
/** The current display mode, if created */
|
/** The current display mode, if created */
|
||||||
private static DisplayMode current_mode;
|
private static DisplayMode current_mode;
|
||||||
|
|
||||||
/** Timer for sync() */
|
/** time at last sync() */
|
||||||
private static long timeThen;
|
private static long lastTime;
|
||||||
|
|
||||||
|
/** Whether the sync() method has been initiated */
|
||||||
|
private static boolean syncInitiated;
|
||||||
|
|
||||||
|
/** adaptive time to yield instead of sleeping in sync()*/
|
||||||
|
private static long adaptiveYieldTime = 1000*1000;
|
||||||
|
|
||||||
/** X coordinate of the window */
|
/** X coordinate of the window */
|
||||||
private static int x = -1;
|
private static int x = -1;
|
||||||
|
@ -401,41 +407,84 @@ public final class Display {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long timeLate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Best sync method that works reliably.
|
* An accurate sync method that adapts automatically
|
||||||
*
|
* to the system it runs on to provide reliable results.
|
||||||
|
*
|
||||||
* @param fps The desired frame rate, in frames per second
|
* @param fps The desired frame rate, in frames per second
|
||||||
*/
|
*/
|
||||||
public static void sync(int fps) {
|
public static void sync(int fps) {
|
||||||
long timeNow;
|
if (fps <= 0) return;
|
||||||
long gapTo;
|
if (!syncInitiated) initiateSyncTimer();
|
||||||
long savedTimeLate;
|
|
||||||
synchronized ( GlobalLock.lock ) {
|
long sleepTime = 1000000000 / fps; // nanoseconds to sleep this frame
|
||||||
gapTo = Sys.getTimerResolution() / fps + timeThen;
|
// adaptiveYieldTime + remainder micro & nano seconds if smaller than sleepTime
|
||||||
timeNow = Sys.getTime();
|
long yieldTime = Math.min(sleepTime, adaptiveYieldTime + sleepTime % (1000*1000));
|
||||||
savedTimeLate = timeLate;
|
long overSleep = 0; // time the sync goes over by
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while ( gapTo > timeNow + savedTimeLate ) {
|
while (true) {
|
||||||
Thread.sleep(1);
|
long t = getTime() - lastTime;
|
||||||
timeNow = Sys.getTime();
|
|
||||||
|
if (t < sleepTime - yieldTime) {
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
else if (t < sleepTime) {
|
||||||
|
// burn the last few CPU cycles to ensure accuracy
|
||||||
|
Thread.yield();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
overSleep = t - sleepTime;
|
||||||
|
break; // exit while loop
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {}
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
|
lastTime = getTime() - Math.min(overSleep, sleepTime);
|
||||||
|
|
||||||
|
// auto tune the amount of time to yield
|
||||||
|
if (overSleep > adaptiveYieldTime) {
|
||||||
|
// increase by 500 microseconds (half a ms)
|
||||||
|
adaptiveYieldTime = Math.min(adaptiveYieldTime + 500*1000, sleepTime);
|
||||||
}
|
}
|
||||||
|
else if (overSleep < adaptiveYieldTime - 1000*1000) {
|
||||||
synchronized ( GlobalLock.lock ) {
|
// decrease by 5 microseconds
|
||||||
if ( gapTo < timeNow )
|
adaptiveYieldTime = Math.max(adaptiveYieldTime - 5*1000, 1000*1000);
|
||||||
timeLate = timeNow - gapTo;
|
|
||||||
else
|
|
||||||
timeLate = 0;
|
|
||||||
|
|
||||||
timeThen = timeNow;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get System Nano Time
|
||||||
|
* @return will return the current time in nano's
|
||||||
|
*/
|
||||||
|
private static long getTime() {
|
||||||
|
return (Sys.getTime() * 1000000000) / Sys.getTimerResolution();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On windows the sleep functions can be highly inaccurate by
|
||||||
|
* over 10ms making in unusable. However it can be forced to
|
||||||
|
* be a bit more accurate by running a separate sleeping daemon
|
||||||
|
* thread.
|
||||||
|
*/
|
||||||
|
private static void initiateSyncTimer() {
|
||||||
|
syncInitiated = true;
|
||||||
|
|
||||||
|
if (!System.getProperty("os.name").startsWith("Win")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread timerAccuracyThread = new Thread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
Thread.sleep(Long.MAX_VALUE);
|
||||||
|
} catch (Exception e) {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
timerAccuracyThread.setDaemon(true);
|
||||||
|
timerAccuracyThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
/** @return the title of the window */
|
/** @return the title of the window */
|
||||||
public static String getTitle() {
|
public static String getTitle() {
|
||||||
|
|
Loading…
Reference in New Issue