Gameloop etc questions

mikael

27-07-2010 11:23:29

I noticed that gameloop does not work right on slow machine (or I tested it with underclocking
my computer). FPS can be 200 (!!) but using keyboard to moving, it "jumps".
It's because of timeSinceLastFrame is time to time 0 and then 0.00xxx.

Couple of other issues I noticed, on my 4870 card, robots textures are not right on
opengl mode (works with my older computer).
On cameratrack demo, this plane stretches uv:s on the plane, is it right? (wasnt like that on axiom).

And last one, ÄÖ äö letters, I cant get them to work (if I use these in .overlay file, program crashes when parsing them).
Now I use "hack" that converts all ÄÖäö letters standard ascii letters -> "<>_'" which I dont use but I have to edit .fontdef file everytime I create new font. Not much trouble but is there
easier way?

Beauty

05-08-2010 18:53:08

Related to the time problems - I had the same trouble some time ago.

I suppose the reason is the default timer of .NET (or Windows in general?).
The time values looks like very hight precision.
But this value will not be updated so often. (maybe one update after one milisecond?)
This is the reason why you have many 0 values and a few != 0.
It's like a watch which tells you a time with precision of a second, but its' clock hand will only move a few times per minute.
You know what I mean?

A solution is: don't ask Ogre.
Instead use the QueryPerformanceCounter.
It's no common timer which returns a time value.
It's based to the hardware of the current system and allows to get the most precise timing values on Windows systems.
If you need time values, it's easy to calculate them from the counter values.

Here I found 2 related posts:
I didn't wrap anything from the PlatformManager, the .NET API is more than adequate :wink:
nah its not really, standard dotnet timer functions are nowhere near accurate.

If you want to use high performance timing in C# i guess its best to use this:

using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Threading;

namespace Win32
{
internal class HiPerfTimer
{
[DllImport("Kernel32.dll")]
private static extern bool QueryPerformanceCounter(
out long lpPerformanceCount);

[DllImport("Kernel32.dll")]
private static extern bool QueryPerformanceFrequency(
out long lpFrequency);

private long startTime, stopTime;
private long freq;

private bool started;

// Constructor
public HiPerfTimer()
{
startTime = 0;
stopTime = 0;

if (QueryPerformanceFrequency(out freq) == false)
{
// high-performance counter not supported
throw new Win32Exception();
}
}

// Start the timer
public void Start()
{
// lets do the waiting threads there work
Thread.Sleep(0);
started = true;
QueryPerformanceCounter(out startTime);
}

// Stop the timer
public void Stop()
{
started = false;
QueryPerformanceCounter(out stopTime);
}

// Returns the duration of the timer (in seconds)
public double Duration
{
get
{
return (double)(stopTime - startTime) / (double) freq;
}
}

public bool isStarted()
{
return started;
}
}
}

Hope its usefull ;)


@ravenger:
You can do the same with System.Diagnostics.Stopwatch.



Here is an old discussion - which also contains the timer problem and the query performance counter.
Maybe there are also code snippets.
viewtopic.php?f=8&t=8013&start=0&hilit=QueryPerformanceCounter


Alternatively search for Query Performance Counter (with spaces for details) or QueryPerformanceCounter (without spaces for source code) in the internet.

mstoyke

05-08-2010 21:16:17

Using the QueryPerformanceCounter functions can cause problems on multi-core CPUs, at least there were some serious problems in the past. I don't know if the .NET Stopwatch class is aware of the problem and contains a fix.

On most machines you should be fine using any of these methods. If you have strange effects, just remember that you might have to take care of these bugs.

A little more detail:
On multi-core CPUs the performance counter on different cores can have different values. If the scheduler changes your program flow to another core, you might get strange readings like large differences between calls. If you create a time delta every frame (currentTimeStamp - lastTimeStamp) and you get negative values, you just encountered this problem.

The easiest way is to use a background thread with an affinity mask that binds it to only one core. So the functions will always use the same performance counter and the problem is solved. You'll have to do this in C or C++ code, because .NET has its own internal scheduler that is independent of the system scheduler and you can't change the affinity mask in a reliable way.

More information about QueryPerformanceCounter problems can be found here and here.

Beauty

06-08-2010 00:56:37

Good information - thanks.

mikael

12-08-2010 16:01:25

Thanks to all for your help, good links and will make own gameloop and use some of these fps-limiter code, will test these when have some time.

Beauty

12-08-2010 21:21:48

Now I'm reading about time measurement for about one hour.
I found the note that the Stopwatch class is much more precise than the common timer class.
Somebody wrote a measurement test program (4 years ago when the computers were slower).
The result: The precision was nearly on tick level. (1 tick = 0.0001 milli seconds) This means the measurement precision is about 100..200 nano seconds.
I didn't know this .NET class before, but it seems to be a suitable for FPS calculations and FPS limitations.
I hope this class has no problems with fluctuating CPU frequencies.


Additionally I want to quote a hint, which somebody wrotes in 2006:
As far as I know - the highest precision you get by multimedia timers of the Windows API. It's using P/Invoke.
update: The new name is High Precision Event Timer. See it's Wikipedia page.

Beauty

13-08-2010 01:19:23

Unfortunately somebody in an other forum gives me an answer and said, that the

Stopwatch class is just like a wrapper for the QueryPerformanceCounter.
It contains no improved calculation for fluctuating CPU frequencies.

Zonder

13-08-2010 11:48:06


A little more detail:
On multi-core CPUs the performance counter on different cores can have different values. If the scheduler changes your program flow to another core, you might get strange readings like large differences between calls. If you create a time delta every frame (currentTimeStamp - lastTimeStamp) and you get negative values, you just encountered this problem.


