1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-09-18 16:32:32 +02:00

Add Improved Profiling statistics

This commit is contained in:
Daniel Evans 2015-04-12 19:47:47 +01:00
parent 9eb1a415c5
commit 97afc19cfb
8 changed files with 123 additions and 25 deletions

View File

@ -8,6 +8,9 @@ SET(BUILD_TESTS TRUE CACHE BOOL "Build test suite")
SET(BUILD_OLD_TOOLS FALSE CACHE BOOL "Build old datadump and analyzer tools") SET(BUILD_OLD_TOOLS FALSE CACHE BOOL "Build old datadump and analyzer tools")
SET(BUILD_TOOLS FALSE CACHE BOOL "Build editor application") SET(BUILD_TOOLS FALSE CACHE BOOL "Build editor application")
# Features
SET(ENABLE_PROFILE_RENDERER TRUE CACHE BOOL "Enable higher precision rendering profiler")
# Options # Options
SET(ENABLE_SCRIPT_DEBUG FALSE CACHE BOOL "Enable verbose script execution") SET(ENABLE_SCRIPT_DEBUG FALSE CACHE BOOL "Enable verbose script execution")
@ -26,6 +29,12 @@ ENDIF()
# Make GLM use radians # Make GLM use radians
add_definitions(-DGLM_FORCE_RADIANS) add_definitions(-DGLM_FORCE_RADIANS)
IF(${ENABLE_PROFILE_RENDERER})
add_definitions(-DRENDER_PROFILER=1)
else()
add_definitions(-DRENDER_PROFILER=0)
ENDIF()
IF(${ENABLE_SCRIPT_DEBUG}) IF(${ENABLE_SCRIPT_DEBUG})
add_definitions(-DSCM_DEBUG_INSTRUCTIONS) add_definitions(-DSCM_DEBUG_INSTRUCTIONS)
ENDIF() ENDIF()

View File

@ -8,6 +8,8 @@
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <memory> #include <memory>
#define RW_USING(feature) 1 == feature
#define NO_WATER_INDEX 48 #define NO_WATER_INDEX 48
#define WATER_LQ_DATA_SIZE 64 #define WATER_LQ_DATA_SIZE 64
#define WATER_HQ_DATA_SIZE 128 #define WATER_HQ_DATA_SIZE 128

View File

@ -194,10 +194,11 @@ public:
WaterRenderer water; WaterRenderer water;
TextRenderer text; TextRenderer text;
// Rendering timers // Profiling data
GLuint timeObj; Renderer::ProfileInfo profObjects;
GLuint timeSky; Renderer::ProfileInfo profSky;
GLuint timeWater; Renderer::ProfileInfo profWater;
Renderer::ProfileInfo profEffects;
}; };
#endif #endif

View File

@ -117,11 +117,32 @@ public:
const SceneUniformData& getSceneData() const; const SceneUniformData& getSceneData() const;
/**
* Profiling data returned by popDebugGroup.
* Not all fields will be populated, depending on
* USING(RENDER_PROFILER)
*/
struct ProfileInfo
{
GLuint64 timerStart;
GLuint64 duration;
unsigned int primitives;
unsigned int draws;
unsigned int textures;
unsigned int buffers;
unsigned int uploads;
};
/** /**
* Signals the start of a debug group * Signals the start of a debug group
*/ */
virtual void pushDebugGroup(const std::string& title) = 0; virtual void pushDebugGroup(const std::string& title) = 0;
virtual GLuint popDebugGroup() = 0; /**
* Ends the current debug group and returns the profiling information
* for that group. The returned value is valid until the next call to
* pushDebugGroup
*/
virtual const ProfileInfo& popDebugGroup() = 0;
private: private:
glm::ivec2 viewport; glm::ivec2 viewport;
@ -185,7 +206,7 @@ public:
void invalidate(); void invalidate();
virtual void pushDebugGroup(const std::string& title); virtual void pushDebugGroup(const std::string& title);
virtual GLuint popDebugGroup(); virtual const ProfileInfo& popDebugGroup();
private: private:
DrawBuffer* currentDbuff; DrawBuffer* currentDbuff;
@ -205,13 +226,19 @@ private:
currentUBO = buffer; currentUBO = buffer;
} }
glBufferData(GL_UNIFORM_BUFFER, sizeof(T), &data, GL_DYNAMIC_DRAW); glBufferData(GL_UNIFORM_BUFFER, sizeof(T), &data, GL_DYNAMIC_DRAW);
#if RW_USING(RENDER_PROFILER)
if( currentDebugDepth > 0 )
{
profileInfo[currentDebugDepth-1].uploads++;
}
#endif
} }
GLuint UBOObject; GLuint UBOObject;
GLuint UBOScene; GLuint UBOScene;
// Debug group profiling timers // Debug group profiling timers
GLuint64 debugTimes[MAX_DEBUG_DEPTH]; ProfileInfo profileInfo[MAX_DEBUG_DEPTH];
GLuint debugQuery; GLuint debugQuery;
int currentDebugDepth; int currentDebugDepth;
}; };

View File

