3131#include " profiling.h"
3232
3333#if defined(GODOT_USE_TRACY)
34+ // Use the tracy profiler.
35+
36+ #include " core/os/mutex.h"
37+ #include " core/string/string_name.h"
38+ #include " core/templates/paged_allocator.h"
39+ #include " core/variant/variant.h"
40+
41+ namespace TracyInternal {
42+ static bool configured = false ;
43+
44+ // Implementation similar to StringName.
45+ struct StringInternData {
46+ StringName name;
47+ CharString name_utf8;
48+ uint32_t hash = 0 ;
49+
50+ StringInternData *prev = nullptr ;
51+ StringInternData *next = nullptr ;
52+
53+ StringInternData () {}
54+ };
55+
56+ struct SourceLocationInternData {
57+ const StringInternData *file;
58+ const StringInternData *function;
59+ uint32_t function_ptr_hash = 0 ;
60+
61+ tracy::SourceLocationData source_location_data;
62+
63+ SourceLocationInternData *prev = nullptr ;
64+ SourceLocationInternData *next = nullptr ;
65+
66+ SourceLocationInternData () {}
67+ };
68+
69+ struct TracyInternTable {
70+ constexpr static uint32_t TABLE_BITS = 16 ;
71+ constexpr static uint32_t TABLE_LEN = 1 << TABLE_BITS;
72+ constexpr static uint32_t TABLE_MASK = TABLE_LEN - 1 ;
73+
74+ static inline BinaryMutex mutex;
75+
76+ static inline SourceLocationInternData *source_location_table[TABLE_LEN];
77+ static inline PagedAllocator<SourceLocationInternData> source_location_allocator;
78+
79+ static inline StringInternData *string_table[TABLE_LEN];
80+ static inline PagedAllocator<StringInternData> string_allocator;
81+ };
82+
83+ const StringInternData *_intern_name (const StringName &p_name, uint32_t p_hash) {
84+ CRASH_COND (!configured);
85+
86+ const uint32_t idx = p_hash & TracyInternTable::TABLE_MASK;
87+
88+ StringInternData *_data = TracyInternTable::string_table[idx];
89+
90+ while (_data) {
91+ if (_data->hash == p_hash) {
92+ return _data;
93+ }
94+ _data = _data->next ;
95+ }
96+
97+ _data = TracyInternTable::string_allocator.alloc ();
98+ _data->name = StringName (p_name, true );
99+ _data->name_utf8 = p_name.operator String ().utf8 ();
100+
101+ _data->next = TracyInternTable::string_table[idx];
102+ _data->prev = nullptr ;
103+
104+ if (TracyInternTable::string_table[idx]) {
105+ TracyInternTable::string_table[idx]->prev = _data;
106+ }
107+ TracyInternTable::string_table[idx] = _data;
108+
109+ return _data;
110+ }
111+
112+ const char *intern_name (const StringName &p_name) {
113+ const uint32_t hash = HashMapHasherDefault::hash (p_name);
114+ MutexLock lock (TracyInternTable::mutex);
115+ return _intern_name (p_name, hash)->name_utf8 .get_data ();
116+ }
117+
118+ const tracy::SourceLocationData *intern_source_location (const void *p_function_ptr, const StringName &p_file, const StringName &p_function, uint32_t p_line) {
119+ CRASH_COND (!configured);
120+
121+ const uint32_t hash = HashMapHasherDefault::hash (p_function_ptr);
122+ const uint32_t idx = hash & TracyInternTable::TABLE_MASK;
123+
124+ MutexLock lock (TracyInternTable::mutex);
125+ SourceLocationInternData *_data = TracyInternTable::source_location_table[idx];
126+
127+ while (_data) {
128+ if (_data->function_ptr_hash == hash && _data->file ->name == p_file && _data->function ->name == p_function && _data->source_location_data .line == p_line) {
129+ return &_data->source_location_data ;
130+ }
131+ _data = _data->next ;
132+ }
133+
134+ _data = TracyInternTable::source_location_allocator.alloc ();
135+
136+ _data->function_ptr_hash = hash;
137+ _data->file = _intern_name (p_file, HashMapHasherDefault::hash (p_file));
138+ _data->function = _intern_name (p_function, HashMapHasherDefault::hash (p_function));
139+
140+ _data->source_location_data .file = _data->file ->name_utf8 .get_data ();
141+ _data->source_location_data .function = _data->function ->name_utf8 .get_data ();
142+ _data->source_location_data .name = _data->source_location_data .function ;
143+
144+ _data->source_location_data .line = p_line;
145+ _data->source_location_data .color = 0x478cbf ; // godot_logo_blue
146+
147+ _data->next = TracyInternTable::source_location_table[idx];
148+ _data->prev = nullptr ;
149+
150+ if (TracyInternTable::source_location_table[idx]) {
151+ TracyInternTable::source_location_table[idx]->prev = _data;
152+ }
153+ TracyInternTable::source_location_table[idx] = _data;
154+
155+ return &_data->source_location_data ;
156+ }
157+ } // namespace TracyInternal
158+
34159void godot_init_profiler () {
160+ ERR_FAIL_COND (TracyInternal::configured);
161+ for (uint32_t i = 0 ; i < TracyInternal::TracyInternTable::TABLE_LEN; i++) {
162+ TracyInternal::TracyInternTable::source_location_table[i] = nullptr ;
163+ }
164+ TracyInternal::configured = true ;
165+
35166 // Send our first event to tracy; otherwise it doesn't start collecting data.
36167 // FrameMark is kind of fitting because it communicates "this is where we started tracing".
37168 FrameMark;
38169}
170+
171+ void godot_cleanup_profiler () {
172+ ERR_FAIL_COND (!TracyInternal::configured);
173+ MutexLock lock (TracyInternal::TracyInternTable::mutex);
174+
175+ for (uint32_t i = 0 ; i < TracyInternal::TracyInternTable::TABLE_LEN; i++) {
176+ while (TracyInternal::TracyInternTable::source_location_table[i]) {
177+ TracyInternal::SourceLocationInternData *d = TracyInternal::TracyInternTable::source_location_table[i];
178+ TracyInternal::TracyInternTable::source_location_table[i] = TracyInternal::TracyInternTable::source_location_table[i]->next ;
179+ TracyInternal::TracyInternTable::source_location_allocator.free (d);
180+ }
181+
182+ while (TracyInternal::TracyInternTable::string_table[i]) {
183+ TracyInternal::StringInternData *d = TracyInternal::TracyInternTable::string_table[i];
184+ TracyInternal::TracyInternTable::string_table[i] = TracyInternal::TracyInternTable::string_table[i]->next ;
185+ TracyInternal::TracyInternTable::string_allocator.free (d);
186+ }
187+ }
188+ TracyInternal::configured = false ;
189+ }
190+
39191#elif defined(GODOT_USE_PERFETTO)
40192PERFETTO_TRACK_EVENT_STATIC_STORAGE ();
41193
@@ -47,8 +199,17 @@ void godot_init_profiler() {
47199 perfetto::Tracing::Initialize (args);
48200 perfetto::TrackEvent::Register ();
49201}
202+
203+ void godot_cleanup_profiler () {
204+ // Stub
205+ }
206+
50207#else
51208void godot_init_profiler () {
52209 // Stub
53210}
211+
212+ void godot_cleanup_profiler () {
213+ // Stub
214+ }
54215#endif
0 commit comments