Unravel Engine C++ Reference
Loading...
Searching...
No Matches
statistics_panel.cpp
Go to the documentation of this file.
1#include "statistics_panel.h"
2#include "imgui/imgui.h"
3#include "imgui_widgets/utils.h"
4#include "monopp/mono_gc_handle.h"
5#include "statistics_utils.h"
6
7
8#include <engine/engine.h>
11#include <engine/ecs/scene.h>
13#include <graphics/graphics.h>
14#include <math/math.h>
15
16#include <algorithm>
17#include <array>
18#include <numeric>
19
20namespace unravel
21{
22
23// Constants
24namespace
25{
26 constexpr float plot_height = 50.0f;
27 constexpr float max_frame_time_ms = 200.0f;
28 constexpr float max_passes = 200.0f;
29 constexpr float profiler_scale = 3.0f;
30 constexpr float profiler_max_width = 30.0f;
31 constexpr float resource_bar_width = 90.0f;
32 constexpr float megabyte_divisor = 1024.0f * 1024.0f;
33
34 // Colors for profiler bars
35 constexpr ImVec4 cpu_color{0.2f, 0.8f, 0.2f, 1.0f}; // More professional green
36 constexpr ImVec4 gpu_color{0.2f, 0.6f, 1.0f, 1.0f}; // More professional blue
37 constexpr ImVec4 warning_color{1.0f, 0.7f, 0.0f, 1.0f}; // Warning orange
38 constexpr ImVec4 error_color{1.0f, 0.3f, 0.3f, 1.0f}; // Error red
39
40 // Static sample data instances
41 statistics_utils::sample_data frame_time_samples;
42 statistics_utils::sample_data graphics_passes_samples;
43 statistics_utils::sample_data gpu_memory_samples;
44 statistics_utils::sample_data render_target_memory_samples;
45 statistics_utils::sample_data texture_memory_samples;
46}
47
49{
50 // No specific initialization needed currently
51}
52
54{
55 // No specific cleanup needed currently
56}
57
59{
60 // No per-frame update logic needed currently
61}
62
64{
65 // No per-frame render logic needed currently
66}
67
69{
70 if(ImGui::Begin(name, nullptr, ImGuiWindowFlags_MenuBar))
71 {
72 draw_menubar(ctx);
73 draw_statistics_content();
74 }
75 ImGui::End();
76}
77
78auto statistics_panel::draw_menubar(rtti::context& ctx) -> void
79{
80 if(ImGui::BeginMenuBar())
81 {
82 if(ImGui::BeginMenu("View " ICON_MDI_ARROW_DOWN_BOLD))
83 {
84 ImGui::Checkbox("Show Editor Stats", &show_editor_stats_);
85 ImGui::SetItemTooltip("Show editor/UI related draw calls and triangles\n(Focus on scene stats when disabled)");
86 ImGui::EndMenu();
87 }
88
89 if(ImGui::BeginMenu("Rendering " ICON_MDI_ARROW_DOWN_BOLD))
90 {
92 if(ImGui::Checkbox("Static Mesh Batching", &batching_enabled))
93 {
95 }
96 ImGui::SetItemTooltip("Enable/disable static mesh batching for performance comparison");
97 ImGui::EndMenu();
98 }
99 ImGui::EndMenuBar();
100 }
101}
102
103auto statistics_panel::draw_statistics_content() -> void
104{
105 const auto& io = ImGui::GetIO();
106 const auto area = ImGui::GetContentRegionAvail();
107 const float overlay_width = area.x;
108
109 // Update sample data with current frame statistics
110 update_sample_data();
111
112 // Draw main statistics sections
113 draw_frame_statistics(overlay_width);
114 draw_profiler_section();
115 draw_memory_info_section(overlay_width);
116 draw_resources_section();
117}
118
119auto statistics_panel::draw_frame_statistics(float overlay_width) -> void
120{
121 auto stats = gfx::get_stats();
122 const auto& io = ImGui::GetIO();
123
124 const double to_cpu_ms = 1000.0 / static_cast<double>(stats->cpuTimerFreq);
125 const double to_gpu_ms = 1000.0 / static_cast<double>(stats->gpuTimerFreq);
126
127 // Performance Overview Section
128 if(ImGui::CollapsingHeader(ICON_MDI_CHART_LINE "\tPerformance Overview", ImGuiTreeNodeFlags_DefaultOpen))
129 {
131
132 // Frame time statistics with color coding
133 const float avg_frame_time = frame_time_samples.get_average();
134 const float fps = 1000.0f / avg_frame_time;
135
136 // Color code based on performance
137 ImVec4 fps_color = cpu_color; // Green for good performance
138 if(fps < 30.0f)
139 {
140 fps_color = error_color; // Red for poor
141 }
142 else if(fps < 55.0f)
143 {
144 fps_color = warning_color; // Orange for moderate
145 }
146
147 // Performance summary with better layout
148 ImGui::BeginColumns("PerformanceColumns", 2, ImGuiOldColumnFlags_NoResize);
149 ImGui::SetColumnWidth(0, overlay_width * 0.5f);
150
151 ImGui::Text("Frame Time:");
152 ImGui::Text(" Average: %.3f ms", avg_frame_time);
153 ImGui::Text(" Min/Max: %.3f / %.3f ms", frame_time_samples.get_min(), frame_time_samples.get_max());
154
155 ImGui::NextColumn();
156
157 ImGui::TextColored(fps_color, "FPS: %.1f", fps);
158 if(fps < 55.0f)
159 {
160 ImGui::SameLine();
161 ImGui::TextColored(warning_color, fps < 30.0f ? " (Poor)" : " (Low)");
162 }
163
164 // GPU Memory usage with color coding
165 if(stats->gpuMemoryUsed > 0)
166 {
167 std::array<char, 64> gpu_used_str;
168 bx::prettify(gpu_used_str.data(), gpu_used_str.size(), stats->gpuMemoryUsed);
169
170 ImGui::Text("GPU Memory:");
171
172 if(stats->gpuMemoryMax > 0)
173 {
174 // Full memory info with percentage when max is available
175 const float gpu_usage_percentage = (static_cast<float>(stats->gpuMemoryUsed) / static_cast<float>(stats->gpuMemoryMax)) * 100.0f;
176
177 // Color code based on GPU memory usage (only for percentage)
178 ImVec4 gpu_memory_color = cpu_color; // Green for low usage
179 if(gpu_usage_percentage > 80.0f)
180 {
181 gpu_memory_color = error_color; // Red for high
182 }
183 else if(gpu_usage_percentage > 60.0f)
184 {
185 gpu_memory_color = warning_color; // Orange for medium
186 }
187
188 std::array<char, 64> gpu_max_str;
189 bx::prettify(gpu_max_str.data(), gpu_max_str.size(), stats->gpuMemoryMax);
190
191 ImGui::Text("%s / %s", gpu_used_str.data(), gpu_max_str.data());
192 ImGui::SameLine();
193 ImGui::TextColored(gpu_memory_color, "(%.1f%%)", gpu_usage_percentage);
194 }
195 else
196 {
197 // Only current usage when max is not available
198 ImGui::Text("%s used", gpu_used_str.data());
199 ImGui::SameLine();
200 ImGui::TextColored(warning_color, "(max unknown)");
201 }
202 }
203 else
204 {
205 ImGui::Text("GPU Memory:");
206 ImGui::TextColored(warning_color, "No data available");
207 }
208
209 ImGui::EndColumns();
210
211 // Frame time plot with improved overlay
212 std::array<char, 256> frame_text_overlay;
213 bx::snprintf(frame_text_overlay.data(),
214 frame_text_overlay.size(),
215 "Performance: %.1f FPS (%.3f ms avg)\nRange: %.3f - %.3f ms",
216 fps, avg_frame_time,
217 frame_time_samples.get_min(),
218 frame_time_samples.get_max());
219
220 ImGui::SetNextWindowViewportToCurrent();
221 ImGui::PlotLines("##FrameTime",
222 frame_time_samples.get_values(),
224 frame_time_samples.get_offset(),
225 frame_text_overlay.data(),
226 0.0f,
227 max_frame_time_ms,
228 ImVec2(overlay_width, plot_height));
229
230 ImGui::Separator();
231
232 // CPU/GPU timing with better formatting
233 const auto submit_cpu_ms = static_cast<double>(stats->cpuTimeEnd - stats->cpuTimeBegin) * to_cpu_ms;
234 const auto submit_gpu_ms = static_cast<double>(stats->gpuTimeEnd - stats->gpuTimeBegin) * to_gpu_ms;
235
236 ImGui::BeginColumns("TimingColumns", 4, ImGuiOldColumnFlags_NoResize);
237 ImGui::SetColumnWidth(0, overlay_width * 0.25f);
238 ImGui::SetColumnWidth(1, overlay_width * 0.25f);
239 ImGui::SetColumnWidth(2, overlay_width * 0.25f);
240
241 ImGui::TextColored(cpu_color, "CPU Submit");
242 ImGui::Text("%.3f ms", submit_cpu_ms);
243
244 ImGui::NextColumn();
245 ImGui::TextColored(gpu_color, "GPU Submit");
246 ImGui::Text("%.3f ms", submit_gpu_ms);
247
248 ImGui::NextColumn();
249 ImGui::Text("GPU Latency");
250 ImGui::Text("%d frames", stats->maxGpuLatency);
251
252 ImGui::NextColumn();
253 ImGui::Text("Draw Calls");
254
255 std::uint32_t scene_calls = 0, editor_calls = 0, total_calls = 0;
256 get_draw_call_breakdown(stats, scene_calls, editor_calls, total_calls);
257
258 if(show_editor_stats_)
259 {
260 ImGui::Text("%u total", total_calls);
261 }
262 else
263 {
264 ImGui::Text("%u scene", scene_calls);
265 }
266
267 ImGui::EndColumns();
268
269 ImGui::PopFont();
270 }
271
272 // Rendering Statistics Section
273 if(ImGui::CollapsingHeader(ICON_MDI_CUBE_OUTLINE "\tRendering Statistics", ImGuiTreeNodeFlags_DefaultOpen))
274 {
276
277 // Render passes at the top
278 ImGui::Text("Render Passes: %u", gfx::render_pass::get_last_frame_max_pass_id());
279 ImGui::Separator();
280
281 // Draw call counts
282 draw_call_counts(stats, io);
283
284 ImGui::Separator();
285
286 // Primitive counts
287 draw_primitive_counts(stats, io);
288
289 // Pipeline stats
290 draw_pipeline_stats();
291
292
293 ImGui::PopFont();
294 }
295}
296
297auto statistics_panel::draw_pipeline_stats() -> void
298{
299 if(ImGui::CollapsingHeader(ICON_MDI_CUBE_OUTLINE "\tPipeline Statistics", ImGuiTreeNodeFlags_DefaultOpen))
300 {
301 for(auto scn : scene::get_all_scenes())
302 {
303 if(!scn)
304 {
305 continue;
306 }
307
308 scn->registry->view<camera_component>().each([&](auto e, auto&& camera_comp)
309 {
310 auto& pipeline = camera_comp.get_pipeline_data().get_pipeline();
311 const auto& stats = pipeline->get_stats();
312 if(!stats.anything_drawn())
313 {
314 return;
315 }
316
317 ImGui::Text("Pipeline Stats for %s:", scn->tag.c_str());
318 ImGui::Indent();
319
320 ImGui::Text("Drawn Particles: %u", stats.drawn_particles);
321 ImGui::Text("Drawn Particles Batches: %u", stats.drawn_particles_batches);
322 ImGui::Text("Drawn Models: %u", stats.drawn_models);
323 ImGui::Text("Drawn Skinned Models: %u", stats.drawn_skinned_models);
324 ImGui::Text("Drawn Lights: %u", stats.drawn_lights);
325 ImGui::Text("Drawn Lights Casting Shadows: %u", stats.drawn_lights_casting_shadows);
326
327 // Static Mesh Batching Statistics
328 if(stats.batching_stats.total_batches > 0 || stats.batching_stats.total_instances > 0)
329 {
330 ImGui::Separator();
331 ImGui::Text("Static Mesh Batching:");
332 ImGui::Indent();
333
334 const auto& batch_stats = stats.batching_stats;
335 ImGui::Text("Total Batches: %u", batch_stats.total_batches);
336 ImGui::Text("Total Instances: %u", batch_stats.total_instances);
337 ImGui::Text("Avg Batch Size: %.1f", batch_stats.average_batch_size);
338 ImGui::Text("Batching Efficiency: %.1f%%", batch_stats.batching_efficiency * 100.0f);
339
340 if(batch_stats.split_batches > 0)
341 {
342 ImGui::TextColored(ImVec4(1.0f, 0.7f, 0.0f, 1.0f), "Split Batches: %u", batch_stats.split_batches);
343 }
344
345 // Performance timings
346 if(batch_stats.collection_time_ms > 0.0f || batch_stats.preparation_time_ms > 0.0f)
347 {
348 ImGui::Text("Collection Time: %.3f ms", batch_stats.collection_time_ms);
349 ImGui::Text("Preparation Time: %.3f ms", batch_stats.preparation_time_ms);
350 ImGui::Text("Submission Time: %.3f ms", batch_stats.submission_time_ms);
351 }
352
353 // Memory usage
354 if(batch_stats.instance_buffer_memory_used > 0)
355 {
356 float memory_mb = static_cast<float>(batch_stats.instance_buffer_memory_used) / (1024.0f * 1024.0f);
357 ImGui::Text("Instance Buffer Memory: %.2f MB", memory_mb);
358 }
359
360 ImGui::Unindent();
361 }
362
363 ImGui::Unindent();
364 });
365 }
366 }
367}
368
369auto statistics_panel::draw_profiler_section() -> void
370{
371 if(!ImGui::CollapsingHeader(ICON_MDI_CLOCK_OUTLINE "\tProfiler"))
372 {
373 return;
374 }
375
377
378 // CPU Profiler - always shown
379 ImGui::Text("CPU Profiler:");
380 draw_app_profiler_data();
381
382 ImGui::Separator();
383
384 // GPU Profiler controls
385 ImGui::AlignTextToFramePadding();
386 ImGui::Text("GPU Profiler:");
387 ImGui::SameLine();
388
389 if(ImGui::Checkbox("Enable##GPUProfiler", &enable_gpu_profiler_))
390 {
391 if(enable_gpu_profiler_)
392 {
393 gfx::set_debug(BGFX_DEBUG_PROFILER);
394 }
395 else
396 {
397 gfx::set_debug(BGFX_DEBUG_NONE);
398 }
399 }
400
401 // GPU Profiler data - conditionally shown
402 if(enable_gpu_profiler_)
403 {
404 auto stats = gfx::get_stats();
405
406 if(stats->numViews == 0)
407 {
408 ImGui::TextColored(warning_color, "No GPU profiling data available.");
409 ImGui::Text("Profiler may be initializing...");
410 }
411 else
412 {
413 ImGui::Text("GPU Timing (per view/encoder):");
414 draw_profiler_bars(stats);
415 }
416 }
417 else
418 {
419 ImGui::TextColored(warning_color, "GPU profiler is disabled.");
420 ImGui::Text("Enable to see detailed GPU timing information.");
421 }
422
423 ImGui::PopFont();
424}
425
426auto statistics_panel::draw_memory_info_section(float overlay_width) -> void
427{
428 if(!ImGui::CollapsingHeader(ICON_MDI_INFORMATION "\tMemory Usage"))
429 {
430 return;
431 }
432
434
435
436 ImGui::BeginGroup();
437 std::array<char, 64> str_max;
438 bx::prettify(str_max.data(), str_max.size(), static_cast<uint64_t>(mono::gc_get_heap_size()));
439
440 std::array<char, 64> str_used;
441 bx::prettify(str_used.data(), str_used.size(), mono::gc_get_used_size());
442
443 ImGui::TextUnformatted(fmt::format("GC Heap Size: {}", str_max.data()).c_str());
444 ImGui::TextUnformatted(fmt::format("GC Used Size: {}", str_used.data()).c_str());
445 ImGui::EndGroup();
446
447 ImGui::SameLine();
448 if(ImGui::Button("Collect GC"))
449 {
450 mono::gc_collect();
451 }
452
453
454 auto stats = gfx::get_stats();
455 auto gpu_memory_max = stats->gpuMemoryMax;
456
457 // GPU memory section
458 if(stats->gpuMemoryUsed > 0)
459 {
460 draw_gpu_memory_section(stats, gpu_memory_max, overlay_width);
461 }
462 else
463 {
464 ImGui::TextColored(warning_color, "No GPU memory usage data available");
465 }
466
467 // Render target memory section
468 draw_render_target_memory_section(stats, gpu_memory_max, overlay_width);
469
470 // Texture memory section
471 draw_texture_memory_section(stats, gpu_memory_max, overlay_width);
472
473 ImGui::Unindent();
474 ImGui::PopFont();
475}
476
477auto statistics_panel::draw_resources_section() -> void
478{
479 if(!ImGui::CollapsingHeader(ICON_MDI_PUZZLE "\tGPU Resources"))
480 {
481 return;
482 }
483
484 const auto caps = gfx::get_caps();
485 const auto stats = gfx::get_stats();
486 const float item_height = ImGui::GetTextLineHeightWithSpacing();
487
489
490 ImGui::Text("Resource Usage (Current / Maximum):");
491 ImGui::Separator();
492
493 // Group resources by category for better organization
494 ImGui::Text("Buffers:");
495 ImGui::Indent();
496
497 using namespace statistics_utils;
498 draw_resource_bar("TIB", "Transient Index Buffer Used",
499 stats->transientIbUsed, caps->limits.transientIbSize,
500 resource_bar_width, item_height);
501
502 draw_resource_bar("TVB", "Transient Vertex Buffer Used",
503 stats->transientVbUsed, caps->limits.transientVbSize,
504 resource_bar_width, item_height);
505
506 draw_resource_bar("DIB", "Dynamic Index Buffers",
507 stats->numDynamicIndexBuffers, caps->limits.maxDynamicIndexBuffers,
508 resource_bar_width, item_height);
509
510 draw_resource_bar("DVB", "Dynamic Vertex Buffers",
511 stats->numDynamicVertexBuffers, caps->limits.maxDynamicVertexBuffers,
512 resource_bar_width, item_height);
513
514 draw_resource_bar(" IB", "Index Buffers",
515 stats->numIndexBuffers, caps->limits.maxIndexBuffers,
516 resource_bar_width, item_height);
517
518 draw_resource_bar(" VB", "Vertex Buffers",
519 stats->numVertexBuffers, caps->limits.maxVertexBuffers,
520 resource_bar_width, item_height);
521
522 ImGui::Unindent();
523 ImGui::Separator();
524
525 ImGui::Text("Shading:");
526 ImGui::Indent();
527
528 draw_resource_bar(" P", "Shader Programs",
529 stats->numPrograms, caps->limits.maxPrograms,
530 resource_bar_width, item_height);
531
532 draw_resource_bar(" S", "Shaders",
533 stats->numShaders, caps->limits.maxShaders,
534 resource_bar_width, item_height);
535
536 draw_resource_bar(" U", "Uniforms",
537 stats->numUniforms, caps->limits.maxUniforms,
538 resource_bar_width, item_height);
539
540 ImGui::Unindent();
541 ImGui::Separator();
542
543 ImGui::Text("Rendering:");
544 ImGui::Indent();
545
546 draw_resource_bar(" T", "Textures",
547 stats->numTextures, caps->limits.maxTextures,
548 resource_bar_width, item_height);
549
550 draw_resource_bar(" FB", "Frame Buffers",
551 stats->numFrameBuffers, caps->limits.maxFrameBuffers,
552 resource_bar_width, item_height);
553
554 draw_resource_bar(" VD", "Vertex Layouts",
555 stats->numVertexLayouts, caps->limits.maxVertexLayouts,
556 resource_bar_width, item_height);
557
558 draw_resource_bar(" OQ", "Occlusion Queries",
559 stats->numOcclusionQueries, caps->limits.maxOcclusionQueries,
560 resource_bar_width, item_height);
561
562 ImGui::Unindent();
563 ImGui::PopFont();
564}
565
566// Private helper methods
567
568auto statistics_panel::update_sample_data() -> void
569{
570 auto stats = gfx::get_stats();
571 const double to_cpu_ms = 1000.0 / static_cast<double>(stats->cpuTimerFreq);
572 const double frame_ms = static_cast<double>(stats->cpuTimeFrame) * to_cpu_ms;
573
574 frame_time_samples.push_sample(static_cast<float>(frame_ms));
575 graphics_passes_samples.push_sample(static_cast<float>(gfx::render_pass::get_last_frame_max_pass_id()));
576 gpu_memory_samples.push_sample(static_cast<float>(stats->gpuMemoryUsed) / megabyte_divisor);
577 render_target_memory_samples.push_sample(static_cast<float>(stats->rtMemoryUsed) / megabyte_divisor);
578 texture_memory_samples.push_sample(static_cast<float>(stats->textureMemoryUsed) / megabyte_divisor);
579}
580
581auto statistics_panel::get_draw_call_breakdown(const gfx::stats* stats, std::uint32_t& scene_calls, std::uint32_t& editor_calls, std::uint32_t& total_calls) -> void
582{
583 const ImGuiIO& io = ImGui::GetIO();
584 total_calls = stats->numDraw;
585 editor_calls = ImGui::GetDrawCalls();
586 editor_calls = std::min(editor_calls, total_calls);
587 scene_calls = total_calls - editor_calls;
588}
589
590auto statistics_panel::draw_primitive_counts(const gfx::stats* stats, const ImGuiIO& io) -> void
591{
592 const std::uint32_t total_primitives = std::accumulate(std::begin(stats->numPrims), std::end(stats->numPrims), 0u);
593 std::uint32_t ui_primitives = io.MetricsRenderIndices / 3;
594 ui_primitives = std::min(ui_primitives, total_primitives);
595 const auto scene_primitives = total_primitives - ui_primitives;
596
597 ImGui::Text("Triangle Counts:");
598 ImGui::Indent();
599
600 if(show_editor_stats_)
601 {
602 // Show detailed breakdown with editor stats
603 ImGui::BeginColumns("PrimitiveColumns", 2, ImGuiOldColumnFlags_NoResize);
604 ImGui::SetColumnWidth(0, 120.0f);
605
606 ImGui::Text("Scene:");
607 ImGui::NextColumn();
608 ImGui::TextColored(cpu_color, "%u triangles", scene_primitives);
609 ImGui::NextColumn();
610
611 ImGui::Text("Editor:");
612 ImGui::NextColumn();
613 ImGui::TextColored(gpu_color, "%u triangles", ui_primitives);
614 ImGui::NextColumn();
615
616 ImGui::Text("Total:");
617 ImGui::NextColumn();
618 ImGui::Text("%u triangles", total_primitives);
619
620 ImGui::EndColumns();
621 }
622 else
623 {
624 // Show only scene stats (main focus)
625 ImGui::TextColored(cpu_color, "Scene: %u triangles", scene_primitives);
626 }
627
628 ImGui::Unindent();
629}
630
631auto statistics_panel::draw_call_counts(const gfx::stats* stats, const ImGuiIO& io) -> void
632{
633 std::uint32_t scene_calls = 0, editor_calls = 0, total_calls = 0;
634 get_draw_call_breakdown(stats, scene_calls, editor_calls, total_calls);
635
636 ImGui::Text("GPU Commands:");
637 ImGui::Indent();
638
639 if(show_editor_stats_)
640 {
641 // Show detailed breakdown with editor stats
642 ImGui::BeginColumns("CallCountColumns", 2, ImGuiOldColumnFlags_NoResize);
643 ImGui::SetColumnWidth(0, 120.0f);
644
645 // Draw calls section
646 ImGui::Text("Draw Calls:");
647 ImGui::NextColumn();
648 ImGui::Text("%u total", total_calls);
649 ImGui::NextColumn();
650
651 ImGui::Text(" Scene:");
652 ImGui::NextColumn();
653 ImGui::TextColored(cpu_color, "%u calls", scene_calls);
654 ImGui::NextColumn();
655
656 ImGui::Text(" Editor:");
657 ImGui::NextColumn();
658 ImGui::TextColored(gpu_color, "%u calls", editor_calls);
659 ImGui::NextColumn();
660
661 // Other command types
662 ImGui::Text("Compute:");
663 ImGui::NextColumn();
664 ImGui::Text("%u calls", stats->numCompute);
665 ImGui::NextColumn();
666
667 ImGui::Text("Blit:");
668 ImGui::NextColumn();
669 ImGui::Text("%u calls", stats->numBlit);
670
671 ImGui::EndColumns();
672 }
673 else
674 {
675 // Show only scene-focused stats
676 ImGui::TextColored(cpu_color, "Scene Draw Calls: %u", scene_calls);
677
678 // Still show compute and blit as they're typically scene-related
679 if(stats->numCompute > 0 || stats->numBlit > 0)
680 {
681 ImGui::BeginColumns("SceneCallColumns", 2, ImGuiOldColumnFlags_NoResize);
682 ImGui::SetColumnWidth(0, 120.0f);
683
684 if(stats->numCompute > 0)
685 {
686 ImGui::Text("Compute:");
687 ImGui::NextColumn();
688 ImGui::Text("%u calls", stats->numCompute);
689 ImGui::NextColumn();
690 }
691
692 if(stats->numBlit > 0)
693 {
694 ImGui::Text("Blit:");
695 ImGui::NextColumn();
696 ImGui::Text("%u calls", stats->numBlit);
697 ImGui::NextColumn();
698 }
699
700 ImGui::EndColumns();
701 }
702 }
703
704 ImGui::Unindent();
705}
706
707auto statistics_panel::draw_profiler_bars(const gfx::stats* stats) -> void
708{
709 const float item_height = ImGui::GetTextLineHeightWithSpacing();
710 const float item_height_with_spacing = ImGui::GetFrameHeightWithSpacing();
711 const double to_cpu_ms = 1000.0 / static_cast<double>(stats->cpuTimerFreq);
712 const double to_gpu_ms = 1000.0 / static_cast<double>(stats->gpuTimerFreq);
713
714 // Draw encoder stats
715 draw_encoder_stats(stats, item_height, item_height_with_spacing, to_cpu_ms);
716
717 ImGui::Separator();
718
719 // Draw view stats
720 draw_view_stats(stats, item_height, item_height_with_spacing, to_cpu_ms, to_gpu_ms);
721}
722
723auto statistics_panel::draw_encoder_stats(const gfx::stats* stats, float item_height, float item_height_with_spacing, double to_cpu_ms) -> void
724{
725 if(ImGui::BeginListBox("Encoders", ImVec2(ImGui::GetWindowWidth(), stats->numEncoders * item_height_with_spacing)))
726 {
727 ImGuiListClipper clipper;
728 clipper.Begin(stats->numEncoders, item_height);
729
730 while(clipper.Step())
731 {
732 for(int32_t pos = clipper.DisplayStart; pos < clipper.DisplayEnd; ++pos)
733 {
734 const bgfx::EncoderStats& encoder_stats = stats->encoderStats[pos];
735 ImGui::PushID(pos);
736 ImGui::Text("%3d", pos);
737 ImGui::SameLine(64.0f);
738
739 const float max_width = profiler_max_width * profiler_scale;
740 const float cpu_ms = static_cast<float>((encoder_stats.cpuTimeEnd - encoder_stats.cpuTimeBegin) * to_cpu_ms);
741 const float cpu_width = bx::clamp(cpu_ms * profiler_scale, 1.0f, max_width);
742
743 if(statistics_utils::draw_progress_bar(cpu_width, max_width, item_height, cpu_color))
744 {
745 ImGui::SetItemTooltipEx("Encoder %d, CPU: %f [ms]", pos, cpu_ms);
746 }
747
748 ImGui::PopID();
749 }
750 }
751 ImGui::EndListBox();
752 }
753}
754
755auto statistics_panel::draw_view_stats(const gfx::stats* stats, float item_height, float item_height_with_spacing, double to_cpu_ms, double to_gpu_ms) -> void
756{
757 if(ImGui::BeginListBox("Views", ImVec2(ImGui::GetWindowWidth(), stats->numViews * item_height_with_spacing)))
758 {
759 ImGuiListClipper clipper;
760 clipper.Begin(stats->numViews, item_height);
761
762 while(clipper.Step())
763 {
764 for(int32_t pos = clipper.DisplayStart; pos < clipper.DisplayEnd; ++pos)
765 {
766 const bgfx::ViewStats& view_stats = stats->viewStats[pos];
767 ImGui::PushID(view_stats.view);
768 ImGui::Text("%3d %3d %s", pos, view_stats.view, view_stats.name);
769
770 const float max_width = profiler_max_width * profiler_scale;
771 const float cpu_time_elapsed = static_cast<float>((view_stats.cpuTimeEnd - view_stats.cpuTimeBegin) * to_cpu_ms);
772 const float gpu_time_elapsed = static_cast<float>((view_stats.gpuTimeEnd - view_stats.gpuTimeBegin) * to_gpu_ms);
773 const float cpu_width = bx::clamp(cpu_time_elapsed * profiler_scale, 1.0f, max_width);
774 const float gpu_width = bx::clamp(gpu_time_elapsed * profiler_scale, 1.0f, max_width);
775
776 ImGui::SameLine(64.0f);
777
778 ImGui::PushID("cpu");
779 if(statistics_utils::draw_progress_bar(cpu_width, max_width, item_height, cpu_color))
780 {
781 ImGui::SetItemTooltipEx("View %d \"%s\", CPU: %f [ms]", pos, view_stats.name, cpu_time_elapsed);
782 }
783 ImGui::PopID();
784
785 ImGui::SameLine();
786
787 ImGui::PushID("gpu");
788 if(statistics_utils::draw_progress_bar(gpu_width, max_width, item_height, gpu_color))
789 {
790 ImGui::SetItemTooltipEx("View: %d \"%s\", GPU: %f [ms]", pos, view_stats.name, gpu_time_elapsed);
791 }
792 ImGui::PopID();
793
794 ImGui::PopID();
795 }
796 }
797 ImGui::EndListBox();
798 }
799}
800
801auto statistics_panel::draw_app_profiler_data() -> void
802{
803 auto profiler = get_app_profiler();
804 const auto& data = profiler->get_per_frame_data_read();
805
806 for(const auto& [name, per_frame_data] : data)
807 {
808 ImGui::TextUnformatted(
809 fmt::format("{:>7.3f}ms [{:^5}] - {}",
810 per_frame_data.time,
811 per_frame_data.samples,
812 fmt::string_view(name.data(), name.size())).c_str());
813 }
814}
815
816auto statistics_panel::draw_gpu_memory_section(const gfx::stats* stats, int64_t& gpu_memory_max, float overlay_width) -> void
817{
818 gpu_memory_max = std::max(stats->gpuMemoryUsed, stats->gpuMemoryMax);
819
820 std::array<char, 64> str_max;
821 bx::prettify(str_max.data(), str_max.size(), static_cast<uint64_t>(gpu_memory_max));
822
823 std::array<char, 64> str_used;
824 bx::prettify(str_used.data(), str_used.size(), stats->gpuMemoryUsed);
825
826 const float usage_percentage = gpu_memory_max > 0 ?
827 (static_cast<float>(stats->gpuMemoryUsed) / static_cast<float>(gpu_memory_max)) * 100.0f : 0.0f;
828
829 // Color code based on usage
830 ImVec4 usage_color = cpu_color; // Green for low usage
831 if(usage_percentage > 80.0f)
832 {
833 usage_color = error_color; // Red for high
834 }
835 else if(usage_percentage > 60.0f)
836 {
837 usage_color = warning_color; // Orange for medium
838 }
839
840 ImGui::Separator();
841 ImGui::Text("General GPU Memory:");
842 ImGui::Indent();
843 ImGui::Text("Usage: %s / %s", str_used.data(), str_max.data());
844 ImGui::SameLine();
845 ImGui::TextColored(usage_color, "(%.1f%%)", usage_percentage);
846
847 ImGui::SetNextWindowViewportToCurrent();
848 ImGui::PlotLines("##GPUMemory",
849 gpu_memory_samples.get_values(),
851 gpu_memory_samples.get_offset(),
852 "GPU Memory Usage Over Time",
853 0.0f,
854 static_cast<float>(gpu_memory_max),
855 ImVec2(overlay_width, plot_height));
856 ImGui::Unindent();
857}
858
859auto statistics_panel::draw_render_target_memory_section(const gfx::stats* stats, int64_t& gpu_memory_max, float overlay_width) -> void
860{
861 gpu_memory_max = std::max(stats->rtMemoryUsed, gpu_memory_max);
862
863 std::array<char, 64> str_max;
864 bx::prettify(str_max.data(), str_max.size(), static_cast<uint64_t>(gpu_memory_max));
865
866 std::array<char, 64> str_used;
867 bx::prettify(str_used.data(), str_used.size(), stats->rtMemoryUsed);
868
869 const float usage_percentage = gpu_memory_max > 0 ?
870 (static_cast<float>(stats->rtMemoryUsed) / static_cast<float>(gpu_memory_max)) * 100.0f : 0.0f;
871
872 // Color code based on usage
873 ImVec4 usage_color = cpu_color; // Green for low usage
874 if(usage_percentage > 80.0f)
875 {
876 usage_color = error_color; // Red for high
877 }
878 else if(usage_percentage > 60.0f)
879 {
880 usage_color = warning_color; // Orange for medium
881 }
882
883 ImGui::Separator();
884 ImGui::Text("Render Target Memory:");
885 ImGui::Indent();
886 ImGui::Text("Usage: %s / %s", str_used.data(), str_max.data());
887 ImGui::SameLine();
888 ImGui::TextColored(usage_color, "(%.1f%%)", usage_percentage);
889
890 ImGui::SetNextWindowViewportToCurrent();
891 ImGui::PlotLines("##RenderTargetMemory",
892 render_target_memory_samples.get_values(),
894 render_target_memory_samples.get_offset(),
895 "Render Target Memory Usage Over Time",
896 0.0f,
897 static_cast<float>(gpu_memory_max),
898 ImVec2(overlay_width, plot_height));
899 ImGui::Unindent();
900}
901
902auto statistics_panel::draw_texture_memory_section(const gfx::stats* stats, int64_t& gpu_memory_max, float overlay_width) -> void
903{
904 gpu_memory_max = std::max(stats->textureMemoryUsed, gpu_memory_max);
905
906 std::array<char, 64> str_max;
907 bx::prettify(str_max.data(), str_max.size(), static_cast<uint64_t>(gpu_memory_max));
908
909 std::array<char, 64> str_used;
910 bx::prettify(str_used.data(), str_used.size(), stats->textureMemoryUsed);
911
912 const float usage_percentage = gpu_memory_max > 0 ?
913 (static_cast<float>(stats->textureMemoryUsed) / static_cast<float>(gpu_memory_max)) * 100.0f : 0.0f;
914
915 // Color code based on usage
916 ImVec4 usage_color = cpu_color; // Green for low usage
917 if(usage_percentage > 80.0f)
918 {
919 usage_color = error_color; // Red for high
920 }
921 else if(usage_percentage > 60.0f)
922 {
923 usage_color = warning_color; // Orange for medium
924 }
925
926 ImGui::Separator();
927 ImGui::Text("Texture Memory:");
928 ImGui::Indent();
929 ImGui::Text("Usage: %s / %s", str_used.data(), str_max.data());
930 ImGui::SameLine();
931 ImGui::TextColored(usage_color, "(%.1f%%)", usage_percentage);
932
933 ImGui::SetNextWindowViewportToCurrent();
934 ImGui::PlotLines("##TextureMemory",
935 texture_memory_samples.get_values(),
937 texture_memory_samples.get_offset(),
938 "Texture Memory Usage Over Time",
939 0.0f,
940 static_cast<float>(gpu_memory_max),
941 ImVec2(overlay_width, plot_height));
942 ImGui::Unindent();
943}
944
945} // namespace unravel
static void set_static_mesh_batching_enabled(bool enabled)
Enable or disable static mesh batching globally.
static auto is_static_mesh_batching_enabled() -> bool
Check if static mesh batching is enabled globally.
auto on_frame_ui_render(rtti::context &ctx, const char *name) -> void
Render the statistics panel UI.
auto on_frame_update(rtti::context &ctx, delta_t dt) -> void
Update the statistics panel logic each frame.
auto deinit(rtti::context &ctx) -> void
Deinitialize the statistics panel and clean up resources.
auto init(rtti::context &ctx) -> void
Initialize the statistics panel.
auto on_frame_render(rtti::context &ctx, delta_t dt) -> void
Render the statistics panel each frame.
static constexpr uint32_t NUM_SAMPLES
std::chrono::duration< float > delta_t
std::string name
Definition hub.cpp:27
#define ICON_MDI_CLOCK_OUTLINE
#define ICON_MDI_CUBE_OUTLINE
#define ICON_MDI_CHART_LINE
#define ICON_MDI_ARROW_DOWN_BOLD
#define ICON_MDI_PUZZLE
#define ICON_MDI_INFORMATION
void PushFont(Font::Enum _font)
Definition imgui.cpp:617
uint64_t GetDrawCalls()
Definition imgui.cpp:715
bgfx::Stats stats
Definition graphics.h:30
void set_debug(uint32_t _debug)
Definition graphics.cpp:312
const stats * get_stats()
Definition graphics.cpp:292
const caps * get_caps()
Definition graphics.cpp:287
bgfx::Caps caps
Definition graphics.h:29
auto draw_resource_bar(const char *name, const char *tooltip, uint32_t current_value, uint32_t max_value, float max_width, float height) -> void
Draw a resource usage bar with label and percentage.
auto draw_progress_bar(float width, float max_width, float height, const ImVec4 &color) -> bool
Draw a colored progress bar with hover effects.
auto get_app_profiler() -> performance_profiler *
Definition profiler.cpp:6
static auto get_last_frame_max_pass_id() -> gfx::view_id
static auto get_all_scenes() -> const std::vector< scene * > &
Definition scene.cpp:119