|
|
View previous topic - View next topic |
Author |
Message |
tcaudilllg Dragonmaster
Joined: 20 Jun 2002 Posts: 1731 Location: Cedar Bluff, VA
|
Posted: Wed Sep 02, 2009 4:39 am Post subject: Frame skipping |
[quote] |
|
How is it done?
|
|
Back to top |
|
|
Hajo Demon Hunter
Joined: 30 Sep 2003 Posts: 779 Location: Between chair and keyboard.
|
Posted: Wed Sep 02, 2009 9:55 am Post subject: |
[quote] |
|
I only know this in the area of games that run at a fixed frame rate, e.g. 25 fps.
If the game notices that the computer is too slow to reach 25 fps, it starts skipping, every n-th frame. Starting with n=25 and lowering n till it can maintain a constant frame rate with enough CPU time left for the rest of the game.
It just means not to calculate/draw every n-th frame.
|
|
Back to top |
|
|
Ninkazu Demon Hunter
Joined: 08 Aug 2002 Posts: 945 Location: Location:
|
Posted: Wed Sep 02, 2009 12:38 pm Post subject: |
[quote] |
|
Hajo, I disagree. I've implemented my own frame skipping algorithm, and have had it critiqued by a couple former professional game developers (while at NVIDIA) - they said that mine was the standard technique.
The idea is to completely separate the three: game logic, sound and graphics rendering. Say I have a counter that ticks for every expected frame draw - this is accomplished, say, in Allegro by doing this
Code: | // in defines
#define TARGET_FPS 30
volatile int t;
void tick() { ++t; }
// in init
install_int_ex(tick, BPS_TO_TIMER(TARGET_FPS)); |
Whenever we get to starting another frame, we decrement t until it's 0, performing a frame's game logic each time. The reason for this is if the previous rendering took too long, we would have t > 1 and thus must skip frames to catch up.
In the main loop you can imagine that t == 0 is a common occurrence on fast computers, so you want to only perform game logic updates and rendering when t > 0. Otherwise you should yield the process so you don't take 100% CPU.
|
|
Back to top |
|
|
Hajo Demon Hunter
Joined: 30 Sep 2003 Posts: 779 Location: Between chair and keyboard.
|
Posted: Wed Sep 02, 2009 3:58 pm Post subject: |
[quote] |
|
Sounds better than my simple approach :)
|
|
Back to top |
|
|
DeveloperX 202192397
Joined: 04 May 2003 Posts: 1626 Location: Decatur, IL, USA
|
Posted: Thu Sep 03, 2009 1:26 am Post subject: |
[quote] |
|
Code: |
// main.cpp
// Project: Allegro Game Template v6.1
// Author: Richard Marks <ccpsceo@gmail.com>
/*
****************************************************************************
* Copyright (c) 2009, Richard Marks, CCPS Solutions, *
* Undefined Aeon Software. *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the *
* "Software"), to deal in the Software without restriction, including *
* without limitation the rights to use, copy, modify, merge, publish, *
* distribute, distribute with modifications, sub-license, and/or sell *
* copies of the Software, and to permit persons to whom the Software is *
* furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included *
* in all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. *
* IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
* *
* Except as contained in this notice, the name(s) of the above copyright *
* holders shall not be used in advertising or otherwise to promote the *
* sale, use or other dealings in this Software without prior written *
* authorization. *
****************************************************************************
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <allegro.h>
// we need this for handling timing
static volatile int allegrotimerspeedcounter = 0;
static void my_allegro_timer_speed_controller()
{
allegrotimerspeedcounter++;
}
END_OF_FUNCTION(my_allegro_timer_speed_controller)
// we need this to handle closing the window via the [X] button (you NEED this)
static volatile bool mainthreadisrunning = true;
static void my_allegro_close_button_handler()
{
mainthreadisrunning = false;
}
END_OF_FUNCTION(my_allegro_close_button_handler)
bool setup_game();
void shutdown_game();
// constants for the screen resolution
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;
// bitmap for drawing on
BITMAP* backbuffer = 0;
int main(int argc, char* argv[])
{
// init allegro and add keyboard and mouse support
allegro_init();
install_timer();
install_keyboard();
install_mouse();
// set the video mode to 800x600 @ 16bpp
set_color_depth(16);
set_gfx_mode(GFX_AUTODETECT_WINDOWED, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0);
set_window_title("Allegro Game Template v6.1 -- Richard Marks <ccpsceo@gmail.com>");
// create the back buffer bitmap
backbuffer = create_bitmap(SCREEN_W, SCREEN_H);
// seed the random number generator
srand(time(0));
// lock the static functions and variables we need for handling timing and closing the window via the [X] button
LOCK_FUNCTION(my_allegro_close_button_handler);
LOCK_FUNCTION(my_allegro_timer_speed_controller);
LOCK_VARIABLE(allegrotimerspeedcounter);
// set the callback function for the close-button to our global handler function
set_close_button_callback(my_allegro_close_button_handler);
// set our FPS lock timing global function
install_int_ex(my_allegro_timer_speed_controller, BPS_TO_TIMER(30));
// setup the game
if (!setup_game())
{
fprintf(stderr, "The game initialization has failed. Cannot continue!\n");
exit(1);
}
// main loop
bool gameover = false;
while(!gameover)
{
// if our global is ever false
if (!mainthreadisrunning)
{
gameover = true;
}
// we only update during our FPS lock time
while (allegrotimerspeedcounter > 0)
{
if (keyboard_needs_poll())
{
poll_keyboard();
}
if (key[KEY_ESC])
{
gameover = true;
}
// TODO: *** update stuff here
// decrement the global timing var so that we can leave the update loop!
allegrotimerspeedcounter--;
}
// start rendering the scene
clear_bitmap(backbuffer);
// TODO: *** draw stuff here
// make it all visible
blit(backbuffer, screen, 0, 0, 0, 0, backbuffer->w, backbuffer->h);
// a little rest to keep CPU usage down ^-^
rest(10);
}
// shutdown the game
shutdown_game();
// clean up the back buffer
if (backbuffer)
{
destroy_bitmap(backbuffer);
}
return 0;
}
END_OF_MAIN();
bool setup_game()
{
// load game resources and stuff here
// return false if something failed to load or init
// return true to let the main() function know that we're good to go
return true;
}
void shutdown_game()
{
// unallocate anything you allocated for your game here
}
|
Is what I usually use in my stuff. _________________ Principal Software Architect
Rambling Indie Games, LLC
See my professional portfolio
|
|
Back to top |
|
|
Ninkazu Demon Hunter
Joined: 08 Aug 2002 Posts: 945 Location: Location:
|
Posted: Thu Sep 03, 2009 12:53 pm Post subject: |
[quote] |
|
Yes, that would be what I suggested, however you draw too often. I suggest this modification
Code: |
if (allegrotimerspeedcounter > 0)
{
// we only update during our FPS lock time
while (allegrotimerspeedcounter > 0)
{
if (keyboard_needs_poll())
{
poll_keyboard();
}
if (key[KEY_ESC])
{
gameover = true;
}
// TODO: *** update stuff here
// decrement the global timing var so that we can leave the update loop!
allegrotimerspeedcounter--;
}
// start rendering the scene
clear_bitmap(backbuffer);
// TODO: *** draw stuff here
// make it all visible
blit(backbuffer, screen, 0, 0, 0, 0, backbuffer->w, backbuffer->h);
}
else
rest(1);
|
The reason for this is that you want to make sure you only draw when it is appropriate. Your code could possibly render a frame more than once, since allegrotimerspeedcounter == 0 is a possibility, updating no logic, but drawing the screen again. Next I changed rest(10) to rest(1) since rest(1) is the same as yield(), and is best for releasing the process. rest(10) could possibly rest too long and skip the possibility for an update. This is especially true on slower computers.
|
|
Back to top |
|
|
Mattias Gustavsson Mage
Joined: 10 Nov 2007 Posts: 457 Location: Royal Leamington Spa, UK
|
|
Back to top |
|
|
DeveloperX 202192397
Joined: 04 May 2003 Posts: 1626 Location: Decatur, IL, USA
|
Posted: Thu Sep 03, 2009 2:17 pm Post subject: |
[quote] |
|
@ Ninkazu: Thanks man. I typically use rest(10) to save my CPU, rest(1) doesn't do much for being nice to my computer for some reason...
@ Mattias: rest(0) effectively does nothing whatsoever from what I've seen. _________________ Principal Software Architect
Rambling Indie Games, LLC
See my professional portfolio
|
|
Back to top |
|
|
DeveloperX 202192397
Joined: 04 May 2003 Posts: 1626 Location: Decatur, IL, USA
|
Posted: Thu Sep 03, 2009 2:36 pm Post subject: |
[quote] |
|
Taking the above into consideration, I have put together a new template.
Code: |
// main.cpp
// Project: Allegro Game Template v6.2
// Author: Richard Marks <ccpsceo@gmail.com>
/*
****************************************************************************
* Copyright (c) 2009, Richard Marks, CCPS Solutions, *
* Undefined Aeon Software. *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the *
* "Software"), to deal in the Software without restriction, including *
* without limitation the rights to use, copy, modify, merge, publish, *
* distribute, distribute with modifications, sub-license, and/or sell *
* copies of the Software, and to permit persons to whom the Software is *
* furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included *
* in all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. *
* IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
* *
* Except as contained in this notice, the name(s) of the above copyright *
* holders shall not be used in advertising or otherwise to promote the *
* sale, use or other dealings in this Software without prior written *
* authorization. *
****************************************************************************
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <allegro.h>
////////////////////////////////////////////////////////////////////////////////
// we need this for handling timing
static volatile int allegrotimerspeedcounter = 0;
static void my_allegro_timer_speed_controller()
{
allegrotimerspeedcounter++;
}
END_OF_FUNCTION(my_allegro_timer_speed_controller)
// we need this to handle closing the window via the [X] button (you NEED this)
static volatile bool mainthreadisrunning = true;
static void my_allegro_close_button_handler()
{
mainthreadisrunning = false;
}
END_OF_FUNCTION(my_allegro_close_button_handler)
////////////////////////////////////////////////////////////////////////////////
bool setup_game();
void update_game();
void render_game();
void shutdown_game();
////////////////////////////////////////////////////////////////////////////////
// constants for the screen resolution
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;
const int WINDOW_COLOR_DEPTH = 16;
const bool WINDOW_USE_FULLSCREEN = false;
// the text in the window caption bar
const char* WINDOW_CAPTION = "Allegro Game Template v6.2 -- Richard Marks <ccpsceo@gmail.com>";
// if we want to enable the mouse
const bool ENABLE_MOUSE_SUPPORT = true;
// the FPS (frames per second) we should lock to
#define FRAME_LOCK_RATE 30
// bitmap for drawing on
BITMAP* backbuffer = 0;
////////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
// init allegro and add keyboard and optional mouse support
allegro_init();
install_timer();
install_keyboard();
if (ENABLE_MOUSE_SUPPORT)
{
install_mouse();
}
// set the video mode
set_color_depth(WINDOW_COLOR_DEPTH);
set_gfx_mode(
(WINDOW_USE_FULLSCREEN) ?
GFX_AUTODETECT_FULLSCREEN :
GFX_AUTODETECT_WINDOWED,
WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0);
// set the window caption text
set_window_title(WINDOW_CAPTION);
// create the back buffer bitmap
backbuffer = create_bitmap(SCREEN_W, SCREEN_H);
// seed the random number generator
srand(time(0));
// lock the static functions and variables we need for handling timing and closing the window via the [X] button
LOCK_FUNCTION(my_allegro_close_button_handler);
LOCK_FUNCTION(my_allegro_timer_speed_controller);
LOCK_VARIABLE(allegrotimerspeedcounter);
// set the callback function for the close-button to our global handler function
set_close_button_callback(my_allegro_close_button_handler);
// set our FPS lock timing global function
install_int_ex(my_allegro_timer_speed_controller, BPS_TO_TIMER(FRAME_LOCK_RATE));
// setup the game
if (!setup_game())
{
fprintf(stderr, "The game initialization has failed. Cannot continue!\n");
exit(1);
}
// main loop
bool gameover = false;
while(!gameover)
{
// if our global is ever false
if (!mainthreadisrunning)
{
gameover = true;
}
// we only draw when the FPS should be locked
if (allegrotimerspeedcounter > 0)
{
// we only update during our FPS lock time
while (allegrotimerspeedcounter > 0)
{
// ensure the keyboard data is current
if (keyboard_needs_poll())
{
poll_keyboard();
}
// ensure the mosue data is current
if (ENABLE_MOUSE_SUPPORT)
{
if (mouse_needs_poll())
{
poll_mouse();
}
}
// update
update_game();
// decrement the global timing var so that we can leave the update loop!
allegrotimerspeedcounter--;
}
// start rendering the scene
render_game();
if (ENABLE_MOUSE_SUPPORT)
{
show_mouse(backbuffer);
}
// make it all visible
blit(backbuffer, screen, 0, 0, 0, 0, backbuffer->w, backbuffer->h);
}
else
{
// a little rest to keep CPU usage down ^-^
rest(1);
}
}
// shutdown the game
shutdown_game();
// clean up the back buffer
if (backbuffer)
{
if (ENABLE_MOUSE_SUPPORT)
{
show_mouse(0);
}
destroy_bitmap(backbuffer);
}
return 0;
}
END_OF_MAIN();
////////////////////////////////////////////////////////////////////////////////
bool setup_game()
{
// load game resources and stuff here
// return false if something failed to load or init
// return true to let the main() function know that we're good to go
return true;
}
////////////////////////////////////////////////////////////////////////////////
void update_game()
{
// TODO: *** update stuff here
// pressing ESC will end the main loop
if (key[KEY_ESC])
{
mainthreadisrunning = false;
}
}
////////////////////////////////////////////////////////////////////////////////
void render_game()
{
clear_bitmap(backbuffer);
// TODO: *** draw stuff here
}
////////////////////////////////////////////////////////////////////////////////
void shutdown_game()
{
// deallocate anything you allocated for your game here
}
|
Thanks again Ninkazu. rest(1) worked for me in this use. :) _________________ Principal Software Architect
Rambling Indie Games, LLC
See my professional portfolio
|
|
Back to top |
|
|
Mattias Gustavsson Mage
Joined: 10 Nov 2007 Posts: 457 Location: Royal Leamington Spa, UK
|
Posted: Thu Sep 03, 2009 6:40 pm Post subject: |
[quote] |
|
From: http://alleg.sourceforge.net/stabledocs/en/alleg005.html
Quote: |
void rest(unsigned int time);
This function waits for the specified number of milliseconds.
Passing 0 as parameter will not wait, but just yield. This can be useful in order to "play nice" with other processes. Other values will cause CPU time to be dropped on most platforms. This will look better to users, and also does things like saving battery power and making fans less noisy.
Note that calling this inside your active game loop is a bad idea, as you never know when the OS will give you the CPU back, so you could end up missing the vertical retrace and skipping frames. On the other hand, on multitasking operating systems it is good form to give up the CPU for a while if you will not be using it.
|
But hey, I don't use it myself, so I don't know. Just thought that if "rest" works anything like "Sleep", it would mean that it waits for the specified number of milliseconds, or if 0 is specified yields the remainder of the slice. _________________ www.mattiasgustavsson.com - My blog
www.rivtind.com - My Fantasy world and isometric RPG engine
www.pixieuniversity.com - Software 2D Game Engine
|
|
Back to top |
|
|
Ninkazu Demon Hunter
Joined: 08 Aug 2002 Posts: 945 Location: Location:
|
Posted: Fri Sep 04, 2009 1:03 pm Post subject: |
[quote] |
|
You're right. Thanks.
|
|
Back to top |
|
|
|
Page 1 of 1 |
All times are GMT
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|