One way to resolve this would be to test the threads current cpu then set the thread affinity to that cpu only (before starting the game loop) Then there will be no hopping about onto different cpus.

mstoyke

13-08-2010 17:53:24

One way to resolve this would be to test the threads current cpu then set the thread affinity to that cpu only (before starting the game loop) Then there will be no hopping about onto different cpus.

This is actually very easy in C++, but a big issue in C#.

The .NET framework does not match managed thread 1:1 to system threads. So even if you manage to get a handle to the current thread and use some interop to change its affinity mask, you can't be sure that the managed thread will only run on one CPU. In fact you could make things worse, because the system thread with the affinity mask set might now be used for other managed thread that will not run on any other cores/CPUs anymore.

I did a lot in the past to find a reliable solution for this problem and ended up using some piece of C code that creates its own thread and updates an internal timestamp with the performance counter. Then I used P/Invoke to access the timestamp from C#.

The funny thing about this is, after putting all this effort into the matter I had to realize that even the worst timers were still good enough for my application and it did not improve anything about the timing in my project. There are other problems that can easily keep you from getting smooth animations and lag-free rendering.

The average frame-time for most game projects is around 10ms to 30ms, anything above 100 fps does not make any difference at all for the slow human eye. What really provides the impression of smooth rendering is a constant framerate and, that's most important on modern flat screens, use of v-sync.

On a multitasking OS the worst that can happen to your game is some other processes that consume a lot of processing power. So make sure they can't cause too many problems by raising your games task priority a little bit. The "above normal" priority will do a good job in most cases here.

Also make sure to measure the rendering time for every frame. If you have a lot of frames that take more than 1/60th of a second, just adjust the game to target 30 fps. Keeping constant 30 fps will provide a much better impression than trying to do 60 fps and skipping a frame every now and then.

Also try to tune your frame rate to the display refresh rate. 60 fps on a 100 Hz screen just look awful, better waste some time and try to use 50 fps.

Beauty

13-08-2010 21:00:39

I had a discussion on a german C# forum and did read several pages.
Now I created a page in the Ogre wiki with useful links related to the QueryPerformanceCounter:
http://www.ogre3d.org/tikiwiki/tiki-ind ... nceCounter


Related to my qualms about fluctuating CPU frequencies - we don't need to worry.
The value of QueryPerformanceFrequency never changes, while a system is running.

I did a lot in the past to find a reliable solution for this problem and ended up using some piece of C code that creates its own thread and updates an internal timestamp with the performance counter. Then I used P/Invoke to access the timestamp from C#.
I had a similar idea. If it's possible to publish the code or library, it could be useful for some people. My main project contains a sonar simulation for underwater vehicles. There is would be good to have a precise time stamp counter.
Because of mstyokes note (core swapping) I have a reason, why my application did crash sometimes (because of strange time values). I solved this issue by checking and fixing for bad counter values. But a reliable counter would be better.


If you have a lot of frames that take more than 1/60th of a second, just adjust the game to target 30 fps. Keeping constant 30 fps will provide a much better impression than trying to do 60 fps and skipping a frame every now and then.
Some time ago I wrote a code which checks the fps speed and forced the application for short sleeps. But it didn't work well. Then I recognized, the problem was the slow update of the DateTime.Now.Ticks value.
Now I think about to use the QueryPerformanceCounter instead, but I worry about the multicore problems.
Also I read, a short sleep can cause a much more longer sleep than wanted.

How do you force a constant frame rate?
Maybe you use System.Windows.Forms.Timer as trigger and set the interval to 1/30th of a second?


You wrote useful things.
Maybe I should copy some parts to a wiki page. Then people can find it in the future. Otherwise its like dead knowledge.
........ update: it's done

Beauty

13-08-2010 22:03:32

Now I found the post with my 2 years old code snippet for FPS limitation (that not works correct).
viewtopic.php?p=46584#p46584

There I also published my test results. It shows, that the value of DateTime.Now.Ticks was updated only each ~15 milliseconds.

Later in the thread somebody modified my code for the QueryPerformanceCounter. It's in Visual Basic, but with an online converter you quickly make C# of it :wink:
viewtopic.php?p=46633#p46633

I have search about game loop development some time, and i found a very good article about this topic, take look

http://dewitters.koonsolo.com/gameloop.html

He also published a C# game loop related to that article.
viewtopic.php?p=47464#p47464

Here is a second C# game loop by the same boy. It's something about reducing FPS without a defined FPS target value.
viewtopic.php?p=47546#p47546

Day7

17-08-2010 14:38:45

It might be messed up a bit, but it works for me so far
RenderOneFrame() ftw :)

public static void Render()
{
Stopwatch stopwatch = new Stopwatch();

long UpdateFPS = 50;
long SkipTicks = 1000 / UpdateFPS;
long MaxFrameSkip = 10;
long Loops = 0;

long MaxFPS = int.Parse(Config.MaxFPS); // type max fps here
long LastTick = 0;
long SkipFrame = 0;

stopwatch.Start();

if (MaxFPS <= 0)
MaxFPS = 0;
else
{
LastTick = stopwatch.ElapsedMilliseconds;
SkipFrame = 1000 / MaxFPS;
}

long NextTick = stopwatch.ElapsedMilliseconds;

RenderRunning = true;
while (RenderRunning)
{
Loops = 0;

while (stopwatch.ElapsedMilliseconds > NextTick && Loops < MaxFrameSkip)
{
GameManager.GameUpdate(); //my func
SceneUpdate(); //my func
Application.DoEvents();
NextTick += SkipTicks;
Loops++;
}

switch (MaxFPS)
{
case 0:
root.RenderOneFrame();
break;

default:
if (LastTick + SkipFrame <= stopwatch.ElapsedMilliseconds)
{
root.RenderOneFrame();
LastTick = stopwatch.ElapsedMilliseconds;
}
break;
}

}

stopwatch.Reset();
}

