Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET Redraw at 60 FPS?

Tags:

c#

winforms

I've got some animations running in an OpenGL context window, so I need to constantly redraw it. So I came up with the following code:

private void InitializeRedrawTimer()
{
    var timer = new Timer();
    timer.Interval = 1000 / 60;
    timer.Tick += new EventHandler(timer_Tick);
    timer.Start();
}

private void timer_Tick(object sender, EventArgs e)
{
   glWin.Draw();
}

That only gives me 40 FPS though. If I set the interval to 1 ms however, I can achieve 60. So where did the other 20 ms go? Is that just due to poor accuracy of the timer or what? What if I wanted to let my program run as fast as possible, is there a way to continuously call the draw func?

like image 467
mpen Avatar asked Jan 14 '10 09:01

mpen


People also ask

Why is my FPS stuck at 60?

You probably have a 60 Hz monitor and V-sync turned on which locks your frame rate to your monitor's refresh rate, which can be fixed by turning V-sync off.

Why is my FPS capped?

If your FPS (frames per second) is unstable or capped, it may be related to your VSync settings. Enabling VSync synchronises the frame rate of the game with the refresh rate on your monitor, which helps with screen tearing.


1 Answers

You could try implementing a game loop.

http://blogs.msdn.com/tmiller/archive/2005/05/05/415008.aspx

The basic loop (slightly modified from his original version and the version in the new SDK for ease of reading):

public void MainLoop()
{
        // Hook the application’s idle event
        System.Windows.Forms.Application.Idle += new EventHandler(OnApplicationIdle);
        System.Windows.Forms.Application.Run(myForm);
}    

private void OnApplicationIdle(object sender, EventArgs e)
{
    while (AppStillIdle)
    {
         // Render a frame during idle time (no messages are waiting)
         UpdateEnvironment();
         Render3DEnvironment();
    }
}

private bool AppStillIdle
{
     get
    {
        NativeMethods.Message msg;
        return !NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
     }
}

//And the declarations for those two native methods members:        
[StructLayout(LayoutKind.Sequential)]
public struct Message
{
    public IntPtr hWnd;
    public WindowMessage msg;
    public IntPtr wParam;
    public IntPtr lParam;
    public uint time;
    public System.Drawing.Point p;
}

[System.Security.SuppressUnmanagedCodeSecurity] // We won’t use this maliciously
[DllImport(“User32.dll”, CharSet=CharSet.Auto)]
public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);

Simple, elegant, effective. No extra allocations, no extra collections, it just works.. The Idle event fires when there’s no messages in the queue, and then the handler keeps looping continuously until a message does appear, in which case it stops.. Once all the messages are handled, the idle event is fired again, and the process starts over.

This link describes one that uses the application's Idle event. It may be of use. You can simply perform a little time test or sleep to slow it down to your required fps. Try using the System.Diagnostics.StopWatch class for the most accurate timer.

I hope this helps a little.

like image 87
Nanook Avatar answered Nov 01 '22 10:11

Nanook