|
From: <ka...@us...> - 2012-03-24 00:05:05
|
Revision: 3755
http://java-game-lib.svn.sourceforge.net/java-game-lib/?rev=3755&view=rev
Author: kappa1
Date: 2012-03-24 00:04:52 +0000 (Sat, 24 Mar 2012)
Log Message:
-----------
Replace Display.sync(int fps) with an even better implementation, special thanks to Riven.
Modified Paths:
--------------
trunk/LWJGL/src/java/org/lwjgl/opengl/Display.java
Added Paths:
-----------
trunk/LWJGL/src/java/org/lwjgl/opengl/Sync.java
Modified: trunk/LWJGL/src/java/org/lwjgl/opengl/Display.java
===================================================================
--- trunk/LWJGL/src/java/org/lwjgl/opengl/Display.java 2012-03-21 19:34:51 UTC (rev 3754)
+++ trunk/LWJGL/src/java/org/lwjgl/opengl/Display.java 2012-03-24 00:04:52 UTC (rev 3755)
@@ -79,18 +79,6 @@
/** The current display mode, if created */
private static DisplayMode current_mode;
- /** time at last sync() */
- private static long lastTime;
-
- /** Whether the sync() method has been initiated */
- private static boolean syncInitiated;
-
- /** whether to disable adaptive yield time in sync() method */
- private static boolean adaptiveTimeDisabled;
-
- /** adaptive time to yield instead of sleeping in sync()*/
- private static long adaptiveYieldTime;
-
/** X coordinate of the window */
private static int x = -1;
@@ -411,98 +399,16 @@
}
/**
- * An accurate sync method that adapts automatically
- * to the system it runs on to provide reliable results.
+ * An accurate sync method that will attempt to run an application loop
+ * at a constant frame rate.
*
- * @param fps The desired frame rate, in frames per second
+ * It should be called once every frame.
+ *
+ * @param fps - the desired frame rate, in frames per second
*/
public static void sync(int fps) {
- if (fps <= 0) return;
- if (!syncInitiated) initiateSyncTimer();
-
- long sleepTime = 1000000000 / fps; // nanoseconds to sleep this frame
- // adaptiveYieldTime + remainder micro & nano seconds if smaller than sleepTime
- long yieldTime = adaptiveTimeDisabled ? 0 : Math.min(sleepTime, adaptiveYieldTime + sleepTime % (1000*1000));
- long overSleep = 0; // time the sync goes over by
-
- try {
- while (true) {
- long t = getTime() - lastTime;
-
- 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) {}
-
- lastTime = getTime() - Math.min(overSleep, sleepTime);
-
- if (!adaptiveTimeDisabled) {
- // auto tune the amount of time to yield
- if (overSleep > adaptiveYieldTime) {
- // increase by 200 microseconds (1/5 a ms)
- adaptiveYieldTime = Math.min(adaptiveYieldTime + 200*1000, sleepTime);
- }
- else if (overSleep < adaptiveYieldTime - 2*1000*1000) {
- // fast decrease by 50 microseconds for large under sleeps
- adaptiveYieldTime = Math.max(adaptiveYieldTime - 50*1000, 0);
- }
- else if (overSleep < adaptiveYieldTime - 200*1000) {
- // slower but finer decrease by 2 microseconds
- adaptiveYieldTime = Math.max(adaptiveYieldTime - 2*1000, 0);
- }
- }
+ Sync.sync(fps);
}
-
- /**
- * Get System Nano Time
- * @return will return the current time in nano's
- */
- private static long getTime() {
- return (Sys.getTime() * 1000000000) / Sys.getTimerResolution();
- }
-
- /**
- * Initialise the sync(fps) method.
- */
- private static void initiateSyncTimer() {
- syncInitiated = true;
- lastTime = getTime();
-
- String osName = System.getProperty("os.name");
-
- if (osName.startsWith("Mac") || osName.startsWith("Darwin")) {
- // disabled on mac as it uses too much cpu, besides
- // Thread.sleep is pretty accurate on mac by default
- adaptiveTimeDisabled = true;
- return;
- }
-
- if (osName.startsWith("Win")) {
- // 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.
- 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 */
public static String getTitle() {
Added: trunk/LWJGL/src/java/org/lwjgl/opengl/Sync.java
===================================================================
--- trunk/LWJGL/src/java/org/lwjgl/opengl/Sync.java (rev 0)
+++ trunk/LWJGL/src/java/org/lwjgl/opengl/Sync.java 2012-03-24 00:04:52 UTC (rev 3755)
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2002-2012 LWJGL Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'LWJGL' nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.lwjgl.opengl;
+
+import org.lwjgl.Sys;
+
+/**
+* A highly accurate sync method that continually adapts to the system
+* it runs on to provide reliable results.
+*
+* @author Riven
+* @author kappaOne
+*/
+class Sync {
+
+ /** number of nano seconds in a second */
+ private static final long NANOS_IN_SECOND = 1000L * 1000L * 1000L;
+
+ /** The time to sleep/yield until the next frame */
+ private static long nextFrame = 0;
+
+ /** whether the initialisation code has run */
+ private static boolean initialised = false;
+
+ /** stored results of how long sleep/yields took to calculate averages */
+ private static RunningAvg sleepDurations = new RunningAvg(10);
+ private static RunningAvg yieldDurations = new RunningAvg(10);
+
+
+ /**
+ * An accurate sync method that will attempt to run an application loop
+ * at a constant frame rate.
+ *
+ * It should be called once every frame.
+ *
+ * @param fps - the desired frame rate, in frames per second
+ */
+ public static void sync(int fps) {
+ if (fps <= 0) return;
+ if (!initialised) initialise();
+
+ try {
+ // sleep until the average sleep time is greater than the time remaining till nextFrame
+ for (long t0 = getTime(), t1; (nextFrame - t0) > sleepDurations.avg(); t0 = t1) {
+ Thread.sleep(1);
+ sleepDurations.add((t1 = getTime()) - t0); // update average sleep time
+ }
+
+ // slowly dampen sleep average if too high to avoid over yielding
+ sleepDurations.dampenForLowResTicker();
+
+ // yield until the average yield time is greater than the time remaining till nextFrame
+ for (long t0 = getTime(), t1; (nextFrame - t0) > yieldDurations.avg(); t0 = t1) {
+ Thread.yield();
+ yieldDurations.add((t1 = getTime()) - t0); // update average yield time
+ }
+ } catch (InterruptedException e) {
+
+ }
+
+ // schedule next frame, drop frame(s) if already too late for next frame
+ nextFrame = Math.max(nextFrame + NANOS_IN_SECOND / fps, getTime());
+ }
+
+ /**
+ * This method will initialise the sync method by setting initial
+ * values for sleepDurations/yieldDurations and nextFrame variables.
+ *
+ * If running windows on windows it will start the sleep timer fix.
+ */
+ private static void initialise() {
+ initialised = true;
+
+ sleepDurations.init(1000 * 1000);
+ yieldDurations.init((int) (-(getTime() - getTime()) * 1.333));
+
+ nextFrame = getTime();
+
+ String osName = System.getProperty("os.name");
+
+ if (osName.startsWith("Win")) {
+ // 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.
+ Thread timerAccuracyThread = new Thread(new Runnable() {
+ public void run() {
+ try {
+ Thread.sleep(Long.MAX_VALUE);
+ } catch (Exception e) {}
+ }
+ });
+
+ timerAccuracyThread.setDaemon(true);
+ timerAccuracyThread.start();
+ }
+ }
+
+ /**
+ * Get the system time in nano seconds
+ *
+ * @return will return the current time in nano's
+ */
+ private static long getTime() {
+ return (Sys.getTime() * NANOS_IN_SECOND) / Sys.getTimerResolution();
+ }
+
+ private static class RunningAvg {
+ private final long[] slots;
+ private int offset;
+
+ private static final long DAMPEN_THRESHOLD = 10 * 1000L * 1000L; // 10ms
+ private static final float DAMPEN_FACTOR = 0.9f; // don't change: 0.9f is exactly right!
+
+ public RunningAvg(int slotCount) {
+ this.slots = new long[slotCount];
+ this.offset = 0;
+ }
+
+ public void init(long value) {
+ while (this.offset < this.slots.length) {
+ this.slots[this.offset++] = value;
+ }
+ }
+
+ public void add(long value) {
+ this.slots[this.offset++ % this.slots.length] = value;
+ this.offset %= this.slots.length;
+ }
+
+ public long avg() {
+ long sum = 0;
+ for (int i = 0; i < this.slots.length; i++) {
+ sum += this.slots[i];
+ }
+ return sum / this.slots.length;
+ }
+
+ public void dampenForLowResTicker() {
+ if (this.avg() > DAMPEN_THRESHOLD) {
+ for (int i = 0; i < this.slots.length; i++) {
+ this.slots[i] *= DAMPEN_FACTOR;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|