tess 0.4.0
Performance-first tile and path simulation substrate
Loading...
Searching...
No Matches
panels.h
1#pragma once
2
3// Reference Dear ImGui debug panels over the diagnostics export snapshots.
4//
5// This header is opt-in and dependency-free for tess core: it is compiled only
6// when the CONSUMER defines both TESS_ENABLE_IMGUI and TESS_ENABLE_DIAGNOSTICS
7// on its own target, and it never fetches or links Dear ImGui itself. The
8// consumer must include <imgui.h> BEFORE this header (the panels call into the
9// ImGui namespace); when the gates are on but ImGui has not been included, the
10// #error below fires instead of emitting confusing name-lookup failures.
11//
12// tess.h does NOT include this header, so a diagnostics build that does not use
13// ImGui never sees it. Only the three most stable ImGui text primitives are
14// used (Text, TextUnformatted, Separator) so the panels compile across ImGui
15// versions and uint64 values are printed through unsigned long long casts for
16// portable printf-style formatting.
17//
18// Threading: the panels only read the snapshot copies they are passed, so they
19// are safe on a render thread -- but producing those copies is not. Capture
20// them per export.h's threading contract (on the recording thread, or with
21// capture externally synchronized against recording), then hand the snapshot
22// to the thread that draws.
23
24#include <tess/diagnostics/diagnostics.h>
25
26#if defined(TESS_ENABLE_IMGUI) && TESS_DIAGNOSTICS_ENABLED
27
28#ifndef IMGUI_VERSION
29#error "tess/debug/imgui/panels.h requires <imgui.h> to be included first"
30#endif
31
32#include <tess/diagnostics/export.h>
33#include <tess/diagnostics/trace.h>
34
35#include <cstddef>
36#include <cstdint>
37
38namespace tess::debug::imgui {
39
40namespace detail {
41[[nodiscard]] inline auto to_ull(std::uint64_t value) noexcept
42 -> unsigned long long {
43 return static_cast<unsigned long long>(value);
44}
45} // namespace detail
46
48[[nodiscard]] inline auto category_name(
49 diagnostics::TraceCategory category) noexcept -> const char* {
50 switch (category) {
51 case diagnostics::TraceCategory::General:
52 return "General";
53 case diagnostics::TraceCategory::Path:
54 return "Path";
55 case diagnostics::TraceCategory::Topology:
56 return "Topology";
57 case diagnostics::TraceCategory::Queued:
58 return "Queued";
59 case diagnostics::TraceCategory::Planner:
60 return "Planner";
61 case diagnostics::TraceCategory::Scheduler:
62 return "Scheduler";
63 case diagnostics::TraceCategory::Render:
64 return "Render";
65 case diagnostics::TraceCategory::Count:
66 return "Count";
67 }
68 return "?";
69}
70
72inline void draw_timing_panel(const diagnostics::TimingSnapshot& timing) {
73 ImGui::TextUnformatted("Timing (ns)");
74 ImGui::Separator();
75 for (std::size_t index = 0; index < diagnostics::trace_category_count;
76 ++index) {
77 const auto category = static_cast<diagnostics::TraceCategory>(index);
78 const auto& stats = timing.category(category);
79 const auto average =
80 stats.samples == 0 ? std::uint64_t{0} : stats.total_ns / stats.samples;
81 ImGui::Text("%-10s n=%llu total=%llu avg=%llu min=%llu max=%llu",
82 category_name(category), detail::to_ull(stats.samples),
83 detail::to_ull(stats.total_ns), detail::to_ull(average),
84 detail::to_ull(stats.min_ns), detail::to_ull(stats.max_ns));
85 }
86}
87
89inline void draw_path_counters_panel(const diagnostics::PathCounters& path) {
90 ImGui::TextUnformatted("Path counters");
91 ImGui::Separator();
92 ImGui::Text("heap push / pop: %llu / %llu", detail::to_ull(path.heap_pushes),
93 detail::to_ull(path.heap_pops));
94 ImGui::Text("relax ok / attempts: %llu / %llu",
95 detail::to_ull(path.relax_successes),
96 detail::to_ull(path.relax_attempts));
97 ImGui::Text("touched nodes: %llu", detail::to_ull(path.touched_nodes));
98 ImGui::Text("passability checks: %llu",
99 detail::to_ull(path.passability_checks));
100}
101
103inline void draw_queued_counters_panel(
104 const diagnostics::QueuedPhaseCounters& queued) {
105 ImGui::TextUnformatted("Queued phase counters");
106 ImGui::Separator();
107 ImGui::Text("phase calls / ops: %llu / %llu",
108 detail::to_ull(queued.phase_calls),
109 detail::to_ull(queued.phase_operations));
110 ImGui::Text("failures: %llu", detail::to_ull(queued.phase_failures));
111 ImGui::Text("dirty merged: %llu", detail::to_ull(queued.dirty_chunks_merged));
112}
113
115inline void draw_allocation_counters_panel(
116 const diagnostics::AllocationCounters& allocation) {
117 ImGui::TextUnformatted("Allocation counters");
118 ImGui::Separator();
119 ImGui::Text("alloc: %llu (%llu bytes)",
120 detail::to_ull(allocation.allocations),
121 detail::to_ull(allocation.allocation_bytes));
122 ImGui::Text("free: %llu (%llu bytes)",
123 detail::to_ull(allocation.deallocations),
124 detail::to_ull(allocation.deallocation_bytes));
125}
126
128inline void draw_diagnostics_panel(
129 const diagnostics::DiagnosticsSnapshot& snapshot) {
130 draw_timing_panel(snapshot.timing);
131 ImGui::Separator();
132 draw_path_counters_panel(snapshot.path);
133 ImGui::Separator();
134 draw_queued_counters_panel(snapshot.queued);
135 ImGui::Separator();
136 draw_allocation_counters_panel(snapshot.allocation);
137}
138
139} // namespace tess::debug::imgui
140
141#endif // defined(TESS_ENABLE_IMGUI) && TESS_DIAGNOSTICS_ENABLED