Unravel Engine C++ Reference
Loading...
Searching...
No Matches
imgui_interface.cpp
Go to the documentation of this file.
1#include "imgui_interface.h"
3#include "imgui/imgui.h"
4#include "imgui_widgets/utils.h"
6#include <editor/events.h>
8
10#include <engine/events.h>
12#include <graphics/graphics.h>
13
14#include <logging/logging.h>
15
16#include <algorithm>
17#include <string>
18
19namespace unravel
20{
21
22namespace
23{
24
25constexpr ImVec4 memory_label_color{0.42f, 0.42f, 0.42f, 1.0f};
26constexpr ImVec4 memory_value_color{0.50f, 0.50f, 0.50f, 1.0f};
27constexpr ImU32 gpu_bar_color = IM_COL32(58, 121, 187, 165);
28constexpr ImU32 other_system_ram_bar_color = IM_COL32(145, 118, 72, 210);
29constexpr ImU32 process_ram_bar_color = IM_COL32(88, 168, 196, 220);
30constexpr ImU32 memory_bar_track_color = IM_COL32(32, 32, 32, 255);
31constexpr float memory_bar_height = 3.0f;
32constexpr float memory_section_alpha = 0.92f;
33
34void draw_memory_bar(float fraction, ImU32 fill_color)
35{
36 fraction = std::clamp(fraction, 0.0f, 1.0f);
37 const float bar_width = ImGui::GetContentRegionAvail().x;
38 const ImVec2 bar_pos = ImGui::GetCursorScreenPos();
39 ImDrawList* draw_list = ImGui::GetWindowDrawList();
40
41 draw_list->AddRectFilled(bar_pos,
42 ImVec2(bar_pos.x + bar_width, bar_pos.y + memory_bar_height),
43 memory_bar_track_color,
44 memory_bar_height * 0.5f);
45
46 if(fraction > 0.0f)
47 {
48 draw_list->AddRectFilled(bar_pos,
49 ImVec2(bar_pos.x + bar_width * fraction, bar_pos.y + memory_bar_height),
50 fill_color,
51 memory_bar_height * 0.5f);
52 }
53
54 ImGui::Dummy(ImVec2(bar_width, memory_bar_height));
55}
56
57void draw_stacked_ram_bar(float process_fraction, float system_used_fraction)
58{
59 process_fraction = std::clamp(process_fraction, 0.0f, 1.0f);
60 system_used_fraction = std::clamp(system_used_fraction, process_fraction, 1.0f);
61 const float other_used_fraction = system_used_fraction - process_fraction;
62
63 const float bar_width = ImGui::GetContentRegionAvail().x;
64 const ImVec2 bar_pos = ImGui::GetCursorScreenPos();
65 ImDrawList* draw_list = ImGui::GetWindowDrawList();
66 const float bar_radius = memory_bar_height * 0.5f;
67 const float bar_bottom = bar_pos.y + memory_bar_height;
68 const ImVec2 bar_max(bar_pos.x + bar_width, bar_bottom);
69
70 // Full width = total system RAM. Dark track = free memory.
71 draw_list->AddRectFilled(bar_pos, bar_max, memory_bar_track_color, bar_radius);
72
73 float x = bar_pos.x;
74
75 auto draw_segment = [&](float fraction, ImU32 color) -> void
76 {
77 if(fraction <= 0.0f)
78 {
79 return;
80 }
81
82 float segment_width = bar_width * fraction;
83 if(segment_width > 0.0f && segment_width < 1.0f)
84 {
85 segment_width = 1.0f;
86 }
87
88 draw_list->AddRectFilled(ImVec2(x, bar_pos.y),
89 ImVec2(x + segment_width, bar_bottom),
90 color);
91 x += segment_width;
92 };
93
94 // Stacked from the left: [other used][process][free]
95 draw_segment(other_used_fraction, other_system_ram_bar_color);
96 draw_segment(process_fraction, process_ram_bar_color);
97
98 ImGui::Dummy(ImVec2(bar_width, memory_bar_height));
99}
100
101void draw_memory_stat_row(const char* label, const std::string& value_text, const char* tooltip = nullptr)
102{
103 ImGui::PushStyleColor(ImGuiCol_Text, memory_label_color);
104 ImGui::TextUnformatted(label);
105 ImGui::PopStyleColor();
106 if(tooltip != nullptr)
107 {
108 ImGui::SetItemTooltipEx("%s", tooltip);
109 }
110
111 const float value_width = ImGui::CalcTextSize(value_text.c_str()).x;
112 ImGui::PushStyleColor(ImGuiCol_Text, memory_value_color);
113 ImGui::AlignedItem(1.0f, ImGui::GetContentRegionAvail().x, value_width, [&]() -> void {
114 ImGui::TextUnformatted(value_text.c_str());
115 });
116 ImGui::PopStyleColor();
117}
118
119void draw_loading_memory_stats()
120{
121 const auto* stats = gfx::get_stats();
122 if(stats != nullptr && stats->gpuMemoryUsed > 0)
123 {
124 std::string gpu_text;
125 float gpu_fraction = 0.0f;
126
127 if(stats->gpuMemoryMax > 0)
128 {
129 const float pct = (static_cast<float>(stats->gpuMemoryUsed) /
130 static_cast<float>(stats->gpuMemoryMax)) *
131 100.0f;
132 gpu_text = fmt::format("{} / {} ({:.0f}%)",
133 format_bytes(stats->gpuMemoryUsed),
134 format_bytes(stats->gpuMemoryMax),
135 static_cast<double>(pct));
136 gpu_fraction = pct / 100.0f;
137 }
138 else
139 {
140 gpu_text = format_bytes(stats->gpuMemoryUsed);
141 }
142
143 draw_memory_stat_row("GPU",
144 gpu_text,
145 "GPU video memory allocated by the renderer vs the device budget.");
146 if(stats->gpuMemoryMax > 0)
147 {
148 draw_memory_bar(gpu_fraction, gpu_bar_color);
149 }
150 }
151
152 const int64_t rss_bytes = platform::get_process_resident_set_bytes();
153 const int64_t system_total_bytes = platform::get_system_physical_memory_bytes();
154 const int64_t system_used_bytes = platform::get_system_used_physical_memory_bytes();
155 if(rss_bytes > 0 || system_used_bytes > 0)
156 {
157 if(stats != nullptr && stats->gpuMemoryUsed > 0)
158 {
159 ImGui::Spacing();
160 }
161
162 if(system_total_bytes > 0 && system_used_bytes > 0)
163 {
164 const int64_t other_used_bytes = std::max<int64_t>(0, system_used_bytes - rss_bytes);
165 const float system_pct =
166 (static_cast<float>(system_used_bytes) / static_cast<float>(system_total_bytes)) * 100.0f;
167 const float process_pct =
168 (static_cast<float>(rss_bytes) / static_cast<float>(system_total_bytes)) * 100.0f;
169
170 std::string ram_text;
171 if(rss_bytes > 0)
172 {
173 ram_text = fmt::format("{} ({} Process) / {}",
174 format_bytes(system_used_bytes),
175 format_bytes(rss_bytes),
176 format_bytes(system_total_bytes));
177 }
178 else
179 {
180 ram_text = fmt::format("{} / {}",
181 format_bytes(system_used_bytes),
182 format_bytes(system_total_bytes));
183 }
184
185 const std::string ram_tooltip = fmt::format(
186 "Bar = total system RAM.\n"
187 "Amber: other system usage. Cyan (at the used edge): this process.\n"
188 "Dark: free memory.\n"
189 "Used: {:.0f}% of system ({} other + {} process).",
190 static_cast<double>(system_pct),
191 format_bytes(other_used_bytes),
192 format_bytes(rss_bytes));
193
194 draw_memory_stat_row("RAM", ram_text, ram_tooltip.c_str());
195 draw_stacked_ram_bar(process_pct / 100.0f, system_pct / 100.0f);
196 }
197 else if(rss_bytes > 0)
198 {
199 draw_memory_stat_row("RAM", format_bytes(rss_bytes), "Process resident memory (RSS).");
200 }
201 }
202}
203
204auto has_loading_memory_stats() -> bool
205{
206 const auto* stats = gfx::get_stats();
207 if(stats != nullptr && stats->gpuMemoryUsed > 0)
208 {
209 return true;
210 }
212 {
213 return true;
214 }
216}
217
218} // namespace
219
221{
222 auto& ev = ctx.get_cached<events>();
223
224 ev.on_os_event.connect(sentinel_, 1000, this, &imgui_interface::on_os_event);
225 ev.on_frame_render.connect(sentinel_, -100000, this, &imgui_interface::on_frame_ui_render);
226}
227
229{
230 if(inited_)
231 {
232 imguiDestroy();
233 }
234}
235
237{
238 APPLOG_TRACE("{}::{}", hpp::type_name_str(*this), __func__);
239
240 const auto& rend = ctx.get_cached<renderer>();
241 const auto& main_window = rend.get_main_window();
242 imguiCreate(main_window, 14.0f);
243
245
246 inited_ = true;
247 return true;
248}
249
251{
252 APPLOG_TRACE("{}::{}", hpp::type_name_str(*this), __func__);
253
255 return true;
256}
257
259{
260 APPLOG_TRACE("{}::{}", hpp::type_name_str(*this), __func__);
261
262 return true;
263}
264
266 const std::string& stage,
267 size_t completed,
268 size_t total,
269 const std::string& current_job)
270{
271
272
273 auto now = std::chrono::steady_clock::now();
274 auto dt = now - last_frame_time_;
275
276 if(dt < std::chrono::milliseconds(32) && completed != total)
277 {
278 return;
279 }
280
281 last_frame_time_ = now;
282
283 const auto& rend = ctx.get_cached<renderer>();
284 auto window = rend.get_main_window();
285 if(!window)
286 {
287 return;
288 }
289
290 os::event e{};
291 while(os::poll_event(e))
292 {
294 }
295
296 auto& present_pass = window->begin_present_pass();
297 present_pass.clear();
298
299 for(int i = 0; i < 1; ++i)
300 {
301 imguiBeginFrame(1.0f / 60.0f);
302 draw_loading_overlay(stage, completed, total, current_job);
303
304 auto& main_surface = window->get_surface();
305 gfx::render_pass pass("ImGui/Loading Pass");
306 pass.bind(main_surface.get());
307 imguiEndFrame(pass.id);
308
309 gfx::render_pass end_pass(gfx::render_pass::get_max_pass_id(), "Backbuffer/Loading Present");
310 end_pass.bind();
311 gfx::frame();
312 }
313
314}
315
316void imgui_interface::draw_loading_overlay(const std::string& stage,
317 size_t completed,
318 size_t total,
319 const std::string& current_job)
320{
321 const auto* viewport = ImGui::GetMainViewport();
322
323 // Full-screen dimmed backdrop
324 ImGui::SetNextWindowPos(viewport->WorkPos);
325 ImGui::SetNextWindowSize(viewport->WorkSize);
326 ImGui::SetNextWindowViewport(viewport->ID);
327 ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
328 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
329 ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.08f, 0.08f, 0.08f, 1.0f));
330
331 ImGuiWindowFlags backdrop_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse |
332 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
333 ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus |
334 ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoSavedSettings;
335
336 ImGui::Begin("##loading_backdrop", nullptr, backdrop_flags);
337 ImGui::PopStyleVar(2);
338 ImGui::PopStyleColor();
339
340 // Centered card
341 constexpr float card_width = 480.0f;
342 float card_x = viewport->WorkPos.x + (viewport->WorkSize.x - card_width) * 0.5f;
343 float card_y = viewport->WorkPos.y + viewport->WorkSize.y * 0.38f;
344 ImGui::SetNextWindowPos(ImVec2(card_x, card_y));
345
346 ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f);
347 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(32.0f, 28.0f));
348 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.16f, 0.16f, 0.16f, 1.0f));
349 ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.22f, 0.22f, 0.22f, 1.0f));
350
351 if(ImGui::BeginChild("##loading_card", ImVec2(card_width, 0),
352 ImGuiChildFlags_Borders | ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_AlwaysUseWindowPadding))
353 {
354 // Title
356 auto title_text = "Unravel Engine";
357 float title_width = ImGui::CalcTextSize(title_text).x;
358
359 ImGui::AlignedItem(0.5f, ImGui::GetContentRegionAvail().x, title_width, [&]()
360 {
361 ImGui::TextUnformatted(title_text);
362 });
363
364 ImGui::PopFont();
365
366 ImGui::Spacing();
367 ImGui::Spacing();
368
369 // Separator
370 ImGui::PushStyleColor(ImGuiCol_Separator, ImVec4(0.25f, 0.25f, 0.25f, 1.0f));
371 ImGui::Separator();
372 ImGui::PopStyleColor();
373
374 ImGui::Spacing();
375 ImGui::Spacing();
376 ImGui::Spacing();
377
378 // Stage name
380 ImGui::TextUnformatted(stage.c_str());
381
382 auto spinner_size = ImGui::GetTextLineHeight();
383
384 ImGui::SameLine();
385 ImGui::AlignedItem(1.0f, ImGui::GetContentRegionAvail().x, spinner_size, [&]() {
386 ImSpinner::Spinner<ImSpinner::SpinnerTypeT::e_st_eclipse>("spinner",
387 ImSpinner::Radius{spinner_size * 0.5f},
388 ImSpinner::Thickness{4.0f},
389 ImSpinner::Color{ImSpinner::white},
390 ImSpinner::Speed{6.0f});
391 });
392 ImGui::PopFont();
393
394 ImGui::Spacing();
395 ImGui::Spacing();
396
397 // Custom progress bar
398 float fraction = (total > 0) ? static_cast<float>(completed) / static_cast<float>(total) : 0.0f;
399 constexpr float bar_height = 6.0f;
400 float bar_width = ImGui::GetContentRegionAvail().x;
401 ImVec2 bar_pos = ImGui::GetCursorScreenPos();
402
403 ImDrawList* draw_list = ImGui::GetWindowDrawList();
404
405 // Background track
406 draw_list->AddRectFilled(bar_pos,
407 ImVec2(bar_pos.x + bar_width, bar_pos.y + bar_height),
408 IM_COL32(30, 30, 30, 255),
409 bar_height * 0.5f);
410
411 // Filled portion with accent color
412 if(fraction > 0.0f)
413 {
414 float fill_width = bar_width * fraction;
415 draw_list->AddRectFilled(bar_pos,
416 ImVec2(bar_pos.x + fill_width, bar_pos.y + bar_height),
417 IM_COL32(58, 121, 187, 255),
418 bar_height * 0.5f);
419 }
420
421 ImGui::Dummy(ImVec2(bar_width, bar_height));
422
423 ImGui::Spacing();
424
425
426 // Current job name
427 if(!current_job.empty())
428 {
429 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.45f, 0.45f, 0.45f, 1.0f));
430 ImGui::TextWrapped("%s", current_job.c_str());
431 ImGui::PopStyleColor();
432 }
433
434 if(total > 0)
435 {
436 ImGui::SameLine();
437 // Progress count -- right-aligned
438 auto progress_text = fmt::format("{} / {}", completed, total);
439 float count_width = ImGui::CalcTextSize(progress_text.c_str()).x;
440 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.55f, 0.55f, 0.55f, 1.0f));
441 ImGui::AlignedItem(1.0f, ImGui::GetContentRegionAvail().x, count_width, [&]() {
442 ImGui::TextUnformatted(progress_text.c_str());
443 });
444 ImGui::PopStyleColor();
445 }
446
447 ImGui::Spacing();
448 ImGui::Spacing();
449 ImGui::Spacing();
450
451 if(has_loading_memory_stats())
452 {
453 ImGui::PushStyleColor(ImGuiCol_Separator, ImVec4(0.22f, 0.22f, 0.22f, 1.0f));
454 ImGui::Separator();
455 ImGui::PopStyleColor();
456
457 ImGui::Spacing();
458
460 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, memory_section_alpha);
461 draw_loading_memory_stats();
462 ImGui::PopStyleVar();
463 ImGui::PopFont();
464 }
465
466 }
467 ImGui::EndChild();
468 ImGui::PopStyleColor(2);
469 ImGui::PopStyleVar(2);
470
471 ImGui::End();
472}
473
474void imgui_interface::on_os_event(rtti::context& ctx, os::event& e)
475{
477}
478
479void imgui_interface::on_frame_ui_render(rtti::context& ctx, delta_t dt)
480{
481 const auto& ev = ctx.get_cached<ui_events>();
482
483 const auto& rend = ctx.get_cached<renderer>();
484 const auto& main_window = rend.get_main_window();
485 const auto& main_surface = main_window->get_surface();
486
487 APP_SCOPE_PERF("ImGui Frame");
488 imguiBeginFrame(dt.count());
489
490 ev.on_frame_ui_render(ctx, dt);
491
492 gfx::render_pass pass("ImGui/Pass");
493 pass.bind(main_surface.get());
494 imguiEndFrame(pass.id);
495}
496
497} // namespace unravel
btScalar fraction
void render_loading_frame(rtti::context &ctx, const std::string &stage, size_t completed, size_t total, const std::string &current_job={})
imgui_interface(rtti::context &ctx)
auto deinit(rtti::context &ctx) -> bool
auto init_finalize(rtti::context &ctx) -> bool
Phase 2: creates the cubemap shader program from compiled editor:/ assets.
auto init_basic(rtti::context &ctx) -> bool
Phase 1: creates ImGui context, embedded shaders, fonts. No asset dependencies.
float x
std::chrono::duration< float > delta_t
void imguiCreate(unravel::render_window *window, float _fontSize, bx::AllocatorI *_allocator)
Definition imgui.cpp:612
void imguiProcessEvent(os::event &e)
Definition imgui.cpp:627
void imguiBeginFrame(float dt)
Definition imgui.cpp:632
void imguiEndFrame(gfx::view_id id)
Definition imgui.cpp:638
void imguiCreateCubemapProgram()
Definition imgui.cpp:617
void imguiDestroy()
Definition imgui.cpp:622
const char * tooltip
#define APPLOG_TRACE(...)
Definition logging.h:17
void PushFont(Font::Enum _font)
Definition imgui.cpp:646
bgfx::Stats stats
Definition graphics.h:31
uint32_t frame(uint8_t _flags)
Definition graphics.cpp:432
const stats * get_stats()
Definition graphics.cpp:450
void set_unity_theme()
auto get_system_used_physical_memory_bytes() -> int64_t
auto get_process_resident_set_bytes() -> int64_t
auto get_system_physical_memory_bytes() -> int64_t
auto format_bytes(std::uint64_t bytes, std::uint8_t num_frac) -> std::string
std::vector< math::color > color
#define APP_SCOPE_PERF(name_literal)
Create a scoped performance timer that records to the timeline profiler. Only accepts string literals...
Definition profiler.h:675
@ Regular
Definition imgui.h:51
static auto get_max_pass_id() -> gfx::view_id
gfx::view_id id
void bind(const frame_buffer *fb=nullptr) const
auto get_cached() -> T &
Definition context.hpp:49
hpp::event< void(rtti::context &, os::event &e)> on_os_event
os events
Definition events.h:35
auto get_main_window() const -> render_window *
Definition renderer.cpp:316