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:
funto66
2011-07-09 19:13:17 +00:00
parent 9b4ce3f9d0
commit de401bc234
11 changed files with 412 additions and 16 deletions

View File

@@ -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 \

View File

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

View 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;

View File

@@ -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.

View File

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

View File

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

View File

@@ -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" />

View File

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

View File

@@ -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
View 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
View 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