1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-10-06 09:07:19 +02:00

Merge pull request #621 from danhedron/feat/use_microprofile

Use microprofile for profiling
This commit is contained in:
Daniel Evans 2018-09-04 00:41:01 +01:00 committed by GitHub
commit 52dbe78b4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 299 additions and 309 deletions

View File

@ -43,6 +43,7 @@ install:
- for /f %%i in ('git rev-parse HEAD') do set COMMIT_HASH=%%i
- echo APPVEYOR_REPO_COMMIT=%APPVEYOR_REPO_COMMIT%, COMMIT_HASH=%COMMIT_HASH%
- if NOT "%APPVEYOR_REPO_COMMIT%" == "%COMMIT_HASH%" echo "Appveyor git hash does not match git checkout hash"
- git submodule update --init --recursive
build_script:
- ctest -VV -S "%APPVEYOR_BUILD_FOLDER%/cmake/ctest/script_ci.ctest"

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "external/microprofile/microprofile"]
path = external/microprofile/microprofile
url = https://github.com/jonasmr/microprofile.git

View File

@ -65,6 +65,8 @@ endif()
# Create a rw_interface TARGET that holds all compiler options
include("${PROJECT_SOURCE_DIR}/cmake_configure.cmake")
add_subdirectory(external)
add_subdirectory(rwcore)
add_subdirectory(rwengine)
add_subdirectory(rwgame)

View File

@ -169,6 +169,7 @@ set(_CONFIGURE_OPTIONS
"-DCMAKE_BUILD_TYPE=${_CMAKE_BUILD_TYPE}"
"-DENABLE_SANITIZERS=${ENABLE_SANITIZERS}"
"-DUSE_CONAN=${USE_CONAN}"
"-DENABLE_PROFILING=TRUE"
)
message(STATUS "Configuring...")

3
external/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,3 @@
if(ENABLE_PROFILING)
add_subdirectory(microprofile)
endif()

23
external/microprofile/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,23 @@
add_library(microprofile STATIC
microprofile/microprofile.h
microprofile/microprofile_html.h
microprofile/microprofile.cpp
)
target_include_directories(microprofile
PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/microprofile"
)
target_compile_definitions(microprofile
PUBLIC
MICROPROFILE_GPU_TIMERS=0
)
find_package(Threads REQUIRED)
target_link_libraries(microprofile
PUBLIC
Threads::Threads
)
add_library(microprofile::microprofile ALIAS microprofile)

1
external/microprofile/microprofile vendored Submodule

@ -0,0 +1 @@
Subproject commit b49ae80262b206a579808dc3e442f0d8aafe81e9

View File