mstoyke

17-08-2010 19:01:15

It might be messed up a bit, but it works for me so far
RenderOneFrame() ftw :)


That's a good example of how do to it, even though I only looked at it and did not try it. The only thing that might be a good idea to add here is a call to Thread.Sleep(0) while you're waiting. It will give the scheduler a better chance of executing other threads, especially if you raised your applications priority a little bit.

In some (rare) cases it might cause problems with a fluent framerate, but in most cases it can improve it a lot. If you don't yield (Sleep(0) == yield) while waiting there might be a greater chance for the scheduler to switch to other threads when you least expect it, but if you call yield, your application will usually get at least one whole slot of CPU time to use.

Beauty

17-08-2010 22:09:24

Thanks for publishing your code snippet :D
What was your pattern? The gameloop article at dewitters.koonsolo.com?

For me one thing is looking strange. You compare millicesonds with ticks. (One tick is 0.0001 milliseconds.)
stopwatch.ElapsedMilliseconds > NextTick


@mstoyke: Where do you want to add Thread.Sleep(0)?

default:
if (LastTick + SkipFrame <= stopwatch.ElapsedMilliseconds)
{
root.RenderOneFrame();
LastTick = stopwatch.ElapsedMilliseconds;
// ........................................ ADD SLEEP HERE ?
}
// ........................................ OR HERE ?
break;

// ........................................ OR SOMEWHERE ELSE ?


What you mean with yield? I don't understand.
if (Sleep(0) == yield) { ... }


By the way - I found no sleep in the loop. It just skipps the rendering call when the loop is too fast.
Ok, I know that short sleep commands can pause much more longer than wanted. If this would be a problem - I don't know.
But when a loop doesn't sleep, I suppose the render thread still catches much CPU power. So other threads/applications have still less CPU power. Or is my assumption wrong?


When the code is fine, I want to add it to the wiki.
But not today, because today I worked in the wiki for many ours and now I'm tired.

mstoyke

17-08-2010 23:19:57

@mstoyke: Where do you want to add Thread.Sleep(0)?

default:
if (LastTick + SkipFrame <= stopwatch.ElapsedMilliseconds)
{
root.RenderOneFrame();
LastTick = stopwatch.ElapsedMilliseconds;
}
else
{
Thread.Sleep(0);
}
break;


What you mean with yield? I don't understand.
if (Sleep(0) == yield) { ... }


Sorry, I should have written that more detailed. To "yield" just means to return control to the scheduler, in Windows applications this is usually done by calling "Sleep(0)". It makes it easier for the scheduler to decide when to switch between tasks or threads, because it's like saying "I'm idle at the moment, please, Mr. Scheduler, now is a good time to give some share of the CPU to other tasks" ;)

This makes most sense after rising the application priority a little bit, because the scheduler will return control to our application immediately if no other threads need to execute at the moment. On the other hand, if another thread needs some time with the CPU, it's a good time, because we want to waste some time anyways until our next frame should be rendered.

I'm used to pthreads and the function is called pthread_yield.

Day7

17-08-2010 23:54:57

Lets say you want to sleep for Sleep(1), but theres something related to multimedia on windows os that will force sleep to be like Sleep(10).

I had that issue, while searching how to fix it i found that Counter strike servers might have ~64 FPS lock. So they launched windows media player and -how come- that does fix problem. I tried it, while my application was running with 64 FPS, started WMP - then i had about 300 fps... strange but true.

Then i found its related to Window Kernel Timer Resolution. To fix that issue You need winmm.dll and use this:
[DllImport("winmm.dll", EntryPoint = "timeBeginPeriod")]
public static extern uint MM_BeginPeriod(uint uMilliseconds);
[DllImport("winmm.dll", EntryPoint = "timeEndPeriod")]
public static extern uint MM_EndPeriod(uint uMilliseconds);


MM_BeginPeriod(1); // before rendering

MM_EndPeriod(1); //before exiting application


Generally when You use Sleep() to limit fps and You are getting 64 FPS lock with VSYNC OFF - thats the way how to fix it.

Yet thats too strange for me, thats the reason why i choose not to use sleep, still high CPU usage is annoying... but what else... Just leave it.

If You ask for comparing Tick to miliseconds... i just called that var NextTick.

I wrote it sometime ago, all i remember is that i had issue with Sleep and Timer - so dont use those please.

Let it clear ur mind :D
void RenderLikeFunction()
{
Stopwatch stopwatch = new Stopwatch();

long UpdateFPS = 50; // type fps for game update (physics or so)
long MaxFPS = 120; // type max fps to render
long MaxFrameSkip = 10; // limit game update to let draw graphic stuff

long SkipUpdate = 1000 / UpdateFPS;
long SkipFrame = 1000 / MaxFPS;
long Loops = 0;
long LastFrame = 0;
long NextFrame = 0;

stopwatch.Start();

RenderRunning = true;
while (RenderRunning) //just do FALSE to stop render
{
Loops = 0;

while (stopwatch.ElapsedMilliseconds > NextFrame && Loops < MaxFrameSkip)
{
GameManager.GameUpdate(); //my func
SceneUpdate(); //my func
Application.DoEvents();
NextFrame += SkipUpdate;
Loops++;
}

switch (MaxFPS)
{
case 0:
root.RenderOneFrame();
break;

default:
if (LastFrame + SkipFrame <= stopwatch.ElapsedMilliseconds)
{
root.RenderOneFrame();
LastFrame = stopwatch.ElapsedMilliseconds;
}
break;
}

}

stopwatch.Reset();
}
Is it readable now ? :) should be

