Inertia's picture

Simulation Mainloop?

Hello again,

I've never really liked the idea to use the idle function for updating the scene, so I tried to come up with a better solution on my own. The reason why I'm looking for a different way is that my simulation should update ODE at ~150 Hz, OpenGL at ~60 Hz and OpenAL at ~30Hz.

The design goals are:

1.a) Must provide a way to execute a code-block at a specified Frequency, never more often than that.
1.b) if the code-block execution takes longer than (1 second / Frequency) it should be handled gracefully by drawing less frames. But it should always attempt to reach the desired Frequency tho.
2) The code-block may never be executed at the same time by multiple threads/events.
3) A way to split work for multiple CPU

I've tried this with timer events, but 1.b) and 2) turned out to be a problems with this. Also there is a threadpool involved which creates/destroys hundrets of threads per second which is kinda costly. Threads appear to be the best choice, since it allows to set thread Priority for tasks and also the ideal processor for the task (e.g. OpenGL/OpenAL at logical processor 1, ODE at logical processor 2).

The only drawback I see atm is that it requires a dllimport of kernel32.dll (and I don't have the slightest clue how this maps to MacOS/Linux), but in the worst case (that there are no equivalent functions for non-windows OS) there is still the possibility to skip the step to manually assign threads to processors.

A spawned thread looks something like this:

    private static void OpenGLStaticLoop( )
        {
            long current = Stopwatch.GetTimestamp();
            long next = current;
 
            while ( true )
            {
                current = Stopwatch.GetTimestamp();
 
                if ( current >= next )
                {
                    next += OpenGLTicksPerFrame;
 
                    // lock, draw, do work etc.
                    for ( int i = 0; i < 1000; i++ )
                    { double dummycalculation = Math.Pow( i, i ) * Math.Sin( i ) / Math.Sqrt( i ); }
                    Thread.Sleep( 10 ); // drawing should take ~16.6ms
                    // done drawing etc.
                }
                else
                {
                    Thread.Sleep( 0 ); // end timeslice and enter WaitSleepJoin state
                }
            }
        }

where:

OpenGLTicksPerFrame = (long) ( ( 1.0 / OpenGLFramesPerSecond ) * Stopwatch.Frequency );

What I like about this solution is that it doesn't utilize 100% of your CPU, if the work is less than what your system can handle. This could be useful to design low-priority threads that only run when there are spare cycles (e.g. update procedural textures).

Comments and Critique are both desired and appreciated. I find it rather hard to judge this solution, since i'm not entirely objective.

-Inertia


Comments

Comment viewing options

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

The separation of game code and logic it a tricky thing. Much of the code MUST be on the main thread, (system event processing, OpenGL calls, etc). I find spawning a bunch of threads bad, but instead make specific thread for things that can be offloaded such as file loading and network traffic processing. Splitting off the logic that handles the objects being rendered is more tricky but can be done.

I ended up writing my own game loop:

Initialize();
 
ProcessEvents();
 
UpdateFrequency = 1000.0f / updatesPerSecond;
UpdateTick = Environment.TickCount;
Update(UpdateTick);
 
RenderFrequency = 1000.0f / framesPerSecond;
RenderTick = Environment.TickCount;
Render(RenderTick);
 
while (!exit)
{
    ProcessEvents();
 
    bool updated = false;
 
    if( UpdateFrequency > 0 )
    {
        while( Environment.TickCount - UpdateTick >= UpdateFrequency )
        {
            UpdateTick += UpdateFrequency;
            Update(UpdateTick);
            ProcessEvents();
            updated = true;
        }
    }
 
    if( RenderFrequency > 0 )
    {
        int frameCount = (int)((System.Environment.TickCount - RenderTick) / RenderFrequency);
 
        if( frameCount > 0 )
        {
            RenderTick += frameCount * RenderFrequency;
            Render(RenderTick);
            updated = true;
        }
    }
 
    if(!updated)
    {
        Thread.Sleep(1);
    }
}
 
Shutdown();

When I run this and have very little being done in the Render and Update functions, I use 0% - 1% cpu.

I call ProcessEvents() so often since I found when using SDL, if too many events queue up the OS can lose some of them.