@ -233,8 +233,6 @@ void GameRenderer::renderWorld(const ViewCamera &camera, float alpha)
// Store the input camera, // Store the input camera,
_camera = camera; _camera = camera;
timeObj = timeSky = timeWater = 0;
// Set the viewport // Set the viewport
const glm::ivec2& vp = getRenderer()->getViewport(); const glm::ivec2& vp = getRenderer()->getViewport();
glViewport(0, 0, vp.x, vp.y); glViewport(0, 0, vp.x, vp.y);
@ -368,7 +366,7 @@ void GameRenderer::renderWorld(const ViewCamera &camera, float alpha)
transparentDrawQueue.clear(); transparentDrawQueue.clear();
renderer->popDebugGroup(); renderer->popDebugGroup();
timeObj = renderer->popDebugGroup(); profObjects = renderer->popDebugGroup();
// Render arrows above anything that isn't radar only (or hidden) // Render arrows above anything that isn't radar only (or hidden)
ModelRef& arrowModel = engine->gameData.models["arrow"]; ModelRef& arrowModel = engine->gameData.models["arrow"];
@ -426,7 +424,7 @@ void GameRenderer::renderWorld(const ViewCamera &camera, float alpha)
water.render(this, engine); water.render(this, engine);
timeWater = renderer->popDebugGroup(); profWater = renderer->popDebugGroup();
renderer->pushDebugGroup("Sky"); renderer->pushDebugGroup("Sky");
@ -442,11 +440,11 @@ void GameRenderer::renderWorld(const ViewCamera &camera, float alpha)
renderer->draw(glm::mat4(), &skyDbuff, dp); renderer->draw(glm::mat4(), &skyDbuff, dp);
timeSky = renderer->popDebugGroup(); profSky = renderer->popDebugGroup();
renderer->pushDebugGroup("Effects"); renderer->pushDebugGroup("Effects");
renderEffects(); renderEffects();
renderer->popDebugGroup(); profEffects = renderer->popDebugGroup();
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);

View File

@ -121,6 +121,12 @@ void OpenGLRenderer::useDrawBuffer(DrawBuffer* dbuff)
glBindVertexArray(dbuff->getVAOName()); glBindVertexArray(dbuff->getVAOName());
currentDbuff = dbuff; currentDbuff = dbuff;
bufferCounter++; bufferCounter++;
#if RW_USING(RENDER_PROFILER)
if( currentDebugDepth > 0 )
{
profileInfo[currentDebugDepth-1].buffers++;
}
#endif
} }
} }
@ -132,6 +138,13 @@ void OpenGLRenderer::useTexture(GLuint unit, GLuint tex)
glBindTexture(GL_TEXTURE_2D, tex); glBindTexture(GL_TEXTURE_2D, tex);
currentTextures[unit] = tex; currentTextures[unit] = tex;
textureCounter++; textureCounter++;
#if RW_USING(RENDER_PROFILER)
if( currentDebugDepth > 0 )
{
profileInfo[currentDebugDepth-1].textures++;
}
#endif
} }
} }
@ -261,6 +274,14 @@ void OpenGLRenderer::draw(const glm::mat4& model, DrawBuffer* draw, const Render
uploadUBO(UBOObject, oudata); uploadUBO(UBOObject, oudata);
drawCounter++; drawCounter++;
#if RW_USING(RENDER_PROFILER)
if( currentDebugDepth > 0 )
{
profileInfo[currentDebugDepth-1].draws++;
profileInfo[currentDebugDepth-1].primitives += p.count;
}
#endif
glDrawElements(draw->getFaceType(), p.count, GL_UNSIGNED_INT, glDrawElements(draw->getFaceType(), p.count, GL_UNSIGNED_INT,
(void*) (sizeof(RenderIndex) * p.start)); (void*) (sizeof(RenderIndex) * p.start));
} }
@ -284,6 +305,14 @@ void OpenGLRenderer::drawArrays(const glm::mat4& model, DrawBuffer* draw, const
uploadUBO(UBOObject, oudata); uploadUBO(UBOObject, oudata);
drawCounter++; drawCounter++;
#if RW_USING(RENDER_PROFILER)
if( currentDebugDepth > 0 )
{
profileInfo[currentDebugDepth-1].draws++;
profileInfo[currentDebugDepth-1].primitives += p.count;
}
#endif
glDrawArrays(draw->getFaceType(), p.start, p.count); glDrawArrays(draw->getFaceType(), p.start, p.count);
} }
@ -297,30 +326,52 @@ void OpenGLRenderer::invalidate()
void OpenGLRenderer::pushDebugGroup(const std::string& title) void OpenGLRenderer::pushDebugGroup(const std::string& title)
{ {
#if RW_USING(RENDER_PROFILER)
if( GLEW_KHR_debug ) if( GLEW_KHR_debug )
{ {
glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, title.c_str()); glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, title.c_str());
ProfileInfo& prof = profileInfo[currentDebugDepth];
prof.buffers = prof.draws = prof.textures = prof.uploads = prof.primitives = 0;
glQueryCounter(debugQuery, GL_TIMESTAMP); glQueryCounter(debugQuery, GL_TIMESTAMP);
glGetQueryObjectui64v(debugQuery, GL_QUERY_RESULT, &debugTimes[currentDebugDepth]); glGetQueryObjectui64v(debugQuery, GL_QUERY_RESULT, &prof.timerStart);
currentDebugDepth++; currentDebugDepth++;
assert( currentDebugDepth < MAX_DEBUG_DEPTH ); assert( currentDebugDepth < MAX_DEBUG_DEPTH );
} }
#endif
} }
GLuint OpenGLRenderer::popDebugGroup() const Renderer::ProfileInfo& OpenGLRenderer::popDebugGroup()
{ {
#if RW_USING(RENDER_PROFILER)
if( GLEW_KHR_debug ) if( GLEW_KHR_debug )
{ {
glPopDebugGroup(); glPopDebugGroup();
currentDebugDepth--; currentDebugDepth--;
assert( currentDebugDepth >= 0 ); assert( currentDebugDepth >= 0 );
ProfileInfo& prof = profileInfo[currentDebugDepth];
glQueryCounter(debugQuery, GL_TIMESTAMP); glQueryCounter(debugQuery, GL_TIMESTAMP);
GLuint64 current_time; GLuint64 current_time;
glGetQueryObjectui64v(debugQuery, GL_QUERY_RESULT, &current_time); glGetQueryObjectui64v(debugQuery, GL_QUERY_RESULT, &current_time);
return current_time - debugTimes[currentDebugDepth]; prof.duration = current_time - prof.timerStart;
// Add counters to the parent group
if( currentDebugDepth > 0 )
{
ProfileInfo& p = profileInfo[currentDebugDepth-1];
p.draws += prof.draws;
p.buffers += prof.buffers;
p.primitives += prof.primitives;
p.textures += prof.textures;
p.uploads += prof.uploads;
}
return prof;
} }
return 0; #endif
return profileInfo[0];
} }

