sub-millisecond but can be improved if we get rid of SDL timers.legacy
@@ -7,7 +7,7 @@ libcommon_a_SOURCES = \ | |||
game.cpp game.h tiler.cpp tiler.h tileset.cpp tileset.h \ | |||
scene.cpp scene.h font.cpp font.h layer.cpp layer.h map.cpp map.h \ | |||
joystick.cpp joystick.h asset.cpp asset.h ticker.cpp ticker.h \ | |||
forge.cpp forge.h video.cpp video.h \ | |||
forge.cpp forge.h video.cpp video.h timer.cpp timer.h \ | |||
debugfps.cpp debugfps.h | |||
libcommon_a_CXXFLAGS = `pkg-config --cflags sdl gl SDL_image` | |||
@@ -65,7 +65,7 @@ void DebugFps::TickRender(float delta_time) | |||
char buf[1024]; | |||
sprintf(buf, "%3.2f ms (%3.2f fps) -- max %3.2f ms -- #%i", | |||
mean, 1000.0f / mean, max, data->frame); | |||
1000.0f * mean, 1.0f / mean, 1000.0f * max, data->frame); | |||
data->font->Print(10, 10, buf); | |||
data->font->Print(11, 10, buf); | |||
data->font->Print(10, 11, buf); | |||
@@ -21,9 +21,8 @@ | |||
static volatile int quit = 0; | |||
static GTimer *timer; | |||
static float delta_time; | |||
static int ticking = 0; | |||
static float const FPS = 30.0f; | |||
static gint main_quit(GtkWidget *widget, GdkEventExpose *event) | |||
{ | |||
@@ -38,8 +37,6 @@ static gint main_quit(GtkWidget *widget, GdkEventExpose *event) | |||
static gboolean tick(void *widget) | |||
{ | |||
// FIXME: do not do anything if the previous tick was too recent? | |||
delta_time = 1000.0f * g_timer_elapsed(timer, NULL); | |||
g_timer_start(timer); | |||
// FIXME: only quit if all assets have been cleaned | |||
if (quit) | |||
@@ -48,7 +45,7 @@ static gboolean tick(void *widget) | |||
ticking = 1; | |||
/* Tick the game */ | |||
Ticker::TickGame(delta_time); | |||
Ticker::TickGame(); | |||
gtk_widget_draw(GTK_WIDGET(widget), NULL); | |||
@@ -82,8 +79,9 @@ static gint draw(GtkWidget *widget, GdkEventExpose *event) | |||
/* Clear the screen, tick the renderer, and show the frame */ | |||
Video::Clear(); | |||
Ticker::TickRender(delta_time); | |||
Ticker::TickRender(); | |||
gtk_gl_area_swapbuffers(GTK_GL_AREA(widget)); | |||
Ticker::ClampFps(FPS); | |||
} | |||
return TRUE; | |||
@@ -158,9 +156,8 @@ int main(int argc, char **argv) | |||
new DebugFps(); | |||
//gtk_idle_add(tick, glarea); | |||
gtk_timeout_add(33, tick, glarea); | |||
gtk_timeout_add(1000 / FPS, tick, glarea); | |||
timer = g_timer_new(); | |||
gtk_main(); | |||
return EXIT_SUCCESS; | |||
@@ -18,6 +18,8 @@ | |||
#include "ticker.h" | |||
#include "video.h" | |||
static int const FPS = 30.0f; | |||
int main(int argc, char **argv) | |||
{ | |||
/* Initialise SDL */ | |||
@@ -39,9 +41,6 @@ int main(int argc, char **argv) | |||
SDL_ShowCursor(0); | |||
SDL_WM_GrabInput(SDL_GRAB_ON); | |||
/* Initialise timer */ | |||
Uint32 ticks = SDL_GetTicks(); | |||
/* Initialise OpenGL */ | |||
Video::Setup(video->w, video->h); | |||
@@ -54,22 +53,16 @@ int main(int argc, char **argv) | |||
while (!game->Finished()) | |||
{ | |||
/* Compute delta time */ | |||
Uint32 newticks = SDL_GetTicks(); | |||
float delta_time = (float)(newticks - ticks); | |||
ticks = newticks; | |||
/* Tick the game */ | |||
Ticker::TickGame(delta_time); | |||
Ticker::TickGame(); | |||
/* Clear the screen, tick the renderer, and show the frame */ | |||
Video::Clear(); | |||
Ticker::TickRender(delta_time); | |||
Ticker::TickRender(); | |||
SDL_GL_SwapBuffers(); | |||
/* Clamp to desired framerate */ | |||
while (SDL_GetTicks() < ticks + (33.33333f - 0.5f)) | |||
SDL_Delay(1); | |||
Ticker::ClampFps(FPS); | |||
} | |||
SDL_Quit(); | |||
@@ -13,6 +13,7 @@ | |||
#include "ticker.h" | |||
#include "asset.h" | |||
#include "timer.h" | |||
/* | |||
* Ticker implementation class | |||
@@ -29,6 +30,8 @@ public: | |||
{ | |||
for (int i = 0; i < Asset::GROUP_COUNT; i++) | |||
list[i] = NULL; | |||
timer = new Timer(); | |||
bias = 0.0f; | |||
} | |||
~TickerData() | |||
@@ -37,12 +40,19 @@ public: | |||
if (nassets) | |||
fprintf(stderr, "ERROR: still %i assets in ticker\n", nassets); | |||
#endif | |||
delete timer; | |||
} | |||
private: | |||
/* Asset management */ | |||
Asset *todo; | |||
Asset *list[Asset::GROUP_COUNT]; | |||
int nassets; | |||
/* FPS management */ | |||
float delta_time; | |||
Timer *timer; | |||
float bias; | |||
} | |||
tickerdata; | |||
@@ -61,8 +71,11 @@ void Ticker::Register(Asset *asset) | |||
data->todo = asset; | |||
} | |||
void Ticker::TickGame(float delta_time) | |||
void Ticker::TickGame() | |||
{ | |||
data->delta_time = data->timer->GetSeconds(); | |||
data->bias += data->delta_time; | |||
/* Insert waiting objects in the appropriate lists */ | |||
while (data->todo) | |||
{ | |||
@@ -93,15 +106,23 @@ void Ticker::TickGame(float delta_time) | |||
for (int i = 0; i < Asset::GROUP_COUNT; i++) | |||
for (Asset *a = data->list[i]; a; a = a->next) | |||
if (!a->destroy) | |||
a->TickGame(delta_time); | |||
a->TickGame(data->delta_time); | |||
} | |||
void Ticker::TickRender(float delta_time) | |||
void Ticker::TickRender() | |||
{ | |||
/* Tick objects for the render loop */ | |||
for (int i = 0; i < Asset::GROUP_COUNT; i++) | |||
for (Asset *a = data->list[i]; a; a = a->next) | |||
if (!a->destroy) | |||
a->TickRender(delta_time); | |||
a->TickRender(data->delta_time); | |||
} | |||
void Ticker::ClampFps(float fps) | |||
{ | |||
float ideal_time = 1.0f / fps; | |||
data->timer->WaitSeconds(ideal_time - data->bias); | |||
data->bias -= ideal_time; | |||
} | |||
@@ -21,8 +21,9 @@ class Ticker | |||
public: | |||
static void Register(Asset *asset); | |||
static void TickGame(float delta_time); | |||
static void TickRender(float delta_time); | |||
static void TickGame(); | |||
static void TickRender(); | |||
static void ClampFps(float fps); | |||
}; | |||
#endif // __DH_TICKER_H__ | |||
@@ -0,0 +1,113 @@ | |||
// | |||
// Deus Hax (working title) | |||
// Copyright (c) 2010 Sam Hocevar <sam@hocevar.net> | |||
// | |||
#if defined HAVE_CONFIG_H | |||
# include "config.h" | |||
#endif | |||
#include <cstdlib> | |||
#include <cstdio> | |||
#include <stdint.h> | |||
#if defined __linux__ | |||
# include <sys/time.h> | |||
#elif defined _WIN32 | |||
# define WIN32_LEAN_AND_MEAN | |||
# include <windows.h> | |||
#else | |||
# include <SDL.h> | |||
#endif | |||
// XXX | |||
#include <SDL.h> | |||
#include "timer.h" | |||
/* | |||
* Timer implementation class | |||
*/ | |||
class TimerData | |||
{ | |||
friend class Timer; | |||
private: | |||
TimerData() | |||
{ | |||
#if defined __linux__ | |||
gettimeofday(&tv0, NULL); | |||
#elif defined _WIN32 | |||
LARGE_INTEGER tmp; | |||
QueryPerformanceFrequency(&tmp); | |||
seconds_per_cycle = 1.0f / tmp.QuadPart; | |||
QueryPerformanceCounter(&cycles0); | |||
#else | |||
SDL_Init(SDL_INIT_TIMER); | |||
ticks0 = SDL_GetTicks(); | |||
#endif | |||
} | |||
float GetSeconds(bool update) | |||
{ | |||
float ret; | |||
#if defined __linux__ | |||
struct timeval tv; | |||
gettimeofday(&tv, NULL); | |||
ret = 1e-6f * (tv.tv_usec - tv0.tv_usec) + (tv.tv_sec - tv0.tv_sec); | |||
if (update) | |||
tv0 = tv; | |||
#elif defined _WIN32 | |||
LARGE_INTEGER cycles; | |||
QueryPerformanceCounter(&cycles); | |||
ret = seconds_per_cycle * (cycles.QuadPart - cycles0.QuadPart); | |||
if (update) | |||
cycles0 = cycles; | |||
#else | |||
Uint32 ticks = SDL_GetTicks(); | |||
ret = 1e-3f * (ticks - ticks0); | |||
if (update) | |||
ticks0 = ticks; | |||
#endif | |||
return ret; | |||
} | |||
#if defined __linux__ | |||
struct timeval tv0; | |||
#elif defined _WIN32 | |||
float seconds_per_cycle; | |||
LARGE_INTEGER cycles0; | |||
#else | |||
Uint32 ticks0; | |||
#endif | |||
}; | |||
/* | |||
* Timer public class | |||
*/ | |||
Timer::Timer() | |||
{ | |||
data = new TimerData(); | |||
} | |||
Timer::~Timer() | |||
{ | |||
delete data; | |||
} | |||
float Timer::GetSeconds() | |||
{ | |||
return data->GetSeconds(true); | |||
} | |||
void Timer::WaitSeconds(float seconds) | |||
{ | |||
float sleep = seconds - data->GetSeconds(false); | |||
if (sleep <= 1e-4f) | |||
return; | |||
SDL_Delay((int)(sleep * 1000.0f + 0.5f)); | |||
} | |||
@@ -0,0 +1,30 @@ | |||
// | |||
// Deus Hax (working title) | |||
// Copyright (c) 2010 Sam Hocevar <sam@hocevar.net> | |||
// | |||
// | |||
// The Timer class | |||
// --------------- | |||
// | |||
#if !defined __DH_TIMER_H__ | |||
#define __DH_TIMER_H__ | |||
class TimerData; | |||
class Timer | |||
{ | |||
public: | |||
Timer(); | |||
~Timer(); | |||
float GetSeconds(); | |||
void WaitSeconds(float milliseconds); | |||
private: | |||
TimerData *data; | |||
}; | |||
#endif // __DH_TIMER_H__ | |||