Coverage Report

Created: 2025-07-11 06:36

/src/ogre/OgreMain/include/OgreProfiler.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
-----------------------------------------------------------------------------
3
This source file is part of OGRE
4
    (Object-oriented Graphics Rendering Engine)
5
For the latest info, see http://www.ogre3d.org/
6
7
Copyright (c) 2000-2014 Torus Knot Software Ltd
8
9
Permission is hereby granted, free of charge, to any person obtaining a copy
10
of this software and associated documentation files (the "Software"), to deal
11
in the Software without restriction, including without limitation the rights
12
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
copies of the Software, and to permit persons to whom the Software is
14
furnished to do so, subject to the following conditions:
15
16
The above copyright notice and this permission notice shall be included in
17
all copies or substantial portions of the Software.
18
19
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
THE SOFTWARE.
26
-----------------------------------------------------------------------------
27
*/
28
/*
29
30
    Although the code is original, many of the ideas for the profiler were borrowed from 
31
"Real-Time In-Game Profiling" by Steve Rabin which can be found in Game Programming
32
Gems 1.
33
34
    This code can easily be adapted to your own non-Ogre project. The only code that is 
35
Ogre-dependent is in the visualization/logging routines and the use of the Timer class.
36
37
    Enjoy!
38
39
*/
40
41
#ifndef __Profiler_H__
42
#define __Profiler_H__
43
44
#include "OgrePrerequisites.h"
45
#include "OgreSingleton.h"
46
#include "OgreHeaderPrefix.h"
47
48
#if OGRE_PROFILING == 1
49
#   define OgreProfile( a ) Ogre::Profile _OgreProfileInstance( (a) )
50
#   define OgreProfileBegin( a ) Ogre::Profiler::getSingleton().beginProfile( (a) )
51
#   define OgreProfileEnd( a ) Ogre::Profiler::getSingleton().endProfile( (a) )
52
#   define OgreProfileGroup( a, g ) Ogre::Profile OGRE_TOKEN_PASTE(_OgreProfileInstance, __LINE__) ( (a), (g) )
53
#   define OgreProfileBeginGroup( a, g ) Ogre::Profiler::getSingleton().beginProfile( (a), (g) )
54
#   define OgreProfileEndGroup( a, g ) Ogre::Profiler::getSingleton().endProfile( (a), (g) )
55
#   define OgreProfileBeginGPUEvent( g ) Ogre::Root::getSingleton().getRenderSystem()->beginProfileEvent(g)
56
#   define OgreProfileEndGPUEvent( g ) Ogre::Root::getSingleton().getRenderSystem()->endProfileEvent()
57
#   define OgreProfileMarkGPUEvent( e ) Ogre::Root::getSingleton().getRenderSystem()->markProfileEvent(e)
58
#else
59
#   define OgreProfile( a )
60
#   define OgreProfileBegin( a )
61
#   define OgreProfileEnd( a )
62
#   define OgreProfileGroup( a, g ) 
63
#   define OgreProfileBeginGroup( a, g ) 
64
#   define OgreProfileEndGroup( a, g ) 
65
#   define OgreProfileBeginGPUEvent( e )
66
#   define OgreProfileEndGPUEvent( e )
67
#   define OgreProfileMarkGPUEvent( e )
68
#endif
69
70
namespace Ogre {
71
    /** \addtogroup Core
72
    *  @{
73
    */
74
    /** \addtogroup General
75
    *  @{
76
    */
77
    /** List of reserved profiling masks
78
    */
79
    enum ProfileGroupMask
80
    {
81
        /// User default profile
82
        OGREPROF_USER_DEFAULT = 0x00000001,
83
        /// All in-built Ogre profiling will match this mask
84
        OGREPROF_ALL = 0xFF000000,
85
        /// General processing
86
        OGREPROF_GENERAL = 0x80000000,
87
        /// Culling
88
        OGREPROF_CULLING = 0x40000000,
89
        /// Rendering
90
        OGREPROF_RENDERING = 0x20000000
91
    };
92
93
    /** Represents the total timing information of a profile
94
        since profiles can be called more than once each frame
95
    */
96
    struct ProfileFrame 
97
    {
98
99
        /// The total time this profile has taken this frame
100
        ulong   frameTime;
101
102
        /// The number of times this profile was called this frame
103
        uint    calls;
104
105
        /// The hierarchical level of this profile, 0 being the main loop
106
        uint    hierarchicalLvl;
107
108
    };
109
110
    /// Represents a history of each profile during the duration of the app
111
    struct ProfileHistory 
112
    {
113
        /// The current percentage of frame time this profile has taken
114
        Real    currentTimePercent; 
115
        /// The current frame time this profile has taken in milliseconds
116
        Real    currentTimeMillisecs;
117
118
        /// The maximum percentage of frame time this profile has taken
119
        Real    maxTimePercent; 
120
        /// The maximum frame time this profile has taken in milliseconds
121
        Real    maxTimeMillisecs; 
122
123
        /// The minimum percentage of frame time this profile has taken
124
        Real    minTimePercent; 
125
        /// The minimum frame time this profile has taken in milliseconds
126
        Real    minTimeMillisecs; 
127
128
        /// The number of times this profile has been called each frame
129
        uint    numCallsThisFrame;
130
131
        /// The total percentage of frame time this profile has taken
132
        Real    totalTimePercent;
133
        /// The total frame time this profile has taken in milliseconds
134
        Real    totalTimeMillisecs;
135
136
        /// The total number of times this profile was called
137
        /// (used to calculate average)
138
        ulong   totalCalls; 
139
140
        /// The hierarchical level of this profile, 0 being the root profile
141
        uint    hierarchicalLvl;
142
143
    };
144
145
    /// Represents an individual profile call
146
    class _OgreExport ProfileInstance : public ProfilerAlloc
147
    {
148
        friend class Profiler;
149
    public:
150
        ProfileInstance(void);
151
        virtual ~ProfileInstance(void);
152
153
        typedef std::map<String,ProfileInstance*> ProfileChildren;
154
155
        void logResults();
156
        void reset();
157
158
0
        inline bool watchForMax(void) { return history.currentTimePercent == history.maxTimePercent; }
159
0
        inline bool watchForMin(void) { return history.currentTimePercent == history.minTimePercent; }
160
        inline bool watchForLimit(Real limit, bool greaterThan = true)
161
0
        {
162
0
            if (greaterThan)
163
0
                return history.currentTimePercent > limit;
164
0
            else
165
0
                return history.currentTimePercent < limit;
166
0
        }
167
168
        bool watchForMax(const String& profileName);
169
        bool watchForMin(const String& profileName);
170
        bool watchForLimit(const String& profileName, Real limit, bool greaterThan = true);
171
                                
172
        /// The name of the profile
173
        String          name;
174
175
        /// The name of the parent, null if root
176
        ProfileInstance* parent;
177
178
        ProfileChildren children;
179
180
        ProfileFrame frame;
181
        ulong frameNumber;
182
183
        ProfileHistory history;
184
185
        /// The time this profile was started
186
        ulong           currTime;
187
188
        /// Represents the total time of all child profiles to subtract
189
        /// from this profile
190
        ulong           accum;
191
192
        /// The hierarchical level of this profile, 0 being the root profile
193
        uint            hierarchicalLvl;
194
    };
195
196
    /** ProfileSessionListener should be used to visualize profile results.
197
        Concrete impl. could be done using Overlay's but its not limited to 
198
        them you can also create a custom listener which sends the profile
199
        informtaion over a network.
200
    */
201
    class _OgreExport ProfileSessionListener
202
    {
203
    public:
204
0
        virtual ~ProfileSessionListener() {}
205
206
        /// Create the internal resources
207
        virtual void initializeSession() = 0;
208
209
        /// All internal resources should be deleted here
210
        virtual void finializeSession() = 0;
211
212
        /** If the profiler disables this listener then it
213
            should hide its panels (if any exists) or stop
214
            sending data over the network
215
        */
216
0
        virtual void changeEnableState(bool enabled) {}; 
217
        
218
        /// Here we get the real profiling information which we can use 
219
0
        virtual void displayResults(const ProfileInstance& instance, ulong maxTotalFrameTime) {};
220
    };
221
222
    /** The profiler allows you to measure the performance of your code
223
224
        Do not create profiles directly from this unless you want a profile to last
225
        outside of its scope (i.e. the main game loop). For most cases, use the macro
226
        OgreProfile(name) and braces to limit the scope. You must enable the Profile
227
        before you can used it with setEnabled(true). If you want to disable profiling
228
        in Ogre, simply set the macro OGRE_PROFILING to 0.
229
        @author Amit Mathew (amitmathew (at) yahoo (dot) com)
230
        @todo resolve artificial cap on number of profiles displayed
231
        @todo fix display ordering of profiles not called every frame
232
    */
233
    class _OgreExport Profiler : 
234
        public Singleton<Profiler>,
235
        public ProfilerAlloc
236
    {
237
        public:
238
            Profiler();
239
            ~Profiler();
240
241
            /** Sets the timer for the profiler */
242
            void setTimer(Timer* t);
243
244
            /** Retrieves the timer for the profiler */
245
            Timer* getTimer();
246
247
            /** Begins a profile
248
249
                Use the macro OgreProfileBegin(name) instead of calling this directly 
250
                so that profiling can be ignored in the release version of your app. 
251
252
                You only use the macro (or this) if you want a profile to last outside
253
                of its scope (i.e. the main game loop). If you use this function, make sure you 
254
                use a corresponding OgreProfileEnd(name). Usually you would use the macro 
255
                OgreProfile(name). This function will be ignored for a profile that has been 
256
                disabled or if the profiler is disabled.
257
            @param profileName Must be unique and must not be an empty string
258
            @param groupID A profile group identifier, which can allow you to mask profiles
259
            */
260
            void beginProfile(const String& profileName, uint32 groupID = (uint32)OGREPROF_USER_DEFAULT);
261
262
            /** Ends a profile
263
264
                Use the macro OgreProfileEnd(name) instead of calling this directly so that
265
                profiling can be ignored in the release version of your app.
266
267
                This function is usually not called directly unless you want a profile to
268
                last outside of its scope. In most cases, using the macro OgreProfile(name) 
269
                which will call this function automatically when it goes out of scope. Make 
270
                sure the name of this profile matches its corresponding beginProfile name. 
271
                This function will be ignored for a profile that has been disabled or if the
272
                profiler is disabled.
273
            @param profileName Must be unique and must not be an empty string
274
            @param groupID A profile group identifier, which can allow you to mask profiles
275
            */
276
            void endProfile(const String& profileName, uint32 groupID = (uint32)OGREPROF_USER_DEFAULT);
277
278
            /** Sets whether this profiler is enabled. Only takes effect after the
279
                the frame has ended.
280
                @remarks When this is called the first time with the parameter true,
281
                it initializes the GUI for the Profiler
282
            */
283
            void setEnabled(bool enabled);
284
285
            /** Gets whether this profiler is enabled */
286
            bool getEnabled() const;
287
288
            /** Enables a previously disabled profile 
289
            @remarks Can be safely called in the middle of the profile.
290
            */
291
            void enableProfile(const String& profileName);
292
293
            /** Disables a profile
294
            @remarks Can be safely called in the middle of the profile.
295
            */
296
            void disableProfile(const String& profileName);
297
298
            /** Set the mask which all profiles must pass to be enabled. 
299
            */
300
0
            void setProfileGroupMask(uint32 mask) { mProfileMask = mask; }
301
            /** Get the mask which all profiles must pass to be enabled. 
302
            */
303
0
            uint32 getProfileGroupMask() const { return mProfileMask; }
304
305
            /** Returns true if the specified profile reaches a new frame time maximum
306
            @remarks If this is called during a frame, it will be reading the results
307
            from the previous frame. Therefore, it is best to use this after the frame
308
            has ended.
309
            */
310
            bool watchForMax(const String& profileName);
311
312
            /** Returns true if the specified profile reaches a new frame time minimum
313
            @remarks If this is called during a frame, it will be reading the results
314
            from the previous frame. Therefore, it is best to use this after the frame
315
            has ended.
316
            */
317
            bool watchForMin(const String& profileName);
318
319
            /** Returns true if the specified profile goes over or under the given limit
320
                frame time
321
            @remarks If this is called during a frame, it will be reading the results
322
            from the previous frame. Therefore, it is best to use this after the frame
323
            has ended.
324
            @param profileName Must be unique and must not be an empty string
325
            @param limit A number between 0 and 1 representing the percentage of frame time
326
            @param greaterThan If true, this will return whether the limit is exceeded. Otherwise,
327
            it will return if the frame time has gone under this limit.
328
            */
329
            bool watchForLimit(const String& profileName, Real limit, bool greaterThan = true);
330
331
            /** Outputs current profile statistics to the log */
332
            void logResults();
333
334
            /** Clears the profiler statistics */
335
            void reset();
336
337
            /** Sets the Profiler so the display of results are updated every n frames*/
338
            void setUpdateDisplayFrequency(uint freq);
339
340
            /** Gets the frequency that the Profiler display is updated */
341
            uint getUpdateDisplayFrequency() const;
342
343
            /**
344
345
                Register a ProfileSessionListener from the Profiler
346
            @param listener
347
                A valid listener derived class
348
            */
349
            void addListener(ProfileSessionListener* listener);
350
351
            /**
352
353
                Unregister a ProfileSessionListener from the Profiler
354
            @param listener
355
                A valid listener derived class
356
            */
357
            void removeListener(ProfileSessionListener* listener);
358
359
            /// @copydoc Singleton::getSingleton()
360
            static Profiler& getSingleton(void);
361
            /// @copydoc Singleton::getSingleton()
362
            static Profiler* getSingletonPtr(void);
363
364
        private:
365
            friend class ProfileInstance;
366
367
            typedef std::vector<ProfileSessionListener*> TProfileSessionListener;
368
            TProfileSessionListener mListeners;
369
370
            /** Initializes the profiler's GUI elements */
371
            void initialize();
372
373
            void displayResults();
374
375
            /** Processes frame stats for all of the mRoot's children */
376
            void processFrameStats(void);
377
            /** Processes specific ProfileInstance and it's children recursively.*/
378
            void processFrameStats(ProfileInstance* instance, Real& maxFrameTime);
379
380
            /** Handles a change of the profiler's enabled state*/
381
            void changeEnableState();
382
383
            // lol. Uses typedef; put's original container type in name.
384
            typedef std::set<String> DisabledProfileMap;
385
            typedef ProfileInstance::ProfileChildren ProfileChildren;
386
387
            ProfileInstance* mCurrent;
388
            ProfileInstance* mLast;
389
            ProfileInstance mRoot;
390
391
            /// Holds the names of disabled profiles
392
            DisabledProfileMap mDisabledProfiles;
393
394
            /// Whether the GUI elements have been initialized
395
            bool mInitialized;
396
397
            /// The number of frames that must elapse before the current
398
            /// frame display is updated
399
            uint mUpdateDisplayFrequency;
400
401
            /// The number of elapsed frame, used with mUpdateDisplayFrequency
402
            uint mCurrentFrame;
403
404
            /// The timer used for profiling
405
            Timer* mTimer;
406
407
            /// The total time each frame takes
408
            ulong mTotalFrameTime;
409
410
            /// Whether this profiler is enabled
411
            bool mEnabled;
412
413
            /// Keeps track of the new enabled/disabled state that the user has requested
414
            /// which will be applied after the frame ends
415
            bool mNewEnableState;
416
417
            /// Mask to decide whether a type of profile is enabled or not
418
            uint32 mProfileMask;
419
420
            /// The max frame time recorded
421
            ulong mMaxTotalFrameTime;
422
423
            /// Rolling average of millisecs
424
            Real mAverageFrameTime;
425
            bool mResetExtents;
426
427
428
    }; // end class
429
430
    /** An individual profile that will be processed by the Profiler
431
432
        Use the macro OgreProfile(name) instead of instantiating this profile directly
433
434
        We use this Profile to allow scoping rules to signify the beginning and end of
435
        the profile. Use the Profiler singleton (through the macro OgreProfileBegin(name)
436
        and OgreProfileEnd(name)) directly if you want a profile to last
437
        outside of a scope (i.e. the main game loop).
438
        @author Amit Mathew (amitmathew (at) yahoo (dot) com)
439
    */
440
    class Profile : public ProfilerAlloc
441
    {
442
443
    public:
444
        Profile(const String& profileName, uint32 groupID = (uint32)OGREPROF_USER_DEFAULT)
445
            : mName(profileName), mGroupID(groupID)
446
0
        {
447
0
            Profiler::getSingleton().beginProfile(profileName, groupID);
448
0
        }
449
0
        ~Profile() { Profiler::getSingleton().endProfile(mName, mGroupID); }
450
451
    private:
452
        /// The name of this profile
453
        String mName;
454
        /// The group ID
455
        uint32 mGroupID;
456
    };
457
    /** @} */
458
    /** @} */
459
460
} // end namespace
461
462
#include "OgreHeaderSuffix.h"
463
464
#endif