void RaiseUpdateFrame(Stopwatch update_watch, ref double next_update, FrameEventArgs update_args)
{
int num_updates = 0;
double total_update_time = 0;
// Cap the maximum time drift to 1 second (e.g. when the process is suspended).
double time = update_watch.Elapsed.TotalSeconds;
if (time <= 0)
{
// Protect against negative Stopwatch.Elapsed values.
// See http://connect.microsoft.com/VisualStudio/feedback/details/94083/stopwatch-returns-negative-elapsed-time
update_watch.Reset();
update_watch.Start();
return;
}
if (time > 1.0)
time = 1.0;
// Raise UpdateFrame events until we catch up with our target update rate.
while (next_update - time <= 0 && time > 0)
{
next_update -= time;
update_args.Time = time;
OnUpdateFrameInternal(update_args);
time = update_time = Math.Max(update_watch.Elapsed.TotalSeconds, 0) - time;
// Stopwatches are not accurate over long time periods.
// We accumulate the total elapsed time into the time variable
// while reseting the Stopwatch frequently.
update_watch.Reset();
update_watch.Start();
// Don't schedule a new update more than 1 second in the future.
// Sometimes the hardware cannot keep up with updates
// (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
// once the load becomes lighter.
next_update += TargetUpdatePeriod;
next_update = Math.Max(next_update, -1.0);
total_update_time += update_time;
// Allow up to 10 consecutive UpdateFrame events to prevent the
// application from "hanging" when the hardware cannot keep up
// with the requested update rate.
if (++num_updates >= 10 || TargetUpdateFrequency == 0.0)
break;
}
// Calculate statistics
if (num_updates > 0)
{
update_period = total_update_time / (double)num_updates;
}
}