Cheers

mstoyke

18-08-2010 00:38:14

Dont use System.Threading.Thread.Sleep() to limit FPS - that might be biggest mistake ever!

Lets say you want to sleep for Sleep(1), but theres something related to multimedia on windows os that will force sleep to be like Sleep(10).


Yes, that is true for calling Sleep with anything other than 0. But calling Sleep(0) is a special case. If you use .NET4, it's even better to use the new Thread.Yield() function, because it has even less overhead. As I said before, it's not always the best solution, but it's a good way to influence the scheduler a little bit to behave more friendly to the game (or application).

I did a lot of research of testing in this matter, as a professional game developer and lead developer of a multiplayer middleware, threading and all related issues are a very important topic for me. I'm not saying that I'm right and you're wrong, all I say is that in my experience with a lot of large scale projects, it was almost always the best way to ensure a stable framerate and smooth rendering. But it is important to use a slightly risen thread priority for the game got this to be effective (as I said many times before).

There are also some other tricks that might help a lot with complex scenes, like calling time consuming functions after all draw calls, but before flipping buffers. That will make good use of the time that the graphics drivers use internally to process all rendering commands. Calling the flip buffers function will usually block until all rendering tasks are finished, so there is a (sometime very significant) amount of wasted time that a game can use to do some cool stuff instead.

By the way, using experiences with other software (cstrike server) to improve your own projects will not work in 99,9% of all cases :)

Day7

18-08-2010 01:02:55

You are right, but while You use Sleep(0) its like out of Your control - it depend on other threads, while Sleep(1) You can control by making limit while loop is doing, can You agree?

Im bit of scared to use Sleep(0) or Sleep(1) because it needs more precision as for me. Maybe Im just worring too much.

About FPS - 30 isnt enough, honestly I havent meet anyone yet who would play any multiplayer game with lower than ~60 FPS.
My godness! I would eat my whole pillow if i had to play CoD or Battlefield with 30 FPS :lol: finally something made me laugh today :) so glad, my job is getting out of my mind...

@mstoyke take a look at Torchlight, high CPU usage tells me they did worried about Sleep() stuff too :wink: it needs more attention to make this subject clear I believe.

Best regards and good night, I'll reply tomorrow if theres anything else to talk about :) thanks for talking

Beauty

18-08-2010 02:25:35

An interesting discussion.

I looked for similar discussions in the main forum.

I strongly recommend calling sleep() in your main game loop (fine for low-priority scenes such as main menus or cinematics, but 1ms is an absolute eternity for a computer, not to mention the kernel mode context switching involved that makes it measurably longer than 1ms). Instead, just do as I mentioned above and run your main game loop wide open while there is actual gameplay going on; your users will appreciate it.
Postes here. Xavier is a pro developer. He also wrote the book about Ogre.

All in all, here's some pseudocode for a healthy game loop:

F = Performance counter frequency
O = Old time
L = Logic fps step
T = Time
D = Frame delta
U = Logic update counter
-------------------------------------------------------------
F = QueryPerformanceFrequency
O = QueryPerformanceCounter
L = 1.0 / 30
while True {
T = QueryPerformanceCounter
D = ( T - O ) / F
static U = 0
U += D
while ( U >= L ) {
U -= L
ProcessLogic(L)
}
DrawFrame(D)
O = T
}

All variables other than F, O & T are obviously float.

Posted here. Although there might be problems with the QueryPerformanceCounter as mstoyke wrote.


This is not too difficult to write with the help of a physics engine since all the physics engines support variable step times
Variable step is pretty much the worst thing you can do to a physics engine. They all support it, but the results are horrible. You lose deterministic responses, if you do the exact same action twice, the results will be inconsistent.

A good article on time steps, physics and game loops is http://www.gaffer.org/game-physics/fix-your-timestep

Posted here. The link in the last line seems to be interesting!


At first, I thought FPS locking is quite easy and it should be done within frameStarted (if you only have one framelistener). However, it turns out to be more complicated than that because it simply can't be achieved - errors are quite big. I found this article yesterday and it looks more complicated than necessary but could be a good solution:

http://www.mindcontrol.org/~hplus/graphics/game_loop.html

Google for canonical game loop.

Found here.


Physics should always be run at a fixed rate, otherwise the results are non deterministic and can explode. If set up right, some physics libs will subdivide a variable time step into fixed sub steps, but I'd prefer to do it manually, since logic will need something similar anyway.
Found here.


But I thought, I would find more detailed information. (e.g. an ogre snippet or discussion about the sleep() problem)

Day7

18-08-2010 09:39:05

@Beauty I readed lots of articles about game loop too, they are all so similar :)

Indeed using interpolation makes Your game loop perfect.
I can still easily call myself newbie in game programming stuff and thats why taking care of interpolation is much trouble for me.

Now i found theres a bug in my game loop :) and it gets me confused...

With code above MaxFPS = 120; appears by having 125 FPS max, but MaxFPS = 50; gives 50 also. Tried double stopwatch.Elapsed.TotalMiliseconds instead of long stopwatch.ElapsedMiliseconds.

Now MaxFPS = 120; result in around 119.4, MaxFPS = 50; shows 48.9 so generally UpdateFPS and MaxFPS can be different in result.

Any ideas?

// Edit //

I think i got it... All because of integer... float type of var is required but it would hit performance (since FPU would be used).
See this for details about float vs integer
My game update per second stays at 50 anyway - should be enough to handle physics, gui and stuff...

