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/templates/paged_allocator.h"
38+
39+ namespace TracyInternal {
40+ static bool configured = false ;
41+
42+ // Implementation similar to StringName.
43+ struct StringInternData {
44+ StringName name;
45+ CharString name_utf8;
46+
47+ uint32_t hash = 0 ;
48+ StringInternData *prev = nullptr ;
49+ StringInternData *next = nullptr ;
50+
51+ StringInternData () {}
52+ };
53+
54+ struct SourceLocationInternData {
55+ const StringInternData *file;
56+ const StringInternData *function;
57+
58+ tracy::SourceLocationData source_location_data;
59+
60+ uint32_t function_ptr_hash = 0 ;
61+ SourceLocationInternData *prev = nullptr ;
62+ SourceLocationInternData *next = nullptr ;
63+
64+ SourceLocationInternData () {}
65+ };
66+
67+ struct TracyInternTable {
68+ constexpr static uint32_t TABLE_BITS = 16 ;
69+ constexpr static uint32_t TABLE_LEN = 1 << TABLE_BITS;
70+ constexpr static uint32_t TABLE_MASK = TABLE_LEN - 1 ;
71+
72+ static inline BinaryMutex mutex;
73+
74+ static inline SourceLocationInternData *source_location_table[TABLE_LEN];
75+ static inline PagedAllocator<SourceLocationInternData> source_location_allocator;
76+
77+ static inline StringInternData *string_table[TABLE_LEN];
78+ static inline PagedAllocator<StringInternData> string_allocator;
79+ };
80+
81+ const StringInternData *_intern_name (const StringName &p_name) {
82+ CRASH_COND (!configured);
83+
84+ const uint32_t hash = p_name.hash ();
85+ const uint32_t idx = hash & TracyInternTable::TABLE_MASK;
86+
87+ StringInternData *_data = TracyInternTable::string_table[idx];
88+
89+ while (_data) {
90+ if (_data->hash == hash) {
91+ return _data;
92+ }
93+ _data = _data->next ;
94+ }
95+
96+ _data = TracyInternTable::string_allocator.alloc ();
97+ _data->name = p_name;
98+ _data->name_utf8 = p_name.operator String ().utf8 ();
99+
100+ _data->next = TracyInternTable::string_table[idx];
101+ _data->prev = nullptr ;
102+
103+ if (TracyInternTable::string_table[idx]) {
104+ TracyInternTable::string_table[idx]->prev = _data;
105+ }
106+ TracyInternTable::string_table[idx] = _data;
107+
108+ return _data;
109+ }
110+
111+ const char *intern_name (const StringName &p_name) {
112+ MutexLock lock (TracyInternTable::mutex);
113+ return _intern_name (p_name)->name_utf8 .get_data ();
114+ }
115+
116+ const tracy::SourceLocationData *intern_source_location (const void *p_function_ptr, const StringName &p_file, const StringName &p_function, uint32_t p_line) {
117+ CRASH_COND (!configured);
118+
119+ const uint32_t hash = HashMapHasherDefault::hash (p_function_ptr);
120+ const uint32_t idx = hash & TracyInternTable::TABLE_MASK;
121+
122+ MutexLock lock (TracyInternTable::mutex);
123+ SourceLocationInternData *_data = TracyInternTable::source_location_table[idx];
124+
125+ while (_data) {
126+ if (_data->function_ptr_hash == hash && _data->source_location_data .line == p_line && _data->file ->name == p_file && _data->function ->name == p_function) {
127+ return &_data->source_location_data ;
128+ }
129+ _data = _data->next ;
130+ }
131+
132+ _data = TracyInternTable::source_location_allocator.alloc ();
133+
134+ _data->function_ptr_hash = hash;
135+ _data->file = _intern_name (p_file);
136+ _data->function = _intern_name (p_function);
137+
138+ _data->source_location_data .file = _data->file ->name_utf8 .get_data ();
139+ _data->source_location_data .function = _data->function ->name_utf8 .get_data ();
140+ _data->source_location_data .name = _data->source_location_data .function ;
141+
142+ _data->source_location_data .line = p_line;
143+ _data->source_location_data .color = 0x478cbf ; // godot_logo_blue
144+
145+ _data->next = TracyInternTable::source_location_table[idx];
146+ _data->prev = nullptr ;
147+
148+ if (TracyInternTable::source_location_table[idx]) {
149+ TracyInternTable::source_location_table[idx]->prev = _data;
150+ }
151+ TracyInternTable::source_location_table[idx] = _data;
152+
153+ return &_data->source_location_data ;
154+ }
155+ } // namespace TracyInternal
156+
34157void godot_init_profiler () {
158+ MutexLock lock (TracyInternal::TracyInternTable::mutex);
159+ ERR_FAIL_COND (TracyInternal::configured);
160+
161+ for (uint32_t i = 0 ; i < TracyInternal::TracyInternTable::TABLE_LEN; i++) {
162+ TracyInternal::TracyInternTable::source_location_table[i] = nullptr ;
163+ }
164+ for (uint32_t i = 0 ; i < TracyInternal::TracyInternTable::TABLE_LEN; i++) {
165+ TracyInternal::TracyInternTable::string_table[i] = nullptr ;
166+ }
167+
168+ TracyInternal::configured = true ;
169+
35170 // Send our first event to tracy; otherwise it doesn't start collecting data.
36171 // FrameMark is kind of fitting because it communicates "this is where we started tracing".
37172 FrameMark;
38173}
174+
175+ void godot_cleanup_profiler () {
176+ MutexLock lock (TracyInternal::TracyInternTable::mutex);
177+ ERR_FAIL_COND (!TracyInternal::configured);
178+
179+ for (uint32_t i = 0 ; i < TracyInternal::TracyInternTable::TABLE_LEN; i++) {
180+ while (TracyInternal::TracyInternTable::source_location_table[i]) {
181+ TracyInternal::SourceLocationInternData *d = TracyInternal::TracyInternTable::source_location_table[i];
182+ TracyInternal::TracyInternTable::source_location_table[i] = TracyInternal::TracyInternTable::source_location_table[i]->next ;
183+ TracyInternal::TracyInternTable::source_location_allocator.free (d);
184+ }
185+ }
186+ for (uint32_t i = 0 ; i < TracyInternal::TracyInternTable::TABLE_LEN; i++) {
187+ while (TracyInternal::TracyInternTable::string_table[i]) {
188+ TracyInternal::StringInternData *d = TracyInternal::TracyInternTable::string_table[i];
189+ TracyInternal::TracyInternTable::string_table[i] = TracyInternal::TracyInternTable::string_table[i]->next ;
190+ TracyInternal::TracyInternTable::string_allocator.free (d);
191+ }
192+ }
193+
194+ TracyInternal::configured = false ;
195+ }
196+
39197#elif defined(GODOT_USE_PERFETTO)
40198PERFETTO_TRACK_EVENT_STATIC_STORAGE ();
41199
@@ -47,8 +205,17 @@ void godot_init_profiler() {
47205 perfetto::Tracing::Initialize (args);
48206 perfetto::TrackEvent::Register ();
49207}
208+
209+ void godot_cleanup_profiler () {
210+ // Stub
211+ }
212+
50213#else
51214void godot_init_profiler () {
52215 // Stub
53216}
217+
218+ void godot_cleanup_profiler () {
219+ // Stub
220+ }
54221#endif
0 commit comments