Browse Source

Implement a better timing mechanism for fixed framerate. Accuracy is

sub-millisecond but can be improved if we get rid of SDL timers.
legacy
Sam Hocevar sam 14 years ago
parent
commit
8bfb98c160
8 changed files with 183 additions and 28 deletions
  1. +1
    -1
      src/Makefile.am
  2. +1
    -1
      src/debugfps.cpp
  3. +5
    -8
      src/gtk/editor.cpp
  4. +5
    -12
      src/test-map.cpp
  5. +25
    -4
      src/ticker.cpp
  6. +3
    -2
      src/ticker.h
  7. +113
    -0
      src/timer.cpp
  8. +30
    -0
      src/timer.h

+ 1
- 1
src/Makefile.am View File

@@ -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`



+ 1
- 1
src/debugfps.cpp View File

@@ -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);


+ 5
- 8
src/gtk/editor.cpp View File

@@ -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;


+ 5
- 12
src/test-map.cpp View File

@@ -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();


+ 25
- 4
src/ticker.cpp View File

@@ -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;
}


+ 3
- 2
src/ticker.h View File

@@ -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__


+ 113
- 0
src/timer.cpp View File

@@ -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));
}


+ 30
- 0
src/timer.h View File

@@ -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__


Loading…
Cancel
Save