// Edit 2 //

That loop should be fine
void RenderLikeFunction()
{
Stopwatch stopwatch = new Stopwatch();

long UpdateFPS = 50; // Use 25, 40 or 50 - GameUpdate per second (so You can stay with integer)
long MaxFPS = 120; // No need to comment
long MaxFrameSkip = 10; // Limit to GameUpdate loop before each RenderOneFrame()

long SkipUpdate = 1000 / UpdateFPS;
float SkipFrame = 1000f / MaxFPS;

long Loops = 0;
float LastFrame = 0;
float NextFrame = 0;

stopwatch.Start();

RenderRunning = true; // public,
while (RenderRunning) // do false to stop
{
Loops = 0;

while (stopwatch.ElapsedMilliseconds > NextFrame && Loops < MaxFrameSkip)
{
// put here game update function

Application.DoEvents();
NextFrame += SkipUpdate;
Loops++;
}

switch (MaxFPS) // if MaxFPS is 0 then its unlimited
{
case 0:
root.RenderOneFrame();
break;
default:
if (stopwatch.Elapsed.TotalMilliseconds >= LastFrame + SkipFrame)
{
LastFrame = (float)stopwatch.Elapsed.TotalMilliseconds;
// Im not sure if float is better than double
// need to research if double->float convertion is better than using double instead of float

root.RenderOneFrame();

// put here graphic update if needed
}
break;
}
}

stopwatch.Reset();
}
Yeee...

Beauty

18-08-2010 15:20:02

Capture keyboard by graphic??

I don't know if it's a fine solution, but I just use a KeyEventHandler of C#.
It's good enough for moving control.
But if you want to type text to a textBox of a Mogre GUI system, maybe there are better solutions.
As I read MOIS has problems with special letters of other languages and/or other keyboard layouts, but I have no own experience with it.

Here is my (simplified) code snippet for KeyEventHandler:
//-- this is no game loop code snippet --

this.renderPanel = new System.Windows.Forms.Panel(); // Ogre renders to renderPanel
this.renderPanel.KeyDown += new KeyEventHandler(renderPanel_KeyDownHandler);
this.renderPanel.KeyUp += new KeyEventHandler(renderPanel_KeyUpHandler);

void renderPanel_KeyDownHandler(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Up:
case Keys.W:
// do forward translation
break;

case Keys.Down:
case Keys.S:
// do backward translation
break;

// more cases for other keys
}
}


The events will be processed when I call Application.DoEvents() inside of the render loop.
But well, I don't want to hitch hike this thread too much.

Day7

18-08-2010 15:31:05

Well Beauty if You capture input with GameUpdate then mouse pointer will move around screen with 50FPS while everything else goes with 120FPS :)

Thats why I moved it near RenderOneFrame(). Now tried everything and it works well - got equal 120FPS with 50 GameUpdate per second.

Beside i wouldnt use Application.DoEvents() near RenderOneFrame() since we dont want to blast events each frame and that could lower fps aswell :)

Thanks, alles gute xD

mstoyke

18-08-2010 15:58:56

Ok, to end the discussion here (at least from my side), all I have to say is: I have some experience in what I'm recommending. It's up to you if you take the advice or if you ignore it, because it's not about being right or wrong, it's more about hinting others to which issues might arise in developing a game loop. All I ask is, please don't give advice to other users like "never use sleep, because I heard or think it's evil". Instead you should say something like "i think you should not use sleep, because I heard it's evil". It's a much better advice to other developers, especially if they are looking for useful information about something they don't have any experience with at all.

Beauty

18-08-2010 17:44:38

Beside i wouldnt use Application.DoEvents() near RenderOneFrame()
Where in the render loop would you call DoEvents()?
As far as I know it's needed to process events like updating the GUI (if there is a change). It needs time, but the work has to be done.
I call DoEvents() after RenderOneFrame().

Day7

18-08-2010 18:03:29

Look at loop again dear :)

void RenderLikeFunction()
{
Stopwatch stopwatch = new Stopwatch();

long UpdateFPS = 50; // Use 25, 40 or 50 - GameUpdate per second (so You can stay with integer)
long MaxFPS = 120; // No need to comment
long MaxFrameSkip = 10; // Limit to GameUpdate loop before each RenderOneFrame()

long SkipUpdate = 1000 / UpdateFPS;
float SkipFrame = 1000f / MaxFPS;

long Loops = 0;
float LastFrame = 0;
float NextFrame = 0;

stopwatch.Start();

RenderRunning = true; // public,
while (RenderRunning) // do false to stop
{
Loops = 0;

while (stopwatch.ElapsedMilliseconds > NextFrame && Loops < MaxFrameSkip)
{
// put here game update function

Application.DoEvents(); // <---- Hey I am here! :D
NextFrame += SkipUpdate;
Loops++;
}

switch (MaxFPS) // if MaxFPS is 0 then its unlimited
{
case 0:
root.RenderOneFrame();
break;
default:
if (stopwatch.Elapsed.TotalMilliseconds >= LastFrame + SkipFrame)
{
LastFrame = (float)stopwatch.Elapsed.TotalMilliseconds;
// Im not sure if float is better than double
// need to research if double->float convertion is better than using double instead of float

root.RenderOneFrame();

// put here graphic update if needed
}
break;
}
}

stopwatch.Reset();
}


Sorry mstoyke for point wrong way, Youre right. After thinking You could use Sleep() near RenderOneFrame() (because GameUpdate is priority - we dont want delays on anything). Im getting 2800 FPS anyway with clear SkyDome... Im just wonder whats the purpose?

mstoyke

