1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-25 11:52:40 +01:00

Use microprofile for profiling

This commit is contained in:
Daniel Evans 2018-08-30 15:47:12 +01:00
parent 63a2caa471
commit 080a0fe050
5 changed files with 31 additions and 151 deletions

View File

@ -2,73 +2,22 @@
#define _RWENGINE_PROFILER_HPP_
#ifdef RW_PROFILER
#include <chrono>
#include <cstdint>
#include <stack>
#include <string>
#include <vector>
#define time_unit std::chrono::microseconds
#include <rw/debug.hpp>
namespace perf {
struct ProfileEntry {
std::string label;
int64_t start;
int64_t end;
std::vector<ProfileEntry> childProfiles;
};
class Profiler {
ProfileEntry frame;
std::chrono::high_resolution_clock::time_point frameBegin;
std::stack<ProfileEntry> 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<time_unit>(
std::chrono::high_resolution_clock::now() - frameBegin);
currentStack.push({label, now.count(), 0, {}});
}
void endEvent() {
auto now = std::chrono::duration_cast<time_unit>(
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 <microprofile.h>
#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

View File

@ -18,6 +18,7 @@
#include <rw/types.hpp>
#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) {

View File

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

View File

@ -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<glm::vec4> 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<void(const perf::ProfileEntry&, int)> 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<std::string>()(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;

View File

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