I've implemented the QueryPerformance counter stuff in place of the stopwatch code:
/// <summary>/// Object to handle timing operations./// </summary>public class Timer : NamedObject { #region Variables. private long _startTime; // Starting timer time. private bool _useHighResolution; // Flag to indicate that we're using a high resolution timer. private long _frequency; // Frequency of the timer. private double _milliseconds; // Milliseconds. private long _ticks; // Clock ticks. private long _lastTicks; // Last tick count. private long _startingTick; // Starting clock tick. private long _adjustStartTick; // Adjustment starting tick. #endregion #region Properties. /// <summary> /// Property to get the number of milliseconds elapsed since the timer was started. /// </summary> public double Milliseconds { get { GetTime(); return _milliseconds; } } /// <summary> /// Property to get the number of microseconds elapsed since the timer was started. /// </summary> public double Microseconds { get { return Milliseconds * 1000; } } /// <summary> /// Property to return the number of ticks since the timer was started. /// </summary> public long Ticks { get { GetTime(); return _ticks; } } /// <summary> /// Property to set or return whether to use the high resolution timer or not. /// </summary> public bool HighResolutionTimer { get { return _useHighResolution; } set { _useHighResolution = value; Reset(); } } #endregion #region Methods. /// <summary> /// Function to start the timing process. /// </summary> private void GetTime() { long currentTime = 0; // Current time. if (_useHighResolution) { Win32API.QueryPerformanceCounter(ref currentTime); _ticks = currentTime - _startTime; _milliseconds = ((double)_ticks/(double)_frequency) * 1000; // The QueryPerformance counter drifts on some motherboards. // See Microsoft KB: Q274323 for the explanation. ulong check = (ulong)Environment.TickCount - (ulong)_adjustStartTick; // Get tick count. long msDrift = (long)(check - _milliseconds); // Get any millisecond drifts. // If we're out by 100 milliseconds either way, then adjust the time. if ((msDrift < -100) || (msDrift > 100)) { // Get adjustment amount. long adjust = Math.Min(msDrift * _frequency / 1000, _ticks - _lastTicks); _startingTick += adjust; _ticks -= adjust; // Recalcuate the timing. _milliseconds = ((double)_ticks / (double)_frequency) * 1000; } _lastTicks = _ticks; } else { currentTime = Win32API.timeGetTime(); _milliseconds = currentTime - _startTime; _ticks = Environment.TickCount - _startingTick; } } /// <summary> /// Function to reset the timer. /// </summary> public void Reset() { _milliseconds = 0; _ticks = 0; _lastTicks = 0; _adjustStartTick = _startingTick = Environment.TickCount; // Determine which type of timer to use. if (_useHighResolution) { // If this fails, then drop to the lower precision timer. if (!Win32API.QueryPerformanceCounter(ref _startTime)) { _startTime = Win32API.timeGetTime(); _useHighResolution = false; } } else { _startTime = Win32API.timeGetTime(); _useHighResolution = false; } } /// <summary> /// Function to convert the desired frames per second to milliseconds. /// </summary> /// <param name="FPS">Desired frames per second.</param> /// <returns>Frames per second in milliseconds.</returns> public static double FPSToMilliseconds(double FPS) { if (FPS > 0) return (1000/(FPS + 3)); else return 0; } /// <summary> /// Function to convert the desired frames per second to microseconds. /// </summary> /// <param name="FPS">Desired frames per second.</param> /// <returns>Frames per second in microseconds.</returns> public static double FPSToMicroseconds(double FPS) { if (FPS > 0) return (1000000/(FPS + 3)); else return 0; } #endregion #region Constructors/Destructors. /// <summary> /// Constructor. /// </summary> /// <param name="timerName">Name of this timer.</param> internal Timer(string timerName) : base(timerName) { _startTime = 0; _frequency = 0; _useHighResolution = false; _adjustStartTick = _startingTick = Environment.TickCount; _lastTicks = 0; if (Win32API.QueryPerformanceFrequency(ref _frequency)) { if (Win32API.QueryPerformanceCounter(ref _startTime)) _useHighResolution = true; } // Fall back on the low resolution timer if no high resolution one can be found. if (!_useHighResolution) _startTime = Win32API.timeGetTime(); } #endregion}
Unfortunately, I'm still getting the same speed decrease in full screen. I tried bringing down my sprite count from 6000 by 1000 sprites at a time. The framerate went back to 60 FPS (vsync limited) after I got to 3000 sprites (and 3500 sprites as well). However in windowed mode it went from ~50 FPS (with a delta time of ~19 ms) to 80 FPS (with a delta time of ~12 ms). It's almost like the fullscreen adjusted to nearest multiple of a vsync. I tested this theory of course by doubling everything and bringing the FPS down to 30-40 FPS in windowed mode, and it went to 20 FPS in full screen. This is very confusing. I don't know if it's the timers now, I tried this with both the QueryPerformanceCounter and timeGetTime functions and both returned roughly the same intervals. Could it be something driver related? Monitor related (I have an LCD, 8ms refresh)? Or am I high on mushrooms and just hallucinating?
Again, if I turn vSync off, I get roughly the same speed (minus about 4-8 FPS)
(see edit below). You'd think if this were vSync limited that if I got 50 FPS in windowed mode, I should get 50 FPS in fullscreen, but no more than 60 FPS if I'm @ 60 Hz, which I am, but on an LCD it's not supposed to matter (unless I'm not understanding the presentation intervals?)
I dread trying this on my work machine, cause last time I had timing issues like this my work machine couldn't smoothly interpolate the movement of the objects on the screen, thus giving a jerky performance.
Edit:After running it again, I found that it's running a little faster in full screen than windowed, WITHOUT vsync. Still having the same issues with vsync being enabled though. :(
[Edited by - Tape_Worm on July 22, 2006 9:38:54 PM]