18-08-2010 19:56:33

Ok, even through I wanted to end the discussion from my side, one more thing that I wanted to say on the topic :) :

You don't really need to call Sleep() at all if you don't also use vsync. It might improve smoothness of the animations, but it will reduce your overall framerate in some cases. My advice was meant for projects that target a smooth, synchronized framerate with vsync switched on. If you just want to achieve the highest framerate possible it's indeed better to just loop without wasting any time at all.

So the two possible goals someone wants to achieve in his application can either be highest possible framerate, with the drawback of having a lot of tearing artifact, especially on flat screens, or to target smooth rendering and animations, that is where I was heading towards in my posts. The best framerate for a 60Hz flatscreen is 60 fps and for 100Hz it is 100 fps.

If your application needs a lot of rendering power and you can not achieve these framerates in (almost) every frame, it is better to go for half the refresh rate of the screen (30 fps or 50 fps) and make sure you "waste" at least enough time every frame, so that all frames are rendered with the same framerate. It will feel more fluent, because the human eye is very sensitive to fluctuations in the framerate.

I don't know if you see the point in what I just said, but if you try it you may even be surprised to see how even 30 fps can feel very smooth if you just do it in the described way. I was surprised to see a game that was rendering even slower (20 fps on a 60Hz flatscreen), but just because they made sure that every frame took 50ms to render it gave the impression of smooth animations.

Maybe I will add the code I have in mind to my quick start project later, right now I'm too tired because I just returned from GDC and did not get much sleep in the past 3 days.

Beauty

18-08-2010 23:37:47

Look at loop again dear :)
Oh, right. I did read and wrote so much the whole day - do I didn't look to your snippet again :oops:

I was surprised to see a game that was rendering even slower (20 fps on a 60Hz flatscreen), but just because they made sure that every frame took 50ms to render it gave the impression of smooth animations.
Maybe it's like movies in the cinema. There is also a slow fps rate (I think 23), but it's looking well.

The best framerate for a 60Hz flatscreen is 60 fps and for 100Hz it is 100 fps.
How can an application check the fps rate of the currently used screen setting? I didn't find a solution on a quick search.

Your last post I copied to the wiki - to keep this useful information in mind :wink:
I hope you had nice days on the Game Developer Conference!

smiley80

19-08-2010 00:26:53


How can an application check the fps rate of the currently used screen setting? I didn't find a solution on a quick search.

EnumDisplaySettings

mstoyke

19-08-2010 02:16:42

How can an application check the fps rate of the currently used screen setting? I didn't find a solution on a quick search.
There are more than one solutions to this problem. It depends if you use fullscreen or windowed mode. For fullscreen just get the information from the Ogre rendersystem, I don't have the code at hand but it should be fairly easy to do so.

For windowed mode there is a code snippet I found in my bookmarks for C# to obtain the primary display refresh rate:

string op = "";
System.Management.ManagementObjectSearcher searcher =
new System.Management.ManagementObjectSearcher("select * from win32_videocontroller");
foreach (System.Management.ManagementObject videocontroller in searcher.Get())
op += videocontroller["CurrentRefreshRate"] + " Hertz refresh rate\n";
MessageBox.Show(op);

I'm not sure if this will work properly in a multi monitor setup and I did not test the code yet, just copied it from a website.

mikael

19-08-2010 18:58:32

Just figured out, why many demo (from demoscene) asks in setup window that screen Hz, I think this information is actually used in "game loop". Maybe. So, creating very detailed config window (when advanced mode), that can setup all configs, might be that I'll do.

amirabiri

24-08-2010 23:17:09

I don't know if this was mentioned already (sorry, was too lazy to read the whole thread...), but here is the MSDN page on Stopwatch:

http://msdn.microsoft.com/en-us/library ... watch.aspx

It mentions QueryPerformanceFrequency and also mentions the multi-processor problem that mstoyke described, and how to overcome it.

Beauty

04-09-2010 23:11:05

Good note - thanks.

On a multiprocessor computer, it does not matter which processor the thread runs on. However, because of bugs in the BIOS or the Hardware Abstraction Layer (HAL), you can get different timing results on different processors. To specify processor affinity for a thread, use the ProcessThread.ProcessorAffinity method
The method ProcessThread.ProcessorAffinity is also available for .NET 2.0 :D

Day7

21-09-2010 17:57:31

I was searching over inet about Application.DoEvents() because it does some hit to performance and I found those, maybe someone will find it useful:

Reason why not use DoEvents(): http://blogs.msdn.com/b/tmiller/archive ... 57524.aspx
Solution: http://blogs.msdn.com/b/tmiller/archive ... 15008.aspx

Working CLR Profiler: http://www.microsoft.com/downloads/en/d ... laylang=en

// Edit //

Found also that SlimDX implemented it to its own library at SlimDX.Windows.MessagePump
Their tutorial: http://slimdx.org/tutorials/basicwindow.php

So i tried and it does allocate like none of memory compared to Application.DoEvents();

mstoyke

21-09-2010 18:45:22

Reason why not use DoEvents(): http://blogs.msdn.com/b/tmiller/archive ... 57524.aspx
Solution: http://blogs.msdn.com/b/tmiller/archive ... 15008.aspx


Thanks for these links, this is indeed very useful to know if you want to keep a stable framerate in your game.

Beauty

21-09-2010 23:22:43

Yes, interesting links. Thanks :D

In one of the (many) comments I found also some code with the title Game loop in C#. (posted in 2009)
I don't know about the quality, but it seems to be ready for just copy & test it.

Found also that SlimDX implemented it to its own library at SlimDX.Windows.MessagePump
Their tutorial: http://slimdx.org/tutorials/basicwindow.php

