diff --git a/src/java/com/threerings/media/FrameManager.java b/src/java/com/threerings/media/FrameManager.java index 94a30116..9cf8cff4 100644 --- a/src/java/com/threerings/media/FrameManager.java +++ b/src/java/com/threerings/media/FrameManager.java @@ -51,7 +51,7 @@ import com.samskivert.util.RunAnywhere; import com.samskivert.util.StringUtil; import com.threerings.media.timer.MediaTimer; -import com.threerings.media.timer.SystemMediaTimer; +import com.threerings.media.timer.MillisTimer; import com.threerings.media.util.TrailingAverage; import com.threerings.util.unsafe.Unsafe; @@ -127,28 +127,35 @@ public abstract class FrameManager } /** - * Creates a frame manager that will use a {@link SystemMediaTimer} to obtain timing - * information, which is available on every platform, but returns inaccurate time stamps on - * many platforms. + * Creates a frame manager that will try to use a high resolution timer for timing but will + * fall back to {@link MillisTimer}, which is available on every platform, but returns + * inaccurate time stamps on many platforms. * * @see #newInstance(ManagedRoot, MediaTimer) */ public static FrameManager newInstance (ManagedRoot root) { - // first try creating a PerfTimer which is the best if we're using JDK1.4.2 MediaTimer timer = null; - try { - timer = (MediaTimer)Class.forName(PERF_TIMER).newInstance(); - } catch (Throwable t) { - Log.info("Can't use PerfTimer (" + t + ") reverting to " + + for (String timerClass : PERF_TIMERS) { + try { + timer = (MediaTimer)Class.forName(timerClass).newInstance(); + break; + } catch (Throwable t) { + t.printStackTrace(System.err); + // try the next one + } + } + if (timer == null) { + Log.info("Can't use high performance timer, reverting to " + "System.currentTimeMillis() based timer."); - timer = new SystemMediaTimer(); + timer = new MillisTimer(); } return newInstance(root, timer); } /** - * Constructs a frame manager that will do its rendering to the supplied root. + * Constructs a frame manager that will do its rendering to the supplied root and use the + * supplied media timer for timing information. */ public static FrameManager newInstance (ManagedRoot root, MediaTimer timer) { @@ -751,5 +758,8 @@ public abstract class FrameManager "narya.media.fps_display", MediaPrefs.config, false); /** The name of the high-performance timer class we attempt to load. */ - protected static final String PERF_TIMER = "com.threerings.media.timer.PerfTimer"; + protected static final String[] PERF_TIMERS = { + "com.threerings.media.timer.NanoTimer", + "com.threerings.media.timer.PerfTimer", + }; } diff --git a/src/java/com/threerings/media/timer/SystemMediaTimer.java b/src/java/com/threerings/media/timer/MillisTimer.java similarity index 76% rename from src/java/com/threerings/media/timer/SystemMediaTimer.java rename to src/java/com/threerings/media/timer/MillisTimer.java index 2a908d41..6e5bca16 100644 --- a/src/java/com/threerings/media/timer/SystemMediaTimer.java +++ b/src/java/com/threerings/media/timer/MillisTimer.java @@ -22,20 +22,18 @@ package com.threerings.media.timer; /** - * Implements the {@link MediaTimer} interface using {@link - * System#currentTimeMillis} to obtain timing information. + * Implements the {@link MediaTimer} interface using {@link System#currentTimeMillis} to obtain + * timing information. * - *

Note: {@link System#currentTimeMillis} is notoriously - * inaccurate on different platforms. See + *

Note: {@link System#currentTimeMillis} is notoriously inaccurate on different + * platforms. See * bug report 4486109 for more information. * - *

Also note: clock drift on Windows XP is especially - * pronounced and is exacerbated by the fact that WinXP periodically - * resyncs the system clock with the hardware clock, causing discontinuous - * jumps in the progression of time (usually backwards in time). + *

Also note: clock drift on Windows XP is especially pronounced and is exacerbated by + * the fact that WinXP periodically resyncs the system clock with the hardware clock, causing + * discontinuous jumps in the progression of time (usually backwards in time). */ -public class SystemMediaTimer implements MediaTimer +public class MillisTimer implements MediaTimer { // documentation inherited from interface public void reset () diff --git a/src/java/com/threerings/media/timer/NanoTimer.java b/src/java/com/threerings/media/timer/NanoTimer.java new file mode 100644 index 00000000..dd35091a --- /dev/null +++ b/src/java/com/threerings/media/timer/NanoTimer.java @@ -0,0 +1,50 @@ +// +// $Id$ +// +// Nenya library - tools for developing networked games +// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved +// http://www.threerings.net/code/nenya/ +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.threerings.media.timer; + +/** + * Uses the {@link System#nanoTime} method introduced in 1.5 to try to obtain higher resolution + * timestamps than those available via {@link System#currentTimeMillis}. + */ +public class NanoTimer implements MediaTimer +{ + // documentation inherited from interface + public void reset () + { + _resetStamp = System.nanoTime(); + } + + // documentation inherited from interface + public long getElapsedMillis () + { + return (System.nanoTime() - _resetStamp) / 1000000L; + } + + // documentation inherited from interface + public long getElapsedMicros () + { + return (System.nanoTime() - _resetStamp) / 1000L; + } + + /** The time at which this timer was last reset. */ + protected long _resetStamp = System.nanoTime(); +} diff --git a/src/java/com/threerings/media/util/PerformanceMonitor.java b/src/java/com/threerings/media/util/PerformanceMonitor.java index a8c67346..fee962fb 100644 --- a/src/java/com/threerings/media/util/PerformanceMonitor.java +++ b/src/java/com/threerings/media/util/PerformanceMonitor.java @@ -25,42 +25,36 @@ import java.util.HashMap; import com.threerings.media.Log; import com.threerings.media.timer.MediaTimer; -import com.threerings.media.timer.SystemMediaTimer; +import com.threerings.media.timer.NanoTimer; /** - * Provides a simple mechanism for monitoring the number of times an - * action takes place within a certain time period. + * Provides a simple mechanism for monitoring the number of times an action takes place within a + * certain time period. * - *

The action being tracked should be registered with a suitable name - * via {@link #register}, and {@link #tick} should be called each time the - * action is performed. + *

The action being tracked should be registered with a suitable name via {@link #register}, + * and {@link #tick} should be called each time the action is performed. * - *

Whenever {@link #tick} is called and the checkpoint time interval - * has elapsed since the last checkpoint (if any), the observer will be - * notified to that effect by a call to {@link + *

Whenever {@link #tick} is called and the checkpoint time interval has elapsed since the last + * checkpoint (if any), the observer will be notified to that effect by a call to {@link * PerformanceObserver#checkpoint}. * - *

Note that this is not intended to be used as an - * industrial-strength profiling or performance monitoring tool. The - * checkpoint time interval granularity is in milliseconds, not - * microseconds, and the observer's checkpoint() method will - * never be called until/unless a subsequent call to {@link #tick} is made - * after the requested number of milliseconds have passed since the last - * checkpoint. + *

Note that this is not intended to be used as an industrial-strength profiling or + * performance monitoring tool. The checkpoint time interval granularity is in milliseconds, not + * microseconds, and the observer's checkpoint() method will never be called + * until/unless a subsequent call to {@link #tick} is made after the requested number of + * milliseconds have passed since the last checkpoint. */ public class PerformanceMonitor { /** - * Register a new action with an observer, the action name, and - * the milliseconds to wait between checkpointing the action's - * performance. + * Register a new action with an observer, the action name, and the milliseconds to wait + * between checkpointing the action's performance. * * @param obs the action observer. * @param name the action name. * @param delta the milliseconds between checkpoints. */ - public static void register (PerformanceObserver obs, String name, - long delta) + public static void register (PerformanceObserver obs, String name, long delta) { // get the observer's action hashtable HashMap actions = (HashMap)_observers.get(obs); @@ -152,7 +146,7 @@ public class PerformanceMonitor protected static HashMap _observers = new HashMap(); /** Used to obtain high resolution time stamps. */ - protected static MediaTimer _timer = new SystemMediaTimer(); + protected static MediaTimer _timer = new NanoTimer(); } /**