mirror of
https://github.com/Ryujinx/Opentk.git
synced 2024-12-26 05:55:27 +00:00
Major update! Updated timing routines. Added documentation. Added VSync property.
This commit is contained in:
parent
f647ae14d8
commit
f0e5cbb8aa
|
@ -47,9 +47,8 @@ namespace OpenTK
|
||||||
double target_update_period, target_render_period, target_render_period_doubled;
|
double target_update_period, target_render_period, target_render_period_doubled;
|
||||||
// TODO: Implement these:
|
// TODO: Implement these:
|
||||||
double update_time, render_time, event_time;
|
double update_time, render_time, event_time;
|
||||||
|
//bool allow_sleep = true; // If true, GameWindow will call Timer.Sleep() if there is enough time.
|
||||||
int width, height;
|
int width, height;
|
||||||
|
|
||||||
VSyncMode vsync;
|
VSyncMode vsync;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -208,8 +207,8 @@ namespace OpenTK
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Fullscreen
|
public bool Fullscreen
|
||||||
{
|
{
|
||||||
get { return glWindow.Fullscreen; }
|
get { return false;/* return glWindow.Fullscreen; */ }
|
||||||
set { glWindow.Fullscreen = value; }
|
set { /* glWindow.Fullscreen = value; */}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -414,16 +413,16 @@ namespace OpenTK
|
||||||
#region void Run()
|
#region void Run()
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enters the game loop of GameWindow, updating and rendering at the maximum possible frequency.
|
/// Enters the game loop of the GameWindow updating and rendering at the maximum possible frequency.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <see cref="public virtual void Run(float update_frequency, float render_frequency)"/>
|
/// <see cref="public virtual void Run(double update_frequency, double render_frequency)"/>
|
||||||
public void Run()
|
public void Run()
|
||||||
{
|
{
|
||||||
Run(0.0, 0.0);
|
Run(0.0, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Runs the default game loop on GameWindow at the specified update frequency, maintaining the
|
/// Enters the game loop of the GameWindow updating the specified update frequency, while maintaining the
|
||||||
/// maximum possible render frequency.
|
/// maximum possible render frequency.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <see cref="public virtual void Run(double updateFrequency, double renderFrequency)"/>
|
/// <see cref="public virtual void Run(double updateFrequency, double renderFrequency)"/>
|
||||||
|
@ -432,194 +431,11 @@ namespace OpenTK
|
||||||
Run(updateFrequency, 0.0);
|
Run(updateFrequency, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if false
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Runs the default game loop on GameWindow at the specified update and render frequency.
|
/// Enters the game loop of the GameWindow updating and rendering at the specified frequency.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="updateFrequency">If greater than zero, indicates how many times UpdateFrame will be called per second. If less than or equal to zero, UpdateFrame is raised at maximum possible frequency.</param>
|
/// <param name="updates_per_second">The frequency of UpdateFrame events.</param>
|
||||||
/// <param name="renderFrequency">If greater than zero, indicates how many times RenderFrame will be called per second. If less than or equal to zero, RenderFrame is raised at maximum possible frequency.</param>
|
/// <param name="frames_per_second">The frequency of RenderFrame events.</param>
|
||||||
/// <remarks>
|
|
||||||
/// <para>
|
|
||||||
/// A default game loop consists of three parts: Event processing, frame updating and a frame rendering.
|
|
||||||
/// This function will try to maintain the requested updateFrequency at all costs, dropping the renderFrequency if
|
|
||||||
/// there is not enough CPU time.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// It is recommended that you specify a target for update- and renderFrequency.
|
|
||||||
/// Doing so, will yield unused CPU time to other processes, dropping power consumption
|
|
||||||
/// and maximizing batter life. If either frequency is left unspecified, the GameWindow
|
|
||||||
/// will consume all available CPU time (only useful for benchmarks and stress tests).
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// Override this function if you want to change the behaviour of the
|
|
||||||
/// default game loop. If you override this function, you must place
|
|
||||||
/// a call to the ProcessEvents function, to ensure window will respond
|
|
||||||
/// to Operating System events.
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
public virtual void Run(double updateFrequency, double renderFrequency)
|
|
||||||
{
|
|
||||||
// Setup timer
|
|
||||||
Stopwatch watch = new Stopwatch();
|
|
||||||
UpdateFrameEventArgs updateArgs = new UpdateFrameEventArgs();
|
|
||||||
RenderFrameEventArgs renderArgs = new RenderFrameEventArgs();
|
|
||||||
|
|
||||||
// Setup update and render rates. If updateFrequency or renderFrequency <= 0.0, use full throttle for that frequency.
|
|
||||||
double next_update = 0.0, next_render = 0.0;
|
|
||||||
double start_time;
|
|
||||||
|
|
||||||
double update_watch = 0.0, render_watch = 0.0;
|
|
||||||
int num_updates = 1;
|
|
||||||
double t0 = 0.0, t1 = 0.0, t2 = 0.0, t3 = 0.0;
|
|
||||||
|
|
||||||
if (updateFrequency > 0.0)
|
|
||||||
{
|
|
||||||
next_update = updateTimeTarget = 1.0 / updateFrequency;
|
|
||||||
}
|
|
||||||
if (renderFrequency > 0.0)
|
|
||||||
{
|
|
||||||
next_render = renderTimeTarget = 1.0 / renderFrequency;
|
|
||||||
}
|
|
||||||
renderTargetDoubled = renderTimeTarget * 2.0;
|
|
||||||
|
|
||||||
this.OnLoad(EventArgs.Empty);
|
|
||||||
|
|
||||||
// Enter main loop:
|
|
||||||
// (1) Update total frame time (capped at 0.1 sec)
|
|
||||||
// (2) Process events and update event_time
|
|
||||||
// (3) Raise UpdateFrame event(s) and update update_time.
|
|
||||||
// If there is enough CPU time, update and render events will be 1 on 1.
|
|
||||||
// If there is not enough time, render events will be dropped in order to match the requested updateFrequency.
|
|
||||||
// If the requested updateFrequency can't be matched, processing will slow down.
|
|
||||||
// (4) Raise RenderFrame event and update render_time.
|
|
||||||
// (5) If there is any CPU time left, and we are not running full-throttle, Sleep() to lower CPU usage.
|
|
||||||
Debug.Print("Entering main loop.");
|
|
||||||
while (this.Exists && !IsExiting)
|
|
||||||
{
|
|
||||||
watch.Reset();
|
|
||||||
watch.Start();
|
|
||||||
|
|
||||||
//frameTime = watch.Elapsed.TotalSeconds;
|
|
||||||
/*
|
|
||||||
// Adaptive VSync control:
|
|
||||||
bool disable_vsync = VSync == VSyncMode.Adaptive && Context.VSync && renderTime > renderTargetDoubled;
|
|
||||||
bool enable_vsync = VSync == VSyncMode.Adaptive && !Context.VSync && renderTime <= renderTargetDoubled;
|
|
||||||
if (disable_vsync)
|
|
||||||
{
|
|
||||||
//Debug.Print("Disabled vsync");
|
|
||||||
Title = "Off";
|
|
||||||
Context.VSync = false;
|
|
||||||
}
|
|
||||||
else if (enable_vsync)
|
|
||||||
{
|
|
||||||
//Debug.Print("Enabled vsync");
|
|
||||||
Title = "On";
|
|
||||||
Context.VSync = true;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
t0 = watch.Elapsed.TotalSeconds;
|
|
||||||
// Process events and update eventTime
|
|
||||||
eventTime = t2 + t3 + t0; // t2 and t3 come from the previous run through the loop.
|
|
||||||
this.ProcessEvents();
|
|
||||||
|
|
||||||
if (!IsExiting)
|
|
||||||
{
|
|
||||||
// --- UpdateFrame ---
|
|
||||||
// Raise the necessary amount of UpdateFrame events to keep
|
|
||||||
// the UpdateFrame rate constant. If the user didn't set an
|
|
||||||
// UpdateFrame rate, raise only one event.
|
|
||||||
|
|
||||||
t1 = watch.Elapsed.TotalSeconds - t0;
|
|
||||||
|
|
||||||
start_time = t3 + t0 + t1; // t3 come from the previous run through the loop.
|
|
||||||
update_watch += start_time;
|
|
||||||
if (num_updates > 0)
|
|
||||||
{
|
|
||||||
updateTime = update_watch / (double)num_updates;
|
|
||||||
num_updates = 0;
|
|
||||||
update_watch = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
next_update -= start_time;
|
|
||||||
updateArgs.Time = update_watch;
|
|
||||||
if (next_update <= 0.0)
|
|
||||||
{
|
|
||||||
//updateArgs.Time += watch.Elapsed.TotalSeconds;
|
|
||||||
double prev_update = watch.Elapsed.TotalSeconds;
|
|
||||||
this.OnUpdateFrameInternal(updateArgs);
|
|
||||||
updateArgs.Time = watch.Elapsed.TotalSeconds - prev_update;
|
|
||||||
|
|
||||||
++num_updates;
|
|
||||||
|
|
||||||
// Schedule next update
|
|
||||||
//if (updateTimeTarget != 0.0)
|
|
||||||
{
|
|
||||||
next_update += updateTimeTarget;
|
|
||||||
next_update -= (watch.Elapsed.TotalSeconds - start_time);
|
|
||||||
}
|
|
||||||
//else
|
|
||||||
// break; // User didn't request a fixed UpdateFrame rate.
|
|
||||||
}
|
|
||||||
// --------------------
|
|
||||||
t2 = watch.Elapsed.TotalSeconds - t1;
|
|
||||||
// --- Render Frame ---
|
|
||||||
// Raise RenderFrame event and update render_time.
|
|
||||||
|
|
||||||
start_time = t0 + t1 + t2;
|
|
||||||
render_watch += start_time;
|
|
||||||
next_render -= start_time;
|
|
||||||
if (next_render <= 0.0)
|
|
||||||
{
|
|
||||||
// Update framerate counters
|
|
||||||
renderTime = renderArgs.Time = render_watch;
|
|
||||||
render_watch = 0.0;
|
|
||||||
|
|
||||||
this.OnRenderFrameInternal(renderArgs);
|
|
||||||
|
|
||||||
next_render += renderTimeTarget;
|
|
||||||
next_render -= (watch.Elapsed.TotalSeconds - start_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------
|
|
||||||
|
|
||||||
// If there is any CPU time left, and we are not running full-throttle, Sleep() to lower CPU usage.
|
|
||||||
/*
|
|
||||||
if (renderTime < renderTimeTarget && updateTime < updateTimeTarget)
|
|
||||||
{
|
|
||||||
int sleep_time = (int)System.Math.Truncate(1000.0 * System.Math.Min(renderTimeTarget - renderTime - eventTime,
|
|
||||||
updateTimeTarget - updateTime - eventTime));
|
|
||||||
if (sleep_time < 0)
|
|
||||||
sleep_time = 0;
|
|
||||||
Thread.Sleep(sleep_time);
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
loop_time_clock = watch.Elapsed.TotalSeconds;
|
|
||||||
if (loop_time_clock > 0.05)
|
|
||||||
loop_time_clock = 0.05;
|
|
||||||
render_time_clock += loop_time_clock;
|
|
||||||
update_time_clock += loop_time_clock;
|
|
||||||
*/
|
|
||||||
//if (loop_time_clock > 0.1)
|
|
||||||
// loop_time_clock = 0.1;
|
|
||||||
|
|
||||||
t3 = watch.Elapsed.TotalSeconds - t2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OnUnloadInternal(EventArgs.Empty);
|
|
||||||
|
|
||||||
if (this.Exists)
|
|
||||||
{
|
|
||||||
glWindow.DestroyWindow();
|
|
||||||
while (this.Exists)
|
|
||||||
{
|
|
||||||
this.ProcessEvents();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
public void Run(double updates_per_second, double frames_per_second)
|
public void Run(double updates_per_second, double frames_per_second)
|
||||||
{
|
{
|
||||||
if (updates_per_second < 0.0 || updates_per_second > 200.0)
|
if (updates_per_second < 0.0 || updates_per_second > 200.0)
|
||||||
|
@ -636,24 +452,38 @@ namespace OpenTK
|
||||||
UpdateFrameEventArgs update_args = new UpdateFrameEventArgs();
|
UpdateFrameEventArgs update_args = new UpdateFrameEventArgs();
|
||||||
RenderFrameEventArgs render_args = new RenderFrameEventArgs();
|
RenderFrameEventArgs render_args = new RenderFrameEventArgs();
|
||||||
|
|
||||||
GC.Collect(2);
|
double sleep_granularity; // In seconds.
|
||||||
GC.WaitForPendingFinalizers();
|
|
||||||
GC.Collect(2);
|
//GC.Collect(2);
|
||||||
|
//GC.WaitForPendingFinalizers();
|
||||||
|
//GC.Collect(2);
|
||||||
|
|
||||||
|
// Find the minimum granularity of the Thread.Sleep() function.
|
||||||
|
// TODO: Disabled - see comment on Thread.Sleep() problems below.
|
||||||
|
//update_watch.Start();
|
||||||
|
//const int test_times = 5;
|
||||||
|
//for (int i = test_times; --i > 0; )
|
||||||
|
// Thread.Sleep(1);
|
||||||
|
//update_watch.Stop();
|
||||||
|
//sleep_granularity = System.Math.Round(1000.0 * update_watch.Elapsed.TotalSeconds / test_times, MidpointRounding.AwayFromZero) / 1000.0;
|
||||||
|
//update_watch.Reset(); // We don't want to affect the first UpdateFrame!
|
||||||
|
|
||||||
OnLoadInternal(EventArgs.Empty);
|
OnLoadInternal(EventArgs.Empty);
|
||||||
|
|
||||||
|
Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;
|
||||||
|
|
||||||
while (!isExiting)
|
while (!isExiting)
|
||||||
{
|
{
|
||||||
// Events
|
// Process events
|
||||||
ProcessEvents();
|
ProcessEvents();
|
||||||
|
|
||||||
if (isExiting)
|
if (isExiting)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Updates
|
// Raise UpdateFrame events
|
||||||
time = update_watch.Elapsed.TotalSeconds;
|
time = update_watch.Elapsed.TotalSeconds;
|
||||||
if (time > 0.1)
|
if (time > 1.0)
|
||||||
time = 0.1;
|
time = 1.0;
|
||||||
while (next_update - time <= 0.0)
|
while (next_update - time <= 0.0)
|
||||||
{
|
{
|
||||||
next_update = next_update - time + TargetUpdatePeriod;
|
next_update = next_update - time + TargetUpdatePeriod;
|
||||||
|
@ -673,6 +503,11 @@ namespace OpenTK
|
||||||
time = update_watch.Elapsed.TotalSeconds;
|
time = update_watch.Elapsed.TotalSeconds;
|
||||||
next_update -= time;
|
next_update -= time;
|
||||||
update_time_counter += time;
|
update_time_counter += time;
|
||||||
|
|
||||||
|
// Allow up to 10 frames to be dropped.
|
||||||
|
// Prevent the application from hanging with very high update frequencies.
|
||||||
|
if (num_updates >= 10)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (num_updates > 0)
|
if (num_updates > 0)
|
||||||
{
|
{
|
||||||
|
@ -681,13 +516,13 @@ namespace OpenTK
|
||||||
update_time_counter = 0.0;
|
update_time_counter = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frame
|
// Raise RenderFrame events
|
||||||
if (isExiting)
|
if (isExiting)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
time = render_watch.Elapsed.TotalSeconds;
|
time = render_watch.Elapsed.TotalSeconds;
|
||||||
if (time > 0.1)
|
if (time > 1.0)
|
||||||
time = 0.1;
|
time = 1.0;
|
||||||
if (next_render - time <= 0.0)
|
if (next_render - time <= 0.0)
|
||||||
{
|
{
|
||||||
next_render = next_render - time + TargetRenderPeriod;
|
next_render = next_render - time + TargetRenderPeriod;
|
||||||
|
@ -698,8 +533,18 @@ namespace OpenTK
|
||||||
render_args.ScaleFactor = RenderPeriod / UpdatePeriod;
|
render_args.ScaleFactor = RenderPeriod / UpdatePeriod;
|
||||||
OnRenderFrameInternal(render_args);
|
OnRenderFrameInternal(render_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Yield CPU time, if the Thread.Sleep() granularity allows it.
|
||||||
|
// TODO: Disabled because it does not work reliably enough on all systems.
|
||||||
|
// Enable vsync as a workaround.
|
||||||
|
//if (AllowSleep && next_render > sleep_granularity && next_update > sleep_granularity)
|
||||||
|
//{
|
||||||
|
// Thread.Sleep((int)(1000.0 * System.Math.Min(next_render - sleep_granularity, next_update - sleep_granularity)));
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Thread.CurrentThread.Priority = ThreadPriority.Normal;
|
||||||
|
|
||||||
OnUnloadInternal(EventArgs.Empty);
|
OnUnloadInternal(EventArgs.Empty);
|
||||||
|
|
||||||
if (this.Exists)
|
if (this.Exists)
|
||||||
|
@ -981,6 +826,17 @@ namespace OpenTK
|
||||||
|
|
||||||
#region --- GameWindow Timing ---
|
#region --- GameWindow Timing ---
|
||||||
|
|
||||||
|
// TODO: Disabled because it is not reliable enough. Use vsync as a workaround.
|
||||||
|
|
||||||
|
//#region public bool AllowSleep
|
||||||
|
|
||||||
|
//public bool AllowSleep
|
||||||
|
//{
|
||||||
|
// get { return allow_sleep; }
|
||||||
|
// set { allow_sleep = value; }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
#region public double TargetRenderPeriod
|
#region public double TargetRenderPeriod
|
||||||
|
|
||||||
|
@ -1032,7 +888,7 @@ namespace OpenTK
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value <= 1.0)
|
if (value < 1.0)
|
||||||
{
|
{
|
||||||
TargetRenderPeriod = 0.0;
|
TargetRenderPeriod = 0.0;
|
||||||
}
|
}
|
||||||
|
@ -1096,7 +952,7 @@ namespace OpenTK
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value <= 1.0)
|
if (value < 1.0)
|
||||||
{
|
{
|
||||||
TargetUpdatePeriod = 0.0;
|
TargetUpdatePeriod = 0.0;
|
||||||
}
|
}
|
||||||
|
@ -1354,7 +1210,7 @@ namespace OpenTK
|
||||||
#region public enum VSyncMode
|
#region public enum VSyncMode
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates the available VSync modes.
|
/// Enumerates the available VSync modes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum VSyncMode
|
public enum VSyncMode
|
||||||
{
|
{
|
||||||
|
@ -1369,7 +1225,7 @@ namespace OpenTK
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// VSync enabled, but automatically disabled if framerate falls below a specified limit.
|
/// VSync enabled, but automatically disabled if framerate falls below a specified limit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Adaptive
|
Adaptive,
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@ -1426,4 +1282,12 @@ namespace OpenTK
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region --- GameWindow Exceptions ---
|
||||||
|
|
||||||
|
public class GameWindowExitException : ApplicationException
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue