void RaiseRenderFrame(Stopwatch render_watch, ref double next_render, FrameEventArgs render_args)
{
// Cap the maximum time drift to 1 second (e.g. when the process is suspended).
double time = render_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
render_watch.Reset();
render_watch.Start();
return;
}
if (time > 1.0)
{
time = 1.0;
}
double time_left = next_render - time;
if (time_left <= 0.0 && time > 0)
{
// Schedule next render event. The 1 second cap ensures
// the process does not appear to hang.
next_render = time_left + TargetRenderPeriod;
if (next_render < -1.0)
{
next_render = -1.0;
}
render_watch.Reset();
render_watch.Start();
if (time > 0)
{
// Todo: revisit this code. Maybe check average framerate instead?
// Note: VSyncMode.Adaptive enables vsync by default. The code below
// is supposed to disable vsync if framerate becomes too low (half of target
// framerate in the current approach) and reenable once the framerate
// rises again.
// Note 2: calling Context.VSync = true repeatedly seems to cause jitter on
// some configurations. If possible, we should avoid repeated calls.
// Note 3: we may not read/write the VSync property without a current context.
// This may come to pass if the user has moved rendering to his own thread.
if (Context.IsCurrent && VSync == VSyncMode.Adaptive && TargetRenderPeriod != 0)
{
// Check if we have enough time for a vsync
if (RenderTime > 2.0 * TargetRenderPeriod)
{
Context.SwapInterval = 0;
}
else
{
Context.SwapInterval = 1;
}
}
render_period = render_args.Time = time;
OnRenderFrameInternal(render_args);
render_time = render_watch.Elapsed.TotalSeconds;
}
}
}