2#include "imgui_widgets/utils.h"
19 constexpr float plot_height = 50.0f;
20 constexpr float max_frame_time_ms = 200.0f;
21 constexpr float max_passes = 200.0f;
22 constexpr float profiler_scale = 3.0f;
23 constexpr float profiler_max_width = 30.0f;
24 constexpr float resource_bar_width = 90.0f;
25 constexpr float megabyte_divisor = 1024.0f * 1024.0f;
28 constexpr ImVec4 cpu_color{0.2f, 0.8f, 0.2f, 1.0f};
29 constexpr ImVec4 gpu_color{0.2f, 0.6f, 1.0f, 1.0f};
30 constexpr ImVec4 warning_color{1.0f, 0.7f, 0.0f, 1.0f};
31 constexpr ImVec4 error_color{1.0f, 0.3f, 0.3f, 1.0f};
34 statistics_utils::sample_data frame_time_samples;
35 statistics_utils::sample_data graphics_passes_samples;
36 statistics_utils::sample_data gpu_memory_samples;
37 statistics_utils::sample_data render_target_memory_samples;
38 statistics_utils::sample_data texture_memory_samples;
63 if(ImGui::Begin(
name,
nullptr, ImGuiWindowFlags_MenuBar))
66 draw_statistics_content();
71auto statistics_panel::draw_menubar(
rtti::context& ctx) ->
void
73 if(ImGui::BeginMenuBar())
77 ImGui::Checkbox(
"Show Editor Stats", &show_editor_stats_);
78 ImGui::SetItemTooltip(
"Show editor/UI related draw calls and triangles\n(Focus on scene stats when disabled)");
85auto statistics_panel::draw_statistics_content() ->
void
87 const auto& io = ImGui::GetIO();
88 const auto area = ImGui::GetContentRegionAvail();
89 const float overlay_width = area.x;
95 draw_frame_statistics(overlay_width);
96 draw_profiler_section();
97 draw_memory_info_section(overlay_width);
98 draw_resources_section();
101auto statistics_panel::draw_frame_statistics(
float overlay_width) ->
void
104 const auto& io = ImGui::GetIO();
106 const double to_cpu_ms = 1000.0 /
static_cast<double>(
stats->cpuTimerFreq);
107 const double to_gpu_ms = 1000.0 /
static_cast<double>(
stats->gpuTimerFreq);
110 if(ImGui::CollapsingHeader(
ICON_MDI_CHART_LINE "\tPerformance Overview", ImGuiTreeNodeFlags_DefaultOpen))
115 const float avg_frame_time = frame_time_samples.get_average();
116 const float fps = 1000.0f / avg_frame_time;
119 ImVec4 fps_color = cpu_color;
122 fps_color = error_color;
126 fps_color = warning_color;
130 ImGui::BeginColumns(
"PerformanceColumns", 2, ImGuiOldColumnFlags_NoResize);
131 ImGui::SetColumnWidth(0, overlay_width * 0.5f);
133 ImGui::Text(
"Frame Time:");
134 ImGui::Text(
" Average: %.3f ms", avg_frame_time);
135 ImGui::Text(
" Min/Max: %.3f / %.3f ms", frame_time_samples.get_min(), frame_time_samples.get_max());
139 ImGui::TextColored(fps_color,
"FPS: %.1f", fps);
143 ImGui::TextColored(warning_color, fps < 30.0f ?
" (Poor)" :
" (Low)");
147 if(
stats->gpuMemoryUsed > 0)
149 std::array<char, 64> gpu_used_str;
150 bx::prettify(gpu_used_str.data(), gpu_used_str.size(),
stats->gpuMemoryUsed);
152 ImGui::Text(
"GPU Memory:");
154 if(
stats->gpuMemoryMax > 0)
157 const float gpu_usage_percentage = (
static_cast<float>(
stats->gpuMemoryUsed) /
static_cast<float>(
stats->gpuMemoryMax)) * 100.0f;
160 ImVec4 gpu_memory_color = cpu_color;
161 if(gpu_usage_percentage > 80.0f)
163 gpu_memory_color = error_color;
165 else if(gpu_usage_percentage > 60.0f)
167 gpu_memory_color = warning_color;
170 std::array<char, 64> gpu_max_str;
171 bx::prettify(gpu_max_str.data(), gpu_max_str.size(),
stats->gpuMemoryMax);
173 ImGui::Text(
"%s / %s", gpu_used_str.data(), gpu_max_str.data());
175 ImGui::TextColored(gpu_memory_color,
"(%.1f%%)", gpu_usage_percentage);
180 ImGui::Text(
"%s used", gpu_used_str.data());
182 ImGui::TextColored(warning_color,
"(max unknown)");
187 ImGui::Text(
"GPU Memory:");
188 ImGui::TextColored(warning_color,
"No data available");
194 std::array<char, 256> frame_text_overlay;
195 bx::snprintf(frame_text_overlay.data(),
196 frame_text_overlay.size(),
197 "Performance: %.1f FPS (%.3f ms avg)\nRange: %.3f - %.3f ms",
199 frame_time_samples.get_min(),
200 frame_time_samples.get_max());
202 ImGui::SetNextWindowViewportToCurrent();
203 ImGui::PlotLines(
"##FrameTime",
204 frame_time_samples.get_values(),
206 frame_time_samples.get_offset(),
207 frame_text_overlay.data(),
210 ImVec2(overlay_width, plot_height));
215 const auto submit_cpu_ms =
static_cast<double>(
stats->cpuTimeEnd -
stats->cpuTimeBegin) * to_cpu_ms;
216 const auto submit_gpu_ms =
static_cast<double>(
stats->gpuTimeEnd -
stats->gpuTimeBegin) * to_gpu_ms;
218 ImGui::BeginColumns(
"TimingColumns", 4, ImGuiOldColumnFlags_NoResize);
219 ImGui::SetColumnWidth(0, overlay_width * 0.25f);
220 ImGui::SetColumnWidth(1, overlay_width * 0.25f);
221 ImGui::SetColumnWidth(2, overlay_width * 0.25f);
223 ImGui::TextColored(cpu_color,
"CPU Submit");
224 ImGui::Text(
"%.3f ms", submit_cpu_ms);
227 ImGui::TextColored(gpu_color,
"GPU Submit");
228 ImGui::Text(
"%.3f ms", submit_gpu_ms);
231 ImGui::Text(
"GPU Latency");
232 ImGui::Text(
"%d frames",
stats->maxGpuLatency);
235 ImGui::Text(
"Draw Calls");
237 std::uint32_t scene_calls = 0, editor_calls = 0, total_calls = 0;
238 get_draw_call_breakdown(stats, scene_calls, editor_calls, total_calls);
240 if(show_editor_stats_)
242 ImGui::Text(
"%u total", total_calls);
246 ImGui::Text(
"%u scene", scene_calls);
255 if(ImGui::CollapsingHeader(
ICON_MDI_CUBE_OUTLINE "\tRendering Statistics", ImGuiTreeNodeFlags_DefaultOpen))
264 draw_call_counts(stats, io);
269 draw_primitive_counts(stats, io);
276auto statistics_panel::draw_profiler_section() ->
void
286 ImGui::Text(
"CPU Profiler:");
287 draw_app_profiler_data();
292 ImGui::AlignTextToFramePadding();
293 ImGui::Text(
"GPU Profiler:");
296 if(ImGui::Checkbox(
"Enable##GPUProfiler", &enable_gpu_profiler_))
298 if(enable_gpu_profiler_)
309 if(enable_gpu_profiler_)
313 if(
stats->numViews == 0)
315 ImGui::TextColored(warning_color,
"No GPU profiling data available.");
316 ImGui::Text(
"Profiler may be initializing...");
320 ImGui::Text(
"GPU Timing (per view/encoder):");
321 draw_profiler_bars(stats);
326 ImGui::TextColored(warning_color,
"GPU profiler is disabled.");
327 ImGui::Text(
"Enable to see detailed GPU timing information.");
333auto statistics_panel::draw_memory_info_section(
float overlay_width) ->
void
343 auto gpu_memory_max =
stats->gpuMemoryMax;
346 if(
stats->gpuMemoryUsed > 0)
348 draw_gpu_memory_section(stats, gpu_memory_max, overlay_width);
352 ImGui::TextColored(warning_color,
"No GPU memory usage data available");
356 draw_render_target_memory_section(stats, gpu_memory_max, overlay_width);
359 draw_texture_memory_section(stats, gpu_memory_max, overlay_width);
365auto statistics_panel::draw_resources_section() ->
void
374 const float item_height = ImGui::GetTextLineHeightWithSpacing();
378 ImGui::Text(
"Resource Usage (Current / Maximum):");
382 ImGui::Text(
"Buffers:");
385 using namespace statistics_utils;
388 stats->numDynamicIndexBuffers,
caps->limits.maxDynamicIndexBuffers,
389 resource_bar_width, item_height);
392 stats->numDynamicVertexBuffers,
caps->limits.maxDynamicVertexBuffers,
393 resource_bar_width, item_height);
396 stats->numIndexBuffers,
caps->limits.maxIndexBuffers,
397 resource_bar_width, item_height);
400 stats->numVertexBuffers,
caps->limits.maxVertexBuffers,
401 resource_bar_width, item_height);
406 ImGui::Text(
"Shading:");
410 stats->numPrograms,
caps->limits.maxPrograms,
411 resource_bar_width, item_height);
414 stats->numShaders,
caps->limits.maxShaders,
415 resource_bar_width, item_height);
418 stats->numUniforms,
caps->limits.maxUniforms,
419 resource_bar_width, item_height);
424 ImGui::Text(
"Rendering:");
428 stats->numTextures,
caps->limits.maxTextures,
429 resource_bar_width, item_height);
432 stats->numFrameBuffers,
caps->limits.maxFrameBuffers,
433 resource_bar_width, item_height);
436 stats->numVertexLayouts,
caps->limits.maxVertexLayouts,
437 resource_bar_width, item_height);
440 stats->numOcclusionQueries,
caps->limits.maxOcclusionQueries,
441 resource_bar_width, item_height);
449auto statistics_panel::update_sample_data() ->
void
452 const double to_cpu_ms = 1000.0 /
static_cast<double>(
stats->cpuTimerFreq);
453 const double frame_ms =
static_cast<double>(
stats->cpuTimeFrame) * to_cpu_ms;
455 frame_time_samples.push_sample(
static_cast<float>(frame_ms));
457 gpu_memory_samples.push_sample(
static_cast<float>(
stats->gpuMemoryUsed) / megabyte_divisor);
458 render_target_memory_samples.push_sample(
static_cast<float>(
stats->rtMemoryUsed) / megabyte_divisor);
459 texture_memory_samples.push_sample(
static_cast<float>(
stats->textureMemoryUsed) / megabyte_divisor);
462auto 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
464 const ImGuiIO& io = ImGui::GetIO();
465 total_calls =
stats->numDraw;
467 editor_calls = std::min(editor_calls, total_calls);
468 scene_calls = total_calls - editor_calls;
471auto statistics_panel::draw_primitive_counts(
const gfx::stats* stats,
const ImGuiIO& io) ->
void
473 const std::uint32_t total_primitives = std::accumulate(std::begin(
stats->numPrims), std::end(
stats->numPrims), 0u);
474 std::uint32_t ui_primitives = io.MetricsRenderIndices / 3;
475 ui_primitives = std::min(ui_primitives, total_primitives);
476 const auto scene_primitives = total_primitives - ui_primitives;
478 ImGui::Text(
"Triangle Counts:");
481 if(show_editor_stats_)
484 ImGui::BeginColumns(
"PrimitiveColumns", 2, ImGuiOldColumnFlags_NoResize);
485 ImGui::SetColumnWidth(0, 120.0f);
487 ImGui::Text(
"Scene:");
489 ImGui::TextColored(cpu_color,
"%u triangles", scene_primitives);
492 ImGui::Text(
"Editor:");
494 ImGui::TextColored(gpu_color,
"%u triangles", ui_primitives);
497 ImGui::Text(
"Total:");
499 ImGui::Text(
"%u triangles", total_primitives);
506 ImGui::TextColored(cpu_color,
"Scene: %u triangles", scene_primitives);
512auto statistics_panel::draw_call_counts(
const gfx::stats* stats,
const ImGuiIO& io) ->
void
514 std::uint32_t scene_calls = 0, editor_calls = 0, total_calls = 0;
515 get_draw_call_breakdown(stats, scene_calls, editor_calls, total_calls);
517 ImGui::Text(
"GPU Commands:");
520 if(show_editor_stats_)
523 ImGui::BeginColumns(
"CallCountColumns", 2, ImGuiOldColumnFlags_NoResize);
524 ImGui::SetColumnWidth(0, 120.0f);
527 ImGui::Text(
"Draw Calls:");
529 ImGui::Text(
"%u total", total_calls);
532 ImGui::Text(
" Scene:");
534 ImGui::TextColored(cpu_color,
"%u calls", scene_calls);
537 ImGui::Text(
" Editor:");
539 ImGui::TextColored(gpu_color,
"%u calls", editor_calls);
543 ImGui::Text(
"Compute:");
545 ImGui::Text(
"%u calls",
stats->numCompute);
548 ImGui::Text(
"Blit:");
550 ImGui::Text(
"%u calls",
stats->numBlit);
557 ImGui::TextColored(cpu_color,
"Scene Draw Calls: %u", scene_calls);
560 if(
stats->numCompute > 0 ||
stats->numBlit > 0)
562 ImGui::BeginColumns(
"SceneCallColumns", 2, ImGuiOldColumnFlags_NoResize);
563 ImGui::SetColumnWidth(0, 120.0f);
565 if(
stats->numCompute > 0)
567 ImGui::Text(
"Compute:");
569 ImGui::Text(
"%u calls",
stats->numCompute);
573 if(
stats->numBlit > 0)
575 ImGui::Text(
"Blit:");
577 ImGui::Text(
"%u calls",
stats->numBlit);
588auto statistics_panel::draw_profiler_bars(
const gfx::stats* stats) ->
void
590 const float item_height = ImGui::GetTextLineHeightWithSpacing();
591 const float item_height_with_spacing = ImGui::GetFrameHeightWithSpacing();
592 const double to_cpu_ms = 1000.0 /
static_cast<double>(
stats->cpuTimerFreq);
593 const double to_gpu_ms = 1000.0 /
static_cast<double>(
stats->gpuTimerFreq);
596 draw_encoder_stats(stats, item_height, item_height_with_spacing, to_cpu_ms);
601 draw_view_stats(stats, item_height, item_height_with_spacing, to_cpu_ms, to_gpu_ms);
604auto statistics_panel::draw_encoder_stats(
const gfx::stats* stats,
float item_height,
float item_height_with_spacing,
double to_cpu_ms) ->
void
606 if(ImGui::BeginListBox(
"Encoders", ImVec2(ImGui::GetWindowWidth(),
stats->numEncoders * item_height_with_spacing)))
608 ImGuiListClipper clipper;
609 clipper.Begin(
stats->numEncoders, item_height);
611 while(clipper.Step())
613 for(int32_t pos = clipper.DisplayStart; pos < clipper.DisplayEnd; ++pos)
615 const bgfx::EncoderStats& encoder_stats =
stats->encoderStats[pos];
617 ImGui::Text(
"%3d", pos);
618 ImGui::SameLine(64.0f);
620 const float max_width = profiler_max_width * profiler_scale;
621 const float cpu_ms =
static_cast<float>((encoder_stats.cpuTimeEnd - encoder_stats.cpuTimeBegin) * to_cpu_ms);
622 const float cpu_width = bx::clamp(cpu_ms * profiler_scale, 1.0f, max_width);
626 ImGui::SetItemTooltipEx(
"Encoder %d, CPU: %f [ms]", pos, cpu_ms);
636auto 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
638 if(ImGui::BeginListBox(
"Views", ImVec2(ImGui::GetWindowWidth(),
stats->numViews * item_height_with_spacing)))
640 ImGuiListClipper clipper;
641 clipper.Begin(
stats->numViews, item_height);
643 while(clipper.Step())
645 for(int32_t pos = clipper.DisplayStart; pos < clipper.DisplayEnd; ++pos)
647 const bgfx::ViewStats& view_stats =
stats->viewStats[pos];
648 ImGui::PushID(view_stats.view);
649 ImGui::Text(
"%3d %3d %s", pos, view_stats.view, view_stats.name);
651 const float max_width = profiler_max_width * profiler_scale;
652 const float cpu_time_elapsed =
static_cast<float>((view_stats.cpuTimeEnd - view_stats.cpuTimeBegin) * to_cpu_ms);
653 const float gpu_time_elapsed =
static_cast<float>((view_stats.gpuTimeEnd - view_stats.gpuTimeBegin) * to_gpu_ms);
654 const float cpu_width = bx::clamp(cpu_time_elapsed * profiler_scale, 1.0f, max_width);
655 const float gpu_width = bx::clamp(gpu_time_elapsed * profiler_scale, 1.0f, max_width);
657 ImGui::SameLine(64.0f);
659 ImGui::PushID(
"cpu");
662 ImGui::SetItemTooltipEx(
"View %d \"%s\", CPU: %f [ms]", pos, view_stats.name, cpu_time_elapsed);
668 ImGui::PushID(
"gpu");
671 ImGui::SetItemTooltipEx(
"View: %d \"%s\", GPU: %f [ms]", pos, view_stats.name, gpu_time_elapsed);
682auto statistics_panel::draw_app_profiler_data() ->
void
685 const auto& data = profiler->get_per_frame_data_read();
687 for(
const auto& [
name, per_frame_data] : data)
689 ImGui::TextUnformatted(
690 fmt::format(
"{:>7.3f}ms [{:^5}] - {}",
692 per_frame_data.samples,
693 fmt::string_view(
name.data(),
name.size())).c_str());
697auto statistics_panel::draw_gpu_memory_section(
const gfx::stats* stats, int64_t& gpu_memory_max,
float overlay_width) ->
void
699 gpu_memory_max = std::max(
stats->gpuMemoryUsed,
stats->gpuMemoryMax);
701 std::array<char, 64> str_max;
702 bx::prettify(str_max.data(), str_max.size(),
static_cast<uint64_t
>(gpu_memory_max));
704 std::array<char, 64> str_used;
705 bx::prettify(str_used.data(), str_used.size(),
stats->gpuMemoryUsed);
707 const float usage_percentage = gpu_memory_max > 0 ?
708 (
static_cast<float>(
stats->gpuMemoryUsed) /
static_cast<float>(gpu_memory_max)) * 100.0f : 0.0f;
711 ImVec4 usage_color = cpu_color;
712 if(usage_percentage > 80.0f)
714 usage_color = error_color;
716 else if(usage_percentage > 60.0f)
718 usage_color = warning_color;
722 ImGui::Text(
"General GPU Memory:");
724 ImGui::Text(
"Usage: %s / %s", str_used.data(), str_max.data());
726 ImGui::TextColored(usage_color,
"(%.1f%%)", usage_percentage);
728 ImGui::SetNextWindowViewportToCurrent();
729 ImGui::PlotLines(
"##GPUMemory",
730 gpu_memory_samples.get_values(),
732 gpu_memory_samples.get_offset(),
733 "GPU Memory Usage Over Time",
735 static_cast<float>(gpu_memory_max),
736 ImVec2(overlay_width, plot_height));
740auto statistics_panel::draw_render_target_memory_section(
const gfx::stats* stats, int64_t& gpu_memory_max,
float overlay_width) ->
void
742 gpu_memory_max = std::max(
stats->rtMemoryUsed, gpu_memory_max);
744 std::array<char, 64> str_max;
745 bx::prettify(str_max.data(), str_max.size(),
static_cast<uint64_t
>(gpu_memory_max));
747 std::array<char, 64> str_used;
748 bx::prettify(str_used.data(), str_used.size(),
stats->rtMemoryUsed);
750 const float usage_percentage = gpu_memory_max > 0 ?
751 (
static_cast<float>(
stats->rtMemoryUsed) /
static_cast<float>(gpu_memory_max)) * 100.0f : 0.0f;
754 ImVec4 usage_color = cpu_color;
755 if(usage_percentage > 80.0f)
757 usage_color = error_color;
759 else if(usage_percentage > 60.0f)
761 usage_color = warning_color;
765 ImGui::Text(
"Render Target Memory:");
767 ImGui::Text(
"Usage: %s / %s", str_used.data(), str_max.data());
769 ImGui::TextColored(usage_color,
"(%.1f%%)", usage_percentage);
771 ImGui::SetNextWindowViewportToCurrent();
772 ImGui::PlotLines(
"##RenderTargetMemory",
773 render_target_memory_samples.get_values(),
775 render_target_memory_samples.get_offset(),
776 "Render Target Memory Usage Over Time",
778 static_cast<float>(gpu_memory_max),
779 ImVec2(overlay_width, plot_height));
783auto statistics_panel::draw_texture_memory_section(
const gfx::stats* stats, int64_t& gpu_memory_max,
float overlay_width) ->
void
785 gpu_memory_max = std::max(
stats->textureMemoryUsed, gpu_memory_max);
787 std::array<char, 64> str_max;
788 bx::prettify(str_max.data(), str_max.size(),
static_cast<uint64_t
>(gpu_memory_max));
790 std::array<char, 64> str_used;
791 bx::prettify(str_used.data(), str_used.size(),
stats->textureMemoryUsed);
793 const float usage_percentage = gpu_memory_max > 0 ?
794 (
static_cast<float>(
stats->textureMemoryUsed) /
static_cast<float>(gpu_memory_max)) * 100.0f : 0.0f;
797 ImVec4 usage_color = cpu_color;
798 if(usage_percentage > 80.0f)
800 usage_color = error_color;
802 else if(usage_percentage > 60.0f)
804 usage_color = warning_color;
808 ImGui::Text(
"Texture Memory:");
810 ImGui::Text(
"Usage: %s / %s", str_used.data(), str_max.data());
812 ImGui::TextColored(usage_color,
"(%.1f%%)", usage_percentage);
814 ImGui::SetNextWindowViewportToCurrent();
815 ImGui::PlotLines(
"##TextureMemory",
816 texture_memory_samples.get_values(),
818 texture_memory_samples.get_offset(),
819 "Texture Memory Usage Over Time",
821 static_cast<float>(gpu_memory_max),
822 ImVec2(overlay_width, plot_height));
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
#define ICON_MDI_CLOCK_OUTLINE
#define ICON_MDI_CUBE_OUTLINE
#define ICON_MDI_CHART_LINE
#define ICON_MDI_ARROW_DOWN_BOLD
#define ICON_MDI_INFORMATION
void PushFont(Font::Enum _font)
void set_debug(uint32_t _debug)
const stats * get_stats()
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 *
static auto get_last_frame_max_pass_id() -> gfx::view_id