Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Core/Profiler.h
9906 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#pragma once
6
7
JPH_SUPPRESS_WARNINGS_STD_BEGIN
8
#include <mutex>
9
#include <chrono>
10
JPH_SUPPRESS_WARNINGS_STD_END
11
12
#include <Jolt/Core/NonCopyable.h>
13
#include <Jolt/Core/TickCounter.h>
14
#include <Jolt/Core/UnorderedMap.h>
15
16
#if defined(JPH_EXTERNAL_PROFILE)
17
18
JPH_NAMESPACE_BEGIN
19
20
#ifdef JPH_SHARED_LIBRARY
21
/// Functions called when a profiler measurement starts or stops, need to be overridden by the user.
22
using ProfileStartMeasurementFunction = void (*)(const char *inName, uint32 inColor, uint8 *ioUserData);
23
using ProfileEndMeasurementFunction = void (*)(uint8 *ioUserData);
24
25
JPH_EXPORT extern ProfileStartMeasurementFunction ProfileStartMeasurement;
26
JPH_EXPORT extern ProfileEndMeasurementFunction ProfileEndMeasurement;
27
#endif // JPH_SHARED_LIBRARY
28
29
/// Create this class on the stack to start sampling timing information of a particular scope.
30
///
31
/// For statically linked builds, this is left unimplemented intentionally. Needs to be implemented by the user of the library.
32
/// On construction a measurement should start, on destruction it should be stopped.
33
/// For dynamically linked builds, the user should override the ProfileStartMeasurement and ProfileEndMeasurement functions.
34
class alignas(16) ExternalProfileMeasurement : public NonCopyable
35
{
36
public:
37
/// Constructor
38
#ifdef JPH_SHARED_LIBRARY
39
JPH_INLINE ExternalProfileMeasurement(const char *inName, uint32 inColor = 0) { ProfileStartMeasurement(inName, inColor, mUserData); }
40
JPH_INLINE ~ExternalProfileMeasurement() { ProfileEndMeasurement(mUserData); }
41
#else
42
ExternalProfileMeasurement(const char *inName, uint32 inColor = 0);
43
~ExternalProfileMeasurement();
44
#endif
45
46
private:
47
uint8 mUserData[64];
48
};
49
50
JPH_NAMESPACE_END
51
52
//////////////////////////////////////////////////////////////////////////////////////////
53
// Macros to do the actual profiling
54
//////////////////////////////////////////////////////////////////////////////////////////
55
56
JPH_SUPPRESS_WARNING_PUSH
57
JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
58
59
// Dummy implementations
60
#define JPH_PROFILE_START(name)
61
#define JPH_PROFILE_END()
62
#define JPH_PROFILE_THREAD_START(name)
63
#define JPH_PROFILE_THREAD_END()
64
#define JPH_PROFILE_NEXTFRAME()
65
#define JPH_PROFILE_DUMP(...)
66
67
// Scope profiling measurement
68
#define JPH_PROFILE_TAG2(line) profile##line
69
#define JPH_PROFILE_TAG(line) JPH_PROFILE_TAG2(line)
70
71
/// Macro to collect profiling information.
72
///
73
/// Usage:
74
///
75
/// {
76
/// JPH_PROFILE("Operation");
77
/// do operation;
78
/// }
79
///
80
#define JPH_PROFILE(...) ExternalProfileMeasurement JPH_PROFILE_TAG(__LINE__)(__VA_ARGS__)
81
82
// Scope profiling for function
83
#define JPH_PROFILE_FUNCTION() JPH_PROFILE(JPH_FUNCTION_NAME)
84
85
JPH_SUPPRESS_WARNING_POP
86
87
#elif defined(JPH_PROFILE_ENABLED)
88
89
JPH_NAMESPACE_BEGIN
90
91
class ProfileSample;
92
class ProfileThread;
93
94
/// Singleton class for managing profiling information
95
class JPH_EXPORT Profiler : public NonCopyable
96
{
97
public:
98
JPH_OVERRIDE_NEW_DELETE
99
100
/// Constructor
101
Profiler() { UpdateReferenceTime(); }
102
103
/// Increments the frame counter to provide statistics per frame
104
void NextFrame();
105
106
/// Dump profiling statistics at the start of the next frame
107
/// @param inTag If not empty, this overrides the auto incrementing number in the filename of the dump file
108
void Dump(const string_view &inTag = string_view());
109
110
/// Add a thread to be instrumented
111
void AddThread(ProfileThread *inThread);
112
113
/// Remove a thread from being instrumented
114
void RemoveThread(ProfileThread *inThread);
115
116
/// Singleton instance
117
static Profiler * sInstance;
118
119
private:
120
/// Helper class to freeze ProfileSamples per thread while processing them
121
struct ThreadSamples
122
{
123
String mThreadName;
124
ProfileSample * mSamplesBegin;
125
ProfileSample * mSamplesEnd;
126
};
127
128
/// Helper class to aggregate ProfileSamples
129
class Aggregator
130
{
131
public:
132
/// Constructor
133
Aggregator(const char *inName) : mName(inName) { }
134
135
/// Accumulate results for a measurement
136
void AccumulateMeasurement(uint64 inCyclesInCallWithChildren)
137
{
138
mCallCounter++;
139
mTotalCyclesInCallWithChildren += inCyclesInCallWithChildren;
140
mMinCyclesInCallWithChildren = min(inCyclesInCallWithChildren, mMinCyclesInCallWithChildren);
141
mMaxCyclesInCallWithChildren = max(inCyclesInCallWithChildren, mMaxCyclesInCallWithChildren);
142
}
143
144
/// Sort descending by total cycles
145
bool operator < (const Aggregator &inRHS) const
146
{
147
return mTotalCyclesInCallWithChildren > inRHS.mTotalCyclesInCallWithChildren;
148
}
149
150
/// Identification
151
const char * mName; ///< User defined name of this item
152
153
/// Statistics
154
uint32 mCallCounter = 0; ///< Number of times AccumulateMeasurement was called
155
uint64 mTotalCyclesInCallWithChildren = 0; ///< Total amount of cycles spent in this scope
156
uint64 mMinCyclesInCallWithChildren = 0xffffffffffffffffUL; ///< Minimum amount of cycles spent per call
157
uint64 mMaxCyclesInCallWithChildren = 0; ///< Maximum amount of cycles spent per call
158
};
159
160
using Threads = Array<ThreadSamples>;
161
using Aggregators = Array<Aggregator>;
162
using KeyToAggregator = UnorderedMap<const char *, size_t>;
163
164
/// Helper function to aggregate profile sample data
165
static void sAggregate(int inDepth, uint32 inColor, ProfileSample *&ioSample, const ProfileSample *inEnd, Aggregators &ioAggregators, KeyToAggregator &ioKeyToAggregator);
166
167
/// We measure the amount of ticks per second, this function resets the reference time point
168
void UpdateReferenceTime();
169
170
/// Get the amount of ticks per second, note that this number will never be fully accurate as the amount of ticks per second may vary with CPU load, so this number is only to be used to give an indication of time for profiling purposes
171
uint64 GetProcessorTicksPerSecond() const;
172
173
/// Dump profiling statistics
174
void DumpInternal();
175
void DumpChart(const char *inTag, const Threads &inThreads, const KeyToAggregator &inKeyToAggregators, const Aggregators &inAggregators);
176
177
std::mutex mLock; ///< Lock that protects mThreads
178
uint64 mReferenceTick; ///< Tick count at the start of the frame
179
std::chrono::high_resolution_clock::time_point mReferenceTime; ///< Time at the start of the frame
180
Array<ProfileThread *> mThreads; ///< List of all active threads
181
bool mDump = false; ///< When true, the samples are dumped next frame
182
String mDumpTag; ///< When not empty, this overrides the auto incrementing number of the dump filename
183
};
184
185
// Class that contains the information of a single scoped measurement
186
class alignas(16) JPH_EXPORT_GCC_BUG_WORKAROUND ProfileSample : public NonCopyable
187
{
188
public:
189
JPH_OVERRIDE_NEW_DELETE
190
191
const char * mName; ///< User defined name of this item
192
uint32 mColor; ///< Color to use for this sample
193
uint8 mDepth; ///< Calculated depth
194
uint8 mUnused[3];
195
uint64 mStartCycle; ///< Cycle counter at start of measurement
196
uint64 mEndCycle; ///< Cycle counter at end of measurement
197
};
198
199
/// Collects all samples of a single thread
200
class ProfileThread : public NonCopyable
201
{
202
public:
203
JPH_OVERRIDE_NEW_DELETE
204
205
/// Constructor
206
inline ProfileThread(const string_view &inThreadName);
207
inline ~ProfileThread();
208
209
static const uint cMaxSamples = 65536;
210
211
String mThreadName; ///< Name of the thread that we're collecting information for
212
ProfileSample mSamples[cMaxSamples]; ///< Buffer of samples
213
uint mCurrentSample = 0; ///< Next position to write a sample to
214
215
#ifdef JPH_SHARED_LIBRARY
216
JPH_EXPORT static void sSetInstance(ProfileThread *inInstance);
217
JPH_EXPORT static ProfileThread *sGetInstance();
218
#else
219
static inline void sSetInstance(ProfileThread *inInstance) { sInstance = inInstance; }
220
static inline ProfileThread *sGetInstance() { return sInstance; }
221
222
private:
223
static thread_local ProfileThread *sInstance;
224
#endif
225
};
226
227
/// Create this class on the stack to start sampling timing information of a particular scope
228
class JPH_EXPORT ProfileMeasurement : public NonCopyable
229
{
230
public:
231
/// Constructor
232
inline ProfileMeasurement(const char *inName, uint32 inColor = 0);
233
inline ~ProfileMeasurement();
234
235
private:
236
ProfileSample * mSample;
237
ProfileSample mTemp;
238
239
static bool sOutOfSamplesReported;
240
};
241
242
JPH_NAMESPACE_END
243
244
#include "Profiler.inl"
245
246
//////////////////////////////////////////////////////////////////////////////////////////
247
// Macros to do the actual profiling
248
//////////////////////////////////////////////////////////////////////////////////////////
249
250
JPH_SUPPRESS_WARNING_PUSH
251
JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
252
253
/// Start instrumenting program
254
#define JPH_PROFILE_START(name) do { Profiler::sInstance = new Profiler; JPH_PROFILE_THREAD_START(name); } while (false)
255
256
/// End instrumenting program
257
#define JPH_PROFILE_END() do { JPH_PROFILE_THREAD_END(); delete Profiler::sInstance; Profiler::sInstance = nullptr; } while (false)
258
259
/// Start instrumenting a thread
260
#define JPH_PROFILE_THREAD_START(name) do { if (Profiler::sInstance) ProfileThread::sSetInstance(new ProfileThread(name)); } while (false)
261
262
/// End instrumenting a thread
263
#define JPH_PROFILE_THREAD_END() do { delete ProfileThread::sGetInstance(); ProfileThread::sSetInstance(nullptr); } while (false)
264
265
/// Scope profiling measurement
266
#define JPH_PROFILE_TAG2(line) profile##line
267
#define JPH_PROFILE_TAG(line) JPH_PROFILE_TAG2(line)
268
#define JPH_PROFILE(...) ProfileMeasurement JPH_PROFILE_TAG(__LINE__)(__VA_ARGS__)
269
270
/// Scope profiling for function
271
#define JPH_PROFILE_FUNCTION() JPH_PROFILE(JPH_FUNCTION_NAME)
272
273
/// Update frame counter
274
#define JPH_PROFILE_NEXTFRAME() Profiler::sInstance->NextFrame()
275
276
/// Dump profiling info
277
#define JPH_PROFILE_DUMP(...) Profiler::sInstance->Dump(__VA_ARGS__)
278
279
JPH_SUPPRESS_WARNING_POP
280
281
#else
282
283
//////////////////////////////////////////////////////////////////////////////////////////
284
// Dummy profiling instructions
285
//////////////////////////////////////////////////////////////////////////////////////////
286
287
JPH_SUPPRESS_WARNING_PUSH
288
JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
289
290
#define JPH_PROFILE_START(name)
291
#define JPH_PROFILE_END()
292
#define JPH_PROFILE_THREAD_START(name)
293
#define JPH_PROFILE_THREAD_END()
294
#define JPH_PROFILE(...)
295
#define JPH_PROFILE_FUNCTION()
296
#define JPH_PROFILE_NEXTFRAME()
297
#define JPH_PROFILE_DUMP(...)
298
299
JPH_SUPPRESS_WARNING_POP
300
301
#endif
302
303