diff --git a/rwengine/src/core/Profiler.hpp b/rwengine/src/core/Profiler.hpp index 5ae25d93..b3312dae 100644 --- a/rwengine/src/core/Profiler.hpp +++ b/rwengine/src/core/Profiler.hpp @@ -2,73 +2,22 @@ #define _RWENGINE_PROFILER_HPP_ #ifdef RW_PROFILER -#include -#include -#include -#include -#include -#define time_unit std::chrono::microseconds - -#include - -namespace perf { - -struct ProfileEntry { - std::string label; - int64_t start; - int64_t end; - std::vector childProfiles; -}; - -class Profiler { - ProfileEntry frame; - std::chrono::high_resolution_clock::time_point frameBegin; - std::stack currentStack; - -public: - static Profiler& get() { - static Profiler profile; - return profile; - } - - const ProfileEntry& getFrame() const { - return frame; - } - - void startFrame() { - frameBegin = std::chrono::high_resolution_clock::now(); - frame = {"Frame", 0, 0, {}}; - } - - void beginEvent(const std::string& label) { - auto now = std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - frameBegin); - currentStack.push({label, now.count(), 0, {}}); - } - - void endEvent() { - auto now = std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - frameBegin); - RW_CHECK(currentStack.size() > 0, "Perf stack is empty"); - currentStack.top().end = now.count(); - if (currentStack.size() == 1) { - frame.childProfiles.push_back(currentStack.top()); - currentStack.pop(); - } else { - auto tmp = currentStack.top(); - currentStack.pop(); - currentStack.top().childProfiles.push_back(std::move(tmp)); - } - } -}; -} -#define RW_PROFILE_FRAME_BOUNDARY() perf::Profiler::get().startFrame(); -#define RW_PROFILE_BEGIN(label) perf::Profiler::get().beginEvent(label); -#define RW_PROFILE_END() perf::Profiler::get().endEvent(); +#include +#define RW_PROFILE_THREAD(name) MicroProfileOnThreadCreate(name) +#define RW_PROFILE_FRAME_BOUNDARY() MicroProfileFlip(nullptr) +#define RW_PROFILE_SCOPE(label) MICROPROFILE_SCOPEI("Default", label, MP_YELLOW) +#define RW_PROFILE_COUNTER_ADD(name, qty) MICROPROFILE_COUNTER_ADD(name, qty) +#define RW_PROFILE_COUNTER_SET(name, qty) MICROPROFILE_COUNTER_SET(name, qty) +#define RW_TIMELINE_ENTER(name, color) MICROPROFILE_TIMELINE_ENTER_STATIC(color, name) +#define RW_TIMELINE_LEAVE(name) MICROPROFILE_TIMELINE_LEAVE_STATIC(name) #else -#define RW_PROFILE_FRAME_BOUNDARY() -#define RW_PROFILE_BEGIN(label) -#define RW_PROFILE_END() +#define RW_PROFILE_THREAD(name) do {} while (0) +#define RW_PROFILE_FRAME_BOUNDARY() do {} while (0) +#define RW_PROFILE_SCOPE(label) do {} while (0) +#define RW_PROFILE_COUNTER_ADD(name, qty) do {} while (0) +#define RW_PROFILE_COUNTER_SET(name, qty) do {} while (0) +#define RW_TIMELINE_ENTER(name, color) do {} while (0) +#define RW_TIMELINE_LEAVE(name) do {} while (0) #endif #endif diff --git a/rwengine/src/engine/GameData.cpp b/rwengine/src/engine/GameData.cpp index b6d5779e..2658e66b 100644 --- a/rwengine/src/engine/GameData.cpp +++ b/rwengine/src/engine/GameData.cpp @@ -18,6 +18,7 @@ #include #include "core/Logger.hpp" +#include "core/Profiler.hpp" #include "engine/GameState.hpp" #include "engine/GameWorld.hpp" #include "loaders/LoaderCOL.hpp" @@ -349,6 +350,7 @@ void GameData::loadWater(const std::string& path) { } void GameData::loadTXD(const std::string& name) { + RW_PROFILE_COUNTER_ADD("loadTXD", 1); auto slot = name; auto ext = name.find(".txd"); if (ext != std::string::npos) { @@ -368,6 +370,7 @@ void GameData::loadTXD(const std::string& name) { } TextureArchive GameData::loadTextureArchive(const std::string& name) { + RW_PROFILE_COUNTER_ADD("loadTextureArchive", 1); /// @todo refactor loadTXD to use correct file locations auto file = index.openFile(name); if (!file.data) { diff --git a/rwengine/src/render/GameRenderer.cpp b/rwengine/src/render/GameRenderer.cpp index f30d31e9..a3748dfe 100644 --- a/rwengine/src/render/GameRenderer.cpp +++ b/rwengine/src/render/GameRenderer.cpp @@ -306,9 +306,9 @@ void GameRenderer::renderWorld(GameWorld* world, const ViewCamera& camera, } void GameRenderer::renderObjects(const GameWorld *world) { - renderer->useProgram(worldProg.get()); + RW_PROFILE_SCOPE(__func__); - RW_PROFILE_BEGIN("RenderList"); + renderer->useProgram(worldProg.get()); // This is sequential at the moment, it should be easy to make it // run in parallel with a good threading system. @@ -316,8 +316,6 @@ void GameRenderer::renderObjects(const GameWorld *world) { // Naive optimisation, assume 50% hitrate renderList.reserve(world->allObjects.size() * 0.5f); - RW_PROFILE_BEGIN("Build"); - ObjectRenderer objectRenderer(_renderWorld, (cullOverride ? cullingCamera : _camera), _renderAlpha, getMissingTexture()); @@ -366,12 +364,10 @@ void GameRenderer::renderObjects(const GameWorld *world) { objectRenderer.renderClump(arrowModel.get(), model, nullptr, renderList); } - RW_PROFILE_END(); culled += objectRenderer.culled; renderer->pushDebugGroup("Objects"); renderer->pushDebugGroup("RenderList"); // Also parallelizable - RW_PROFILE_BEGIN("Sort"); // Earlier position in the array means earlier object's rendering // Transparent objects should be sorted and rendered after opaque sort(renderList.begin(), renderList.end(), @@ -383,15 +379,10 @@ void GameRenderer::renderObjects(const GameWorld *world) { return false; return (a.sortKey > b.sortKey); }); - RW_PROFILE_END(); - RW_PROFILE_BEGIN("Draw"); renderer->drawBatched(renderList); - RW_PROFILE_END(); renderer->popDebugGroup(); profObjects = renderer->popDebugGroup(); - - RW_PROFILE_END(); } void GameRenderer::renderSplash(GameWorld* world, GLuint splashTexName, glm::u16vec3 fc) { diff --git a/rwgame/RWGame.cpp b/rwgame/RWGame.cpp index 506d1a60..d348a341 100644 --- a/rwgame/RWGame.cpp +++ b/rwgame/RWGame.cpp @@ -45,6 +45,9 @@ RWGame::RWGame(Logger& log, int argc, char* argv[]) : GameBase(log, argc, argv) , data(&log, config.getGameDataPath()) , renderer(&log, &data) { + RW_PROFILE_THREAD("Main"); + RW_TIMELINE_ENTER("Startup", MP_YELLOW); + bool newgame = options.count("newgame"); bool test = options.count("test"); std::string startSave( @@ -106,6 +109,7 @@ RWGame::RWGame(Logger& log, int argc, char* argv[]) }); log.info("Game", "Started"); + RW_TIMELINE_LEAVE("Startup"); } RWGame::~RWGame() { @@ -401,38 +405,31 @@ int RWGame::run() { } float RWGame::tickWorld(const float deltaTime, float accumulatedTime) { + RW_PROFILE_SCOPE(__func__); auto deltaTimeWithTimeScale = deltaTime * world->state->basic.timeScale; - RW_PROFILE_BEGIN("Update"); while (accumulatedTime >= deltaTime) { if (!StateManager::currentState()) { break; } - RW_PROFILE_BEGIN("physics"); world->dynamicsWorld->stepSimulation( deltaTimeWithTimeScale, kMaxPhysicsSubSteps, deltaTime); - RW_PROFILE_END(); - RW_PROFILE_BEGIN("state"); StateManager::get().tick(deltaTimeWithTimeScale); - RW_PROFILE_END(); - RW_PROFILE_BEGIN("engine"); tick(deltaTimeWithTimeScale); - RW_PROFILE_END(); getState()->swapInputState(); accumulatedTime -= deltaTime; } - RW_PROFILE_END(); return accumulatedTime; } bool RWGame::updateInput() { - RW_PROFILE_BEGIN("Input"); + RW_PROFILE_SCOPE(__func__); SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { @@ -463,17 +460,16 @@ bool RWGame::updateInput() { GameInput::updateGameInputState(&getState()->input[0], event); - RW_PROFILE_BEGIN("State"); if (StateManager::currentState()) { + RW_PROFILE_SCOPE("State"); StateManager::currentState()->handleEvent(event); } - RW_PROFILE_END() } - RW_PROFILE_END(); return true; } void RWGame::tick(float dt) { + RW_PROFILE_SCOPE(__func__); State* currState = StateManager::get().states.back().get(); static float clockAccumulator = 0.f; @@ -569,8 +565,7 @@ void RWGame::tick(float dt) { } void RWGame::render(float alpha, float time) { - RW_PROFILE_BEGIN("Render"); - RW_PROFILE_BEGIN("engine"); + RW_PROFILE_SCOPE(__func__); lastDraws = getRenderer().getRenderer()->getDrawCount(); @@ -600,29 +595,22 @@ void RWGame::render(float alpha, float time) { renderer.getRenderer()->pushDebugGroup("World"); - RW_PROFILE_BEGIN("world"); renderer.renderWorld(world.get(), viewCam, alpha); - RW_PROFILE_END(); renderer.getRenderer()->popDebugGroup(); renderDebugView(time, viewCam); if (!world->isPaused()) drawOnScreenText(world.get(), &renderer); - RW_PROFILE_END(); - RW_PROFILE_BEGIN("state"); if (StateManager::currentState()) { + RW_PROFILE_SCOPE("state"); StateManager::get().draw(&renderer); } - RW_PROFILE_END(); - RW_PROFILE_END(); - - renderProfile(); } void RWGame::renderDebugView(float time, ViewCamera &viewCam) { - RW_PROFILE_BEGIN("debug"); + RW_PROFILE_SCOPE(__func__); switch (debugview_) { case DebugViewMode::General: renderDebugStats(time); @@ -640,7 +628,6 @@ void RWGame::renderDebugView(float time, ViewCamera &viewCam) { default: break; } - RW_PROFILE_END(); } void RWGame::renderDebugStats(float time) { @@ -851,55 +838,6 @@ void RWGame::renderDebugObjects(float time, ViewCamera& camera) { } } -void RWGame::renderProfile() { -#ifdef RW_PROFILER - auto& frame = perf::Profiler::get().getFrame(); - constexpr float upperlimit = 30000.f; - constexpr float lineHeight = 15.f; - static std::vector perf_colours; - if (perf_colours.size() == 0) { - float c = 8.f; - for (int r = 0; r < c; ++r) { - for (int g = 0; g < c; ++g) { - for (int b = 0; b < c; ++b) { - perf_colours.emplace_back(r / c, g / c, b / c, 1.f); - } - } - } - } - - float xscale = renderer.getRenderer()->getViewport().x / upperlimit; - TextRenderer::TextInfo ti; - ti.align = TextRenderer::TextInfo::TextAlignment::Left; - ti.font = FONT_ARIAL; - ti.size = lineHeight - 2.f; - ti.baseColour = glm::u8vec3(255); - std::function renderEntry = - [&](const perf::ProfileEntry& entry, int depth) { - int g = 0; - for (auto& event : entry.childProfiles) { - auto duration = event.end - event.start; - float y = 60.f + (depth * (lineHeight + 5.f)); - renderer.drawColour( - perf_colours[(std::hash()(entry.label) * - (g++)) % - perf_colours.size()], - {xscale * event.start, y, xscale * duration, lineHeight}); - ti.screenPosition.x = xscale * (event.start); - ti.screenPosition.y = y + 2.f; - ti.text = GameStringUtil::fromString( - event.label + " " + std::to_string(duration) + " us ", ti.font); - renderer.text.renderText(ti); - renderEntry(event, depth + 1); - } - }; - renderEntry(frame, 0); - ti.screenPosition = glm::vec2(xscale * (16000), 40.f); - ti.text = GameStringUtil::fromString(".16 ms", ti.font); - renderer.text.renderText(ti); -#endif -} - void RWGame::globalKeyEvent(const SDL_Event& event) { const auto toggle_debug = [&](DebugViewMode m) { debugview_ = debugview_ == m ? DebugViewMode::Disabled : m; diff --git a/rwgame/RWGame.hpp b/rwgame/RWGame.hpp index 28c7f4ab..8eab6aff 100644 --- a/rwgame/RWGame.hpp +++ b/rwgame/RWGame.hpp @@ -127,7 +127,6 @@ private: void renderDebugStats(float time); void renderDebugPaths(float time); void renderDebugObjects(float time, ViewCamera& camera); - void renderProfile(); void handleCheatInput(char symbol);