mirror of
https://github.com/Ryujinx/Opentk.git
synced 2025-07-03 18:38:24 +00:00
[OpenTK] More robust timing for UpdateFrame and RenderFrame
FrameEventArgs.Time should no longer drift from clock time measured outside GameWindow.
This commit is contained in:
parent
c5dcc8a93b
commit
3856fcd48e
|
@ -1,4 +1,4 @@
|
||||||
#region License
|
#region License
|
||||||
//
|
//
|
||||||
// The Open Toolkit Library License
|
// The Open Toolkit Library License
|
||||||
//
|
//
|
||||||
|
@ -75,7 +75,7 @@ namespace OpenTK
|
||||||
{
|
{
|
||||||
#region --- Fields ---
|
#region --- Fields ---
|
||||||
|
|
||||||
object exit_lock = new object();
|
readonly Stopwatch watch = new Stopwatch();
|
||||||
|
|
||||||
IGraphicsContext glContext;
|
IGraphicsContext glContext;
|
||||||
|
|
||||||
|
@ -83,6 +83,10 @@ namespace OpenTK
|
||||||
|
|
||||||
double update_period, render_period;
|
double update_period, render_period;
|
||||||
double target_update_period, target_render_period;
|
double target_update_period, target_render_period;
|
||||||
|
|
||||||
|
double update_timestamp; // timestamp of last UpdateFrame event
|
||||||
|
double render_timestamp; // timestamp of last RenderFrame event
|
||||||
|
|
||||||
// TODO: Implement these:
|
// TODO: Implement these:
|
||||||
double update_time, render_time;
|
double update_time, render_time;
|
||||||
VSyncMode vsync;
|
VSyncMode vsync;
|
||||||
|
@ -402,10 +406,6 @@ namespace OpenTK
|
||||||
//Move += DispatchUpdateAndRenderFrame;
|
//Move += DispatchUpdateAndRenderFrame;
|
||||||
//Resize += DispatchUpdateAndRenderFrame;
|
//Resize += DispatchUpdateAndRenderFrame;
|
||||||
|
|
||||||
Debug.Print("Calibrating Stopwatch to account for drift");
|
|
||||||
CalibrateStopwatch();
|
|
||||||
Debug.Print("Stopwatch overhead: {0}", stopwatch_overhead);
|
|
||||||
|
|
||||||
Debug.Print("Entering main loop.");
|
Debug.Print("Entering main loop.");
|
||||||
watch.Start();
|
watch.Start();
|
||||||
while (true)
|
while (true)
|
||||||
|
@ -431,65 +431,36 @@ namespace OpenTK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double stopwatch_overhead = 0;
|
|
||||||
void CalibrateStopwatch()
|
|
||||||
{
|
|
||||||
// Make sure everything is JITted
|
|
||||||
watch.Start();
|
|
||||||
stopwatch_overhead = watch.Elapsed.TotalSeconds;
|
|
||||||
watch.Stop();
|
|
||||||
watch.Reset();
|
|
||||||
// Measure stopwatch overhead
|
|
||||||
const int count = 10;
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
watch.Start();
|
|
||||||
double sample = watch.Elapsed.TotalSeconds;
|
|
||||||
if (sample < 0 || sample > 0.1)
|
|
||||||
{
|
|
||||||
// calculation failed, repeat
|
|
||||||
i--;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
stopwatch_overhead += sample;
|
|
||||||
watch.Stop();
|
|
||||||
watch.Reset();
|
|
||||||
}
|
|
||||||
stopwatch_overhead /= 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stopwatch watch = new Stopwatch();
|
|
||||||
double update_timestamp = 0;
|
|
||||||
double render_timestamp = 0;
|
|
||||||
double update_elapsed = 0;
|
|
||||||
double render_elapsed = 0;
|
|
||||||
void DispatchUpdateAndRenderFrame(object sender, EventArgs e)
|
void DispatchUpdateAndRenderFrame(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
const int max_frameskip = 10;
|
const int max_frameskip = 10;
|
||||||
int frameskip = 0;
|
int frameskip = 0;
|
||||||
double timestamp = 0;
|
double timestamp = watch.Elapsed.TotalSeconds;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
// Raise UpdateFrame events until we catch up with our target update rate.
|
// Raise UpdateFrame events until we catch up with our target update rate.
|
||||||
timestamp = watch.Elapsed.TotalSeconds;
|
double update_elapsed = MathHelper.Clamp(timestamp - update_timestamp, 0.0, 1.0);
|
||||||
update_elapsed = MathHelper.Clamp(timestamp - update_timestamp, 0.0, 1.0);
|
if (RaiseUpdateFrame(update_elapsed, ref next_update))
|
||||||
|
{
|
||||||
update_timestamp = timestamp;
|
update_timestamp = timestamp;
|
||||||
RaiseUpdateFrame(update_elapsed, ref next_update);
|
}
|
||||||
} while (next_update > 0 && ++frameskip < max_frameskip);
|
|
||||||
// Calculate statistics
|
|
||||||
//update_period = total_update_time / (double)num_updates;
|
|
||||||
|
|
||||||
timestamp = watch.Elapsed.TotalSeconds;
|
timestamp = watch.Elapsed.TotalSeconds;
|
||||||
render_elapsed = MathHelper.Clamp(timestamp - render_timestamp, 0.0, 1.0);
|
} while (next_update <= 0 && ++frameskip < max_frameskip);
|
||||||
|
|
||||||
|
double render_elapsed = MathHelper.Clamp(timestamp - render_timestamp, 0.0, 1.0);
|
||||||
|
if (RaiseRenderFrame(render_elapsed, ref next_render))
|
||||||
|
{
|
||||||
render_timestamp = timestamp;
|
render_timestamp = timestamp;
|
||||||
RaiseRenderFrame(render_elapsed, ref next_render);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RaiseUpdateFrame(double time, ref double next_update)
|
bool RaiseUpdateFrame(double time, ref double next_update)
|
||||||
{
|
{
|
||||||
double time_left = next_render - time;
|
if (time > 0)
|
||||||
if (time_left <= 0.0 && time > 0)
|
{
|
||||||
|
next_update -= time;
|
||||||
|
if (next_update <= 0.0)
|
||||||
{
|
{
|
||||||
update_args.Time = time;
|
update_args.Time = time;
|
||||||
OnUpdateFrameInternal(update_args);
|
OnUpdateFrameInternal(update_args);
|
||||||
|
@ -499,28 +470,36 @@ namespace OpenTK
|
||||||
// (e.g. when the update rate is too high, or the UpdateFrame processing
|
// (e.g. when the update rate is too high, or the UpdateFrame processing
|
||||||
// is too costly). This cap ensures we can catch up in a reasonable time
|
// is too costly). This cap ensures we can catch up in a reasonable time
|
||||||
// once the load becomes lighter.
|
// once the load becomes lighter.
|
||||||
next_update = time_left + TargetUpdatePeriod;
|
next_update += TargetUpdatePeriod;
|
||||||
next_update = Math.Max(next_update, -1.0);
|
next_update = Math.Max(next_update, -1.0);
|
||||||
|
update_period = Math.Max(next_update, 0.0);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void RaiseRenderFrame(double time, ref double next_render)
|
|
||||||
{
|
bool RaiseRenderFrame(double time, ref double next_render)
|
||||||
double time_left = next_render - time;
|
|
||||||
if (time_left <= 0.0 && time > 0)
|
|
||||||
{
|
{
|
||||||
if (time > 0)
|
if (time > 0)
|
||||||
{
|
{
|
||||||
render_period = render_args.Time = time;
|
next_render -= time;
|
||||||
|
if (next_render <= 0.0)
|
||||||
|
{
|
||||||
|
render_args.Time = time;
|
||||||
OnRenderFrameInternal(render_args);
|
OnRenderFrameInternal(render_args);
|
||||||
}
|
|
||||||
|
|
||||||
// Schedule next render event. The 1 second cap ensures
|
// Schedule next render event. The 1 second cap ensures
|
||||||
// the process does not appear to hang.
|
// the process does not appear to hang.
|
||||||
next_render = time_left + TargetRenderPeriod;
|
next_render += TargetRenderPeriod;
|
||||||
next_update = Math.Max(next_update, -1.0);
|
next_render = Math.Max(next_render, -1.0);
|
||||||
|
render_period = Math.Max(next_render, 0.0);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue