载入中...
搜索中...
未找到
ImGuiBackend.cpp
浏览该文件的文档.
2#include "ui/Theme.h"
3
4#include "common/Exception.h"
6#include "common/config.h"
7#include "graphics/Graphics.h"
8
9#include <imgui.h>
10#include <imgui_impl_sdl.h>
11
12#ifdef EVENGINE_WEBGPU
14#include "imgui_impl_wgpu.h"
15#else
17#include <imgui_impl_vulkan.h>
18#include <vulkan/vulkan.h>
19#include <SDL2/SDL_vulkan.h>
20#endif
21
22#include <algorithm>
23#include <cmath>
24#include <cstdio>
25#include <cstdlib>
26#include <string>
27#include <vector>
28
29
30namespace eve::ui {
31namespace {
32
33#ifndef EVENGINE_WEBGPU
34void checkVk(VkResult err) {
35 if (err == 0) return;
36 throw eve::Exception("ImGui Vulkan error: VkResult = %d", int(err));
37}
38#endif
39
41constexpr float kBaseFontSizePx = 16.f;
42
43std::vector<const char *> regularFontCandidates() {
44#if defined(_WIN32)
45 static const std::string winDir = [] {
46 const char *dir = getenv("WINDIR");
47 return dir ? std::string(dir) : std::string("C:\\Windows");
48 }();
49 static const std::vector<std::string> paths = {
50 winDir + "\\Fonts\\segoeui.ttf", winDir + "\\Fonts\\arial.ttf",
51 winDir + "\\Fonts\\tahoma.ttf", winDir + "\\Fonts\\msyh.ttc",
52 };
53#elif defined(__APPLE__)
54 static const std::vector<std::string> paths = {
55 "/System/Library/Fonts/Helvetica.ttc",
56 "/System/Library/Fonts/SFNS.ttf",
57 "/Library/Fonts/Arial.ttf",
58 };
59#elif defined(EVENGINE_ANDROID)
60 static const std::vector<std::string> paths = {
61 "/system/fonts/NotoSansCJK-Regular.ttc",
62 "/system/fonts/DroidSansFallback.ttf",
63 "/system/fonts/Roboto-Regular.ttf",
64 };
65#else
66 static const std::vector<std::string> paths = {
67 "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
68 "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
69 "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
70 "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc",
71 "/usr/share/fonts/truetype/arphic/uming.ttc",
72 };
73#endif
74 std::vector<const char *> out;
75 out.reserve(paths.size());
76 for (const auto &p : paths) out.push_back(p.c_str());
77 return out;
78}
79
80std::vector<const char *> iconFontCandidates() {
81 static const std::vector<std::string> paths = {
82 "fonts/FontAwesome.ttf",
83 "test/fonts/FontAwesome.ttf",
84 };
85 std::vector<const char *> out;
86 out.reserve(paths.size());
87 for (const auto &p : paths) out.push_back(p.c_str());
88 return out;
89}
90
91bool fileExists(const char *path) {
92 if (!path || !*path) return false;
93 FILE *f = fopen(path, "rb");
94 if (!f) return false;
95 fclose(f);
96 return true;
97}
98
99} // namespace
100
101std::unique_ptr<UIBackend> createImGuiBackend() {
102 return std::make_unique<ImGuiBackend>();
103}
104
106
107bool ImGuiBackend::init(SDL_Window *window, eve::graphics::Graphics *gfx) {
108 if (initialized_) return true;
109 if (!window || !gfx) return false;
110 StartupStage initStage("ui: ImGui backend init (first frame)");
111
112 gfx_ = gfx;
113 window_ = window;
114 dpiScale_ = computeDpiScale();
115 uiScale_ = computeInitialScale();
116
117 IMGUI_CHECKVERSION();
118 ctx_ = ::ImGui::CreateContext();
119 // Start from unified theme tokens (not a one-off ImGui palette).
120 setThemeDpiScale(dpiScale_);
121 setThemeUiScale(uiScale_);
122 applyThemeToImGui(globalTheme(), uiScale_);
123
124 ImGui_ImplSDL2_InitForVulkan(window);
125
126#ifdef EVENGINE_WEBGPU
127 auto *wgg = dynamic_cast<eve::graphics::webgpu::Graphics *>(gfx);
128 if (!wgg) return false;
129 ImGui_ImplWGPU_Init(wgg->getDevice().Get(), 2, wgg->getSurfaceFormat());
130 loadFonts();
132 fontsUploaded_ = true;
133#else
134 auto *vkg = dynamic_cast<eve::graphics::vulkan::Graphics *>(gfx);
135 if (!vkg) return false;
136 if (!vkg->getSwapchainRenderPass()) return false;
137
138 auto &device = vkg->getDevice();
139 VkDescriptorPoolSize poolSizes[] = {
140 {VK_DESCRIPTOR_TYPE_SAMPLER, 1000},
141 {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000},
142 {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000},
143 {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000},
144 {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000},
145 {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000},
146 {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000},
147 {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000},
148 {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000},
149 {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000},
150 {VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000},
151 };
152 VkDescriptorPoolCreateInfo poolInfo{};
153 poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
154 poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
155 poolInfo.maxSets = 1000 * uint32_t(sizeof(poolSizes) / sizeof(poolSizes[0]));
156 poolInfo.poolSizeCount = uint32_t(sizeof(poolSizes) / sizeof(poolSizes[0]));
157 poolInfo.pPoolSizes = poolSizes;
158
159 VkDescriptorPool pool = VK_NULL_HANDLE;
160 checkVk(vkCreateDescriptorPool(static_cast<VkDevice>(device.instance), &poolInfo, nullptr, &pool));
161 imguiDescriptorPool_ = pool;
162
163 uint32_t imageCount = vkg->getSwapchainImageCount();
164 if (imageCount < 2) imageCount = 2;
165
166 vkg->ensureUiColorResources();
167
168 ImGui_ImplVulkan_InitInfo initInfo{};
169 initInfo.Instance = static_cast<VkInstance>(vkg->getInstance().instance);
170 initInfo.PhysicalDevice = static_cast<VkPhysicalDevice>(device.physical_device.instance);
171 initInfo.Device = static_cast<VkDevice>(device.instance);
172 initInfo.QueueFamily = device.get_queue_index(vkb::QueueType::graphics);
173 initInfo.Queue = static_cast<VkQueue>(device.getQueue(vkb::QueueType::graphics));
174 initInfo.PipelineCache = VK_NULL_HANDLE;
175 initInfo.DescriptorPool = pool;
176 initInfo.MinImageCount = imageCount;
177 initInfo.ImageCount = imageCount;
178 initInfo.MSAASamples = static_cast<VkSampleCountFlagBits>(vkg->getUiMsaaSamples());
179 initInfo.CheckVkResultFn = [](VkResult err) {
180 if (err != 0) fprintf(stderr, "ImGui Vulkan: VkResult %d\n", int(err));
181 };
182
183 ImGui_ImplVulkan_Init(&initInfo, static_cast<VkRenderPass>(vkg->getUiMsaaRenderPass()));
184
185 {
186 StartupStage fontStage(" ui: font load + atlas upload");
187 loadFonts();
188
189 vkb::executeImmediately(device.instance, vkg->getUploadPool(),
190 device.getQueue(vkb::QueueType::graphics), [&](vk::CommandBuffer cb) {
191 ImGui_ImplVulkan_CreateFontsTexture(static_cast<VkCommandBuffer>(cb));
192 });
193 ImGui_ImplVulkan_DestroyFontUploadObjects();
194 fontsUploaded_ = true;
195 }
196#endif
197
198 gfx_->setPresentOverlay(&ImGuiBackend::presentOverlayThunk, this);
199 // The ImGui context + Vulkan pipeline are bound to the native window. When
200 // the window is destroyed, tear down so the next init() rebuilds against a
201 // fresh window — even if SDL hands back the same pointer.
202 gfx_->addWindowDestroyedCallback(&ImGuiBackend::windowDestroyedThunk, this);
203
204 initialized_ = true;
205 return true;
206}
207
208void ImGuiBackend::windowDestroyedThunk(void *userdata) {
209 auto *self = static_cast<ImGuiBackend *>(userdata);
210 if (self) self->shutdown();
211}
212
214 if (!initialized_) return;
215 if (frameOpen_) {
216 ImGui::EndFrame();
217 frameOpen_ = false;
218 }
219 if (gfx_) {
220 if (gfx_->getPresentOverlayUser() == this) gfx_->setPresentOverlay(nullptr, nullptr);
221 }
222#ifdef EVENGINE_WEBGPU
224 ImGui_ImplSDL2_Shutdown();
225 ImGui::DestroyContext();
226#else
227 auto *vkg = dynamic_cast<eve::graphics::vulkan::Graphics *>(gfx_);
228 if (vkg) {
229 vkDeviceWaitIdle(static_cast<VkDevice>(vkg->getDevice().instance));
230 }
231 // The ImGui backend data lives on the context this backend created. It may
232 // not be the currently active context (headless tests switch contexts), so
233 // explicitly select it before tearing down ImGui_Impl* state.
234 if (ctx_) {
235 ::ImGuiContext *prev = ::ImGui::GetCurrentContext();
236 ::ImGuiContext *mine = ctx_;
237 ::ImGui::SetCurrentContext(mine);
238 ::ImGui_ImplVulkan_Shutdown();
239 ::ImGui_ImplSDL2_Shutdown();
240 ::ImGui::DestroyContext(mine);
241 ctx_ = nullptr;
242 // Restore the previous current context unless it was the one we just
243 // destroyed (DestroyContext already reset it to null).
244 if (prev && prev != mine) ::ImGui::SetCurrentContext(prev);
245 else ::ImGui::SetCurrentContext(nullptr);
246 }
247 if (imguiDescriptorPool_ && vkg) {
248 vkDestroyDescriptorPool(static_cast<VkDevice>(vkg->getDevice().instance),
249 static_cast<VkDescriptorPool>(imguiDescriptorPool_), nullptr);
250 imguiDescriptorPool_ = nullptr;
251 }
252 if (imguiTexturePool_ && vkg) {
253 vkDestroyDescriptorPool(static_cast<VkDevice>(vkg->getDevice().instance),
254 static_cast<VkDescriptorPool>(imguiTexturePool_), nullptr);
255 imguiTexturePool_ = nullptr;
256 }
257 if (imguiTextureLayout_ && vkg) {
258 vkDestroyDescriptorSetLayout(static_cast<VkDevice>(vkg->getDevice().instance),
259 static_cast<VkDescriptorSetLayout>(imguiTextureLayout_),
260 nullptr);
261 imguiTextureLayout_ = nullptr;
262 }
263#endif
264 gfx_ = nullptr;
265 window_ = nullptr;
266 fontsUploaded_ = false;
267 initialized_ = false;
268}
269
270void ImGuiBackend::processEvent(const SDL_Event *event) {
271 if (!initialized_ || !event) return;
272 ImGui_ImplSDL2_ProcessEvent(event);
273}
274
276 if (!initialized_ || !window_) return;
277 if (frameOpen_) {
278 ImGui::EndFrame();
279 frameOpen_ = false;
280 }
281#ifdef EVENGINE_WEBGPU
283#else
284 ImGui_ImplVulkan_NewFrame();
285#endif
286 ImGui_ImplSDL2_NewFrame(window_);
287 ImGui::NewFrame();
288 frameOpen_ = true;
289}
290
291void ImGuiBackend::applyScale(float scale) {
292 if (!initialized_) return;
293 scale = std::clamp(scale, 0.5f, 5.f);
294 const bool changed = scale != uiScale_;
295 uiScale_ = scale;
296 setThemeDpiScale(dpiScale_);
299 if (changed) rebuildFonts();
300}
301
303 if (!initialized_) return;
304 applyScale(scale);
305}
306
307float ImGuiBackend::computeInitialScale() const {
308#if defined(EVENGINE_ANDROID) || defined(EVENGINE_IOS)
309 float ddpi = 160.f;
310 if (SDL_GetDisplayDPI(0, &ddpi, nullptr, nullptr) != 0 || ddpi < 1.f) ddpi = 320.f;
311 float s = ddpi / 160.f;
312 return std::clamp(s, 1.75f, 3.25f);
313#else
314 // Desktop: ImGui's backend already applies the display density via
315 // io.DisplayFramebufferScale, so the logical (point-space) UI scale stays
316 // 1.0. Keeping it constant makes the UI match the OS UI size on
317 // high-resolution displays instead of growing with the pixel ratio.
318 (void)window_;
319 return 1.f;
320#endif
321}
322
323float ImGuiBackend::computeDpiScale() const {
324 int logicalW = 0, logicalH = 0, pixelW = 0, pixelH = 0;
325 SDL_GetWindowSize(window_, &logicalW, &logicalH);
326#ifdef EVENGINE_WEBGPU
327 // No GL context on WebGPU; drawable size == window size for the canvas.
328 SDL_GetWindowSize(window_, &pixelW, &pixelH);
329#else
330 SDL_Vulkan_GetDrawableSize(window_, &pixelW, &pixelH);
331#endif
332 if (logicalW > 0 && pixelW > 0) {
333 float s = float(pixelW) / float(logicalW);
334 if (s > 0.f) return std::clamp(s, 1.f, 4.f);
335 }
336 return 1.f;
337}
338
339void ImGuiBackend::loadFonts() {
340 ImGuiIO &io = ImGui::GetIO();
341 ImFontAtlas *atlas = io.Fonts;
342 if (!atlas) return;
343
344 // Rasterize at the physical DPI resolution so glyphs stay crisp; the
345 // FontGlobalScale set in applyThemeToImGui cancels this so the logical
346 // text size stays constant regardless of display density.
347 const float sizePx = kBaseFontSizePx * dpiScale_;
348
349 ImFontConfig cfg{};
350 cfg.OversampleH = 2;
351 cfg.OversampleV = 2;
352 // Include CJK glyphs so Chinese/Japanese text works on any platform whose
353 // system font provides them (atlas grows by a few MB — acceptable).
354 fontRanges_.clear();
355 ImFontGlyphRangesBuilder rangeBuilder;
356 rangeBuilder.AddRanges(atlas->GetGlyphRangesDefault());
357 rangeBuilder.AddRanges(atlas->GetGlyphRangesChineseFull());
358 rangeBuilder.BuildRanges(&fontRanges_);
359 cfg.GlyphRanges = fontRanges_.Data;
360
361 bool added = false;
362 for (const char *path : regularFontCandidates()) {
363 if (!fileExists(path)) continue;
364 if (atlas->AddFontFromFileTTF(path, sizePx, &cfg)) {
365 added = true;
366 break;
367 }
368 }
369 if (!added) atlas->AddFontDefault(&cfg);
370
371 ImFontConfig iconCfg{};
372 iconCfg.OversampleH = 2;
373 iconCfg.OversampleV = 2;
374 iconCfg.MergeMode = true;
375 iconCfg.PixelSnapH = true;
376 static const ImWchar iconRanges[] = {0xF000, 0xF8FF, 0};
377 for (const char *path : iconFontCandidates()) {
378 if (!fileExists(path)) continue;
379 if (atlas->AddFontFromFileTTF(path, sizePx, &iconCfg, iconRanges)) break;
380 }
381}
382
383void ImGuiBackend::rebuildFonts() {
384#ifdef EVENGINE_WEBGPU
385 loadFonts();
388 fontsUploaded_ = true;
389#else
390 auto *vkg = dynamic_cast<eve::graphics::vulkan::Graphics *>(gfx_);
391 if (!vkg) return;
392 loadFonts();
393 ImGui_ImplVulkan_DestroyFontUploadObjects();
394 auto &device = vkg->getDevice();
395 vkb::executeImmediately(device.instance, vkg->getUploadPool(),
396 device.getQueue(vkb::QueueType::graphics),
397 [&](vk::CommandBuffer cb) {
398 ImGui_ImplVulkan_CreateFontsTexture(
399 static_cast<VkCommandBuffer>(cb));
400 });
401 ImGui_ImplVulkan_DestroyFontUploadObjects();
402 fontsUploaded_ = true;
403#endif
404}
405
406uint64_t ImGuiBackend::registerTexture(graphics::Texture *tex) {
407 if (!initialized_ || !tex || !tex->gpuHandle) return 0;
408 RegisteredTexture reg;
409#ifdef EVENGINE_WEBGPU
410 auto *gt = static_cast<eve::graphics::webgpu::GpuTexture *>(tex->gpuHandle);
411 reg.imId = (ImTextureID)(intptr_t)gt->view.Get();
412 if (!reg.imId) return 0;
413#else
414 auto *vkg = dynamic_cast<eve::graphics::vulkan::Graphics *>(gfx_);
415 if (!vkg) return 0;
416 auto *gt = static_cast<eve::graphics::vulkan::GpuTexture *>(tex->gpuHandle);
417 const VkDevice device = static_cast<VkDevice>(vkg->getDevice().instance);
418 if (!imguiTextureLayout_) {
419 VkDescriptorSetLayoutBinding binding{};
420 binding.binding = 0;
421 binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
422 binding.descriptorCount = 1;
423 binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
424 VkDescriptorSetLayoutCreateInfo info{};
425 info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
426 info.bindingCount = 1;
427 info.pBindings = &binding;
428 VkDescriptorSetLayout layout = VK_NULL_HANDLE;
429 checkVk(vkCreateDescriptorSetLayout(device, &info, nullptr, &layout));
430 imguiTextureLayout_ = layout;
431 }
432 if (!imguiTexturePool_) {
433 VkDescriptorPoolSize size{};
434 size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
435 size.descriptorCount = 512;
436 VkDescriptorPoolCreateInfo info{};
437 info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
438 info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
439 info.maxSets = 512;
440 info.poolSizeCount = 1;
441 info.pPoolSizes = &size;
442 VkDescriptorPool pool = VK_NULL_HANDLE;
443 checkVk(vkCreateDescriptorPool(device, &info, nullptr, &pool));
444 imguiTexturePool_ = pool;
445 }
446 VkDescriptorSet set = VK_NULL_HANDLE;
447 VkDescriptorSetAllocateInfo ai{};
448 ai.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
449 ai.descriptorPool = static_cast<VkDescriptorPool>(imguiTexturePool_);
450 ai.descriptorSetCount = 1;
451 const VkDescriptorSetLayout layout = static_cast<VkDescriptorSetLayout>(imguiTextureLayout_);
452 ai.pSetLayouts = &layout;
453 checkVk(vkAllocateDescriptorSets(device, &ai, &set));
454 VkDescriptorImageInfo imageInfo{};
455 imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
456 imageInfo.imageView = static_cast<VkImageView>(gt->imageView());
457 imageInfo.sampler = static_cast<VkSampler>(gt->sampler);
458 VkWriteDescriptorSet write{};
459 write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
460 write.dstSet = set;
461 write.dstBinding = 0;
462 write.descriptorCount = 1;
463 write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
464 write.pImageInfo = &imageInfo;
465 vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
466 reg.imId = set;
467#endif
468 reg.width = tex->getWidth();
469 reg.height = tex->getHeight();
470 const uint64_t key = nextTextureKey_++;
471 textures_[key] = reg;
472 return key;
473}
474
475void ImGuiBackend::unregisterTexture(uint64_t id) {
476 auto it = textures_.find(id);
477 if (it == textures_.end()) return;
478#ifndef EVENGINE_WEBGPU
479 auto *vkg = dynamic_cast<eve::graphics::vulkan::Graphics *>(gfx_);
480 if (vkg && imguiTexturePool_) {
481 VkDescriptorSet set = static_cast<VkDescriptorSet>(it->second.imId);
482 vkFreeDescriptorSets(static_cast<VkDevice>(vkg->getDevice().instance),
483 static_cast<VkDescriptorPool>(imguiTexturePool_), 1, &set);
484 }
485#endif
486 textures_.erase(it);
487}
488
489bool ImGuiBackend::textureSize(uint64_t id, int *w, int *h) const {
490 auto it = textures_.find(id);
491 if (it == textures_.end()) return false;
492 if (w) *w = it->second.width;
493 if (h) *h = it->second.height;
494 return true;
495}
496
497void *ImGuiBackend::textureHandle(uint64_t id) const {
498 auto it = textures_.find(id);
499 return it == textures_.end() ? nullptr : static_cast<void *>(it->second.imId);
500}
501
503 if (!initialized_) return false;
504 return ImGui::GetIO().WantCaptureMouse;
505}
506
508 if (!initialized_) return false;
509 return ImGui::GetIO().WantCaptureKeyboard;
510}
511
512void ImGuiBackend::renderDrawData(void *commandBuffer) {
513 if (!initialized_ || !commandBuffer) return;
514 ImGui::Render();
515 frameOpen_ = false;
516#ifdef EVENGINE_WEBGPU
517 WGPURenderPassEncoder enc = *static_cast<WGPURenderPassEncoder *>(commandBuffer);
518 ImGui_ImplWGPU_RenderDrawData(ImGui::GetDrawData(), enc);
519#else
520 ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(),
521 static_cast<VkCommandBuffer>(commandBuffer));
522#endif
523}
524
525void ImGuiBackend::presentOverlayThunk(void *userdata, void *commandBuffer) {
526 auto *self = static_cast<ImGuiBackend *>(userdata);
527 if (!self) return;
528 self->renderDrawData(commandBuffer);
529}
530
531} // namespace eve::ui
std::string layout
vkb::Device & device
int h
int w
float f
glm::vec4 p[6]
float scale
Definition TreeMesh.cpp:122
V3 dir
Definition TreeMesh.cpp:121
uint32_t s
Definition Weather.cpp:28
void addWindowDestroyedCallback(WindowDestroyedCallback cb, void *userdata)
Definition Graphics.h:664
void * getPresentOverlayUser() const
Definition Graphics.h:678
void setPresentOverlay(PresentOverlayFn fn, void *userdata)
Definition Graphics.h:673
Dear ImGui + SDL input + Vulkan present overlay.
void shutdown() override
bool wantCaptureKeyboard() const override
void newFrame() override
bool init(SDL_Window *window, eve::graphics::Graphics *gfx) override
bool wantCaptureMouse() const override
void setScale(float scale) override
Scale fonts + ImGui style metrics (1 = default desktop).
void processEvent(const SDL_Event *event) override
IMGUI_IMPL_API void ImGui_ImplWGPU_NewFrame()
IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateFontsTexture()
IMGUI_IMPL_API void ImGui_ImplWGPU_RenderDrawData(ImDrawData *draw_data, WGPURenderPassEncoder pass_encoder)
IMGUI_IMPL_API bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format)
IMGUI_IMPL_API void ImGui_ImplWGPU_Shutdown()
IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects()
void applyThemeToImGui(const Theme &theme)
Push tokens into ImGui style. Metrics are multiplied by uiScale (default: themeUiScale()).
Definition Theme.cpp:135
void setThemeDpiScale(float dpiScale)
Definition Theme.cpp:129
std::unique_ptr< UIBackend > createImGuiBackend()
Default backend: Dear ImGui + SDL + Vulkan (see ui/imgui/).
void setThemeUiScale(float scale)
Logical (point-space) UI scale. Default 1.0.
Definition Theme.cpp:123
WidgetDesc window(std::string title, std::vector< WidgetDesc > children, std::string id)
Top-level window widget with a title bar.
Definition Widget.cpp:225
Theme & globalTheme()
Definition Theme.cpp:99
Texture resources backed by a wgpu texture + view + sampler + bind groups.
Definition Graphics.h:51