| @@ -8,7 +8,7 @@ libcommon_a_SOURCES = \ | |||||
| scene.cpp scene.h font.cpp font.h layer.cpp layer.h map.cpp map.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 \ | joystick.cpp joystick.h asset.cpp asset.h ticker.cpp ticker.h \ | ||||
| forge.cpp forge.h video.cpp video.h timer.cpp timer.h \ | forge.cpp forge.h video.cpp video.h timer.cpp timer.h \ | ||||
| debugfps.cpp debugfps.h | |||||
| profiler.cpp profiler.h debugfps.cpp debugfps.h | |||||
| libcommon_a_CXXFLAGS = `pkg-config --cflags sdl gl SDL_image` | libcommon_a_CXXFLAGS = `pkg-config --cflags sdl gl SDL_image` | ||||
| test_map_SOURCES = test-map.cpp sdlinput.cpp sdlinput.h | test_map_SOURCES = test-map.cpp sdlinput.cpp sdlinput.h | ||||
| @@ -11,6 +11,7 @@ | |||||
| #include "debugfps.h" | #include "debugfps.h" | ||||
| #include "forge.h" | #include "forge.h" | ||||
| #include "profiler.h" | |||||
| /* | /* | ||||
| * DebugFps implementation class | * DebugFps implementation class | ||||
| @@ -20,10 +21,7 @@ class DebugFpsData | |||||
| { | { | ||||
| friend class DebugFps; | friend class DebugFps; | ||||
| static int const HISTORY = 30; | |||||
| private: | private: | ||||
| float history[HISTORY]; | |||||
| Font *font; | Font *font; | ||||
| int frame; | int frame; | ||||
| }; | }; | ||||
| @@ -36,8 +34,6 @@ DebugFps::DebugFps() | |||||
| { | { | ||||
| data = new DebugFpsData(); | data = new DebugFpsData(); | ||||
| for (int i = 0; i < DebugFpsData::HISTORY; i++) | |||||
| data->history[i] = 0.0f; | |||||
| data->font = Forge::GetFont("gfx/font/ascii.png"); | data->font = Forge::GetFont("gfx/font/ascii.png"); | ||||
| data->frame = 0; | data->frame = 0; | ||||
| } | } | ||||
| @@ -51,24 +47,33 @@ void DebugFps::TickRender(float delta_time) | |||||
| { | { | ||||
| Asset::TickGame(delta_time); | Asset::TickGame(delta_time); | ||||
| data->history[data->frame % DebugFpsData::HISTORY] = delta_time; | |||||
| data->frame++; | data->frame++; | ||||
| float mean = 0.0f, max = 0.0f; | |||||
| for (int i = 0; i < DebugFpsData::HISTORY; i++) | |||||
| { | |||||
| mean += data->history[i]; | |||||
| if (data->history[i] > max) | |||||
| max = data->history[i]; | |||||
| } | |||||
| mean /= DebugFpsData::HISTORY; | |||||
| char buf[1024]; | char buf[1024]; | ||||
| sprintf(buf, "%3.2f ms (%3.2f fps) -- max %3.2f ms -- #%i", | |||||
| 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); | |||||
| sprintf(buf, "%2.2f fps (%i)", | |||||
| 1.0f / Profiler::GetMean(Profiler::STAT_TICK_FRAME), data->frame); | |||||
| data->font->PrintBold(10, 10, buf); | |||||
| sprintf(buf, "Game % 7.2f % 7.2f", | |||||
| 1e3f * Profiler::GetMean(Profiler::STAT_TICK_GAME), | |||||
| 1e3f * Profiler::GetMax(Profiler::STAT_TICK_GAME)); | |||||
| data->font->PrintBold(10, 28, buf); | |||||
| sprintf(buf, "Render % 7.2f % 7.2f", | |||||
| 1e3f * Profiler::GetMean(Profiler::STAT_TICK_RENDER), | |||||
| 1e3f * Profiler::GetMax(Profiler::STAT_TICK_RENDER)); | |||||
| data->font->PrintBold(10, 46, buf); | |||||
| sprintf(buf, "Blit % 7.2f % 7.2f", | |||||
| 1e3f * Profiler::GetMean(Profiler::STAT_TICK_BLIT), | |||||
| 1e3f * Profiler::GetMax(Profiler::STAT_TICK_BLIT)); | |||||
| data->font->PrintBold(10, 64, buf); | |||||
| sprintf(buf, "Frame % 7.2f % 7.2f", | |||||
| 1e3f * Profiler::GetMean(Profiler::STAT_TICK_FRAME), | |||||
| 1e3f * Profiler::GetMax(Profiler::STAT_TICK_FRAME)); | |||||
| data->font->PrintBold(10, 82, buf); | |||||
| } | } | ||||
| DebugFps::~DebugFps() | DebugFps::~DebugFps() | ||||
| @@ -77,7 +77,8 @@ static gint draw(GtkWidget *widget, GdkEventExpose *event) | |||||
| { | { | ||||
| ticking = 0; | ticking = 0; | ||||
| /* Clear the screen, tick the renderer, and show the frame */ | |||||
| /* Clear the screen, tick the renderer, show the frame and | |||||
| * clamp to desired framerate. */ | |||||
| Video::Clear(); | Video::Clear(); | ||||
| Ticker::TickRender(); | Ticker::TickRender(); | ||||
| gtk_gl_area_swapbuffers(GTK_GL_AREA(widget)); | gtk_gl_area_swapbuffers(GTK_GL_AREA(widget)); | ||||
| @@ -0,0 +1,80 @@ | |||||
| // | |||||
| // 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> | |||||
| #include "profiler.h" | |||||
| #include "timer.h" | |||||
| /* | |||||
| * Profiler implementation class | |||||
| */ | |||||
| static class ProfilerData | |||||
| { | |||||
| friend class Profiler; | |||||
| static int const HISTORY = 30; | |||||
| public: | |||||
| ProfilerData() | |||||
| { | |||||
| for (int i = 0; i < HISTORY; i++) | |||||
| history[i] = 0.0f; | |||||
| frame = 0; | |||||
| mean = max = 0.0f; | |||||
| } | |||||
| private: | |||||
| float history[HISTORY]; | |||||
| Timer timer; | |||||
| int frame; | |||||
| float mean, max; | |||||
| } | |||||
| data[Profiler::STAT_COUNT]; | |||||
| /* | |||||
| * Profiler public class | |||||
| */ | |||||
| void Profiler::Start(int id) | |||||
| { | |||||
| data[id].timer.GetSeconds(); | |||||
| } | |||||
| void Profiler::Stop(int id) | |||||
| { | |||||
| float delta_time = data[id].timer.GetSeconds(); | |||||
| data[id].history[data->frame % ProfilerData::HISTORY] = delta_time; | |||||
| data[id].frame++; | |||||
| data[id].mean = 0.0f; | |||||
| data[id].max = 0.0f; | |||||
| for (int i = 0; i < ProfilerData::HISTORY; i++) | |||||
| { | |||||
| data[id].mean += data[id].history[i]; | |||||
| if (data[id].history[i] > data[id].max) | |||||
| data[id].max = data[id].history[i]; | |||||
| } | |||||
| data[id].mean /= ProfilerData::HISTORY; | |||||
| } | |||||
| float Profiler::GetMean(int id) | |||||
| { | |||||
| return data[id].mean; | |||||
| } | |||||
| float Profiler::GetMax(int id) | |||||
| { | |||||
| return data[id].max; | |||||
| } | |||||
| @@ -0,0 +1,36 @@ | |||||
| // | |||||
| // Deus Hax (working title) | |||||
| // Copyright (c) 2010 Sam Hocevar <sam@hocevar.net> | |||||
| // | |||||
| // | |||||
| // The Profiler class | |||||
| // ------------------- | |||||
| // The Profiler is a static class that collects statistic counters. | |||||
| // | |||||
| #if !defined __DH_PROFILER_H__ | |||||
| #define __DH_PROFILER_H__ | |||||
| #include <stdint.h> | |||||
| class Profiler | |||||
| { | |||||
| public: | |||||
| enum | |||||
| { | |||||
| STAT_TICK_FRAME = 0, | |||||
| STAT_TICK_GAME, | |||||
| STAT_TICK_RENDER, | |||||
| STAT_TICK_BLIT, | |||||
| STAT_COUNT | |||||
| }; | |||||
| static void Start(int id); | |||||
| static void Stop(int id); | |||||
| static float GetMean(int id); | |||||
| static float GetMax(int id); | |||||
| }; | |||||
| #endif // __DH_PROFILER_H__ | |||||
| @@ -16,6 +16,7 @@ | |||||
| #include "debugfps.h" | #include "debugfps.h" | ||||
| #include "game.h" | #include "game.h" | ||||
| #include "ticker.h" | #include "ticker.h" | ||||
| #include "profiler.h" | |||||
| #include "video.h" | #include "video.h" | ||||
| static float const FPS = 30.0f; | static float const FPS = 30.0f; | ||||
| @@ -56,12 +57,11 @@ int main(int argc, char **argv) | |||||
| /* Tick the game */ | /* Tick the game */ | ||||
| Ticker::TickGame(); | Ticker::TickGame(); | ||||
| /* Clear the screen, tick the renderer, and show the frame */ | |||||
| /* Clear the screen, tick the renderer, show the frame and | |||||
| * clamp to desired framerate. */ | |||||
| Video::Clear(); | Video::Clear(); | ||||
| Ticker::TickRender(); | Ticker::TickRender(); | ||||
| SDL_GL_SwapBuffers(); | SDL_GL_SwapBuffers(); | ||||
| /* Clamp to desired framerate */ | |||||
| Ticker::ClampFps(FPS); | Ticker::ClampFps(FPS); | ||||
| } | } | ||||
| @@ -11,6 +11,7 @@ | |||||
| #include <cstdio> | #include <cstdio> | ||||
| #include <stdint.h> | #include <stdint.h> | ||||
| #include "profiler.h" | |||||
| #include "ticker.h" | #include "ticker.h" | ||||
| #include "asset.h" | #include "asset.h" | ||||
| #include "timer.h" | #include "timer.h" | ||||
| @@ -30,7 +31,6 @@ public: | |||||
| { | { | ||||
| for (int i = 0; i < Asset::GROUP_COUNT; i++) | for (int i = 0; i < Asset::GROUP_COUNT; i++) | ||||
| list[i] = NULL; | list[i] = NULL; | ||||
| timer = new Timer(); | |||||
| bias = 0.0f; | bias = 0.0f; | ||||
| } | } | ||||
| @@ -40,7 +40,6 @@ public: | |||||
| if (nassets) | if (nassets) | ||||
| fprintf(stderr, "ERROR: still %i assets in ticker\n", nassets); | fprintf(stderr, "ERROR: still %i assets in ticker\n", nassets); | ||||
| #endif | #endif | ||||
| delete timer; | |||||
| } | } | ||||
| private: | private: | ||||
| @@ -49,10 +48,9 @@ private: | |||||
| Asset *list[Asset::GROUP_COUNT]; | Asset *list[Asset::GROUP_COUNT]; | ||||
| int nassets; | int nassets; | ||||
| /* FPS management */ | |||||
| float delta_time; | |||||
| Timer *timer; | |||||
| float bias; | |||||
| /* Fixed framerate management */ | |||||
| Timer timer; | |||||
| float delta_time, bias; | |||||
| } | } | ||||
| tickerdata; | tickerdata; | ||||
| @@ -73,7 +71,11 @@ void Ticker::Register(Asset *asset) | |||||
| void Ticker::TickGame() | void Ticker::TickGame() | ||||
| { | { | ||||
| data->delta_time = data->timer->GetSeconds(); | |||||
| Profiler::Stop(Profiler::STAT_TICK_FRAME); | |||||
| Profiler::Start(Profiler::STAT_TICK_FRAME); | |||||
| Profiler::Start(Profiler::STAT_TICK_GAME); | |||||
| data->delta_time = data->timer.GetSeconds(); | |||||
| data->bias += data->delta_time; | data->bias += data->delta_time; | ||||
| /* Insert waiting objects in the appropriate lists */ | /* Insert waiting objects in the appropriate lists */ | ||||
| @@ -107,22 +109,31 @@ void Ticker::TickGame() | |||||
| for (Asset *a = data->list[i]; a; a = a->next) | for (Asset *a = data->list[i]; a; a = a->next) | ||||
| if (!a->destroy) | if (!a->destroy) | ||||
| a->TickGame(data->delta_time); | a->TickGame(data->delta_time); | ||||
| Profiler::Stop(Profiler::STAT_TICK_GAME); | |||||
| } | } | ||||
| void Ticker::TickRender() | void Ticker::TickRender() | ||||
| { | { | ||||
| Profiler::Start(Profiler::STAT_TICK_RENDER); | |||||
| /* Tick objects for the render loop */ | /* Tick objects for the render loop */ | ||||
| for (int i = 0; i < Asset::GROUP_COUNT; i++) | for (int i = 0; i < Asset::GROUP_COUNT; i++) | ||||
| for (Asset *a = data->list[i]; a; a = a->next) | for (Asset *a = data->list[i]; a; a = a->next) | ||||
| if (!a->destroy) | if (!a->destroy) | ||||
| a->TickRender(data->delta_time); | a->TickRender(data->delta_time); | ||||
| Profiler::Stop(Profiler::STAT_TICK_RENDER); | |||||
| Profiler::Start(Profiler::STAT_TICK_BLIT); | |||||
| } | } | ||||
| void Ticker::ClampFps(float fps) | void Ticker::ClampFps(float fps) | ||||
| { | { | ||||
| float ideal_time = 1.0f / fps; | |||||
| Profiler::Stop(Profiler::STAT_TICK_BLIT); | |||||
| data->timer->WaitSeconds(ideal_time - data->bias); | |||||
| float ideal_time = 1.0f / fps; | |||||
| if (ideal_time > data->bias) | |||||
| data->timer.WaitSeconds(ideal_time - data->bias); | |||||
| data->bias -= ideal_time; | data->bias -= ideal_time; | ||||
| } | } | ||||