Unravel Engine C++ Reference
Loading...
Searching...
No Matches
profiler_eviction_section.cpp
Go to the documentation of this file.
2
5#include <graphics/eviction.h>
6#include <graphics/graphics.h>
7#include <imgui/imgui.h>
8#include <imgui_widgets/tooltips.h>
9
10#include <logging/logging.h>
11
12#include <algorithm>
13#include <array>
14#include <cstdint>
15#include <string>
16
17namespace unravel
18{
19namespace
20{
21
22ImVec4 category_text_color_ex{0.62f, 0.80f, 1.0f, 1.0f};
23ImVec4 muted_text_color_ex{0.55f, 0.55f, 0.55f, 1.0f};
24ImVec4 warn_text_color_ex{0.93f, 0.74f, 0.20f, 1.0f};
25ImVec4 bad_text_color_ex{1.0f, 0.3f, 0.3f, 1.0f};
26
27std::array<const char*, 4> strategy_names{"LRU (least recently used)",
28 "LFU (least frequently used)",
29 "Largest first",
30 "Age TTL"};
31
32auto clamp_u64(std::int64_t value) -> std::uint64_t
33{
34 return static_cast<std::uint64_t>(std::max<std::int64_t>(0, value));
35}
36
37void draw_stat_row(const char* label, const char* value, const ImVec4* color = nullptr)
38{
39 ImGui::TableNextRow();
40 ImGui::TableSetColumnIndex(0);
41 ImGui::AlignTextToFramePadding();
42 ImGui::TextUnformatted(label);
43 ImGui::TableSetColumnIndex(1);
44 ImGui::AlignTextToFramePadding();
45 const float offset = ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(value).x;
46 if(offset > 0.0f)
47 {
48 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offset);
49 }
50 if(color != nullptr)
51 {
52 ImGui::TextColored(*color, "%s", value);
53 return;
54 }
55 ImGui::TextUnformatted(value);
56}
57
58void draw_count_bytes_row(const char* label,
59 std::uint64_t count,
60 std::uint64_t bytes,
61 const ImVec4* color = nullptr)
62{
63 const auto value = fmt::format("{} ({})", count, format_bytes(bytes));
64 draw_stat_row(label, value.c_str(), color);
65}
66
67void draw_count_row(const char* label, std::uint64_t count, const ImVec4* color = nullptr)
68{
69 const auto value = fmt::format("{}", count);
70 draw_stat_row(label, value.c_str(), color);
71}
72
73void draw_ms_row(const char* label, double ms)
74{
75 const auto value = fmt::format("{:.3f} ms", ms);
76 draw_stat_row(label, value.c_str());
77}
78
79void draw_reserve_test_controls(std::uint64_t gpu_max)
80{
81 namespace ev = gfx::eviction;
82
83 ImGui::TextColored(category_text_color_ex, ICON_MDI_MEMORY " Test: GPU memory reserve");
84
85 static int reserve_mb = 1024;
86 ImGui::SliderInt("Reserve (MiB)", &reserve_mb, 64, 16384);
87 ImGui::SetItemTooltipEx("Test: reserve raw, non-evictable GPU memory to push device occupancy\n"
88 "toward the limit, so eviction and near-the-limit allocations (e.g. a\n"
89 "large frame buffer) can be exercised on GPUs with plenty of memory.");
90
91 if(ImGui::SmallButton(ICON_MDI_PLUS_BOX " Reserve"))
92 {
93 ev::debug_consume_memory(static_cast<std::uint64_t>(std::max(0, reserve_mb)) * 1024ull * 1024ull);
94 }
95 ImGui::SetItemTooltipEx("Reserve the above amount of non-evictable GPU memory (additive).");
96 ImGui::SameLine();
97 if(ImGui::SmallButton(ICON_MDI_DELETE " Release"))
98 {
99 ev::debug_release_memory();
100 }
101 ImGui::SetItemTooltipEx("Free all reserved memory and reclaim its VRAM immediately.");
102 ImGui::SameLine();
103 ImGui::TextUnformatted(fmt::format("held: {}", format_bytes(ev::debug_consumed_bytes())).c_str());
104
105 if(gpu_max == 0)
106 {
107 return;
108 }
109
110 constexpr std::uint64_t one_gib = 1024ull * 1024ull * 1024ull;
111 constexpr std::array<std::uint32_t, 7> presets{1, 2, 4, 6, 8, 12, 16};
112
113 ImGui::TextUnformatted("Simulate as if GPU had:");
114 ImGui::SetItemTooltipEx("Test: reserve real VRAM in one click so the device behaves like a\n"
115 "smaller card. Each preset reserves (GPU max - target) of memory, so\n"
116 "the engine hits genuine out-of-memory behavior at the chosen limit.\n"
117 "Only presets smaller than this GPU are shown.");
118 for(const auto gb : presets)
119 {
120 if(static_cast<std::uint64_t>(gb) * one_gib >= gpu_max)
121 {
122 continue; // only simulate budgets smaller than the real one
123 }
124 const auto label = fmt::format("{} GB", gb);
125 ImGui::SameLine();
126 if(ImGui::SmallButton(label.c_str()))
127 {
128 ev::debug_simulate_budget(static_cast<std::uint64_t>(gb) * one_gib);
129 }
130 }
131}
132
133} // namespace
134
136{
137 if(!ImGui::CollapsingHeader(ICON_MDI_SWAP_HORIZONTAL "\tGPU Eviction / Paging"))
138 {
139 return;
140 }
141
142 namespace ev = gfx::eviction;
143
144 if(!ev::is_supported())
145 {
146 ImGui::TextColored(muted_text_color_ex,
148 " Eviction is not supported on this backend\n(it does not report a GPU "
149 "memory budget). Paging is disabled.");
150 return;
151 }
152
153 ImGui::Checkbox("Enabled", &state.enabled);
154 ImGui::SetItemTooltipEx("When enabled, GPU resources are evicted each frame to keep memory\n"
155 "usage under the configured budget. Evicted resources restore\n"
156 "automatically the next time they are used.");
157
158 ImGui::SameLine();
159 if(ImGui::SmallButton(ICON_MDI_DELETE_SWEEP " Force Evict All"))
160 {
161 ev::evict_all();
162 }
163 ImGui::SetItemTooltipEx("Test: evict every evictable resource immediately, ignoring budget and age.");
164 ImGui::SameLine();
165 if(ImGui::SmallButton(ICON_MDI_BACKUP_RESTORE " Restore All"))
166 {
167 ev::restore_all();
168 }
169 ImGui::SetItemTooltipEx("Test: restore every evicted resource immediately.");
170
171 ImGui::Combo("Strategy", &state.strategy, strategy_names.data(), static_cast<int>(strategy_names.size()));
172 ImGui::SetItemTooltipEx("Policy used to pick eviction victims when over budget.");
173
174 ImGui::Checkbox("Auto budget (GPU memory)", &state.auto_budget);
175 ImGui::SetItemTooltipEx("Derive the budget from the backend's reported GPU memory.\n"
176 "Falls back to the manual budget if the backend does not\n"
177 "report a GPU memory limit.");
178
179 const auto* bx = gfx::get_stats();
180 const std::uint64_t gpu_max = (bx != nullptr) ? clamp_u64(bx->gpuMemoryMax) : 0;
181 const std::uint64_t gpu_used = (bx != nullptr) ? clamp_u64(bx->gpuMemoryUsed) : 0;
182
183 if(state.auto_budget && gpu_max > 0)
184 {
185 ImGui::SliderFloat("Budget %", &state.budget_fraction, 0.1f, 1.0f, "%.2f");
186 ImGui::SliderFloat("Target %", &state.target_fraction, 0.1f, 1.0f, "%.2f");
187 state.target_fraction = std::min(state.target_fraction, state.budget_fraction);
188
189 const auto soft = static_cast<std::uint64_t>(static_cast<double>(gpu_max) * state.budget_fraction);
190 const auto target = static_cast<std::uint64_t>(static_cast<double>(gpu_max) * state.target_fraction);
191 const bool over = gpu_used > soft;
192 const auto gpu_line = fmt::format("GPU: {} / {} budget {}, target {}",
193 format_bytes(gpu_used),
194 format_bytes(gpu_max),
195 format_bytes(soft),
196 format_bytes(target));
197 ImGui::TextColored(over ? bad_text_color_ex : muted_text_color_ex, "%s", gpu_line.c_str());
198 if(over)
199 {
200 ImGui::SameLine();
201 ImGui::TextColored(bad_text_color_ex, ICON_MDI_ALERT " OVER");
202 }
203 }
204 else
205 {
206 ImGui::SliderInt("Manual budget (MiB)", &state.manual_budget_mb, 16, 8192);
207 if(state.auto_budget)
208 {
209 ImGui::TextColored(warn_text_color_ex, "Backend reports no GPU memory limit; using manual budget.");
210 }
211 }
212
213 ImGui::SliderInt("Min age (frames)", &state.min_age_frames, 0, 600);
214 ImGui::SetItemTooltipEx("Resources used within this many frames are never evicted (anti-thrash).");
215 ImGui::SliderInt("Max evictions / frame", &state.max_evictions, 0, 1024);
216 ImGui::SetItemTooltipEx("Caps how many resources may be evicted per frame to bound the cost.\n"
217 "0 means unlimited.");
218
219 ImGui::Separator();
220 draw_reserve_test_controls(gpu_max);
221
222 ImGui::Separator();
223
224 const auto stats = ev::get_stats();
225 constexpr ImGuiTableFlags table_flags =
226 ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersOuter;
227 if(!ImGui::BeginTable("##eviction_stats", 2, table_flags))
228 {
229 return;
230 }
231 ImGui::TableSetupColumn("Stat", ImGuiTableColumnFlags_WidthStretch, 1.0f);
232 ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed, 140.0f);
233
234 ImGui::TableNextRow();
235 ImGui::TableSetColumnIndex(0);
236 ImGui::TextColored(category_text_color_ex, "Residency");
237
238 draw_count_bytes_row("Resident", stats.resident_count, stats.resident_bytes);
239 draw_count_bytes_row("Evicted", stats.evicted_count, stats.evicted_bytes);
240 draw_count_row("Registered", stats.registered_count);
241
242 ImGui::TableNextRow();
243 ImGui::TableSetColumnIndex(0);
244 ImGui::TextColored(category_text_color_ex, "Lifetime");
245
246 draw_count_bytes_row("Evictions", stats.total_evictions, stats.total_bytes_evicted);
247 draw_count_bytes_row("Restores", stats.total_restores, stats.total_bytes_restored);
248 draw_count_row("Failed restores",
249 stats.failed_restores,
250 stats.failed_restores > 0 ? &warn_text_color_ex : nullptr);
251 draw_count_row("Thrash events", stats.thrash_events, stats.thrash_events > 0 ? &warn_text_color_ex : nullptr);
252
253 ImGui::TableNextRow();
254 ImGui::TableSetColumnIndex(0);
255 ImGui::TextColored(category_text_color_ex, "Last pass");
256
257 draw_count_row("Scanned", stats.last_pass_scanned);
258 draw_count_row("Evicted", stats.last_pass_evicted);
259 draw_ms_row("Sweep time", stats.last_pass_ms);
260 draw_ms_row("Last restore", stats.last_restore_ms);
261
262 ImGui::EndTable();
263}
264
265} // namespace unravel
std::uint64_t bytes
Definition eviction.cpp:823
#define ICON_MDI_ALERT
#define ICON_MDI_DELETE_SWEEP
#define ICON_MDI_BACKUP_RESTORE
#define ICON_MDI_PLUS_BOX
#define ICON_MDI_DELETE
#define ICON_MDI_SWAP_HORIZONTAL
#define ICON_MDI_MEMORY
Definition imgui.h:25
const stats * get_stats()
Definition graphics.cpp:450
void profiler_draw_eviction_section(eviction_settings &state)
auto format_bytes(std::uint64_t bytes, std::uint8_t num_frac) -> std::string
std::vector< math::color > color
float target_fraction
Evict down to this fraction of the reported maximum (hysteresis; should be < budget).
int manual_budget_mb
Manual budget (MiB) compared against the evictable resident bytes when not using auto budget.
int min_age_frames
Resources used within this many frames are protected from eviction.
float budget_fraction
Start evicting once GPU usage exceeds this fraction of the reported maximum.
int strategy
Selected gfx::eviction::strategy (index).
int max_evictions
Maximum resources evicted per frame (0 = unlimited).
bool enabled
Master toggle: when true the per-frame driver evicts to keep usage under the budget.