Unravel Engine C++ Reference
Loading...
Searching...
No Matches
viewport_stats_overlay.cpp
Go to the documentation of this file.
4#include "imgui_widgets/utils.h"
5#include <imgui/imgui.h>
6#include <imgui/imgui_internal.h>
7
8#include <engine/ecs/scene.h>
11#include <graphics/eviction.h>
12#include <graphics/graphics.h>
13
14#include <bx/string.h>
15
16#include <algorithm>
17#include <array>
18#include <cstdio>
19#include <numeric>
20
21namespace unravel
22{
23namespace
24{
25constexpr float overlay_width = 460.0f;
26constexpr float overlay_padding = 8.0f;
27constexpr float overlay_rounding = 4.0f;
28constexpr float overlay_bg_alpha = 0.75f;
29constexpr ImVec4 overlay_bg_color{0.08f, 0.08f, 0.08f, overlay_bg_alpha};
30
31constexpr ImVec4 color_good{0.2f, 0.8f, 0.2f, 1.0f};
32constexpr ImVec4 color_warning{1.0f, 0.7f, 0.0f, 1.0f};
33constexpr ImVec4 color_bad{1.0f, 0.3f, 0.3f, 1.0f};
34constexpr ImVec4 color_label{0.6f, 0.6f, 0.6f, 1.0f};
35
36// Muted fills for progress bars (the saturated status colors above are too harsh as a solid block).
37constexpr ImVec4 bar_good{0.30f, 0.49f, 0.32f, 1.0f};
38constexpr ImVec4 bar_warning{0.58f, 0.45f, 0.20f, 1.0f};
39constexpr ImVec4 bar_bad{0.55f, 0.28f, 0.28f, 1.0f};
40
41auto get_fps_color(float fps) -> ImVec4
42{
43 if(fps < 30.0f)
44 {
45 return color_bad;
46 }
47 if(fps < 55.0f)
48 {
49 return color_warning;
50 }
51 return color_good;
52}
53
54auto get_memory_color(float percentage) -> ImVec4
55{
56 if(percentage > 80.0f)
57 {
58 return color_bad;
59 }
60 if(percentage > 60.0f)
61 {
62 return color_warning;
63 }
64 return color_good;
65}
66
67void draw_label(const char* label)
68{
69 ImGui::TextColored(color_label, "%s", label);
70 ImGui::SameLine(200.0f);
71}
72
73void draw_eviction_overlay_stats()
74{
75 const auto evict_stats = gfx::eviction::get_stats();
76 if(evict_stats.registered_count == 0)
77 {
78 return;
79 }
80
81 // Group the paging stats under their own labeled sub-header so they read distinctly from the
82 // raw memory counters above.
83 ImGui::Separator();
84 ImGui::TextColored(color_label, " " ICON_MDI_SWAP_HORIZONTAL " Paging");
85 ImGui::SetItemTooltipEx("GPU resource eviction / paging. Idle CPU-backed resources are released\n"
86 "from GPU memory under pressure and restored automatically on next use.");
87
88 // Budget that drives eviction (GPU memory used vs. the configured budget).
89 if(evict_stats.budget_bytes > 0)
90 {
91 const bool over_budget = evict_stats.budget_used_bytes > evict_stats.budget_bytes;
92 const auto used_str = format_bytes(static_cast<int64_t>(evict_stats.budget_used_bytes));
93 const auto budget_str = format_bytes(static_cast<int64_t>(evict_stats.budget_bytes));
94
95 ImGui::BeginGroup();
96 draw_label(" Budget");
97 if(over_budget)
98 {
99 ImGui::TextColored(color_bad, "%s / %s " ICON_MDI_ALERT " OVER", used_str.c_str(), budget_str.c_str());
100 }
101 else
102 {
103 ImGui::TextColored(color_good, "%s / %s", used_str.c_str(), budget_str.c_str());
104 }
105 ImGui::EndGroup();
106 ImGui::SetItemTooltipEx("GPU memory used vs. the eviction budget. When usage exceeds the\n"
107 "budget, resources are evicted down to the target watermark.\n"
108 "Marked red when over budget.");
109
110 ImGui::BeginGroup();
111 draw_label(" Target");
112 ImGui::TextUnformatted(format_bytes(static_cast<int64_t>(evict_stats.target_bytes)).c_str());
113 ImGui::EndGroup();
114 ImGui::SetItemTooltipEx("Watermark the pager evicts down to once over budget\n"
115 "(hysteresis below the budget to avoid thrashing).");
116 }
117
118 // Current residency: how much is resident (still available to evict) vs. already evicted.
119 const auto resident_str = fmt::format("{} ({})",
120 evict_stats.resident_count,
121 format_bytes(static_cast<int64_t>(evict_stats.resident_bytes)));
122 ImGui::BeginGroup();
123 draw_label(" Resident");
124 ImGui::TextUnformatted(resident_str.c_str());
125 ImGui::EndGroup();
126 ImGui::SetItemTooltipEx("Resources currently resident on the GPU that can still be paged out,\n"
127 "and their total GPU memory.");
128
129 const auto evicted_str = fmt::format("{} ({})",
130 evict_stats.evicted_count,
131 format_bytes(static_cast<int64_t>(evict_stats.evicted_bytes)));
132 ImGui::BeginGroup();
133 draw_label(" Evicted");
134 if(evict_stats.evicted_count > 0)
135 {
136 ImGui::TextColored(color_warning, "%s", evicted_str.c_str());
137 }
138 else
139 {
140 ImGui::TextUnformatted(evicted_str.c_str());
141 }
142 ImGui::EndGroup();
143 ImGui::SetItemTooltipEx("Resources currently evicted (their GPU memory reclaimed).\n"
144 "They restore automatically the next time they are used.");
145
146 // Headroom bar: share of the managed pool still resident, i.e. how much can still be freed. The
147 // overlay text is kept short so it always fits the bar.
148 const double pool_bytes = static_cast<double>(evict_stats.resident_bytes + evict_stats.evicted_bytes);
149 const float resident_fraction =
150 pool_bytes > 0.0 ? static_cast<float>(static_cast<double>(evict_stats.resident_bytes) / pool_bytes) : 0.0f;
151 ImVec4 headroom_color = bar_good;
152 if(evict_stats.resident_bytes == 0)
153 {
154 headroom_color = bar_bad;
155 }
156 else if(resident_fraction < 0.2f)
157 {
158 headroom_color = bar_warning;
159 }
160 const auto headroom_overlay = fmt::format("{} free", format_bytes(static_cast<int64_t>(evict_stats.resident_bytes)));
161 ImGui::BeginGroup();
162 draw_label(" Headroom");
163 ImGui::PushStyleColor(ImGuiCol_PlotHistogram, headroom_color);
164 // Fill the remaining content width so the bar (and its overlay text) never runs under the scrollbar.
165 ImGui::ProgressBar(resident_fraction, ImVec2(ImGui::GetContentRegionAvail().x, 0.0f), headroom_overlay.c_str());
166 ImGui::PopStyleColor();
167 ImGui::EndGroup();
168 ImGui::SetItemTooltipEx("Share of the managed pool still resident on the GPU - how much can\n"
169 "still be paged out under further memory pressure. Red when nothing\n"
170 "is left to evict.");
171
172 // Lifetime activity: an eviction count that keeps climbing every frame means paging is constant.
173 ImGui::BeginGroup();
174 draw_label(" Lifetime");
175 ImGui::TextUnformatted(fmt::format("{} evicted", evict_stats.total_evictions).c_str());
176 if(evict_stats.total_restores > 0)
177 {
178 ImGui::SameLine();
179 ImGui::TextColored(color_label, "%s", fmt::format("/ {} restored", evict_stats.total_restores).c_str());
180 }
181 ImGui::EndGroup();
182 ImGui::SetItemTooltipEx("Lifetime evictions (and restores). If the eviction count keeps\n"
183 "climbing every frame, paging is happening constantly - consider\n"
184 "raising the budget or the min-age window.");
185
186 if(evict_stats.thrash_events > 0)
187 {
188 ImGui::BeginGroup();
189 draw_label(" Thrash");
190 ImGui::TextColored(color_warning, "%s", fmt::format("{}", evict_stats.thrash_events).c_str());
191 ImGui::EndGroup();
192 ImGui::SetItemTooltipEx("Resources restored shortly after being evicted. High values\n"
193 "indicate the budget is too tight; raise it or the min-age window.");
194 }
195}
196
197void draw_performance_section()
198{
199 if(!ImGui::CollapsingSection(ICON_MDI_SPEEDOMETER "\tPerformance", ImGuiTreeNodeFlags_DefaultOpen))
200 {
201 return;
202 }
203
204 auto& io = ImGui::GetIO();
205 auto* stats = gfx::get_stats();
206 const double to_cpu_ms = 1000.0 / static_cast<double>(stats->cpuTimerFreq);
207 const double to_gpu_ms = 1000.0 / static_cast<double>(stats->gpuTimerFreq);
208
209 const float fps = io.Framerate;
210 const float frame_ms = 1000.0f / fps;
211
212 ImGui::BeginGroup();
213 ImGui::TextColored(get_fps_color(fps), " FPS: %.1f", static_cast<double>(fps));
214 ImGui::SameLine();
215 ImGui::TextColored(color_label, "(%.2f ms)", static_cast<double>(frame_ms));
216 ImGui::EndGroup();
217 ImGui::SetItemTooltipEx("Frames per second (higher is better)\n"
218 "Green: >55 FPS (smooth)\n"
219 "Yellow: 30-55 FPS (acceptable)\n"
220 "Red: <30 FPS (poor performance)");
221
222 const double cpu_submit_ms = static_cast<double>(stats->cpuTimeEnd - stats->cpuTimeBegin) * to_cpu_ms;
223 const double gpu_submit_ms = static_cast<double>(stats->gpuTimeEnd - stats->gpuTimeBegin) * to_gpu_ms;
224
225 ImGui::BeginGroup();
226 draw_label(" CPU Submit");
227 ImGui::Text("%.3f ms", cpu_submit_ms);
228 ImGui::EndGroup();
229 ImGui::SetItemTooltipEx("Time the CPU spent submitting render commands\n"
230 "to the graphics driver. High values indicate\n"
231 "a CPU-bound rendering bottleneck.");
232
233 ImGui::BeginGroup();
234 draw_label(" GPU Submit");
235 ImGui::Text("%.3f ms", gpu_submit_ms);
236 ImGui::EndGroup();
237 ImGui::SetItemTooltipEx("Time the GPU spent executing render commands.\n"
238 "High values indicate a GPU-bound bottleneck\n"
239 "such as complex shaders or high fill rate.");
240
241 ImGui::BeginGroup();
242 draw_label(" GPU Latency");
243 ImGui::Text("%d frames", stats->maxGpuLatency);
244 ImGui::EndGroup();
245 ImGui::SetItemTooltipEx("Number of frames the GPU is behind the CPU.\n"
246 "Higher latency means more input lag but can\n"
247 "improve throughput. Typically 1-3 frames.");
248}
249
250void draw_scene_section()
251{
252 if(!ImGui::CollapsingSection(ICON_MDI_CUBE_OUTLINE "\tScene", ImGuiTreeNodeFlags_DefaultOpen))
253 {
254 return;
255 }
256
257 auto* stats = gfx::get_stats();
258 const auto& io = ImGui::GetIO();
259
260 const std::uint32_t total_primitives = std::accumulate(
261 std::begin(stats->numPrims), std::end(stats->numPrims), 0u);
262 std::uint32_t ui_primitives = io.MetricsRenderIndices / 3;
263 ui_primitives = std::min(ui_primitives, total_primitives);
264 const auto scene_primitives = total_primitives - ui_primitives;
265
266 std::uint32_t total_calls = stats->numDraw;
267 std::uint32_t editor_calls = ImGui::GetDrawCalls();
268 editor_calls = std::min(editor_calls, total_calls);
269 std::uint32_t scene_calls = total_calls - editor_calls;
270
271 auto format_count = [](std::uint32_t count) -> std::string
272 {
273 if(count >= 1000000)
274 {
275 return fmt::format("{:.1f}M", static_cast<double>(count) / 1000000.0);
276 }
277 if(count >= 1000)
278 {
279 return fmt::format("{:.1f}k", static_cast<double>(count) / 1000.0);
280 }
281 return fmt::format("{}", count);
282 };
283
284 ImGui::BeginGroup();
285 draw_label(" Triangles");
286 ImGui::TextUnformatted(format_count(scene_primitives).c_str());
287 ImGui::EndGroup();
288 ImGui::SetItemTooltipEx("Total triangle count rendered for the scene\n"
289 "(excludes editor UI). Reducing triangle count\n"
290 "via LODs or culling improves GPU performance.");
291
292 ImGui::BeginGroup();
293 draw_label(" Draw Calls");
294 ImGui::Text("%u", scene_calls);
295 ImGui::EndGroup();
296 ImGui::SetItemTooltipEx("Number of draw commands sent to the GPU for\n"
297 "scene rendering (excludes editor UI draws).\n"
298 "Fewer draw calls generally means better performance.");
299
300 ImGui::BeginGroup();
301 draw_label(" Render Passes");
303 ImGui::EndGroup();
304 ImGui::SetItemTooltipEx("Number of rendering passes executed this frame.\n"
305 "Includes geometry, lighting, shadow, and\n"
306 "post-processing passes.");
307
308 {
309 ImGui::BeginGroup();
310 draw_label(" Compute Calls");
311 ImGui::Text("%u", stats->numCompute);
312 ImGui::EndGroup();
313 ImGui::SetItemTooltipEx("Number of GPU compute shader dispatches.\n"
314 "Used for GPGPU tasks.");
315 }
316 {
317 ImGui::BeginGroup();
318 draw_label(" Blit Calls");
319 ImGui::Text("%u", stats->numBlit);
320 ImGui::EndGroup();
321 ImGui::SetItemTooltipEx("Number of GPU blit (copy/transfer) operations.\n"
322 "Used for copying textures, resolving MSAA,\n"
323 "or transferring between render targets.");
324 }
325}
326
327void draw_memory_section()
328{
329 if(!ImGui::CollapsingSection(ICON_MDI_MEMORY "\tMemory"))
330 {
331 return;
332 }
333
334 auto* stats = gfx::get_stats();
335
336 if(stats->gpuMemoryUsed > 0)
337 {
338 auto used_str = format_bytes(stats->gpuMemoryUsed);
339 ImGui::BeginGroup();
340 if(stats->gpuMemoryMax > 0)
341 {
342 auto max_str = format_bytes(stats->gpuMemoryMax);
343 float pct = (static_cast<float>(stats->gpuMemoryUsed) /
344 static_cast<float>(stats->gpuMemoryMax)) * 100.0f;
345 ImGui::TextColored(color_label, " GPU Memory");
346 ImGui::SameLine(200.0f);
347 ImGui::TextColored(get_memory_color(pct), "%s / %s (%.0f%%)",
348 used_str.c_str(), max_str.c_str(), static_cast<double>(pct));
349 }
350 else
351 {
352 draw_label(" GPU Memory");
353 ImGui::TextUnformatted(used_str.c_str());
354 }
355 ImGui::EndGroup();
356 ImGui::SetItemTooltipEx("Total GPU video memory allocated vs available.\n"
357 "Color-coded by usage percentage:\n"
358 "Green: <60%%, Yellow: 60-80%%, Red: >80%%.\n"
359 "High usage may cause performance degradation\n"
360 "or out-of-memory errors.");
361 }
362
363 if(stats->textureMemoryUsed > 0)
364 {
365 ImGui::BeginGroup();
366 draw_label(" Texture Mem");
367 ImGui::TextUnformatted(format_bytes(stats->textureMemoryUsed).c_str());
368 ImGui::EndGroup();
369 ImGui::SetItemTooltipEx("GPU memory consumed by texture resources.\n"
370 "Reduce with smaller textures or compressed formats.");
371 }
372
373 if(stats->rtMemoryUsed > 0)
374 {
375 ImGui::BeginGroup();
376 draw_label(" RT Memory");
377 ImGui::TextUnformatted(format_bytes(stats->rtMemoryUsed).c_str());
378 ImGui::EndGroup();
379 ImGui::SetItemTooltipEx("GPU memory consumed by render targets\n"
380 "(framebuffers). Scales with resolution.");
381 }
382
383 draw_eviction_overlay_stats();
384}
385
386void draw_pipeline_stats(const rendering::pipeline_stats& pstats)
387{
388 ImGui::BeginGroup();
389 draw_label(" Static Models");
390 ImGui::Text("%u (%u Meshes)", pstats.drawn_models, pstats.drawn_static_submeshes);
391 ImGui::EndGroup();
392 ImGui::SetItemTooltipEx("Visible static models and submeshes drawn this frame.\n"
393 "The value in parentheses counts individual submeshes\n"
394 "after per-submesh frustum culling.");
395
396 ImGui::BeginGroup();
397 draw_label(" Skinned Models");
398 ImGui::Text("%u (%u Meshes)", pstats.drawn_skinned_models, pstats.drawn_skinned_submeshes);
399 ImGui::EndGroup();
400 ImGui::SetItemTooltipEx("Visible skinned models and submeshes drawn this frame.\n"
401 "The value in parentheses counts individual submeshes\n"
402 "submitted for GPU skinning.");
403
404 ImGui::BeginGroup();
405 draw_label(" Lights");
406 ImGui::Text("%u", pstats.drawn_lights);
407 ImGui::EndGroup();
408 ImGui::SetItemTooltipEx("Number of active lights evaluated for the\n"
409 "scene. Each additional light increases shading\n"
410 "cost in deferred and forward rendering.");
411
412 ImGui::BeginGroup();
413 draw_label(" Shadow Lights");
414 ImGui::Text("%u", pstats.drawn_lights_casting_shadows);
415 ImGui::EndGroup();
416 ImGui::SetItemTooltipEx("Number of lights with shadow casting enabled.\n"
417 "Each shadow light requires one or more extra\n"
418 "render passes to generate shadow maps.");
419
420 const uint32_t shadow_models = pstats.drawn_models_for_shadows + pstats.drawn_skinned_models_for_shadows;
421 const uint32_t shadow_submeshes = pstats.drawn_submeshes_for_shadows + pstats.drawn_skinned_submeshes_for_shadows;
422 ImGui::BeginGroup();
423 draw_label(" Shadow Models");
424 ImGui::Text("%u (%u meshes)", shadow_models, shadow_submeshes);
425 ImGui::EndGroup();
426 ImGui::SetItemTooltipEx("Models and submeshes rendered into shadow maps.\n"
427 "The mesh count includes cascade and face redraws,\n"
428 "so it can exceed the main-pass submesh count.");
429
430 ImGui::BeginGroup();
431 draw_label(" Particles");
432 ImGui::Text("%u (%u Batches)", pstats.drawn_particles, pstats.drawn_particles_batches);
433 ImGui::EndGroup();
434 ImGui::SetItemTooltipEx("Active particle emitters and their GPU draw\n"
435 "batches. Particles with different materials\n"
436 "or blend modes produce separate batches.");
437
438 const auto& batch = pstats.batching_stats;
439 ImGui::Separator();
440 ImGui::TextColored(color_label, " Batching");
441 ImGui::SetItemTooltipEx("Static mesh batching combines multiple meshes\n"
442 "sharing the same material into fewer draw calls,\n"
443 "reducing CPU overhead.");
444
445 ImGui::BeginGroup();
446 draw_label(" Batches");
447 ImGui::Text("%u (%u Inst)", batch.total_batches, batch.total_instances);
448 ImGui::EndGroup();
449 ImGui::SetItemTooltipEx("Total batches created and total mesh instances\n"
450 "across all batches. A lower batch count relative\n"
451 "to instance count indicates effective batching.");
452
453 ImGui::BeginGroup();
454 draw_label(" Efficiency");
455 ImGui::Text("%.0f%%", static_cast<double>(batch.batching_efficiency * 100.0f));
456 ImGui::EndGroup();
457 ImGui::SetItemTooltipEx("Percentage of eligible meshes successfully\n"
458 "combined into batches. 100%% means all eligible\n"
459 "meshes are batched. Low values may indicate\n"
460 "too many unique materials or shader variants.");
461
462 ImGui::BeginGroup();
463 draw_label(" Saved");
464 ImGui::Text("%u calls", batch.draw_calls_saved);
465 ImGui::EndGroup();
466 ImGui::SetItemTooltipEx("Number of draw calls eliminated by batching.\n"
467 "Higher values mean more CPU time saved from\n"
468 "reduced driver overhead.");
469}
470
471void draw_pipeline_section(const rendering::pipeline_stats& pstats)
472{
473 if(!ImGui::CollapsingSection(ICON_MDI_PIPE "\tPipeline"))
474 {
475 return;
476 }
477
478 draw_pipeline_stats(pstats);
479}
480
481} // namespace
482
483void viewport_stats_overlay::draw(const rendering::pipeline_stats& pstats, state& overlay_state, const char* id)
484{
485 if(!overlay_state.is_visible)
486 {
487 return;
488 }
489
490 auto* window = ImGui::GetCurrentWindow();
491 if(!window || window->SkipItems)
492 {
493 return;
494 }
495
496 auto content_rect = window->ContentRegionRect;
497 float max_height = content_rect.GetHeight() - 2.0f * overlay_padding;
498
499 float pos_x = content_rect.Max.x - overlay_width - overlay_padding;
500 float pos_y = content_rect.Min.y + overlay_padding;
501
502 ImGui::SetCursorScreenPos(ImVec2(pos_x, pos_y));
503
504 ImGui::PushStyleColor(ImGuiCol_ChildBg, overlay_bg_color);
505 ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.15f, 0.15f, 0.15f, 0.9f));
506 ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.22f, 0.22f, 0.22f, 0.9f));
507 ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.18f, 0.18f, 0.18f, 0.9f));
508 ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, overlay_rounding);
509 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.0f, 6.0f));
510 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4.0f, 3.0f));
511
512 ImGuiChildFlags child_flags = ImGuiChildFlags_AlwaysUseWindowPadding | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysAutoResize;
513 ImGuiWindowFlags window_flags = 0;
514
515 ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(overlay_width, max_height));
516 auto child_name = fmt::format("##viewport_stats_{}", id);
517 if(ImGui::BeginChild(child_name.c_str(), ImVec2(overlay_width, 0), child_flags, window_flags))
518 {
519 // ImGui::PushFont(ImGui::Font::SemiBold);
521 auto header_text = ICON_MDI_CHART_LINE " Statistics";
522 auto header_text_width = ImGui::CalcTextSize(header_text).x;
523 ImGui::AlignedItem(0.5f, overlay_width, header_text_width, [&]() -> void {
524 ImGui::TextUnformatted(header_text);
525 });
527
529 draw_performance_section();
530 draw_scene_section();
531 draw_memory_section();
532 draw_pipeline_section(pstats);
533 ImGui::PopFont();
534
535 ImGui::Spacing();
536 ImGui::Separator();
537 ImGui::Spacing();
538
539 float btn_width = ImGui::CalcTextSize(ICON_MDI_CHART_BAR " Open Profiler").x
540 + ImGui::GetStyle().FramePadding.x * 2.0f;
541 ImGui::SetCursorPosX((overlay_width - btn_width) * 0.5f);
542 if(ImGui::Button(ICON_MDI_CHART_BAR " Open Profiler"))
543 {
544 overlay_state.open_profiler_requested = true;
545 }
546 ImGui::Spacing();
547 }
548 ImGui::EndChild();
549
550 ImGui::PopStyleVar(3);
551 ImGui::PopStyleColor(4);
552}
553
555{
556 const float fps = ImGui::GetIO().Framerate;
557 std::array<char, 96> fps_label_buf{};
558 const char* label = ICON_MDI_CHART_LINE " Stats";
559 if(!overlay_state.is_visible)
560 {
561
562 std::snprintf(fps_label_buf.data(),
563 fps_label_buf.size(),
564 ICON_MDI_CHART_LINE_VARIANT " Stats (%.1f FPS)",
565 static_cast<double>(fps));
566 label = fps_label_buf.data();
567 }
568
569 const auto& style = ImGui::GetStyle();
570 const float item_width = ImGui::CalcTextSize(label).x + style.ItemSpacing.x*2;
571
572 ImGui::SameLine();
573
574 ImGui::AlignedItem(1.0f,
575 ImGui::GetContentRegionAvail().x,
576 item_width,
577 [&]() -> void
578 {
579 bool is_visible = overlay_state.is_visible;
580 if(is_visible)
581 {
582 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.2f, 0.8f, 0.2f, 1.0f));
583 }
584 if(ImGui::MenuItem(label, "", is_visible))
585 {
586 overlay_state.is_visible = !overlay_state.is_visible;
587 }
588 if(is_visible)
589 {
590 ImGui::PopStyleColor();
591 }
592 });
593 ImGui::SetItemTooltipEx("%s", overlay_state.is_visible ? "Hide Statistics" : "Show Statistics");
594}
595} // namespace unravel
float x
#define ICON_MDI_CUBE_OUTLINE
#define ICON_MDI_CHART_LINE
#define ICON_MDI_ALERT
#define ICON_MDI_CHART_BAR
#define ICON_MDI_CHART_LINE_VARIANT
#define ICON_MDI_SWAP_HORIZONTAL
#define ICON_MDI_PIPE
#define ICON_MDI_MEMORY
#define ICON_MDI_SPEEDOMETER
void PushFont(Font::Enum _font)
Definition imgui.cpp:646
void PopWindowFontScale()
Definition imgui.cpp:734
uint64_t GetDrawCalls()
Definition imgui.cpp:744
void PushWindowFontScale(float scale)
Definition imgui.cpp:721
auto get_stats() -> stats
Snapshot the current statistics.
Definition eviction.cpp:953
bgfx::Stats stats
Definition graphics.h:31
const stats * get_stats()
Definition graphics.cpp:450
void draw(const rendering::pipeline_stats &pstats, state &overlay_state, const char *id)
Draw a statistics overlay child window at the top-right corner of the current ImGui window....
void draw_stats_toggle(state &overlay_state)
Draw a right-aligned "Stats" toggle button for the menu bar. Toggles the overlay visibility on click.
auto format_bytes(std::uint64_t bytes, std::uint8_t num_frac) -> std::string
static auto get_last_frame_max_pass_id() -> gfx::view_id