Hi,
I'm one of the Zelda Classic developers, and have noticed that lately our project is suffering from mysterious performance problems. About once a second, there's a lag spike of about 20 milliseconds, and we're sure the problem is not with our game logic code: using timers I've determined that the spike occurs pretty much at random in the game loop. As a test I changed the timing code, which used to use wait() to maintain 60 FPS, to a busy loop, and the spike occasionally occurred inside this busy loop, when the game thread clearly wasn't doing anything at all.
This problem definitely occurs in Windows XP, and one of the other developers is reporting this problem on OS X as well. The problem is much more pronounced when triple buffering is turned on, if that help at all.
Do you guys have any idea what is causing this problem, or how to fix it?
Not without seeing code.
Things to check are, make sure you call release_bitmap whenever you call acquire_bitmap (and don't use any non-drawing routines between them), don't use rest() or the like to time your main loop (use timers), etc...
Can you show us your 'busy loop'?
EDIT: What are the code tags on this board?
Here's how it was for testing:
[CODE]
...
LOCK_VARIABLE(logic_counter);
LOCK_FUNCTION(update_logic_counter);
install_int_ex(update_logic_counter, BPS_TO_TIMER(60));
...
void update_logic_counter()
{
++logic_counter;
} END_OF_FUNCTION(update_logic_counter)
void throttleFPS() {
if(Throttlefps ^ key[KEY_TILDE])
{
while(logic_counter < 1);
}
logic_counter = 0;
}
[/CODE]
Here's how it is normally:
[CODE]
void update_logic_counter()
{
++logic_counter;
} END_OF_FUNCTION(update_logic_counter)
void throttleFPS() {
if(Throttlefps ^ key[KEY_TILDE])
{
while(logic_counter < 1)
rest(1);
}
logic_counter = 0;
}
[/CODE]
As you can see, we do use rest() to control the timing, as I don't like pegging the CPU while I wait between frames; is this a bad idea?
EDIT: What are the code tags on this board?
Check the HELP link above the area where you type your message. In particular, they are lower case.
Your code looks okish, however, I would do it slightly differently:
| 1 | |
| 2 | static volatile int gameticks; |
| 3 | |
| 4 | static void inc_game_counter(void) |
| 5 | { |
| 6 | gameticks++; |
| 7 | } |
| 8 | END_OF_STATIC_FUNCTION(inc_game_counter); |
| 9 | |
| 10 | /* And then in, say, main(): */ |
| 11 | |
| 12 | gameticks = 0; |
| 13 | do { |
| 14 | if (gfx_changed) { |
| 15 | update_gfx(); |
| 16 | } else { |
| 17 | rest(); |
| 18 | } |
| 19 | |
| 20 | while (gameticks) { |
| 21 | run_logic(); |
| 22 | gameticks--; |
| 23 | } |
| 24 | |
| 25 | } while (playgame); |
It's pseudocode, but it's almost exactly the code I use.
Changing the timer to fire once per millisecond, and replacing the timing code with the following
I get the following results:
Before waiting the counter's around 15-17, as I'm using triple buffering which apparently automatically vsyncs. After waiting the counter is usually 17, but occasionally 30-40.
Changing the timer to fire once per millisecond
You cannot get this level of timing with Allegro timers. Most OS's return about 10 millisecond precision. I think there are some native calls you can make to get better results.
Fair enough.
static long lastlow; void throttleFPS() { LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); LARGE_INTEGER currtime; QueryPerformanceCounter(&currtime); while(logic_counter < 1) rest(1); QueryPerformanceCounter(&currtime); long newtime = (currtime.LowPart*1000)/freq.LowPart; lastlow = newtime; logic_counter = 0; }
The following is an excerpt of the output:
before: 7
after: 16
before: 8
after: 17
before: 7
after: 17
before: 6
after: 31
before: 3
after: 3
before: 5
after: 16
before: 5
after: 17
before: 4
after: 16
before: 4
after: 18
before: 3
after: 16
before: 3
after: 16
before: 2
after: 17
before: 2
after: 32
before: 1
after: 1
before: 2
after: 17
before: 2
after: 16
before: 2
after: 51
before: 2
after: 34
before: 20
after: 33
before: 2
after: 16
before: 2
after: 17
before: 1
after: 26
Overall what I'd expect, with some anomalies, as you can see, near the end, causing choppy game play.
Replace rest(1) with rest(0). That should do it.
However, doing this will cause CPU usage to spike. (As you are no longer giving up big chunks of CPU time to the OS.)
Calling rest(1) can lose anywhere from 1 ms to over 20 ms to the OS, which will completely mess with your timing.
--- Kris Asick (Gemini)
--- http://www.pixelships.com
That seems to have helped! Thank you.