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+ static uint64_t count = 0 ;
135+ _data = TracyInternTable::source_location_allocator.alloc ();
136+ count++;
137+ print_line (count);
138+
139+ _data->function_ptr_hash = hash;
140+ _data->file = _intern_name (p_file, HashMapHasherDefault::hash (p_file));
141+ _data->function = _intern_name (p_function, HashMapHasherDefault::hash (p_function));
142+
143+ _data->source_location_data .file = _data->file ->name_utf8 .get_data ();
144+ _data->source_location_data .function = _data->function ->name_utf8 .get_data ();
145+ _data->source_location_data .name = _data->source_location_data .function ;
146+
147+ _data->source_location_data .line = p_line;
148+ _data->source_location_data .color = 0x478cbf ; // godot_logo_blue
149+
150+ _data->next = TracyInternTable::source_location_table[idx];
151+ _data->prev = nullptr ;
152+
153+ if (TracyInternTable::source_location_table[idx]) {
154+ TracyInternTable::source_location_table[idx]->prev = _data;
155+ }
156+ TracyInternTable::source_location_table[idx] = _data;
157+
158+ return &_data->source_location_data ;
159+ }
160+ } // namespace TracyInternal
161+
34162void godot_init_profiler () {
163+ ERR_FAIL_COND (TracyInternal::configured);
164+ for (uint32_t i = 0 ; i < TracyInternal::TracyInternTable::TABLE_LEN; i++) {
165+ TracyInternal::TracyInternTable::source_location_table[i] = nullptr ;
166+ }
167+ TracyInternal::configured = true ;
168+
35169 // Send our first event to tracy; otherwise it doesn't start collecting data.
36170 // FrameMark is kind of fitting because it communicates "this is where we started tracing".
37171 FrameMark;
38172}
173+
174+ void godot_cleanup_profiler () {
175+ MutexLock lock (TracyInternal::TracyInternTable::mutex);
176+
177+ for (uint32_t i = 0 ; i < TracyInternal::TracyInternTable::TABLE_LEN; i++) {
178+ while (TracyInternal::TracyInternTable::source_location_table[i]) {
179+ TracyInternal::SourceLocationInternData *d = TracyInternal::TracyInternTable::source_location_table[i];
180+ TracyInternal::TracyInternTable::source_location_table[i] = TracyInternal::TracyInternTable::source_location_table[i]->next ;
181+ TracyInternal::TracyInternTable::source_location_allocator.free (d);
182+ }
183+
184+ while (TracyInternal::TracyInternTable::string_table[i]) {
185+ TracyInternal::StringInternData *d = TracyInternal::TracyInternTable::string_table[i];
186+ TracyInternal::TracyInternTable::string_table[i] = TracyInternal::TracyInternTable::string_table[i]->next ;
187+ TracyInternal::TracyInternTable::string_allocator.free (d);
188+ }
189+ }
190+ TracyInternal::configured = false ;
191+ }
192+
39193#elif defined(GODOT_USE_PERFETTO)
40194PERFETTO_TRACK_EVENT_STATIC_STORAGE ();
41195
@@ -47,8 +201,17 @@ void godot_init_profiler() {
47201 perfetto::Tracing::Initialize (args);
48202 perfetto::TrackEvent::Register ();
49203}
204+
205+ void godot_cleanup_profiler () {
206+ // Stub
207+ }
208+
50209#else
51210void godot_init_profiler () {
52211 // Stub
53212}
213+
214+ void godot_cleanup_profiler () {
215+ // Stub
216+ }
54217#endif
0 commit comments