src/ItemStack.cc | src/ItemStack.cc | ||||
src/Mod.cc | src/Mod.cc | ||||
src/OS.cc | src/OS.cc | ||||
src/PerfCounter.cc | |||||
src/Resource.cc | src/Resource.cc | ||||
src/Tile.cc | src/Tile.cc | ||||
src/World.cc | src/World.cc |
#include "util.h" | |||||
#include <array> | |||||
namespace Swan { | |||||
class PerfCounter { | |||||
public: | |||||
template<typename T = double, size_t size = 64> | |||||
class Counter { | |||||
public: | |||||
void count(T val) { | |||||
buf_[idx_] = val; | |||||
idx_ = (idx_ + 1) % size; | |||||
} | |||||
// Fill a buffer with data, in the order they were written | |||||
template<typename DestT = T> | |||||
void fill(std::array<DestT, size> &buf) { | |||||
size_t r = idx_; | |||||
size_t w = 0; | |||||
do { | |||||
buf[w++] = (DestT)buf_[r]; | |||||
r = (r + 1) % size; | |||||
} while (r != idx_); | |||||
} | |||||
private: | |||||
T buf_[size] = {}; | |||||
size_t idx_ = 0; | |||||
}; | |||||
void render(); | |||||
void countTotalTime(double secs) { total_time_.count(secs); } | |||||
void countFrameTime(double secs) { frame_time_.count(secs); } | |||||
void countGameUpdate(double secs) { game_update_.count(secs); } | |||||
void countGameTick(double secs) { game_tick_.count(secs); } | |||||
void countGameDraw(double secs) { game_draw_.count(secs); } | |||||
void countGameUpdatesPerFrame(double count) { game_updates_per_frame_.count(count); } | |||||
void countRenderPresent(double secs) { render_present_.count(secs); } | |||||
void countMemoryUsage(double bytes) { memory_usage_.count(bytes); } | |||||
private: | |||||
Counter<> total_time_; | |||||
Counter<> frame_time_; | |||||
Counter<> game_update_; | |||||
Counter<> game_tick_; | |||||
Counter<> game_draw_; | |||||
Counter<> game_updates_per_frame_; | |||||
Counter<> render_present_; | |||||
Counter<> memory_usage_; | |||||
}; | |||||
} |
#include <swan/Item.h> | #include <swan/Item.h> | ||||
#include <swan/ItemStack.h> | #include <swan/ItemStack.h> | ||||
#include <swan/Mod.h> | #include <swan/Mod.h> | ||||
#include <swan/PerfCounter.h> | |||||
#include <swan/OS.h> | #include <swan/OS.h> | ||||
#include <swan/Resource.h> | #include <swan/Resource.h> | ||||
#include <swan/SlotVector.h> | #include <swan/SlotVector.h> |
#include "PerfCounter.h" | |||||
#include <imgui.h> | |||||
#include <imgui_plot.h> | |||||
#include "util.h" | |||||
namespace Swan { | |||||
void PerfCounter::render() { | |||||
Deferred<ImGui::End> win; | |||||
if (!ImGui::Begin("Perf Stats")) | |||||
return; | |||||
std::array<float, 64> buf; | |||||
ImGui::PlotConfig conf; | |||||
conf.values = { .ys = buf.data(), .count = 64 }; | |||||
conf.scale = { 0, 1 / 30.0 }; | |||||
conf.frame_size = { ImGui::GetWindowContentRegionWidth(), 30 }, | |||||
total_time_.fill(buf); | |||||
ImGui::Text("Total Time"); | |||||
ImGui::Plot("Total Time", conf); | |||||
render_present_.fill(buf); | |||||
ImGui::Text("Render Present"); | |||||
ImGui::Plot("Render Present", conf); | |||||
frame_time_.fill(buf); | |||||
ImGui::Text("Frame Times"); | |||||
ImGui::Plot("Frame Times", conf); | |||||
game_update_.fill(buf); | |||||
ImGui::Text("Game Update"); | |||||
ImGui::Plot("Game Update", conf); | |||||
game_draw_.fill(buf); | |||||
ImGui::Text("Game Draw"); | |||||
ImGui::Plot("Game Draw", conf); | |||||
game_tick_.fill(buf); | |||||
ImGui::Text("Game Tick"); | |||||
ImGui::Plot("Game Tick", conf); | |||||
game_updates_per_frame_.fill(buf); | |||||
conf.scale = { 0, 3 }; | |||||
ImGui::Text("Game Updates Per Frame"); | |||||
ImGui::Plot("Game Updates Per Frame", conf); | |||||
} | |||||
} |
std::vector<std::string> mods{ "core.mod" }; | std::vector<std::string> mods{ "core.mod" }; | ||||
game.createWorld("core::default", mods); | game.createWorld("core::default", mods); | ||||
PerfCounter pcounter; | |||||
auto prev_time = std::chrono::steady_clock::now(); | auto prev_time = std::chrono::steady_clock::now(); | ||||
float fps_acc = 0; | float fps_acc = 0; | ||||
RTClock update_clock; | RTClock update_clock; | ||||
if (dt <= 1 / 25.0) { | if (dt <= 1 / 25.0) { | ||||
ZoneScopedN("game update"); | ZoneScopedN("game update"); | ||||
pcounter.countGameUpdatesPerFrame(1); | |||||
game.update(dt); | game.update(dt); | ||||
// Complex case: run multiple steps this iteration | // Complex case: run multiple steps this iteration | ||||
} else { | } else { | ||||
int count = (int)ceil(dt / (1/30.0)); | int count = (int)ceil(dt / (1/30.0)); | ||||
pcounter.countGameUpdatesPerFrame(count); | |||||
float delta = dt / (float)count; | float delta = dt / (float)count; | ||||
info << "Delta time " << dt << "s. Running " << count | info << "Delta time " << dt << "s. Running " << count | ||||
<< " updates in one frame, with a delta as if we had " | << " updates in one frame, with a delta as if we had " | ||||
game.update(delta); | game.update(delta); | ||||
} | } | ||||
} | } | ||||
pcounter.countGameUpdate(update_clock.duration()); | |||||
// Tick at a consistent TICK_RATE | // Tick at a consistent TICK_RATE | ||||
tick_acc += dt; | tick_acc += dt; | ||||
tick_acc -= 1.0 / TICK_RATE; | tick_acc -= 1.0 / TICK_RATE; | ||||
RTClock tick_clock; | RTClock tick_clock; | ||||
game.tick(1.0 / TICK_RATE); | game.tick(1.0 / TICK_RATE); | ||||
pcounter.countGameTick(tick_clock.duration()); | |||||
} | } | ||||
{ | { | ||||
ZoneScopedN("game draw"); | ZoneScopedN("game draw"); | ||||
RTClock draw_clock; | RTClock draw_clock; | ||||
game.draw(); | game.draw(); | ||||
pcounter.countGameDraw(draw_clock.duration()); | |||||
} | } | ||||
pcounter.countFrameTime(total_time_clock.duration()); | |||||
pcounter.render(); | |||||
// Render ImGUI | // Render ImGUI | ||||
{ | { | ||||
ZoneScopedN("imgui render"); | ZoneScopedN("imgui render"); | ||||
SDL_RenderPresent(renderer.get()); | SDL_RenderPresent(renderer.get()); | ||||
} | } | ||||
FrameMark | FrameMark | ||||
pcounter.countRenderPresent(present_clock.duration()); | |||||
pcounter.countTotalTime(total_time_clock.duration()); | |||||
} | } | ||||
exit: | exit: |