Bicubic's picture

OnRenderFrame elapsed time

Project:The Open Toolkit library
Version:1.0-beta-3
Component:Code
Category:bug report
Priority:normal
Assigned:Unassigned
Status:closed
Description

I keep track of total elapsed time with something like

protected override void OnRenderFrame(FrameEventArgs e)
        {
                base.OnRenderFrame(e);
                ...TotalTime += e.Time;
                ....
               Matrix4.CreateRotationY((float)TotalTime/400f);
        }

This is used to rotate a quad. On one machine, the rotation is smooth as expected. On another, the rotation is erratic. It slows down to a 10th of what I expect (while fraps reports a steady 60 fps, not that it should matter because we're measuring time and not frames), and speeds up to normal if I am dragging the window, or moving the mouse over the window border. Both are vista machines.


Comments

Bicubic's picture

#1

I should also mention that the same with OnUpdateFrame works correctly on both machines.

the Fiddler's picture

#2

What parameters are you passing to the Run() method? Is vsync enabled on the second machine?

Bicubic's picture

#3

game.Run(60.0);
VSync = VSyncMode.Adaptive;

If VSync is either off or on, this does not occur. Both driver configurations don't force vsync. The machine experiencing this issue is the more powerful one of the two.

the Fiddler's picture

#4

VSyncMode.Adaptive executes this code (GameWindow.cs:494, RaiseRenderFrame method):

            if (VSync == VSyncMode.Adaptive)
            {
                // Check if we have enough time for a vsync
                if (TargetRenderPeriod != 0 && RenderTime > 2.0 * TargetRenderPeriod)
                    Context.VSync = false;
                else
                    Context.VSync = true;
            }

As an experiment, what happens if you add a call to VSync = VSyncMode.On to the top of your RenderFrame handler? (In other words, is this behavior connected to enabling vsync continuously?)

What video cards are these systems equipped with?

Bicubic's picture

#5

Setting VSync to anything at the top of RenderFrame behaves no different than if the same parameter was set on load.
TargetRenderPeriod is showing as 0 on both machines.
Affected card is a 9600GT. Works well on an integrated intel chip, and had a friend test it with unknown hardware; also worked correctly there.

The difference that I see is that RaiseRenderFrame does not necessarily occur at the same rate as OnRenderFrame.

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 > 1.0)
        time = 1.0;
    if (time <= 0)
        return;
    double time_left = next_render - time;
 
 
    if (time_left <= 0.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: remove this?
            if (VSync == VSyncMode.Adaptive)
            {
                // Check if we have enough time for a vsync
                if (TargetRenderPeriod != 0 && RenderTime > 2.0 * TargetRenderPeriod)
                    Context.VSync = false;
                else
                    Context.VSync = true;
            }
 
            render_period = render_args.Time = time;
            OnRenderFrameInternal(render_args);
            render_time = render_watch.Elapsed.TotalSeconds;
        }
    }
}

This version behaves correctly. If this adaptive mode is kept, it might be a good idea to have it take an average over a second or so before turning it off, or I can see it flicking off and on several times a second which will produce even worse jitter.

the Fiddler's picture

#6

Version:1.0-beta-2» 0.9.x-dev
Status:open» fixed

The current approach for VSyncMode.Adaptive behaves like VSyncMode.On when a target framerate is not specified. I will revisit this issue once opentk-1.0 is released (the suggestion on average framerate is very interesting).

In the meantime, I have committed a slightly modified version of the above code, which should completely remove the jitter:

                    // 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.
                    if (VSync == VSyncMode.Adaptive && TargetRenderPeriod != 0)
                    {
                        // Check if we have enough time for a vsync
                        if (RenderTime > 2.0 * TargetRenderPeriod)
                            Context.VSync = false;
                        else
                            Context.VSync = true;
                    }

Conceptually, this works exactly like the code above. However, it removes the repeated calls to Context.VSync = true, which seem to be the cause of the jitter.

Committed to r2571.

Bicubic's picture

#7

Cheers.

the Fiddler's picture

#8

Version:0.9.x-dev» 1.0-beta-3
Status:fixed» closed

Closing issues fixed in opentk-1.0-beta-3.