lid6j86's picture

CLoop and CPrecisiontime

As a quick note, please feel free to correct me on any errors on comments or code that I make. Also, for anyone new that wants to use this code, I would reccomend typing it out manually. It will help you learn the code if you are writing it yourself, even if you are just copying it. It lets you spend time on each line of code, and lets you think about what each piece means.

So two of the most important aspects of using OpenGL are having precision timing and a fast loop. The following two pieces of code together provide that capability. they call C functions to make sure that they are fast (they are called in unsafe zones) These two are derived from C# Game Programming For Serious Game Creation by Daniel Schuller (he uses the Tao framework). This is the book that started me down this path in the first place. Most of it is fairly simple to read, and he does provide some good insight on some basic mathematics you will need to know for any game creation (along with what it does).

The code for the Loop is in its own separate class file, as follows:


using System.Runtime.InteropServices;  //Allows me to declare unsafe zones
using System.Windows.Forms;  //This will need to be added, lets you call Application (class files do not start w/ this)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
 
namespace CsGFXFramework
{
 
    [StructLayout(LayoutKind.Sequential)]  //Ensures that it is sent in the order it is displayed, and not optimized
 
    public struct Message  //this structure sets up how the message var will be sent to the peekmessage function (below), which will write to message in a specific order
    {
        public IntPtr hWnd;
        public Int32 msg;
        public IntPtr wParam;
        public IntPtr lParam;
        public uint time;
        public System.Drawing.Point p;
    }
 
    /// <summary>
    /// CLoop takes a function and runs it continuously.  The function needs to have a double variable in its signature, because of the precision loop explained later
    /// </summary>
 
    public class Cloop
    {
 
        //UNSAFE ZONE//
        [System.Security.SuppressUnmanagedCodeSecurity]   //call C code in unsafe zone to check for system idle, opens User32.dll
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern bool PeekMessage(
           out Message msg,
           IntPtr hWnd,
           uint messageFilterMin,
           uint messageFilterMax,
           uint flags);
        //UNSAFE ZONE//
 
        public delegate void LoopCallback(double elapsedTime); //Create a delegate for function that will be looped continuously. note the signature.
 
        CPreciseTimer _timer = new CPreciseTimer();  //CPreciseTimer is a separate class that is called to (code is listed below)
        LoopCallback _callback; //stores sent function for future use
 
        //Constructor, takes in 1 argument (looping function)
        public Cloop(LoopCallback callback)
        {
            _callback = callback; //set local _callback to function that is sent in args
            Application.Idle += new EventHandler(OnApplicationEnterIdle); //listens for system to be idle and runs the event handler OnApplicationEnterIdle
        }
 
        //Event handler for when the system is idle
        void OnApplicationEnterIdle(object sender, EventArgs e)
        {
            while (IsAppStillIdle()) //determines if the app is still idle, and if it is it continues to run
            {
                _callback(_timer.GetElapsedTime());  //runs the function that was sent as an arg
            }
        }
 
        private bool IsAppStillIdle() //function that looks for system idle  
        {
            Message msg;
            return !PeekMessage(out msg, IntPtr.Zero, 0, 0, 0); //if there is a message in queue, it returns true, so ! is needed to say that it is not idle if there is a message (send false)
        }
    }
 
}

In the program class, you would create a new loop like this:

using CsGFXFramework;  //ensure to add this if you put the Cloop into a separate class library
 
namespace Project
{
     class Program
     {
          public Cloop _loop;
 
         program_load_function()  //this is pseudo-code for whatever your program's function is called
         {
              _loop = new Cloop(gameloop);   //begin looping gameloop
         }
 
         private gameloop(double elapsedTime)
         {
              ///Game logic, rendering, etc goes here...
         }
 
 
     }
}

This allows you to loop gameloop (or whatever you call it at a fast rate, sufficient for rendering.

The other part, Cprecisiontime, is listed below. I put this in a separate class file as well. This allows you to determine the amount of time between updates and keep the rendering process independent of clock speed (so that it runs at the same speed on all computers, no matter if the machine is slower or faster). This doesn't mean you will get the same FPS on all systems, but that the game time itself will run at the same speed on all systems.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
 
namespace CsGFXFramework
{
    public class CPreciseTimer
    {
        //UNSAFE ZONE//
        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport("kernel32")]
        private static extern bool QueryPerformanceFrequency(ref long PerformanceFrequency);  //gets the clock frequency for ticks per second
        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport("kernel32")]
        private static extern bool QueryPerformanceCounter(ref long PerformanceCount);  //gets the number of elapsed ticks for future calculations
        //UNSAFE ZONE//
 
        long _ticksPerSecond = 0;  //initialize variables
        long _previousElapsedTime = 0;
 
        public CPreciseTimer()
        {
            QueryPerformanceFrequency(ref _ticksPerSecond);  //gets the number of ticks per second (frequency) after calling the C function in the constructor
            GetElapsedTime(); //Get rid of first rubbish result
        }
        public double GetElapsedTime()  
        {
            long time = 0;
            QueryPerformanceCounter(ref time);  //gets the number of ticks elapsed, pulled from the cloop
            double elapsedTime = (double)(time - _previousElapsedTime) / (double)_ticksPerSecond;  //gets the total elapsed ticks by subtracting the current number of ticks from the last elapsed number of ticks.  it then divides it by ticks per second to get the actual amount of time that has passed.
            _previousElapsedTime = time;  //sets the previous elapsed ticks for the next calculation
            return elapsedTime;
        }
 
 
    }
 
 
}

So essentially this looks at the clock speed, determines its frequency, then figures out how many ticks passed. it then divides the two to get the actual amount of time that has passed, which iwll be important for the games elapsed time, and rendering speed.

I put these two items into a class library and bring them into new programs as references. These are the first two steps I took for creating OpenGL projects. Just like with this example, I'll make sure to include any references that I use for anyone to research the information for themselves.


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
devintelo's picture

Won't work for me (error in ide as application.idle += ... )
It looks like very complex for a such small goal

lid6j86's picture

Remember it only looks complex because its handling unmanaged c code for the loop. The loop is OS specific because it is looking at the messages being handled by the os. I'll take a look at it and make sure it didnt get messed up on transfer.

Honestly i dont use this piece much anymore because the gamewindow's built in fast loop is quite good. Anything for a window app that you might use glcontrol would probably not need the same type of precision this gives you and you'd be fine using something a little simpler like a timer.