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