Is this also usable for Mogre applications, just by a linked SlimDX.dll file?


In the SlimDX tutorial I read:
Render a frame during idle time (no messages are waiting)
What's the advantage to call RenderFrame() only when the message is empty?
Why this should be faster?
How should this cause a smoother framerate?

For me it's looking that each RenderFrame() would be called indirectly by the event Application.Idle.
But how often will this event be called?
For this way I don't have any idea how to reach for a specific frame rate.

Day7

22-09-2010 07:27:36

Is this also usable for Mogre applications, just by a linked SlimDX.dll file?

Yes, but it requires form eg.

SlimDX.Windows.MessagePump.Run(form, () =>
{
DoWhileAppIdling();
}


In the SlimDX tutorial I read:
Render a frame during idle time (no messages are waiting)
What's the advantage to call RenderFrame() only when the message is empty?
Why this should be faster?
How should this cause a smoother framerate?

By doing RenderFrame() when there are no messages to app, You dont need to use DoEvents()
but they are handled anyway, it does not waste time to allocate memory and stuff it just render,
check events and whatever You wish to.

For me it's looking that each RenderFrame() would be called indirectly by the event Application.Idle.
But how often will this event be called?
For this way I don't have any idea how to reach for a specific frame rate.


All the time when application does nothing.

Beauty You can simply compare it. Get CLR Profiler, write 2 apps one with loop, DoEvents() inside and changes winform label (a++; :D)or something,
another with MessagePump and change winform label when application is idling. Close it after 15 seconds or so - let the CLR Profiler show You results.
Its not proper way to show the difference but should be enough.

Beauty

22-09-2010 11:32:00

For this way I don't have any idea how to reach for a specific frame rate.
I thought about how to combine the code snippet (to get a specific frame rate) of user Day7 with the Event way of the SlimDX tutorial example.

The problem was: How to combine the 2 while loops with the OnApplicationIdle event.
Then I had the idea to change the inner while loop to an if query.

Here is a simplified code of my modification:
It's for a better overview of my changes.
For modificated lines I added the comment // ---MOD---

while (RenderRunning) // do false to stop
{
// Loops = 0; // moved to ELSE block // ---MOD---
// changed "while" to "if" // ---MOD---
if (stopwatch.ElapsedMilliseconds > NextFrame && Loops < MaxFrameSkip) // ---MOD---
{
// ... (game update)
}
else // ---MOD---
{
Loops = 0; // ---MOD---
root.RenderOneFrame();
}
} // while


Now I just have one while loop and can put the content to the OnApplicationIdle() event handler method.
Alternatively this method could be clamped to the callback of MessagePump.Run(form, RenderFrame);


Here is the complete copy of my modificated source code:


Stopwatch stopwatch = new Stopwatch();

long UpdateFPS = 50; // Use 25, 40 or 50 - GameUpdate per second (so You can stay with integer)
long MaxFPS = 120; // No need to comment
long MaxFrameSkip = 10; // Limit to GameUpdate loop before each RenderOneFrame()

long SkipUpdate = 1000 / UpdateFPS;
float SkipFrame = 1000f / MaxFPS;

long Loops = 0;
float LastFrame = 0;
float NextFrame = 0;

Loops = 0; // init value // ---MOD---

stopwatch.Start();

RenderRunning = true; // public,

while (RenderRunning) // do false to stop
{
// Loops = 0; // moved to ELSE block // ---MOD---

// changed "while" to "if" // ---MOD---
if (stopwatch.ElapsedMilliseconds > NextFrame && Loops < MaxFrameSkip) // ---MOD---
{
// put here game update function

// Application.DoEvents(); // ---MOD---
NextFrame += SkipUpdate;
Loops++;
}
else // ---MOD---
{ // ---MOD---
Loops = 0; // ---MOD---
switch (MaxFPS) // if MaxFPS is 0 then its unlimited
{
case 0:
root.RenderOneFrame();
break;
default:
if (stopwatch.Elapsed.TotalMilliseconds >= LastFrame + SkipFrame)
{
LastFrame = (float)stopwatch.Elapsed.TotalMilliseconds;
// Im not sure if float is better than double
// need to research if double->float convertion is better than using double instead of float

root.RenderOneFrame();

// put here graphic update if needed
}
break;
} // switch
} // ---MOD---

} // while

stopwatch.Reset();


Note:
The first part (declarations and start of stopwatch) has to be done before the rendering start.

So this is the result of 1.5 hours coding and reporting.
I didn't test it and don't know if it's correct.
But maybe somebody else like and try it. (Feedback is welcome.) :D

Beauty

22-09-2010 11:42:48

@mstoyke:
Do you think it could be useful to insert a Thread.Sleep(0) call when the "game loop" is done by the Application.Idle event?
Maybe the scheduler recognizes, that the time between 2 idle events is a good moment.
(I mean after finish of OnApplicationIdle() and before the next fire of Application.Idle.)

mstoyke

22-09-2010 19:14:34

Do you think it could be useful to insert a Thread.Sleep(0) call when the "game loop" is done by the Application.Idle event?
Maybe the scheduler recognizes, that the time between 2 idle events is a good moment.
(I mean after finish of OnApplicationIdle() and before the next fire of Application.Idle.)


Calling Thread.Sleep(0) should only be used if you also want to limit the framerate and use vsync. To determine the best place in this renderloop that uses the Idle event you should use a profiler.

If you just want to use a quick-and-dirty way to place the Sleep(0), disable framerate and fps-limiter, then place the Sleep in different places in the loop and measure the framerate. Then leave the Sleep in the place that provided the highest frame rate and re-enable vsync and fps-limiter.

I've never used the idle event, because I only use .NET Forms in the editor where I don't care about framerate. In a game I would use the Ogre render window and the Ogre messagepump, so there should be no performance hit from any managed message queue handling.

Day7

22-09-2010 19:58:14

10 seconds of the same application using DoEvents and MessagePump instead

Beauty

22-09-2010 23:07:42

Do you know a free profiler for C#?
(This would be very interesting for my sonar simulation application.)
I heard there is one in the Visual Studio Team edition, but I only have the professional edition.

The ANTS profiler looks very useful, but it's a commercial product. (I don't have much money.)
In the past they had nice screenshots on their product page. There I saw the source code and beside of each line of code there was a bar chart, which size represented the CPU usage (in %).

