Added a first (unfinished) version of a graphical profiler
git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@9207 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
@@ -436,6 +436,8 @@ supertuxkart_SOURCES = \
|
||||
utils/constants.hpp \
|
||||
utils/constants.cpp \
|
||||
utils/no_copy.hpp \
|
||||
utils/profiler.cpp \
|
||||
utils/profiler.hpp \
|
||||
utils/ptr_vector.hpp \
|
||||
utils/random_generator.cpp \
|
||||
utils/random_generator.hpp \
|
||||
|
||||
@@ -366,7 +366,7 @@ namespace UserConfigParams
|
||||
|
||||
/** True if follow-the-leader debug information should be printed. */
|
||||
PARAM_PREFIX bool m_ftl_debug PARAM_DEFAULT( false );
|
||||
|
||||
|
||||
/** True if currently developed tutorial debugging is enabled. */
|
||||
PARAM_PREFIX bool m_tutorial_debug PARAM_DEFAULT( false );
|
||||
|
||||
@@ -380,6 +380,9 @@ namespace UserConfigParams
|
||||
|
||||
/** True to test funky ambient/diffuse/specularity in RGB & all anisotropic */
|
||||
PARAM_PREFIX bool m_rendering_debug PARAM_DEFAULT( false );
|
||||
|
||||
/** True if graphical profiler should be displayed */
|
||||
PARAM_PREFIX bool m_profiler_enabled PARAM_DEFAULT( false );
|
||||
|
||||
// not saved to file
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "states_screens/dialogs/confirm_resolution_dialog.hpp"
|
||||
#include "states_screens/state_manager.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
#include "utils/profiler.hpp"
|
||||
|
||||
#include <irrlicht.h>
|
||||
|
||||
@@ -1264,6 +1265,10 @@ void IrrDriver::update(float dt)
|
||||
// Either render the gui, or the global elements of the race gui.
|
||||
GUIEngine::render(dt);
|
||||
}
|
||||
|
||||
// Render the profiler
|
||||
if(UserConfigParams::m_profiler_enabled)
|
||||
profiler.draw();
|
||||
|
||||
} // just to mark the begin/end scene block
|
||||
|
||||
@@ -1329,18 +1334,18 @@ bool IrrDriver::OnEvent(const irr::SEvent &event)
|
||||
// can act upon it
|
||||
switch (event.EventType)
|
||||
{
|
||||
case irr::EET_LOG_TEXT_EVENT:
|
||||
case irr::EET_LOG_TEXT_EVENT:
|
||||
{
|
||||
// Ignore 'normal' messages
|
||||
if (event.LogEvent.Level > 1)
|
||||
{
|
||||
// Ignore 'normal' messages
|
||||
if (event.LogEvent.Level > 1)
|
||||
{
|
||||
printf("[IrrDriver Temp Logger] Level %d: %s\n",
|
||||
event.LogEvent.Level,event.LogEvent.Text);
|
||||
}
|
||||
return true;
|
||||
printf("[IrrDriver Temp Logger] Level %d: %s\n",
|
||||
event.LogEvent.Level,event.LogEvent.Text);
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
} // switch
|
||||
|
||||
return false;
|
||||
|
||||
@@ -89,7 +89,7 @@ private:
|
||||
video::E_DRIVER_TYPE getEngineDriverType(int index);
|
||||
|
||||
/** Whether the mouse cursor is currently shown */
|
||||
bool m_pointer_shown;
|
||||
bool m_pointer_shown;
|
||||
|
||||
/** Internal method that applies the resolution in user settings. */
|
||||
void applyResolutionSettings();
|
||||
@@ -173,6 +173,7 @@ public:
|
||||
void showPointer();
|
||||
void hidePointer();
|
||||
bool isPointerShown() const { return m_pointer_shown; }
|
||||
|
||||
void printRenderStats();
|
||||
/** Returns the current real time, which might not be 0 at start of the
|
||||
* application. Value in msec.
|
||||
|
||||
@@ -134,6 +134,10 @@ bool EventHandler::OnEvent (const SEvent &event)
|
||||
event.EventType == EET_KEY_INPUT_EVENT ||
|
||||
event.EventType == EET_JOYSTICK_INPUT_EVENT)
|
||||
{
|
||||
// Remember the mouse position
|
||||
m_mouse_pos.X = event.MouseInput.X;
|
||||
m_mouse_pos.Y = event.MouseInput.Y;
|
||||
|
||||
// FIXME? it may be a bit unclean that all input events go trough the gui module
|
||||
const EventPropagation blockPropagation = input_manager->input(event);
|
||||
return blockPropagation == EVENT_BLOCK;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#ifndef HEADER_EVENT_HANDLER_HPP
|
||||
#define HEADER_EVENT_HANDLER_HPP
|
||||
|
||||
#include <vector2d.h>
|
||||
#include <IEventReceiver.h>
|
||||
#include "input/input.hpp"
|
||||
|
||||
@@ -65,6 +66,9 @@ namespace GUIEngine
|
||||
*/
|
||||
void sendEventToUser(Widget* widget, std::string& name, const int playerID);
|
||||
|
||||
/** Last position of the mouse cursor */
|
||||
irr::core::vector2di m_mouse_pos;
|
||||
|
||||
public:
|
||||
EventHandler();
|
||||
~EventHandler();
|
||||
@@ -83,6 +87,9 @@ namespace GUIEngine
|
||||
void processGUIAction(const PlayerAction action, int deviceID, const unsigned int value,
|
||||
Input::InputType type, const int playerID);
|
||||
|
||||
/** Get the mouse position */
|
||||
const irr::core::vector2di& getMousePos() const { return m_mouse_pos; }
|
||||
|
||||
/** singleton access */
|
||||
static EventHandler* get();
|
||||
};
|
||||
|
||||
@@ -40,8 +40,8 @@
|
||||
<File name="../../network/message.cpp" open="0" top="0" tabpos="0">
|
||||
<Cursor position="2505" topLine="68" />
|
||||
</File>
|
||||
<File name="../../race/highscores.cpp" open="1" top="0" tabpos="5">
|
||||
<Cursor position="3826" topLine="92" />
|
||||
<File name="../../race/highscores.cpp" open="1" top="1" tabpos="4">
|
||||
<Cursor position="3853" topLine="74" />
|
||||
</File>
|
||||
<File name="../../states_screens/challenges.cpp" open="0" top="0" tabpos="3">
|
||||
<Cursor position="2262" topLine="33" />
|
||||
|
||||
@@ -198,6 +198,11 @@ void InputManager::handleStaticAction(int key, int value)
|
||||
Kart* kart = world->getLocalPlayerKart(0);
|
||||
kart->setPowerup(PowerupManager::POWERUP_SWATTER, 10000);
|
||||
}
|
||||
break;
|
||||
|
||||
case KEY_F10:
|
||||
if(world) history->Save();
|
||||
break;
|
||||
|
||||
case KEY_F11:
|
||||
if (UserConfigParams::m_artist_debug_mode && value &&
|
||||
@@ -215,8 +220,10 @@ void InputManager::handleStaticAction(int key, int value)
|
||||
UserConfigParams::m_display_fps =
|
||||
!UserConfigParams::m_display_fps;
|
||||
break;
|
||||
case KEY_F10:
|
||||
if(world) history->Save();
|
||||
|
||||
case KEY_KEY_P:
|
||||
if (UserConfigParams::m_artist_debug_mode && value && control_is_pressed)
|
||||
UserConfigParams::m_profiler_enabled = !UserConfigParams::m_profiler_enabled;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "network/network_manager.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "states_screens/state_manager.hpp"
|
||||
#include "utils/profiler.hpp"
|
||||
|
||||
MainLoop* main_loop = 0;
|
||||
|
||||
@@ -139,9 +140,19 @@ void MainLoop::run()
|
||||
// since the GUI engine is no more to be called then.
|
||||
if (!m_abort)
|
||||
{
|
||||
PROFILER_PUSH_CPU_MARKER("Music manager update", 0x7F, 0x00, 0x00);
|
||||
music_manager->update(dt);
|
||||
PROFILER_POP_CPU_MARKER();
|
||||
|
||||
PROFILER_PUSH_CPU_MARKER("Input manager update", 0x00, 0x7F, 0x00);
|
||||
input_manager->update(dt);
|
||||
PROFILER_POP_CPU_MARKER();
|
||||
|
||||
PROFILER_PUSH_CPU_MARKER("Music manager update", 0x00, 0x00, 0x7F);
|
||||
irr_driver->update(dt);
|
||||
PROFILER_POP_CPU_MARKER();
|
||||
|
||||
PROFILER_SYNC_FRAME();
|
||||
}
|
||||
} // while !m_exit
|
||||
|
||||
|
||||
257
src/utils/profiler.cpp
Normal file
257
src/utils/profiler.cpp
Normal file
@@ -0,0 +1,257 @@
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2004 SuperTuxKart-Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include "profiler.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "guiengine/event_handler.hpp"
|
||||
#include "guiengine/engine.hpp"
|
||||
#include "guiengine/scalable_font.hpp"
|
||||
#include <assert.h>
|
||||
#include <stack>
|
||||
|
||||
Profiler profiler;
|
||||
|
||||
// Unit is in pencentage of the screen dimensions
|
||||
#define MARGIN_X 0.02 // left and right margin
|
||||
#define MARGIN_Y 0.02 // top margin
|
||||
#define LINE_HEIGHT 0.015 // height of a line representing a thread
|
||||
|
||||
#define MARKERS_NAMES_POS core::rect<s32>(10,50,260,80)
|
||||
|
||||
#define TIME_DRAWN_MS 100.0 // the width of the profiler corresponds to TIME_DRAWN_MS milliseconds
|
||||
|
||||
// --- Begin portable precise timer ---
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
|
||||
static double _getTimeMilliseconds()
|
||||
{
|
||||
LARGE_INTEGER freq;
|
||||
QueryPerformanceFrequency(&freq);
|
||||
double perFreq = double(freq.QuadPart) / 1000.0;
|
||||
|
||||
LARGE_INTEGER timer;
|
||||
QueryPerformanceCounter(&timer);
|
||||
return double(timer.QuadPart) / perFreq;
|
||||
}
|
||||
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
static double _getTimeMilliseconds()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return double(tv.tv_sec * 1000) + (double(tv.tv_usec) / 1000.0);
|
||||
}
|
||||
#endif
|
||||
// --- End portable precise timer ---
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Profiler::Profiler()
|
||||
{
|
||||
thread_infos.resize(1); // TODO: monothread now, should support multithreading
|
||||
write_id = 0;
|
||||
time_last_sync = _getTimeMilliseconds();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
Profiler::~Profiler()
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Push a new marker that starts now
|
||||
void Profiler::pushCpuMarker(const char* name, const video::SColor& color)
|
||||
{
|
||||
ThreadInfo& ti = getThreadInfo();
|
||||
MarkerStack& markers_stack = ti.markers_stack[write_id];
|
||||
double start = _getTimeMilliseconds() - time_last_sync;
|
||||
size_t layer = markers_stack.size();
|
||||
|
||||
// Add to the stack of current markers
|
||||
markers_stack.push(Marker(start, -1.0, name, color, layer));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Stop the last pushed marker
|
||||
void Profiler::popCpuMarker()
|
||||
{
|
||||
ThreadInfo& ti = getThreadInfo();
|
||||
assert(ti.markers_stack[write_id].size() > 0);
|
||||
|
||||
MarkerStack& markers_stack = ti.markers_stack[write_id];
|
||||
MarkerList& markers_done = ti.markers_done[write_id];
|
||||
|
||||
// Update the date of end of the marker
|
||||
Marker& marker = markers_stack.top();
|
||||
marker.end = _getTimeMilliseconds() - time_last_sync;
|
||||
|
||||
// Remove the marker from the stack and add it to the list of markers done
|
||||
markers_done.push_front(marker);
|
||||
markers_stack.pop();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Swap buffering for the markers
|
||||
void Profiler::synchronizeFrame()
|
||||
{
|
||||
// Avoid using several times _getTimeMilliseconds(), which would yield different results
|
||||
double now = _getTimeMilliseconds();
|
||||
|
||||
// Swap buffers
|
||||
int old_write_id = write_id;
|
||||
write_id = !write_id;
|
||||
|
||||
// For each thread:
|
||||
ThreadInfoList::iterator it_end = thread_infos.end();
|
||||
for(ThreadInfoList::iterator it = thread_infos.begin() ; it != it_end ; it++)
|
||||
{
|
||||
// Get the thread information
|
||||
ThreadInfo& ti = *it;
|
||||
|
||||
MarkerList& old_markers_done = ti.markers_done[old_write_id];
|
||||
MarkerStack& old_markers_stack = ti.markers_stack[old_write_id];
|
||||
|
||||
MarkerList& new_markers_done = ti.markers_done[write_id];
|
||||
MarkerStack& new_markers_stack = ti.markers_stack[write_id];
|
||||
|
||||
// Clear the containers for the new frame
|
||||
new_markers_done.clear();
|
||||
while(!new_markers_stack.empty())
|
||||
new_markers_stack.pop();
|
||||
|
||||
// Finish the markers in the stack of the previous frame
|
||||
// and start them for the next frame.
|
||||
|
||||
// For each marker in the old stack:
|
||||
while(!old_markers_stack.empty())
|
||||
{
|
||||
// - finish the marker for the previous frame and add it to the old "done" list
|
||||
Marker& m = old_markers_stack.top();
|
||||
m.end = now - time_last_sync;
|
||||
old_markers_done.push_front(m);
|
||||
|
||||
// - start a new one for the new frame
|
||||
Marker new_marker(0.0, -1.0, m.name.c_str(), m.color);
|
||||
new_markers_stack.push(new_marker);
|
||||
|
||||
// - next iteration
|
||||
old_markers_stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
// Remember the date of last synchronization
|
||||
time_last_sync = now;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Draw the markers
|
||||
void Profiler::draw()
|
||||
{
|
||||
video::IVideoDriver* driver = irr_driver->getVideoDriver();
|
||||
std::stack<Marker> hovered_markers;
|
||||
|
||||
drawBackground();
|
||||
|
||||
// Force to show the pointer
|
||||
irr_driver->showPointer();
|
||||
|
||||
int read_id = !write_id;
|
||||
|
||||
// Compute some values for drawing (unit: pixels, but we keep floats for reducing errors accumulation)
|
||||
core::dimension2d<u32> screen_size = driver->getScreenSize();
|
||||
const float profiler_width = (1.0 - 2.0*MARGIN_X) * screen_size.Width;
|
||||
const float x_offset = MARGIN_X*screen_size.Width;
|
||||
const float y_offset = (MARGIN_Y + LINE_HEIGHT)*screen_size.Height;
|
||||
const float line_height = LINE_HEIGHT*screen_size.Height;
|
||||
|
||||
size_t nb_thread_infos = thread_infos.size();
|
||||
|
||||
const float factor = profiler_width / TIME_DRAWN_MS;
|
||||
|
||||
// Get the mouse pos
|
||||
core::position2di mouse_pos = GUIEngine::EventHandler::get()->getMousePos();
|
||||
|
||||
// For each thread:
|
||||
for(size_t i=0 ; i < nb_thread_infos ; i++)
|
||||
{
|
||||
// Draw all markers
|
||||
MarkerList& markers = thread_infos[i].markers_done[read_id];
|
||||
|
||||
if(markers.empty())
|
||||
continue;
|
||||
|
||||
MarkerList::const_iterator it_end = markers.end();
|
||||
for(MarkerList::const_iterator it = markers.begin() ; it != it_end ; it++)
|
||||
{
|
||||
const Marker& m = *it;
|
||||
assert(m.end >= 0.0);
|
||||
core::rect<s32> pos(x_offset + factor*m.start,
|
||||
y_offset + i*line_height,
|
||||
x_offset + factor*m.end,
|
||||
y_offset + (i+1)*line_height);
|
||||
|
||||
pos.UpperLeftCorner.Y += m.layer;
|
||||
pos.LowerRightCorner.Y -= m.layer;
|
||||
|
||||
driver->draw2DRectangle(m.color, pos);
|
||||
|
||||
// If the mouse cursor is over the marker, get its information
|
||||
if(pos.isPointInside(mouse_pos))
|
||||
hovered_markers.push(m);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the hovered markers' names
|
||||
core::stringw text;
|
||||
while(!hovered_markers.empty())
|
||||
{
|
||||
Marker& m = hovered_markers.top();
|
||||
text += std::string(m.name + "\n").c_str();
|
||||
hovered_markers.pop();
|
||||
}
|
||||
|
||||
/*//! draws an text and clips it to the specified rectangle if wanted
|
||||
virtual void draw(const core::stringw& text, const core::rect<s32>& position,
|
||||
video::SColor color, bool hcenter=false,
|
||||
bool vcenter=false, const core::rect<s32>* clip=0);
|
||||
|
||||
virtual void draw(const core::stringw& text, const core::rect<s32>& position,
|
||||
video::SColor color, bool hcenter,
|
||||
bool vcenter, const core::rect<s32>* clip, bool ignoreRTL);*/
|
||||
|
||||
|
||||
gui::ScalableFont* font = GUIEngine::getFont();
|
||||
//font->draw(text, MARKERS_NAMES_POS, video::SColor(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
font->draw(text, core::rect<s32>(50,50,150,150), video::SColor(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// Helper to draw a white background
|
||||
void Profiler::drawBackground()
|
||||
{
|
||||
video::IVideoDriver* driver = irr_driver->getVideoDriver();
|
||||
const core::dimension2d<u32>& screen_size = driver->getScreenSize();
|
||||
|
||||
core::rect<s32> pos(MARGIN_X * screen_size.Width,
|
||||
MARGIN_Y * screen_size.Height,
|
||||
(1.0-MARGIN_X) * screen_size.Width,
|
||||
(MARGIN_Y + 3.0*LINE_HEIGHT) * screen_size.Height);
|
||||
|
||||
video::SColor color(0xFF, 0xFF, 0xFF, 0xFF);
|
||||
driver->draw2DRectangle(color, pos);
|
||||
}
|
||||
99
src/utils/profiler.hpp
Normal file
99
src/utils/profiler.hpp
Normal file
@@ -0,0 +1,99 @@
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2004 SuperTuxKart-Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef PROFILER_HPP
|
||||
#define PROFILER_HPP
|
||||
|
||||
#include <irrlicht.h>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
|
||||
class Profiler;
|
||||
extern Profiler profiler;
|
||||
|
||||
#define PROFILER_PUSH_CPU_MARKER(name, r, g, b) \
|
||||
profiler.pushCpuMarker(name, video::SColor(0xFF, r, g, b))
|
||||
|
||||
#define PROFILER_POP_CPU_MARKER() \
|
||||
profiler.popCpuMarker()
|
||||
|
||||
#define PROFILER_SYNC_FRAME() \
|
||||
profiler.synchronizeFrame()
|
||||
|
||||
using namespace irr;
|
||||
|
||||
/**
|
||||
* \brief class that allows run-time graphical profiling through the use of markers
|
||||
* \ingroup utils
|
||||
*/
|
||||
class Profiler
|
||||
{
|
||||
public:
|
||||
struct Marker
|
||||
{
|
||||
double start; // Times of start and end, in milliseconds,
|
||||
double end; // relatively to the time of last synchronization
|
||||
size_t layer;
|
||||
|
||||
std::string name;
|
||||
video::SColor color;
|
||||
|
||||
Marker(double start, double end, const char* name="N/A", const video::SColor& color=video::SColor(), size_t layer=0)
|
||||
: start(start), end(end), layer(layer), name(name), color(color)
|
||||
{
|
||||
}
|
||||
|
||||
Marker(const Marker& ref)
|
||||
: start(ref.start), end(ref.end), layer(ref.layer), name(ref.name), color(ref.color)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::list<Marker> MarkerList;
|
||||
typedef std::stack<Marker> MarkerStack;
|
||||
|
||||
struct ThreadInfo
|
||||
{
|
||||
MarkerList markers_done[2];
|
||||
MarkerStack markers_stack[2];
|
||||
};
|
||||
|
||||
typedef std::vector<ThreadInfo> ThreadInfoList;
|
||||
|
||||
ThreadInfoList thread_infos;
|
||||
int write_id;
|
||||
double time_last_sync;
|
||||
|
||||
public:
|
||||
Profiler();
|
||||
virtual ~Profiler();
|
||||
|
||||
void pushCpuMarker(const char* name="N/A", const video::SColor& color=video::SColor());
|
||||
void popCpuMarker();
|
||||
void synchronizeFrame();
|
||||
|
||||
void draw();
|
||||
|
||||
protected:
|
||||
// TODO: detect on which thread this is called to support multithreading
|
||||
ThreadInfo& getThreadInfo() { return thread_infos[0]; }
|
||||
void drawBackground();
|
||||
};
|
||||
|
||||
#endif // PROFILER_HPP
|
||||
Reference in New Issue
Block a user