Helios Engine
A modular ECS based data-oriented C++23 game engine framework
Loading...
Searching...
No Matches
profiler.cpp
Go to the documentation of this file.
1#include <pch.hpp>
2
4
5#include <helios/assert.hpp>
7
8#include <atomic>
9#include <cstdint>
10#include <cstdlib>
11#include <optional>
12#include <source_location>
13#include <span>
14#include <string_view>
15
16namespace helios::profile {
17
18namespace {
19
21std::atomic<bool> g_profiler_memory_dispatch_enabled{true};
22std::atomic<bool> g_profiler_finalized{false};
23
24[[nodiscard]] constexpr size_t AlignUp(size_t value,
25 size_t alignment) noexcept {
26 return (value + alignment - 1) & ~(alignment - 1);
27}
28
30 static bool registered = false;
31 if (registered) {
32 return;
33 }
34 registered = true;
35 std::atexit(+[]() noexcept { details::DisableProfilerMemoryDispatch(); });
36}
37
38} // namespace
39
40namespace details {
41
43 return g_memory_dispatch_suspend_count > 0;
44}
45
46bool IsProfilerFinalized() noexcept {
47 return g_profiler_finalized.load(std::memory_order_acquire);
48}
49
51 return g_profiler_memory_dispatch_enabled.load(std::memory_order_acquire);
52}
53
55 g_profiler_memory_dispatch_enabled.store(false, std::memory_order_release);
56}
57
58void ResetProfilerFinalized() noexcept {
59 g_profiler_finalized.store(false, std::memory_order_release);
60}
61
62ScopedMemoryDispatchSuspend::ScopedMemoryDispatchSuspend() noexcept {
63 ++g_memory_dispatch_suspend_count;
64}
65
66ScopedMemoryDispatchSuspend::~ScopedMemoryDispatchSuspend() noexcept {
67 --g_memory_dispatch_suspend_count;
68}
69
70} // namespace details
71
72Profiler::~Profiler() noexcept {
74}
75
76void Profiler::Startup() noexcept {
77 if (!finalized_) [[unlikely]] {
78 return;
79 }
80
81 for (auto&& [_, entry] : backends_) {
82 entry.backend->Startup();
83 }
84}
85
86void Profiler::Shutdown() noexcept {
87 if (!finalized_) [[unlikely]] {
88 return;
89 }
90
91 for (auto&& [_, entry] : backends_) {
92 entry.backend->Shutdown();
93 }
94}
95
96void Profiler::Finalize() noexcept {
97 HELIOS_ASSERT(!finalized_, "Profiler already finalized!");
98
99 size_t offset = 0;
100 for (auto&& [_, entry] : backends_) {
101 offset = AlignUp(offset, alignof(std::max_align_t));
102 entry.storage_offset = offset;
103 entry.storage_size = entry.backend->ZoneStorageSize();
105 entry.storage_size <= kZoneStorageBytes,
106 "Backend '{}' ZoneStorageSize ({}) exceeds kZoneStorageBytes ({})!",
107 entry.backend->Name(), entry.storage_size, kZoneStorageBytes);
108 offset += entry.storage_size;
109 }
110
112 offset <= ScopedZone::kStorage,
113 "Combined backend storage {} bytes exceeds ScopedZone::kStorage {} bytes "
114 "— raise HELIOS_PROFILE_ZONE_STORAGE_BYTES!",
115 offset, ScopedZone::kStorage);
116
117 finalized_ = true;
118 g_profiler_finalized.store(true, std::memory_order_release);
119 RegisterShutdownMemoryDispatchGuard();
120}
121
123 std::span<std::byte> storage) noexcept {
124 if (!finalized_) [[unlikely]] {
125 return;
126 }
127
128 for (auto&& [_, entry] : backends_) {
129 entry.backend->BeginZone(
130 spec, storage.subspan(entry.storage_offset, entry.storage_size));
131 }
132}
133
134void Profiler::EndZone(std::span<std::byte> storage) noexcept {
135 if (!finalized_) [[unlikely]] {
136 return;
137 }
138
139 for (auto&& [_, entry] : backends_) {
140 entry.backend->EndZone(
141 storage.subspan(entry.storage_offset, entry.storage_size));
142 }
143}
144
145void Profiler::ZoneText(std::span<std::byte> storage,
146 std::string_view text) noexcept {
147 if (!finalized_) [[unlikely]] {
148 return;
149 }
150
151 for (auto&& [_, entry] : backends_) {
152 entry.backend->ZoneText(
153 storage.subspan(entry.storage_offset, entry.storage_size), text);
154 }
155}
156
157void Profiler::ZoneValue(std::span<std::byte> storage,
158 uint64_t value) noexcept {
159 if (!finalized_) [[unlikely]] {
160 return;
161 }
162
163 for (auto&& [_, entry] : backends_) {
164 entry.backend->ZoneValue(
165 storage.subspan(entry.storage_offset, entry.storage_size), value);
166 }
167}
168
169void Profiler::ZoneName(std::span<std::byte> storage,
170 std::string_view name) noexcept {
171 if (!finalized_) [[unlikely]] {
172 return;
173 }
174
175 for (auto&& [_, entry] : backends_) {
176 entry.backend->ZoneName(
177 storage.subspan(entry.storage_offset, entry.storage_size), name);
178 }
179}
180
181void Profiler::FrameMark() noexcept {
182 if (!finalized_) [[unlikely]] {
183 return;
184 }
185
186 for (auto&& [_, entry] : backends_) {
187 entry.backend->FrameMark();
188 }
189}
190
191void Profiler::FrameMark(CStringView name) noexcept {
192 if (!finalized_) [[unlikely]] {
193 return;
194 }
195
196 for (auto&& [_, entry] : backends_) {
197 entry.backend->FrameMark(name);
198 }
199}
200
202 if (!finalized_) [[unlikely]] {
203 return;
204 }
205
206 for (auto&& [_, entry] : backends_) {
207 entry.backend->FrameMarkStart(name);
208 }
209}
210
212 if (!finalized_) [[unlikely]] {
213 return;
214 }
215
216 for (auto&& [_, entry] : backends_) {
217 entry.backend->FrameMarkEnd(name);
218 }
219}
220
221void Profiler::Message(std::string_view text, uint32_t color) noexcept {
222 if (!finalized_) [[unlikely]] {
223 return;
224 }
225
226 for (auto&& [_, entry] : backends_) {
227 entry.backend->Message(text, color);
228 }
229}
230
232 if (!finalized_) [[unlikely]] {
233 return;
234 }
235
236 for (auto&& [_, entry] : backends_) {
237 entry.backend->SetThreadName(name);
238 }
239}
240
241void Profiler::Plot(CStringView name, double value) noexcept {
242 if (!finalized_) [[unlikely]] {
243 return;
244 }
245
246 for (auto&& [_, entry] : backends_) {
247 entry.backend->Plot(name, value);
248 }
249}
250
251void Profiler::PlotConfig(CStringView name, PlotFormat type, bool step,
252 bool fill, uint32_t color) noexcept {
253 if (!finalized_) [[unlikely]] {
254 return;
255 }
256
257 for (auto&& [_, entry] : backends_) {
258 entry.backend->PlotConfig(name, type, step, fill, color);
259 }
260}
261
262void Profiler::Alloc(const void* ptr, size_t size,
263 std::optional<CStringView> name, int depth,
264 std::source_location location) noexcept {
265 if (!finalized_ || !details::IsProfilerMemoryDispatchEnabled() ||
266 details::IsMemoryDispatchSuspended()) [[unlikely]] {
267 return;
268 }
269
270 for (auto&& [_, entry] : backends_) {
271 entry.backend->Alloc(ptr, size, name, depth, location);
272 }
273}
274
275void Profiler::Free(const void* ptr, std::optional<CStringView> name, int depth,
276 std::source_location location) noexcept {
277 if (!finalized_ || !details::IsProfilerMemoryDispatchEnabled() ||
278 details::IsMemoryDispatchSuspended()) [[unlikely]] {
279 return;
280 }
281
282 for (auto&& [_, entry] : backends_) {
283 entry.backend->Free(ptr, name, depth, location);
284 }
285}
286
288 if (!finalized_ || !details::IsProfilerMemoryDispatchEnabled()) [[unlikely]] {
289 return;
290 }
291
292 for (auto&& [_, entry] : backends_) {
293 entry.backend->MemoryDiscard(name);
294 }
295}
296
297void Profiler::MemoryDiscard(CStringView name, int depth) noexcept {
298 if (!finalized_ || !details::IsProfilerMemoryDispatchEnabled()) [[unlikely]] {
299 return;
300 }
301
302 for (auto&& [_, entry] : backends_) {
303 entry.backend->MemoryDiscard(name, depth);
304 }
305}
306
307} // namespace helios::profile
#define HELIOS_ASSERT(condition,...)
Assertion macro that aborts execution in debug builds.
Definition assert.hpp:259
void ZoneText(std::span< std::byte > storage, std::string_view text) noexcept
Dispatches a zone text event to all registered backends.
Definition profiler.cpp:145
void Startup() noexcept
Calls Startup() on all registered backends.
Definition profiler.cpp:76
void Free(const void *ptr, std::optional< CStringView > name, int depth, std::source_location loc) noexcept
Dispatches a deallocation event to all registered backends.
Definition profiler.cpp:275
void SetThreadName(CStringView name) noexcept
Dispatches a thread name event to all registered backends.
Definition profiler.cpp:231
void Alloc(const void *ptr, size_t size, std::optional< CStringView > name, int depth, std::source_location loc) noexcept
Dispatches an allocation event to all registered backends.
Definition profiler.cpp:262
void FrameMark() noexcept
Dispatches a frame mark event to all registered backends.
Definition profiler.cpp:181
void EndZone(std::span< std::byte > storage) noexcept
Dispatches a zone end event to all registered backends.
Definition profiler.cpp:134
void MemoryDiscard(CStringView name) noexcept
Dispatches a memory discard event to all registered backends.
Definition profiler.cpp:287
void PlotConfig(CStringView name, PlotFormat type, bool step, bool fill, uint32_t color) noexcept
Dispatches a plot configuration event to all registered backends.
Definition profiler.cpp:251
void FrameMarkEnd(CStringView name) noexcept
Dispatches a frame mark end event to all registered backends.
Definition profiler.cpp:211
void Message(std::string_view text, uint32_t color) noexcept
Dispatches a message event to all registered backends.
Definition profiler.cpp:221
void FrameMarkStart(CStringView name) noexcept
Dispatches a frame mark start event to all registered backends.
Definition profiler.cpp:201
void Finalize() noexcept
Partitions zone storage and marks the profiler ready for use.
Definition profiler.cpp:96
void Shutdown() noexcept
Calls Shutdown() on all registered backends.
Definition profiler.cpp:86
void Plot(CStringView name, double value) noexcept
Dispatches a plot event to all registered backends.
Definition profiler.cpp:241
void ZoneName(std::span< std::byte > storage, std::string_view name) noexcept
Dispatches a zone name event to all registered backends.
Definition profiler.cpp:169
void BeginZone(const ZoneSpec &spec, std::span< std::byte > storage) noexcept
Dispatches a zone begin event to all registered backends.
Definition profiler.cpp:122
void ZoneValue(std::span< std::byte > storage, uint64_t value) noexcept
Dispatches a zone value event to all registered backends.
Definition profiler.cpp:157
static constexpr size_t kStorage
Definition zone.hpp:17
constexpr size_t AlignUp(size_t value, size_t alignment) noexcept
Definition profiler.cpp:24
void ResetProfilerFinalized() noexcept
Definition profiler.cpp:58
bool IsMemoryDispatchSuspended() noexcept
Definition profiler.cpp:42
bool IsProfilerMemoryDispatchEnabled() noexcept
Definition profiler.cpp:50
bool IsProfilerFinalized() noexcept
Definition profiler.cpp:46
void DisableProfilerMemoryDispatch() noexcept
Definition profiler.cpp:54
PlotFormat
Plot value format for timeline plots.
Definition common.hpp:10
constexpr size_t kZoneStorageBytes
Definition config.hpp:12
BasicCStringView< char > CStringView
A view of a null-terminated C string.
Immutable zone description for a single instrumentation site.
Definition common.hpp:22