1 // SuperTuxKart - a fun racing game with go-kart 2 // Copyright (C) 2004-2015 SuperTuxKart-Team 3 // 4 // This program is free software; you can redistribute it and/or 5 // modify it under the terms of the GNU General Public License 6 // as published by the Free Software Foundation; either version 3 7 // of the License, or (at your option) any later version. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with this program; if not, write to the Free Software 16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 18 #ifndef PROFILER_HPP 19 #define PROFILER_HPP 20 21 #include "utils/synchronised.hpp" 22 23 #include <irrlicht.h> 24 25 #include <assert.h> 26 #include <atomic> 27 #include <iostream> 28 #include <list> 29 #include <map> 30 #include <ostream> 31 #include <stack> 32 #include <streambuf> 33 #include <string> 34 #include <vector> 35 36 enum QueryPerf 37 { 38 Q_SHADOWS_CASCADE0, 39 Q_SHADOWS_CASCADE1, 40 Q_SHADOWS_CASCADE2, 41 Q_SHADOWS_CASCADE3, 42 Q_SOLID_PASS, 43 Q_ENVMAP, 44 Q_SUN, 45 Q_POINTLIGHTS, 46 Q_SSAO, 47 Q_LIGHTSCATTER, 48 Q_GLOW, 49 Q_COMBINE_DIFFUSE_COLOR, 50 Q_SKYBOX, 51 Q_TRANSPARENT, 52 Q_PARTICLES, 53 Q_DOF, 54 Q_GODRAYS, 55 Q_BLOOM, 56 Q_TONEMAP, 57 Q_MOTIONBLUR, 58 Q_LIGHTNING, 59 Q_MLAA, 60 Q_GUI, 61 Q_LAST 62 }; 63 64 class Profiler; 65 extern Profiler profiler; 66 67 double getTimeMilliseconds(); 68 69 #define ENABLE_PROFILER 70 71 #ifdef ENABLE_PROFILER 72 #define PROFILER_PUSH_CPU_MARKER(name, r, g, b) \ 73 profiler.pushCPUMarker(name, video::SColor(0xFF, r, g, b)) 74 75 #define PROFILER_POP_CPU_MARKER() \ 76 profiler.popCPUMarker() 77 78 #define PROFILER_SYNC_FRAME() \ 79 profiler.synchronizeFrame() 80 81 #define PROFILER_DRAW() \ 82 profiler.draw() 83 #else 84 #define PROFILER_PUSH_CPU_MARKER(name, r, g, b) 85 #define PROFILER_POP_CPU_MARKER() 86 #define PROFILER_SYNC_FRAME() 87 #define PROFILER_DRAW() 88 #endif 89 90 using namespace irr; 91 92 // ============================================================================ 93 /** \brief class that allows run-time graphical profiling through the use 94 * of markers. 95 * \ingroup utils 96 */ 97 class Profiler 98 { 99 private: 100 // ------------------------------------------------------------------------ 101 class Marker 102 { 103 private: 104 /** An event that is started (pushed) stores the start time in this 105 * variable. */ 106 double m_start; 107 108 /** Duration of the event in this frame (accumulated if this event 109 * should be recorded more than once). */ 110 111 double m_duration; 112 /** Distance of marker from root (for nested events), used to 113 * adjust vertical height when drawing. */ 114 size_t m_layer; 115 public: 116 // -------------------------------------------------------------------- Marker()117 Marker() { m_start = 0; m_duration = 0; m_layer = 0; } 118 119 // -------------------------------------------------------------------- Marker(double start,size_t layer=0)120 Marker(double start, size_t layer=0) 121 : m_start(start), m_duration(0), m_layer(layer) 122 { 123 } 124 // -------------------------------------------------------------------- Marker(const Marker & ref)125 Marker(const Marker& ref) 126 : m_start(ref.m_start), m_duration(ref.m_duration), 127 m_layer(ref.m_layer) 128 { 129 } 130 // -------------------------------------------------------------------- 131 /** Returns the start time of this event marker. */ getStart() const132 double getStart() const { return m_start; } 133 // -------------------------------------------------------------------- 134 /** Returns the end time of this event marker. */ getEnd() const135 double getEnd() const { return m_start+m_duration; } 136 // -------------------------------------------------------------------- 137 /** Returns the duration of this event. */ getDuration() const138 double getDuration() const { return m_duration; } 139 // -------------------------------------------------------------------- getLayer() const140 size_t getLayer() const { return m_layer; } 141 // -------------------------------------------------------------------- 142 /** Called when an entry in the cyclic buffer is reused. Makes sure 143 * that time for a new event can be accumulated. */ clear()144 void clear() { m_duration = 0; } 145 // -------------------------------------------------------------------- 146 /** Sets start time and layer for this event. */ setStart(double start,size_t layer=0)147 void setStart(double start, size_t layer = 0) 148 { 149 m_start = start; m_layer = layer; 150 } // setStart 151 // -------------------------------------------------------------------- 152 /** Sets the end time of this event. */ setEnd(double end)153 void setEnd(double end) 154 { 155 m_duration += (end - m_start); 156 } // setEnd 157 158 }; // class Marker 159 160 // ======================================================================== 161 /** The data for one event. It contains the events colours, all markers 162 * for the buffer period and a stack to detect nesting of markers. 163 */ 164 class EventData 165 { 166 private: 167 /** Colour to use in the on-screen display */ 168 video::SColor m_colour; 169 170 /** Vector of all buffered markers. */ 171 std::vector<Marker> m_all_markers; 172 173 public: EventData()174 EventData() {} EventData(video::SColor colour,int max_size)175 EventData(video::SColor colour, int max_size) 176 { 177 m_all_markers.resize(max_size); 178 m_colour = colour; 179 } // EventData 180 // -------------------------------------------------------------------- 181 /** Records the start of an event for a given frame. */ setStart(size_t frame,double start,int layer)182 void setStart(size_t frame, double start, int layer) 183 { 184 assert(frame < m_all_markers.capacity()); 185 m_all_markers[frame].setStart(start, layer); 186 } // setStart 187 // -------------------------------------------------------------------- 188 /** Records the end of an event for a given frame. */ setEnd(size_t frame,double end)189 void setEnd(size_t frame, double end) 190 { 191 assert(frame < m_all_markers.capacity()); 192 m_all_markers[frame].setEnd(end); 193 } // setEnd 194 // -------------------------------------------------------------------- getMarker(int n) const195 const Marker& getMarker(int n) const { return m_all_markers[n]; } getMarker(int n)196 Marker& getMarker(int n) { return m_all_markers[n]; } 197 // -------------------------------------------------------------------- 198 /** Returns the colour for this event. */ getColour() const199 video::SColor getColour() const { return m_colour; } 200 // -------------------------------------------------------------------- 201 }; // EventData 202 203 // ======================================================================== 204 /** The mapping of event names to the corresponding EventData. */ 205 typedef std::map<std::string, EventData> AllEventData; 206 // ======================================================================== 207 struct ThreadData 208 { 209 /** Stack of events to detect nesting. */ 210 std::vector< std::string > m_event_stack; 211 212 /** This stores the event names in the order in which they occur. 213 * This means that 'outer' events occur here before any child 214 * events. This list is then used to determine the order in which the 215 * bar graphs are drawn, which results in the proper nesting of events.*/ 216 std::vector<std::string> m_ordered_headings; 217 218 AllEventData m_all_event_data; 219 }; // class ThreadData 220 221 // ======================================================================== 222 223 /** Data structure containing all currently buffered markers. The index 224 * is the thread id. */ 225 std::vector< ThreadData> m_all_threads_data; 226 227 /** Buffer for the GPU times (in ms). */ 228 std::vector<int> m_gpu_times; 229 230 /** Counts the threads used. */ 231 std::atomic<int> m_threads_used; 232 233 /** Index of the current frame in the buffer. */ 234 int m_current_frame; 235 236 /** We don't need the bool, but easiest way to get a lock for the whole 237 * instance (since we need to avoid that a synch is done which changes 238 * the current frame while another threaded uses this variable, or 239 * while a new thread is added. */ 240 Synchronised<bool> m_lock; 241 242 /** True if the circular buffer has wrapped around. */ 243 bool m_has_wrapped_around; 244 245 /** The maximum number of frames to be buffered. Used to minimise 246 * reallocations. */ 247 int m_max_frames; 248 249 /** Time of last sync. All start/end times are stored relative 250 * to this time. */ 251 double m_time_last_sync; 252 253 /** Time between now and last sync, used to scale the GUI bar. */ 254 double m_time_between_sync; 255 256 /** List of all event names. This list is sorted to make sure 257 * if the circular buffer is dumped more than once the order 258 * of events remains the same. */ 259 std::vector<std::string> m_all_event_names; 260 261 // Handling freeze/unfreeze by clicking on the display 262 enum FreezeState 263 { 264 UNFROZEN, 265 WAITING_FOR_FREEZE, 266 FROZEN, 267 WAITING_FOR_UNFREEZE, 268 }; 269 270 FreezeState m_freeze_state; 271 272 private: 273 int getThreadID(); 274 void drawBackground(); 275 276 public: 277 Profiler(); 278 virtual ~Profiler(); 279 void init(); 280 void pushCPUMarker(const char* name="N/A", 281 const video::SColor& color=video::SColor()); 282 void popCPUMarker(); 283 void toggleStatus(); 284 void synchronizeFrame(); 285 void draw(); 286 void onClick(const core::vector2di& mouse_pos); 287 void writeToFile(); 288 289 // ------------------------------------------------------------------------ isFrozen() const290 bool isFrozen() const { return m_freeze_state == FROZEN; } 291 292 }; 293 294 #endif // PROFILER_HPP 295