Day7 - which tool you used in your screenshots?

In a game I would use the Ogre render window and the Ogre messagepump
Do you mean Ogre::PlatformManager::messagePump(RenderWindow * rw)?
Or maybe Ogre::WindowEventUtilities::messagePump()?

Day7

22-09-2010 23:20:20

The one from link i gave ? :D

mstoyke means Mogre.WindowEventUtilities.MessagePump(); i bet

Beauty

22-09-2010 23:23:55

Oh, right - I didn't recognized. Maybe I was to tired at night. :lol:
Working CLR Profiler: http://www.microsoft.com/downloads/en/d ... laylang=en

mstoyke

23-09-2010 07:47:30

Do you know a free profiler for C#?
You could try SlimTune or just use performance counters to measure the execution times of your renderloop.

mstoyke means Mogre.WindowEventUtilities.MessagePump(); i bet
That's the one I use.

Day7

23-09-2010 12:29:18

Again better gameloop i believe:
public void StartRender()
{
_renderRunning = true;

double MaxUPS = 50;
long MaxFrameSkip = 10;

double SkipUpdate = 1000d / MaxUPS;
double SkipFrame = 1000d / Main.settings.maxFPS;

long Loops;
double LastFrame = 0;
double NextFrame = 0;

_stopwatch.Start();

while (_renderRunning)
{
Loops = 0;

while (_stopwatch.Elapsed.TotalMilliseconds >= NextFrame && Loops < MaxFrameSkip)
{
NextFrame += SkipUpdate;
Loops++;

// update game here

Mogre.WindowEventUtilities.MessagePump();
Thread.Sleep(0);
}


if (_stopwatch.Elapsed.TotalMilliseconds >= LastFrame + SkipFrame)
{
LastFrame = _stopwatch.Elapsed.TotalMilliseconds;

// update render here

_root.RenderOneFrame();
}

}

stopwatch.Reset();
}


Things changed:
- at game update loop changed ">" to ">=" so its closer to MaxUPS (updates per second =p)
- switched from long to double so its more precisely - i see no performance hit on pc at work, will check at home anyway
- moved variables at beginning of loop - max fps and ups are closer to one i set
- removed DoEvents(); , MessagePump() works better

Now if i set MaxFPS 75 its almost all the time 74.96~75.02, same with MaxUPS.

Beauty

23-09-2010 14:53:21

Oh, a nice and example with a good overview. :D

Calling Thread.Sleep(0) should only be used if you also want to limit the framerate and use vsync.
Is there a way to check by code it vsync is enabled?
If yes, we could extend the code of Day7.
Before the loop check the vsync state and write it to the Boolean flag vsyncUsage.
And inside of the loop write this:
if (vsyncUsage)
Thread.Sleep(0);

Day7

23-09-2010 16:55:27

I have a little idea how to put CPU usage down... but I need to think about it more.

How about to fill space when loop conditions are NOT meet with sleep? think about it, while i do too :D

so i tried this before game update loop:
if (_stopwatch.Elapsed.TotalMilliseconds + 2d < NextFrame && _stopwatch.Elapsed.TotalMilliseconds + 2d < LastFrame + SkipFrame)
{
Thread.Sleep(1);
}


Im pretty sure thats WRONG SOLUTION, but i need anything to research about it and heres result:

With max 75 FPS, 50 UPS:[attachment=0]test.jpg[/attachment] Anyway CPU goes crazy when i turn off fps limit and goes up to +10k FPS but thats ok

Ps. While using Sleep(1); be sure to set those:
[DllImport("winmm.dll", EntryPoint = "timeBeginPeriod")]
static extern uint MM_BeginPeriod(uint uMilliseconds);
[DllImport("winmm.dll", EntryPoint = "timeEndPeriod")]
static extern uint MM_EndPeriod(uint uMilliseconds);


MM_BeginPeriod(1); - before loop
MM_EndPeriod(1); - after

// edit //
Now i see nothing wrong in this... gosh im confused

mstoyke

23-09-2010 21:14:49

Ps. While using Sleep(1); be sure to set those:
[DllImport("winmm.dll", EntryPoint = "timeBeginPeriod")]
static extern uint MM_BeginPeriod(uint uMilliseconds);
[DllImport("winmm.dll", EntryPoint = "timeEndPeriod")]
static extern uint MM_EndPeriod(uint uMilliseconds);


MM_BeginPeriod(1); - before loop
MM_EndPeriod(1); - after

Try to use Sleep(0); instead, or even better Thread.Yield(); in .NET4, instead of Sleep(1); and leave MM_BeginPeriod() alone. The MM_*Period() functions have caused more trouble than help for me in the past and they are not meant to affect the scheduler timeslices in the way you need it for a game project.

Day7

23-09-2010 21:17:08

Sleep(0); does not take CPU usage down and Im willing to stay with .NET 2

How can Sleep(0); do that trick if thread is still working?