@ -151,6 +151,13 @@ target_link_libraries(rwengine
OpenAL::OpenAL
)
if (ENABLE_PROFILING)
target_link_libraries(rwengine
PUBLIC
microprofile::microprofile
)
endif()
target_include_directories(rwengine
PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/src"

View File

@ -2,73 +2,24 @@
#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_SCOPEC(label, colour) MICROPROFILE_SCOPEI("Default", label, colour)
#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_SCOPEC(label, colour) 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

@ -7,6 +7,7 @@
#include <data/Clump.hpp>
#include "core/Profiler.hpp"
#include "core/Logger.hpp"
#include "engine/GameData.hpp"
@ -653,6 +654,7 @@ void handleInstanceResponse(InstanceObject* instance, const btManifoldPoint& mp,
bool GameWorld::ContactProcessedCallback(btManifoldPoint& mp, void* body0,
void* body1) {
RW_PROFILE_SCOPEC(__func__, MP_GOLDENROD1);
auto obA = static_cast<btCollisionObject*>(body0);
auto obB = static_cast<btCollisionObject*>(body1);
@ -689,18 +691,24 @@ bool GameWorld::ContactProcessedCallback(btManifoldPoint& mp, void* body0,
void GameWorld::PhysicsTickCallback(btDynamicsWorld* physWorld,
btScalar timeStep) {
RW_PROFILE_SCOPEC(__func__, MP_CYAN);
GameWorld* world = static_cast<GameWorld*>(physWorld->getWorldUserInfo());
RW_PROFILE_COUNTER_SET("physicsTick/vehiclePool", world->vehiclePool.objects.size());
for (auto& p : world->vehiclePool.objects) {
RW_PROFILE_SCOPEC("VehicleObject", MP_THISTLE1);
VehicleObject* object = static_cast<VehicleObject*>(p.second);
object->tickPhysics(timeStep);
}
RW_PROFILE_COUNTER_SET("physicsTick/pedestrianPool", world->pedestrianPool.objects.size());
for (auto& p : world->pedestrianPool.objects) {
RW_PROFILE_SCOPEC("CharacterObject", MP_THISTLE1);
CharacterObject* object = static_cast<CharacterObject*>(p.second);
object->tickPhysics(timeStep);
}
RW_PROFILE_COUNTER_SET("physicsTick/instancePool", world->instancePool.objects.size());
for (auto& p : world->instancePool.objects) {
InstanceObject* object = static_cast<InstanceObject*>(p.second);
object->tickPhysics(timeStep);

View File

@ -247,94 +247,7 @@ void GameRenderer::renderWorld(GameWorld* world, const ViewCamera& camera,
culled = 0;
renderer->useProgram(worldProg.get());
//===============================================================
// Render List Construction
//---------------------------------------------------------------
RW_PROFILE_BEGIN("RenderList");
// This is sequential at the moment, it should be easy to make it
// run in parallel with a good threading system.
RenderList renderList;
// Naive optimisation, assume 50% hitrate
renderList.reserve(world->allObjects.size() * 0.5f);
RW_PROFILE_BEGIN("Build");
ObjectRenderer objectRenderer(_renderWorld,
(cullOverride ? cullingCamera : _camera),
_renderAlpha, getMissingTexture());
// World Objects
for (auto object : world->allObjects) {
objectRenderer.buildRenderList(object, renderList);
}
// Area indicators
auto sphereModel = getSpecialModel(ZoneCylinderA);
for (auto& i : world->getAreaIndicators()) {
glm::mat4 m(1.f);
m = glm::translate(m, i.position);
m = glm::scale(
m, glm::vec3(i.radius +
0.15f * glm::sin(_renderWorld->getGameTime() * 5.f)));
objectRenderer.renderClump(sphereModel.get(), m, nullptr, renderList);
}
// Render arrows above anything that isn't radar only (or hidden)
auto arrowModel = getSpecialModel(Arrow);
for (auto& blip : world->state->radarBlips) {
auto dm = blip.second.display;
if (dm == BlipData::Hide || dm == BlipData::RadarOnly) {
continue;
}
glm::mat4 model{1.0f};
if (blip.second.target > 0) {
auto object = world->getBlipTarget(blip.second);
if (object) {
model = object->getTimeAdjustedTransform(_renderAlpha);
}
} else {
model = glm::translate(model, blip.second.coord);
}
float a = world->getGameTime() * glm::pi<float>();
model = glm::translate(model,
glm::vec3(0.f, 0.f, 2.5f + glm::sin(a) * 0.5f));
model = glm::rotate(model, a, glm::vec3(0.f, 0.f, 1.f));
model = glm::scale(model, glm::vec3(1.5f, 1.5f, 1.5f));
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
std::sort(renderList.begin(), renderList.end(),
[](const Renderer::RenderInstruction& a,
const Renderer::RenderInstruction& b) {
if (a.drawInfo.blendMode==BlendMode::BLEND_NONE && b.drawInfo.blendMode!=BlendMode::BLEND_NONE) return true;
if (a.drawInfo.blendMode!=BlendMode::BLEND_NONE && b.drawInfo.blendMode==BlendMode::BLEND_NONE) 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();
renderObjects(world);
renderer->pushDebugGroup("Water");
@ -392,6 +305,94 @@ void GameRenderer::renderWorld(GameWorld* world, const ViewCamera& camera,
renderPostProcess();
}
void GameRenderer::renderObjects(const GameWorld *world) {
RW_PROFILE_SCOPE(__func__);
renderer->useProgram(worldProg.get());
RenderList renderList = createObjectRenderList(world);
renderer->pushDebugGroup("Objects");
renderer->pushDebugGroup("RenderList");
renderer->drawBatched(renderList);
renderer->popDebugGroup();
profObjects = renderer->popDebugGroup();
}
RenderList GameRenderer::createObjectRenderList(const GameWorld *world) {
RW_PROFILE_SCOPE(__func__);
// This is sequential at the moment, it should be easy to make it
// run in parallel with a good threading system.
RenderList renderList;
// Naive optimisation, assume 50% hitrate
renderList.reserve(world->allObjects.size() * 0.5f);
ObjectRenderer objectRenderer(_renderWorld,
(cullOverride ? cullingCamera : _camera),
_renderAlpha, getMissingTexture());
// World Objects
for (auto object : world->allObjects) {
objectRenderer.buildRenderList(object, renderList);
}
// Area indicators
auto sphereModel = getSpecialModel(ZoneCylinderA);
for (auto &i : world->getAreaIndicators()) {
glm::mat4 m(1.f);
m = translate(m, i.position);
m = scale(
m, glm::vec3(i.radius +
0.15f * sin(_renderWorld->getGameTime() * 5.f)));
objectRenderer.renderClump(sphereModel.get(), m, nullptr, renderList);
}
// Render arrows above anything that isn't radar only (or hidden)
auto arrowModel = getSpecialModel(Arrow);
for (auto &blip : world->state->radarBlips) {
auto dm = blip.second.display;
if (dm == BlipData::Hide || dm == BlipData::RadarOnly) {
continue;
}
glm::mat4 model{1.0f};
if (blip.second.target > 0) {
auto object = world->getBlipTarget(blip.second);
if (object) {
model = object->getTimeAdjustedTransform(_renderAlpha);
}
} else {
model = translate(model, blip.second.coord);
}
float a = world->getGameTime() * glm::pi<float>();
model = translate(model,
glm::vec3(0.f, 0.f, 2.5f + sin(a) * 0.5f));
model = rotate(model, a, glm::vec3(0.f, 0.f, 1.f));
model = scale(model, glm::vec3(1.5f, 1.5f, 1.5f));
objectRenderer.renderClump(arrowModel.get(), model, nullptr, renderList);
}
culled += objectRenderer.culled;
RW_PROFILE_SCOPE("sortRenderList");
// Also parallelizable
// Earlier position in the array means earlier object's rendering
// Transparent objects should be sorted and rendered after opaque
sort(renderList.begin(), renderList.end(),
[](const Renderer::RenderInstruction &a,
const Renderer::RenderInstruction &b) {
if (a.drawInfo.blendMode == BlendMode::BLEND_NONE && b.drawInfo.blendMode != BlendMode::BLEND_NONE)
return true;
if (a.drawInfo.blendMode != BlendMode::BLEND_NONE && b.drawInfo.blendMode == BlendMode::BLEND_NONE)
return false;
return (a.sortKey > b.sortKey);
});
return renderList;
}
void GameRenderer::renderSplash(GameWorld* world, GLuint splashTexName, glm::u16vec3 fc) {
float fadeTimer = world->getGameTime() - world->state->fadeStart;

View File

@ -172,6 +172,10 @@ private:
}
void drawRect(const glm::vec4& colour, TextureData* texture, glm::vec4& extents);
void renderObjects(const GameWorld *world);
RenderList createObjectRenderList(const GameWorld *world);
};
#endif

View File

@ -6,6 +6,7 @@
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <core/Profiler.hpp>
#include <gl/DrawBuffer.hpp>
#include <rw/debug.hpp>
@ -123,7 +124,7 @@ void OpenGLRenderer::useDrawBuffer(DrawBuffer* dbuff) {
glBindVertexArray(dbuff->getVAOName());
currentDbuff = dbuff;
bufferCounter++;
#ifdef RW_PROFILER
#ifdef RW_GRAPHICS_STATS
if (currentDebugDepth > 0) {
profileInfo[currentDebugDepth - 1].buffers++;
}
@ -140,7 +141,7 @@ void OpenGLRenderer::useTexture(GLuint unit, GLuint tex) {
glBindTexture(GL_TEXTURE_2D, tex);
currentTextures[unit] = tex;
textureCounter++;
#ifdef RW_PROFILER
#ifdef RW_GRAPHICS_STATS
if (currentDebugDepth > 0) {
profileInfo[currentDebugDepth - 1].textures++;
}
@ -286,7 +287,7 @@ void OpenGLRenderer::setDrawState(const glm::mat4& model, DrawBuffer* draw,
uploadUBO(UBOObject, objectData);
drawCounter++;
#ifdef RW_PROFILER
#ifdef RW_GRAPHICS_STATS
if (currentDebugDepth > 0) {
profileInfo[currentDebugDepth - 1].draws++;
profileInfo[currentDebugDepth - 1].primitives += p.count;
@ -310,6 +311,7 @@ void OpenGLRenderer::drawArrays(const glm::mat4& model, DrawBuffer* draw,
}
void OpenGLRenderer::drawBatched(const RenderList& list) {
RW_PROFILE_SCOPE(__func__);
#if 0 // Needs shader changes
// Determine how many batches we need to process the entire list
auto entries = list.size();
@ -416,7 +418,7 @@ void OpenGLRenderer::uploadUBOEntry(Buffer &buffer, const void *data, size_t siz
}
void OpenGLRenderer::pushDebugGroup(const std::string& title) {
#ifdef RW_PROFILER
#ifdef RW_GRAPHICS_STATS
if (ogl_ext_KHR_debug) {
glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, title.c_str());
ProfileInfo& prof = profileInfo[currentDebugDepth];
@ -435,7 +437,7 @@ void OpenGLRenderer::pushDebugGroup(const std::string& title) {
}
const Renderer::ProfileInfo& OpenGLRenderer::popDebugGroup() {
#ifdef RW_PROFILER
#ifdef RW_GRAPHICS_STATS
if (ogl_ext_KHR_debug) {
glPopDebugGroup();
currentDebugDepth--;

View File

@ -6,6 +6,7 @@
#include "ai/PlayerController.hpp"
#include "core/Logger.hpp"
#include "core/Profiler.hpp"
#include "engine/GameState.hpp"
#include "engine/GameWorld.hpp"
#include "script/SCMFile.hpp"
@ -226,6 +227,7 @@ SCMByte* ScriptMachine::getGlobals() {
}
void ScriptMachine::execute(float dt) {
RW_PROFILE_SCOPEC(__func__, MP_ORANGERED);
int ms = dt * 1000.f;
for (auto t = _activeThreads.begin(); t != _activeThreads.end(); ++t) {
auto& thread = *t;

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() {
@ -365,46 +369,9 @@ int RWGame::run() {
bool running = true;
while (StateManager::currentState() && running) {
RW_PROFILE_FRAME_BOUNDARY();
RW_PROFILE_SCOPE("Main Loop");
RW_PROFILE_BEGIN("Input");
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
running = false;
break;
case SDL_WINDOWEVENT:
switch (event.window.event) {
case SDL_WINDOWEVENT_FOCUS_GAINED:
inFocus = true;
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
inFocus = false;
break;
}
break;
case SDL_KEYDOWN:
globalKeyEvent(event);
break;
case SDL_MOUSEMOTION:
event.motion.xrel *= MOUSE_SENSITIVITY_SCALE;
event.motion.yrel *= MOUSE_SENSITIVITY_SCALE;
break;
}
GameInput::updateGameInputState(&getState()->input[0], event);
RW_PROFILE_BEGIN("State");
if (StateManager::currentState()) {
StateManager::currentState()->handleEvent(event);
}
RW_PROFILE_END()
}
RW_PROFILE_END();
running = updateInput();
auto currentFrame = chrono::steady_clock::now();
auto frameTime =
@ -419,48 +386,10 @@ int RWGame::run() {
frameTime = 0.1f;
}
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();
accumulatedTime = tickWorld(deltaTime, accumulatedTime);
}
RW_PROFILE_BEGIN("Render");
RW_PROFILE_BEGIN("engine");
render(1, frameTime);
RW_PROFILE_END();
RW_PROFILE_BEGIN("state");
if (StateManager::currentState()) {
StateManager::get().draw(&renderer);
}
RW_PROFILE_END();
RW_PROFILE_END();
renderProfile();
getWindow().swap();
@ -475,7 +404,75 @@ int RWGame::run() {
return 0;
}
float RWGame::tickWorld(const float deltaTime, float accumulatedTime) {
RW_PROFILE_SCOPEC(__func__, MP_GREEN);
auto deltaTimeWithTimeScale =
deltaTime * world->state->basic.timeScale;
while (accumulatedTime >= deltaTime) {
if (!StateManager::currentState()) {
break;
}
{
RW_PROFILE_SCOPEC("stepSimulation", MP_DARKORANGE1);
world->dynamicsWorld->stepSimulation(
deltaTimeWithTimeScale, kMaxPhysicsSubSteps, deltaTime);
}
StateManager::get().tick(deltaTimeWithTimeScale);
tick(deltaTimeWithTimeScale);
getState()->swapInputState();
accumulatedTime -= deltaTime;
}
return accumulatedTime;
}
bool RWGame::updateInput() {
RW_PROFILE_SCOPE(__func__);
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
return false;
case SDL_WINDOWEVENT:
switch (event.window.event) {
case SDL_WINDOWEVENT_FOCUS_GAINED:
inFocus = true;
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
inFocus = false;
break;
}
break;
case SDL_KEYDOWN:
globalKeyEvent(event);
break;
case SDL_MOUSEMOTION:
event.motion.xrel *= MOUSE_SENSITIVITY_SCALE;
event.motion.yrel *= MOUSE_SENSITIVITY_SCALE;
break;
}
GameInput::updateGameInputState(&getState()->input[0], event);
if (StateManager::currentState()) {
RW_PROFILE_SCOPE("State");
StateManager::currentState()->handleEvent(event);
}
}
return true;
}
void RWGame::tick(float dt) {
RW_PROFILE_SCOPE(__func__);
State* currState = StateManager::get().states.back().get();
static float clockAccumulator = 0.f;
@ -527,22 +524,7 @@ void RWGame::tick(float dt) {
}
}
world->updateEffects();
for (auto& object : world->allObjects) {
object->_updateLastTransform();
object->tick(dt);
}
for (auto& g : world->garages) {
g->tick(dt);
}
for (auto& p : world->payphones) {
p->tick(dt);
}
world->destroyQueuedObjects();
tickObjects(dt);
state.text.tick(dt);
@ -570,7 +552,39 @@ void RWGame::tick(float dt) {
}
}
void RWGame::tickObjects(float dt) const {
RW_PROFILE_SCOPEC(__func__, MP_MAGENTA1);
world->updateEffects();
{
RW_PROFILE_SCOPEC("allObjects", MP_HOTPINK1);
RW_PROFILE_COUNTER_SET("tickObjects/allObjects", world->allObjects.size());
for (auto &object : world->allObjects) {
object->_updateLastTransform();
object->tick(dt);
}
}
{
RW_PROFILE_SCOPEC("garages", MP_HOTPINK2);
for (auto &g : world->garages) {
g->tick(dt);
}
}
{
RW_PROFILE_SCOPEC("payphones", MP_HOTPINK3);
for (auto &p : world->payphones) {
p->tick(dt);
}
}
world->destroyQueuedObjects();
}
void RWGame::render(float alpha, float time) {
RW_PROFILE_SCOPEC(__func__, MP_CORNFLOWERBLUE);
lastDraws = getRenderer().getRenderer()->getDrawCount();
getRenderer().getRenderer()->swap();
@ -599,13 +613,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();
RW_PROFILE_BEGIN("debug");
renderDebugView(time, viewCam);
if (!world->isPaused()) drawOnScreenText(world.get(), &renderer);
if (StateManager::currentState()) {
RW_PROFILE_SCOPE("state");
StateManager::get().draw(&renderer);
}
}
void RWGame::renderDebugView(float time, ViewCamera &viewCam) {
RW_PROFILE_SCOPE(__func__);
switch (debugview_) {
case DebugViewMode::General:
renderDebugStats(time);
@ -623,9 +646,6 @@ void RWGame::render(float alpha, float time) {
default:
break;
}
RW_PROFILE_END();
if (!world->isPaused()) drawOnScreenText(world.get(), &renderer);
}
void RWGame::renderDebugStats(float time) {
@ -836,55 +856,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

@ -128,11 +128,18 @@ private:
void renderDebugStats(float time);
void renderDebugPaths(float time);
void renderDebugObjects(float time, ViewCamera& camera);
void renderProfile();
void handleCheatInput(char symbol);
void globalKeyEvent(const SDL_Event& event);
bool updateInput();
float tickWorld(const float deltaTime, float accumulatedTime);
void renderDebugView(float time, ViewCamera &viewCam);
void tickObjects(float dt) const;
};
#endif