View File

@ -448,7 +448,7 @@ void RWGame::render(float alpha, float time)
drawOnScreenText(engine, renderer); drawOnScreenText(engine, renderer);
} }
void RWGame::renderDebugStats(float time, GLuint worldRenderTime) void RWGame::renderDebugStats(float time, Renderer::ProfileInfo& worldRenderTime)
{ {
// Turn time into milliseconds // Turn time into milliseconds
float time_ms = time * 1000.f; float time_ms = time * 1000.f;
@ -466,16 +466,26 @@ void RWGame::renderDebugStats(float time, GLuint worldRenderTime)
time_average /= average_every_frame; time_average /= average_every_frame;
} }
std::map<std::string, Renderer::ProfileInfo*> profGroups {
{"Objects", &renderer->profObjects},
{"Effects", &renderer->profEffects},
{"Sky", &renderer->profSky},
{"Water", &renderer->profWater},
};
std::stringstream ss; std::stringstream ss;
ss << "Frametime: " << time_ms << " (FPS " << (1.f/time) << ")\n"; ss << "Frametime: " << time_ms << " (FPS " << (1.f/time) << ")\n";
ss << "Average (per " << average_every_frame << " frames); Frametime: " << time_average << " (FPS " << (1000.f/time_average) << ")\n"; ss << "Average (per " << average_every_frame << " frames); Frametime: " << time_average << " (FPS " << (1000.f/time_average) << ")\n";
ss << "Draws: " << lastDraws << " (" << renderer->culled << " Culls)\n"; ss << "Draws: " << lastDraws << " (" << renderer->culled << " Culls)\n";
ss << " Texture binds: " << renderer->getRenderer()->getTextureCount() << "\n"; ss << " Texture binds: " << renderer->getRenderer()->getTextureCount() << "\n";
ss << " Buffer binds: " << renderer->getRenderer()->getBufferCount() << "\n"; ss << " Buffer binds: " << renderer->getRenderer()->getBufferCount() << "\n";
ss << " World time: " << (worldRenderTime/1000000) << "ms\n"; ss << " World time: " << (worldRenderTime.duration/1000000) << "ms\n";
ss << " Objects: " << (renderer->timeObj/1000000) << "ms\n"; for(auto& perf : profGroups)
ss << " Water: " << (renderer->timeWater/1000000) << "ms\n"; {
ss << " Sky: " << (renderer->timeSky/1000000) << "ms\n"; ss << " " << perf.first << ": "
<< perf.second->draws << " draws " << perf.second->primitives << " prims "
<< (perf.second->duration/1000000) << "ms\n";
}
// Count the number of interesting objects. // Count the number of interesting objects.
int peds = 0, cars = 0; int peds = 0, cars = 0;

View File

@ -88,7 +88,7 @@ private:
void tick(float dt); void tick(float dt);
void render(float alpha, float dt); void render(float alpha, float dt);
void renderDebugStats(float time, GLuint worldRenderTime); void renderDebugStats(float time, Renderer::ProfileInfo& worldRenderTime);
void globalKeyEvent(const sf::Event& event); void globalKeyEvent(const sf::Event& event);
}; };