1 | // dear imgui: Renderer for WebGPU |
2 | // This needs to be used along with a Platform Binding (e.g. GLFW) |
3 | // (Please note that WebGPU is currently experimental, will not run on non-beta browsers, and may break.) |
4 | |
5 | // Implemented features: |
6 | // [X] Renderer: User texture binding. Use 'WGPUTextureView' as ImTextureID. Read the FAQ about ImTextureID! |
7 | // [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). |
8 | // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. |
9 | // Missing features: |
10 | // [ ] Renderer: Multi-viewport support (multiple windows). Not meaningful on the web. |
11 | |
12 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. |
13 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. |
14 | // Learn about Dear ImGui: |
15 | // - FAQ https://dearimgui.com/faq |
16 | // - Getting Started https://dearimgui.com/getting-started |
17 | // - Documentation https://dearimgui.com/docs (same as your local docs/ folder). |
18 | // - Introduction, links and more at the top of imgui.cpp |
19 | |
20 | // CHANGELOG |
21 | // (minor and older changes stripped away, please see git history for details) |
22 | // 2024-10-14: Update Dawn support for change of string usages. (#8082, #8083) |
23 | // 2024-10-07: Expose selected render state in ImGui_ImplWGPU_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks. |
24 | // 2024-10-07: Changed default texture sampler to Clamp instead of Repeat/Wrap. |
25 | // 2024-09-16: Added support for optional IMGUI_IMPL_WEBGPU_BACKEND_DAWN / IMGUI_IMPL_WEBGPU_BACKEND_WGPU define to handle ever-changing native implementations. (#7977) |
26 | // 2024-01-22: Added configurable PipelineMultisampleState struct. (#7240) |
27 | // 2024-01-22: (Breaking) ImGui_ImplWGPU_Init() now takes a ImGui_ImplWGPU_InitInfo structure instead of variety of parameters, allowing for easier further changes. |
28 | // 2024-01-22: Fixed pipeline layout leak. (#7245) |
29 | // 2024-01-17: Explicitly fill all of WGPUDepthStencilState since standard removed defaults. |
30 | // 2023-07-13: Use WGPUShaderModuleWGSLDescriptor's code instead of source. use WGPUMipmapFilterMode_Linear instead of WGPUFilterMode_Linear. (#6602) |
31 | // 2023-04-11: Align buffer sizes. Use WGSL shaders instead of precompiled SPIR-V. |
32 | // 2023-04-11: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). |
33 | // 2023-01-25: Revert automatic pipeline layout generation (see https://github.com/gpuweb/gpuweb/issues/2470) |
34 | // 2022-11-24: Fixed validation error with default depth buffer settings. |
35 | // 2022-11-10: Fixed rendering when a depth buffer is enabled. Added 'WGPUTextureFormat depth_format' parameter to ImGui_ImplWGPU_Init(). |
36 | // 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. |
37 | // 2021-11-29: Passing explicit buffer sizes to wgpuRenderPassEncoderSetVertexBuffer()/wgpuRenderPassEncoderSetIndexBuffer(). |
38 | // 2021-08-24: Fixed for latest specs. |
39 | // 2021-05-24: Add support for draw_data->FramebufferScale. |
40 | // 2021-05-19: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) |
41 | // 2021-05-16: Update to latest WebGPU specs (compatible with Emscripten 2.0.20 and Chrome Canary 92). |
42 | // 2021-02-18: Change blending equation to preserve alpha in output buffer. |
43 | // 2021-01-28: Initial version. |
44 | |
45 | // When targeting native platforms (i.e. NOT emscripten), one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN |
46 | // or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be provided. See imgui_impl_wgpu.h for more details. |
47 | #ifndef __EMSCRIPTEN__ |
48 | #if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) == defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) |
49 | #error exactly one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be defined! |
50 | #endif |
51 | #else |
52 | #if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) |
53 | #error neither IMGUI_IMPL_WEBGPU_BACKEND_DAWN nor IMGUI_IMPL_WEBGPU_BACKEND_WGPU may be defined if targeting emscripten! |
54 | #endif |
55 | #endif |
56 | |
57 | #include "imgui.h" |
58 | #ifndef IMGUI_DISABLE |
59 | #include "imgui_impl_wgpu.h" |
60 | #include <limits.h> |
61 | #include <webgpu/webgpu.h> |
62 | |
63 | // Dear ImGui prototypes from imgui_internal.h |
64 | extern ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed = 0); |
65 | #define MEMALIGN(_SIZE,_ALIGN) (((_SIZE) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1)) // Memory align (copied from IM_ALIGN() macro). |
66 | |
67 | // WebGPU data |
68 | struct RenderResources |
69 | { |
70 | WGPUTexture FontTexture = nullptr; // Font texture |
71 | WGPUTextureView FontTextureView = nullptr; // Texture view for font texture |
72 | WGPUSampler Sampler = nullptr; // Sampler for the font texture |
73 | WGPUBuffer Uniforms = nullptr; // Shader uniforms |
74 | WGPUBindGroup CommonBindGroup = nullptr; // Resources bind-group to bind the common resources to pipeline |
75 | ImGuiStorage ImageBindGroups; // Resources bind-group to bind the font/image resources to pipeline (this is a key->value map) |
76 | WGPUBindGroup ImageBindGroup = nullptr; // Default font-resource of Dear ImGui |
77 | WGPUBindGroupLayout ImageBindGroupLayout = nullptr; // Cache layout used for the image bind group. Avoids allocating unnecessary JS objects when working with WebASM |
78 | }; |
79 | |
80 | struct FrameResources |
81 | { |
82 | WGPUBuffer IndexBuffer; |
83 | WGPUBuffer VertexBuffer; |
84 | ImDrawIdx* IndexBufferHost; |
85 | ImDrawVert* VertexBufferHost; |
86 | int IndexBufferSize; |
87 | int VertexBufferSize; |
88 | }; |
89 | |
90 | struct Uniforms |
91 | { |
92 | float MVP[4][4]; |
93 | float Gamma; |
94 | }; |
95 | |
96 | struct ImGui_ImplWGPU_Data |
97 | { |
98 | ImGui_ImplWGPU_InitInfo initInfo; |
99 | WGPUDevice wgpuDevice = nullptr; |
100 | WGPUQueue defaultQueue = nullptr; |
101 | WGPUTextureFormat renderTargetFormat = WGPUTextureFormat_Undefined; |
102 | WGPUTextureFormat depthStencilFormat = WGPUTextureFormat_Undefined; |
103 | WGPURenderPipeline pipelineState = nullptr; |
104 | |
105 | RenderResources renderResources; |
106 | FrameResources* pFrameResources = nullptr; |
107 | unsigned int numFramesInFlight = 0; |
108 | unsigned int frameIndex = UINT_MAX; |
109 | }; |
110 | |
111 | // Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts |
112 | // It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. |
113 | static ImGui_ImplWGPU_Data* ImGui_ImplWGPU_GetBackendData() |
114 | { |
115 | return ImGui::GetCurrentContext() ? (ImGui_ImplWGPU_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; |
116 | } |
117 | |
118 | //----------------------------------------------------------------------------- |
119 | // SHADERS |
120 | //----------------------------------------------------------------------------- |
121 | |
122 | static const char __shader_vert_wgsl[] = R"( |
123 | struct VertexInput { |
124 | @location(0) position: vec2<f32>, |
125 | @location(1) uv: vec2<f32>, |
126 | @location(2) color: vec4<f32>, |
127 | }; |
128 | |
129 | struct VertexOutput { |
130 | @builtin(position) position: vec4<f32>, |
131 | @location(0) color: vec4<f32>, |
132 | @location(1) uv: vec2<f32>, |
133 | }; |
134 | |
135 | struct Uniforms { |
136 | mvp: mat4x4<f32>, |
137 | gamma: f32, |
138 | }; |
139 | |
140 | @group(0) @binding(0) var<uniform> uniforms: Uniforms; |
141 | |
142 | @vertex |
143 | fn main(in: VertexInput) -> VertexOutput { |
144 | var out: VertexOutput; |
145 | out.position = uniforms.mvp * vec4<f32>(in.position, 0.0, 1.0); |
146 | out.color = in.color; |
147 | out.uv = in.uv; |
148 | return out; |
149 | } |
150 | )" ; |
151 | |
152 | static const char __shader_frag_wgsl[] = R"( |
153 | struct VertexOutput { |
154 | @builtin(position) position: vec4<f32>, |
155 | @location(0) color: vec4<f32>, |
156 | @location(1) uv: vec2<f32>, |
157 | }; |
158 | |
159 | struct Uniforms { |
160 | mvp: mat4x4<f32>, |
161 | gamma: f32, |
162 | }; |
163 | |
164 | @group(0) @binding(0) var<uniform> uniforms: Uniforms; |
165 | @group(0) @binding(1) var s: sampler; |
166 | @group(1) @binding(0) var t: texture_2d<f32>; |
167 | |
168 | @fragment |
169 | fn main(in: VertexOutput) -> @location(0) vec4<f32> { |
170 | let color = in.color * textureSample(t, s, in.uv); |
171 | let corrected_color = pow(color.rgb, vec3<f32>(uniforms.gamma)); |
172 | return vec4<f32>(corrected_color, color.a); |
173 | } |
174 | )" ; |
175 | |
176 | static void SafeRelease(ImDrawIdx*& res) |
177 | { |
178 | if (res) |
179 | delete[] res; |
180 | res = nullptr; |
181 | } |
182 | static void SafeRelease(ImDrawVert*& res) |
183 | { |
184 | if (res) |
185 | delete[] res; |
186 | res = nullptr; |
187 | } |
188 | static void SafeRelease(WGPUBindGroupLayout& res) |
189 | { |
190 | if (res) |
191 | wgpuBindGroupLayoutRelease(res); |
192 | res = nullptr; |
193 | } |
194 | static void SafeRelease(WGPUBindGroup& res) |
195 | { |
196 | if (res) |
197 | wgpuBindGroupRelease(res); |
198 | res = nullptr; |
199 | } |
200 | static void SafeRelease(WGPUBuffer& res) |
201 | { |
202 | if (res) |
203 | wgpuBufferRelease(res); |
204 | res = nullptr; |
205 | } |
206 | static void SafeRelease(WGPUPipelineLayout& res) |
207 | { |
208 | if (res) |
209 | wgpuPipelineLayoutRelease(res); |
210 | res = nullptr; |
211 | } |
212 | static void SafeRelease(WGPURenderPipeline& res) |
213 | { |
214 | if (res) |
215 | wgpuRenderPipelineRelease(res); |
216 | res = nullptr; |
217 | } |
218 | static void SafeRelease(WGPUSampler& res) |
219 | { |
220 | if (res) |
221 | wgpuSamplerRelease(res); |
222 | res = nullptr; |
223 | } |
224 | static void SafeRelease(WGPUShaderModule& res) |
225 | { |
226 | if (res) |
227 | wgpuShaderModuleRelease(res); |
228 | res = nullptr; |
229 | } |
230 | static void SafeRelease(WGPUTextureView& res) |
231 | { |
232 | if (res) |
233 | wgpuTextureViewRelease(res); |
234 | res = nullptr; |
235 | } |
236 | static void SafeRelease(WGPUTexture& res) |
237 | { |
238 | if (res) |
239 | wgpuTextureRelease(res); |
240 | res = nullptr; |
241 | } |
242 | |
243 | static void SafeRelease(RenderResources& res) |
244 | { |
245 | SafeRelease(res.FontTexture); |
246 | SafeRelease(res.FontTextureView); |
247 | SafeRelease(res.Sampler); |
248 | SafeRelease(res.Uniforms); |
249 | SafeRelease(res.CommonBindGroup); |
250 | SafeRelease(res.ImageBindGroup); |
251 | SafeRelease(res.ImageBindGroupLayout); |
252 | }; |
253 | |
254 | static void SafeRelease(FrameResources& res) |
255 | { |
256 | SafeRelease(res.IndexBuffer); |
257 | SafeRelease(res.VertexBuffer); |
258 | SafeRelease(res&: res.IndexBufferHost); |
259 | SafeRelease(res&: res.VertexBufferHost); |
260 | } |
261 | |
262 | static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const char* wgsl_source) |
263 | { |
264 | ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); |
265 | |
266 | #ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN |
267 | WGPUShaderSourceWGSL wgsl_desc = {}; |
268 | wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL; |
269 | wgsl_desc.code = { wgsl_source, WGPU_STRLEN }; |
270 | #else |
271 | WGPUShaderModuleWGSLDescriptor wgsl_desc = {}; |
272 | wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor; |
273 | wgsl_desc.code = wgsl_source; |
274 | #endif |
275 | |
276 | WGPUShaderModuleDescriptor desc = {}; |
277 | desc.nextInChain = reinterpret_cast<WGPUChainedStruct*>(&wgsl_desc); |
278 | |
279 | WGPUProgrammableStageDescriptor stage_desc = {}; |
280 | stage_desc.module = wgpuDeviceCreateShaderModule(bd->wgpuDevice, &desc); |
281 | #ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN |
282 | stage_desc.entryPoint = { "main" , WGPU_STRLEN }; |
283 | #else |
284 | stage_desc.entryPoint = "main" ; |
285 | #endif |
286 | return stage_desc; |
287 | } |
288 | |
289 | static WGPUBindGroup ImGui_ImplWGPU_CreateImageBindGroup(WGPUBindGroupLayout layout, WGPUTextureView texture) |
290 | { |
291 | ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); |
292 | WGPUBindGroupEntry image_bg_entries[] = { { nullptr, 0, 0, 0, 0, 0, texture } }; |
293 | |
294 | WGPUBindGroupDescriptor image_bg_descriptor = {}; |
295 | image_bg_descriptor.layout = layout; |
296 | image_bg_descriptor.entryCount = sizeof(image_bg_entries) / sizeof(WGPUBindGroupEntry); |
297 | image_bg_descriptor.entries = image_bg_entries; |
298 | return wgpuDeviceCreateBindGroup(bd->wgpuDevice, &image_bg_descriptor); |
299 | } |
300 | |
301 | static void ImGui_ImplWGPU_SetupRenderState(ImDrawData* draw_data, WGPURenderPassEncoder ctx, FrameResources* fr) |
302 | { |
303 | ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); |
304 | |
305 | // Setup orthographic projection matrix into our constant buffer |
306 | // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). |
307 | { |
308 | float L = draw_data->DisplayPos.x; |
309 | float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; |
310 | float T = draw_data->DisplayPos.y; |
311 | float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; |
312 | float mvp[4][4] = |
313 | { |
314 | { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, |
315 | { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, |
316 | { 0.0f, 0.0f, 0.5f, 0.0f }, |
317 | { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, |
318 | }; |
319 | wgpuQueueWriteBuffer(bd->defaultQueue, bd->renderResources.Uniforms, offsetof(Uniforms, MVP), mvp, sizeof(Uniforms::MVP)); |
320 | float gamma; |
321 | switch (bd->renderTargetFormat) |
322 | { |
323 | case WGPUTextureFormat_ASTC10x10UnormSrgb: |
324 | case WGPUTextureFormat_ASTC10x5UnormSrgb: |
325 | case WGPUTextureFormat_ASTC10x6UnormSrgb: |
326 | case WGPUTextureFormat_ASTC10x8UnormSrgb: |
327 | case WGPUTextureFormat_ASTC12x10UnormSrgb: |
328 | case WGPUTextureFormat_ASTC12x12UnormSrgb: |
329 | case WGPUTextureFormat_ASTC4x4UnormSrgb: |
330 | case WGPUTextureFormat_ASTC5x5UnormSrgb: |
331 | case WGPUTextureFormat_ASTC6x5UnormSrgb: |
332 | case WGPUTextureFormat_ASTC6x6UnormSrgb: |
333 | case WGPUTextureFormat_ASTC8x5UnormSrgb: |
334 | case WGPUTextureFormat_ASTC8x6UnormSrgb: |
335 | case WGPUTextureFormat_ASTC8x8UnormSrgb: |
336 | case WGPUTextureFormat_BC1RGBAUnormSrgb: |
337 | case WGPUTextureFormat_BC2RGBAUnormSrgb: |
338 | case WGPUTextureFormat_BC3RGBAUnormSrgb: |
339 | case WGPUTextureFormat_BC7RGBAUnormSrgb: |
340 | case WGPUTextureFormat_BGRA8UnormSrgb: |
341 | case WGPUTextureFormat_ETC2RGB8A1UnormSrgb: |
342 | case WGPUTextureFormat_ETC2RGB8UnormSrgb: |
343 | case WGPUTextureFormat_ETC2RGBA8UnormSrgb: |
344 | case WGPUTextureFormat_RGBA8UnormSrgb: |
345 | gamma = 2.2f; |
346 | break; |
347 | default: |
348 | gamma = 1.0f; |
349 | } |
350 | wgpuQueueWriteBuffer(bd->defaultQueue, bd->renderResources.Uniforms, offsetof(Uniforms, Gamma), &gamma, sizeof(Uniforms::Gamma)); |
351 | } |
352 | |
353 | // Setup viewport |
354 | wgpuRenderPassEncoderSetViewport(ctx, 0, 0, draw_data->FramebufferScale.x * draw_data->DisplaySize.x, draw_data->FramebufferScale.y * draw_data->DisplaySize.y, 0, 1); |
355 | |
356 | // Bind shader and vertex buffers |
357 | wgpuRenderPassEncoderSetVertexBuffer(ctx, 0, fr->VertexBuffer, 0, fr->VertexBufferSize * sizeof(ImDrawVert)); |
358 | wgpuRenderPassEncoderSetIndexBuffer(ctx, fr->IndexBuffer, sizeof(ImDrawIdx) == 2 ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32, 0, fr->IndexBufferSize * sizeof(ImDrawIdx)); |
359 | wgpuRenderPassEncoderSetPipeline(ctx, bd->pipelineState); |
360 | wgpuRenderPassEncoderSetBindGroup(ctx, 0, bd->renderResources.CommonBindGroup, 0, nullptr); |
361 | |
362 | // Setup blend factor |
363 | WGPUColor blend_color = { 0.f, 0.f, 0.f, 0.f }; |
364 | wgpuRenderPassEncoderSetBlendConstant(ctx, &blend_color); |
365 | } |
366 | |
367 | // Render function |
368 | // (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) |
369 | void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder pass_encoder) |
370 | { |
371 | // Avoid rendering when minimized |
372 | int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); |
373 | int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); |
374 | if (fb_width <= 0 || fb_height <= 0 || draw_data->CmdListsCount == 0) |
375 | return; |
376 | |
377 | // FIXME: Assuming that this only gets called once per frame! |
378 | // If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator. |
379 | ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); |
380 | bd->frameIndex = bd->frameIndex + 1; |
381 | FrameResources* fr = &bd->pFrameResources[bd->frameIndex % bd->numFramesInFlight]; |
382 | |
383 | // Create and grow vertex/index buffers if needed |
384 | if (fr->VertexBuffer == nullptr || fr->VertexBufferSize < draw_data->TotalVtxCount) |
385 | { |
386 | if (fr->VertexBuffer) |
387 | { |
388 | wgpuBufferDestroy(fr->VertexBuffer); |
389 | wgpuBufferRelease(fr->VertexBuffer); |
390 | } |
391 | SafeRelease(res&: fr->VertexBufferHost); |
392 | fr->VertexBufferSize = draw_data->TotalVtxCount + 5000; |
393 | |
394 | WGPUBufferDescriptor vb_desc = |
395 | { |
396 | nullptr, |
397 | "Dear ImGui Vertex buffer" , |
398 | #ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN |
399 | WGPU_STRLEN, |
400 | #endif |
401 | WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex, |
402 | MEMALIGN(fr->VertexBufferSize * sizeof(ImDrawVert), 4), |
403 | false |
404 | }; |
405 | fr->VertexBuffer = wgpuDeviceCreateBuffer(bd->wgpuDevice, &vb_desc); |
406 | if (!fr->VertexBuffer) |
407 | return; |
408 | |
409 | fr->VertexBufferHost = new ImDrawVert[fr->VertexBufferSize]; |
410 | } |
411 | if (fr->IndexBuffer == nullptr || fr->IndexBufferSize < draw_data->TotalIdxCount) |
412 | { |
413 | if (fr->IndexBuffer) |
414 | { |
415 | wgpuBufferDestroy(fr->IndexBuffer); |
416 | wgpuBufferRelease(fr->IndexBuffer); |
417 | } |
418 | SafeRelease(res&: fr->IndexBufferHost); |
419 | fr->IndexBufferSize = draw_data->TotalIdxCount + 10000; |
420 | |
421 | WGPUBufferDescriptor ib_desc = |
422 | { |
423 | nullptr, |
424 | "Dear ImGui Index buffer" , |
425 | #ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN |
426 | WGPU_STRLEN, |
427 | #endif |
428 | WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index, |
429 | MEMALIGN(fr->IndexBufferSize * sizeof(ImDrawIdx), 4), |
430 | false |
431 | }; |
432 | fr->IndexBuffer = wgpuDeviceCreateBuffer(bd->wgpuDevice, &ib_desc); |
433 | if (!fr->IndexBuffer) |
434 | return; |
435 | |
436 | fr->IndexBufferHost = new ImDrawIdx[fr->IndexBufferSize]; |
437 | } |
438 | |
439 | // Upload vertex/index data into a single contiguous GPU buffer |
440 | ImDrawVert* vtx_dst = (ImDrawVert*)fr->VertexBufferHost; |
441 | ImDrawIdx* idx_dst = (ImDrawIdx*)fr->IndexBufferHost; |
442 | for (int n = 0; n < draw_data->CmdListsCount; n++) |
443 | { |
444 | const ImDrawList* draw_list = draw_data->CmdLists[n]; |
445 | memcpy(dest: vtx_dst, src: draw_list->VtxBuffer.Data, n: draw_list->VtxBuffer.Size * sizeof(ImDrawVert)); |
446 | memcpy(dest: idx_dst, src: draw_list->IdxBuffer.Data, n: draw_list->IdxBuffer.Size * sizeof(ImDrawIdx)); |
447 | vtx_dst += draw_list->VtxBuffer.Size; |
448 | idx_dst += draw_list->IdxBuffer.Size; |
449 | } |
450 | int64_t vb_write_size = MEMALIGN((char*)vtx_dst - (char*)fr->VertexBufferHost, 4); |
451 | int64_t ib_write_size = MEMALIGN((char*)idx_dst - (char*)fr->IndexBufferHost, 4); |
452 | wgpuQueueWriteBuffer(bd->defaultQueue, fr->VertexBuffer, 0, fr->VertexBufferHost, vb_write_size); |
453 | wgpuQueueWriteBuffer(bd->defaultQueue, fr->IndexBuffer, 0, fr->IndexBufferHost, ib_write_size); |
454 | |
455 | // Setup desired render state |
456 | ImGui_ImplWGPU_SetupRenderState(draw_data, pass_encoder, fr); |
457 | |
458 | // Setup render state structure (for callbacks and custom texture bindings) |
459 | ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); |
460 | ImGui_ImplWGPU_RenderState render_state; |
461 | render_state.Device = bd->wgpuDevice; |
462 | render_state.RenderPassEncoder = pass_encoder; |
463 | platform_io.Renderer_RenderState = &render_state; |
464 | |
465 | // Render command lists |
466 | // (Because we merged all buffers into a single one, we maintain our own offset into them) |
467 | int global_vtx_offset = 0; |
468 | int global_idx_offset = 0; |
469 | ImVec2 clip_scale = draw_data->FramebufferScale; |
470 | ImVec2 clip_off = draw_data->DisplayPos; |
471 | for (int n = 0; n < draw_data->CmdListsCount; n++) |
472 | { |
473 | const ImDrawList* draw_list = draw_data->CmdLists[n]; |
474 | for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) |
475 | { |
476 | const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i]; |
477 | if (pcmd->UserCallback != nullptr) |
478 | { |
479 | // User callback, registered via ImDrawList::AddCallback() |
480 | // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) |
481 | if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) |
482 | ImGui_ImplWGPU_SetupRenderState(draw_data, pass_encoder, fr); |
483 | else |
484 | pcmd->UserCallback(draw_list, pcmd); |
485 | } |
486 | else |
487 | { |
488 | // Bind custom texture |
489 | ImTextureID tex_id = pcmd->GetTexID(); |
490 | ImGuiID tex_id_hash = ImHashData(data_p: &tex_id, data_size: sizeof(tex_id)); |
491 | auto bind_group = bd->renderResources.ImageBindGroups.GetVoidPtr(tex_id_hash); |
492 | if (bind_group) |
493 | { |
494 | wgpuRenderPassEncoderSetBindGroup(pass_encoder, 1, (WGPUBindGroup)bind_group, 0, nullptr); |
495 | } |
496 | else |
497 | { |
498 | WGPUBindGroup image_bind_group = ImGui_ImplWGPU_CreateImageBindGroup(bd->renderResources.ImageBindGroupLayout, (WGPUTextureView)tex_id); |
499 | bd->renderResources.ImageBindGroups.SetVoidPtr(tex_id_hash, image_bind_group); |
500 | wgpuRenderPassEncoderSetBindGroup(pass_encoder, 1, image_bind_group, 0, nullptr); |
501 | } |
502 | |
503 | // Project scissor/clipping rectangles into framebuffer space |
504 | ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); |
505 | ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); |
506 | |
507 | // Clamp to viewport as wgpuRenderPassEncoderSetScissorRect() won't accept values that are off bounds |
508 | if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } |
509 | if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } |
510 | if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; } |
511 | if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } |
512 | if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) |
513 | continue; |
514 | |
515 | // Apply scissor/clipping rectangle, Draw |
516 | wgpuRenderPassEncoderSetScissorRect(pass_encoder, (uint32_t)clip_min.x, (uint32_t)clip_min.y, (uint32_t)(clip_max.x - clip_min.x), (uint32_t)(clip_max.y - clip_min.y)); |
517 | wgpuRenderPassEncoderDrawIndexed(pass_encoder, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); |
518 | } |
519 | } |
520 | global_idx_offset += draw_list->IdxBuffer.Size; |
521 | global_vtx_offset += draw_list->VtxBuffer.Size; |
522 | } |
523 | platform_io.Renderer_RenderState = nullptr; |
524 | } |
525 | |
526 | static void ImGui_ImplWGPU_CreateFontsTexture() |
527 | { |
528 | // Build texture atlas |
529 | ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); |
530 | ImGuiIO& io = ImGui::GetIO(); |
531 | unsigned char* pixels; |
532 | int width, height, size_pp; |
533 | io.Fonts->GetTexDataAsRGBA32(out_pixels: &pixels, out_width: &width, out_height: &height, out_bytes_per_pixel: &size_pp); |
534 | |
535 | // Upload texture to graphics system |
536 | { |
537 | WGPUTextureDescriptor tex_desc = {}; |
538 | #ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN |
539 | tex_desc.label = { "Dear ImGui Font Texture" , WGPU_STRLEN }; |
540 | #else |
541 | tex_desc.label = "Dear ImGui Font Texture" ; |
542 | #endif |
543 | tex_desc.dimension = WGPUTextureDimension_2D; |
544 | tex_desc.size.width = width; |
545 | tex_desc.size.height = height; |
546 | tex_desc.size.depthOrArrayLayers = 1; |
547 | tex_desc.sampleCount = 1; |
548 | tex_desc.format = WGPUTextureFormat_RGBA8Unorm; |
549 | tex_desc.mipLevelCount = 1; |
550 | tex_desc.usage = WGPUTextureUsage_CopyDst | WGPUTextureUsage_TextureBinding; |
551 | bd->renderResources.FontTexture = wgpuDeviceCreateTexture(bd->wgpuDevice, &tex_desc); |
552 | |
553 | WGPUTextureViewDescriptor tex_view_desc = {}; |
554 | tex_view_desc.format = WGPUTextureFormat_RGBA8Unorm; |
555 | tex_view_desc.dimension = WGPUTextureViewDimension_2D; |
556 | tex_view_desc.baseMipLevel = 0; |
557 | tex_view_desc.mipLevelCount = 1; |
558 | tex_view_desc.baseArrayLayer = 0; |
559 | tex_view_desc.arrayLayerCount = 1; |
560 | tex_view_desc.aspect = WGPUTextureAspect_All; |
561 | bd->renderResources.FontTextureView = wgpuTextureCreateView(bd->renderResources.FontTexture, &tex_view_desc); |
562 | } |
563 | |
564 | // Upload texture data |
565 | { |
566 | WGPUImageCopyTexture dst_view = {}; |
567 | dst_view.texture = bd->renderResources.FontTexture; |
568 | dst_view.mipLevel = 0; |
569 | dst_view.origin = { 0, 0, 0 }; |
570 | dst_view.aspect = WGPUTextureAspect_All; |
571 | WGPUTextureDataLayout layout = {}; |
572 | layout.offset = 0; |
573 | layout.bytesPerRow = width * size_pp; |
574 | layout.rowsPerImage = height; |
575 | WGPUExtent3D size = { (uint32_t)width, (uint32_t)height, 1 }; |
576 | wgpuQueueWriteTexture(bd->defaultQueue, &dst_view, pixels, (uint32_t)(width * size_pp * height), &layout, &size); |
577 | } |
578 | |
579 | // Create the associated sampler |
580 | // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) |
581 | { |
582 | WGPUSamplerDescriptor sampler_desc = {}; |
583 | sampler_desc.minFilter = WGPUFilterMode_Linear; |
584 | sampler_desc.magFilter = WGPUFilterMode_Linear; |
585 | sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Linear; |
586 | sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge; |
587 | sampler_desc.addressModeV = WGPUAddressMode_ClampToEdge; |
588 | sampler_desc.addressModeW = WGPUAddressMode_ClampToEdge; |
589 | sampler_desc.maxAnisotropy = 1; |
590 | bd->renderResources.Sampler = wgpuDeviceCreateSampler(bd->wgpuDevice, &sampler_desc); |
591 | } |
592 | |
593 | // Store our identifier |
594 | static_assert(sizeof(ImTextureID) >= sizeof(bd->renderResources.FontTexture), "Can't pack descriptor handle into TexID, 32-bit not supported yet." ); |
595 | io.Fonts->SetTexID((ImTextureID)bd->renderResources.FontTextureView); |
596 | } |
597 | |
598 | static void ImGui_ImplWGPU_CreateUniformBuffer() |
599 | { |
600 | ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); |
601 | WGPUBufferDescriptor ub_desc = |
602 | { |
603 | nullptr, |
604 | "Dear ImGui Uniform buffer" , |
605 | #ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN |
606 | WGPU_STRLEN, |
607 | #endif |
608 | WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform, |
609 | MEMALIGN(sizeof(Uniforms), 16), |
610 | false |
611 | }; |
612 | bd->renderResources.Uniforms = wgpuDeviceCreateBuffer(bd->wgpuDevice, &ub_desc); |
613 | } |
614 | |
615 | bool ImGui_ImplWGPU_CreateDeviceObjects() |
616 | { |
617 | ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); |
618 | if (!bd->wgpuDevice) |
619 | return false; |
620 | if (bd->pipelineState) |
621 | ImGui_ImplWGPU_InvalidateDeviceObjects(); |
622 | |
623 | // Create render pipeline |
624 | WGPURenderPipelineDescriptor graphics_pipeline_desc = {}; |
625 | graphics_pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; |
626 | graphics_pipeline_desc.primitive.stripIndexFormat = WGPUIndexFormat_Undefined; |
627 | graphics_pipeline_desc.primitive.frontFace = WGPUFrontFace_CW; |
628 | graphics_pipeline_desc.primitive.cullMode = WGPUCullMode_None; |
629 | graphics_pipeline_desc.multisample = bd->initInfo.PipelineMultisampleState; |
630 | |
631 | // Bind group layouts |
632 | WGPUBindGroupLayoutEntry common_bg_layout_entries[2] = {}; |
633 | common_bg_layout_entries[0].binding = 0; |
634 | common_bg_layout_entries[0].visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment; |
635 | common_bg_layout_entries[0].buffer.type = WGPUBufferBindingType_Uniform; |
636 | common_bg_layout_entries[1].binding = 1; |
637 | common_bg_layout_entries[1].visibility = WGPUShaderStage_Fragment; |
638 | common_bg_layout_entries[1].sampler.type = WGPUSamplerBindingType_Filtering; |
639 | |
640 | WGPUBindGroupLayoutEntry image_bg_layout_entries[1] = {}; |
641 | image_bg_layout_entries[0].binding = 0; |
642 | image_bg_layout_entries[0].visibility = WGPUShaderStage_Fragment; |
643 | image_bg_layout_entries[0].texture.sampleType = WGPUTextureSampleType_Float; |
644 | image_bg_layout_entries[0].texture.viewDimension = WGPUTextureViewDimension_2D; |
645 | |
646 | WGPUBindGroupLayoutDescriptor common_bg_layout_desc = {}; |
647 | common_bg_layout_desc.entryCount = 2; |
648 | common_bg_layout_desc.entries = common_bg_layout_entries; |
649 | |
650 | WGPUBindGroupLayoutDescriptor image_bg_layout_desc = {}; |
651 | image_bg_layout_desc.entryCount = 1; |
652 | image_bg_layout_desc.entries = image_bg_layout_entries; |
653 | |
654 | WGPUBindGroupLayout bg_layouts[2]; |
655 | bg_layouts[0] = wgpuDeviceCreateBindGroupLayout(bd->wgpuDevice, &common_bg_layout_desc); |
656 | bg_layouts[1] = wgpuDeviceCreateBindGroupLayout(bd->wgpuDevice, &image_bg_layout_desc); |
657 | |
658 | WGPUPipelineLayoutDescriptor layout_desc = {}; |
659 | layout_desc.bindGroupLayoutCount = 2; |
660 | layout_desc.bindGroupLayouts = bg_layouts; |
661 | graphics_pipeline_desc.layout = wgpuDeviceCreatePipelineLayout(bd->wgpuDevice, &layout_desc); |
662 | |
663 | // Create the vertex shader |
664 | WGPUProgrammableStageDescriptor vertex_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__shader_vert_wgsl); |
665 | graphics_pipeline_desc.vertex.module = vertex_shader_desc.module; |
666 | graphics_pipeline_desc.vertex.entryPoint = vertex_shader_desc.entryPoint; |
667 | |
668 | // Vertex input configuration |
669 | WGPUVertexAttribute attribute_desc[] = |
670 | { |
671 | { WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, pos), 0 }, |
672 | { WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, uv), 1 }, |
673 | { WGPUVertexFormat_Unorm8x4, (uint64_t)offsetof(ImDrawVert, col), 2 }, |
674 | }; |
675 | |
676 | WGPUVertexBufferLayout buffer_layouts[1]; |
677 | buffer_layouts[0].arrayStride = sizeof(ImDrawVert); |
678 | buffer_layouts[0].stepMode = WGPUVertexStepMode_Vertex; |
679 | buffer_layouts[0].attributeCount = 3; |
680 | buffer_layouts[0].attributes = attribute_desc; |
681 | |
682 | graphics_pipeline_desc.vertex.bufferCount = 1; |
683 | graphics_pipeline_desc.vertex.buffers = buffer_layouts; |
684 | |
685 | // Create the pixel shader |
686 | WGPUProgrammableStageDescriptor pixel_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__shader_frag_wgsl); |
687 | |
688 | // Create the blending setup |
689 | WGPUBlendState blend_state = {}; |
690 | blend_state.alpha.operation = WGPUBlendOperation_Add; |
691 | blend_state.alpha.srcFactor = WGPUBlendFactor_One; |
692 | blend_state.alpha.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha; |
693 | blend_state.color.operation = WGPUBlendOperation_Add; |
694 | blend_state.color.srcFactor = WGPUBlendFactor_SrcAlpha; |
695 | blend_state.color.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha; |
696 | |
697 | WGPUColorTargetState color_state = {}; |
698 | color_state.format = bd->renderTargetFormat; |
699 | color_state.blend = &blend_state; |
700 | color_state.writeMask = WGPUColorWriteMask_All; |
701 | |
702 | WGPUFragmentState fragment_state = {}; |
703 | fragment_state.module = pixel_shader_desc.module; |
704 | fragment_state.entryPoint = pixel_shader_desc.entryPoint; |
705 | fragment_state.targetCount = 1; |
706 | fragment_state.targets = &color_state; |
707 | |
708 | graphics_pipeline_desc.fragment = &fragment_state; |
709 | |
710 | // Create depth-stencil State |
711 | WGPUDepthStencilState depth_stencil_state = {}; |
712 | depth_stencil_state.format = bd->depthStencilFormat; |
713 | #ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN |
714 | depth_stencil_state.depthWriteEnabled = WGPUOptionalBool_False; |
715 | #else |
716 | depth_stencil_state.depthWriteEnabled = false; |
717 | #endif |
718 | depth_stencil_state.depthCompare = WGPUCompareFunction_Always; |
719 | depth_stencil_state.stencilFront.compare = WGPUCompareFunction_Always; |
720 | depth_stencil_state.stencilFront.failOp = WGPUStencilOperation_Keep; |
721 | depth_stencil_state.stencilFront.depthFailOp = WGPUStencilOperation_Keep; |
722 | depth_stencil_state.stencilFront.passOp = WGPUStencilOperation_Keep; |
723 | depth_stencil_state.stencilBack.compare = WGPUCompareFunction_Always; |
724 | depth_stencil_state.stencilBack.failOp = WGPUStencilOperation_Keep; |
725 | depth_stencil_state.stencilBack.depthFailOp = WGPUStencilOperation_Keep; |
726 | depth_stencil_state.stencilBack.passOp = WGPUStencilOperation_Keep; |
727 | |
728 | // Configure disabled depth-stencil state |
729 | graphics_pipeline_desc.depthStencil = (bd->depthStencilFormat == WGPUTextureFormat_Undefined) ? nullptr : &depth_stencil_state; |
730 | |
731 | bd->pipelineState = wgpuDeviceCreateRenderPipeline(bd->wgpuDevice, &graphics_pipeline_desc); |
732 | |
733 | ImGui_ImplWGPU_CreateFontsTexture(); |
734 | ImGui_ImplWGPU_CreateUniformBuffer(); |
735 | |
736 | // Create resource bind group |
737 | WGPUBindGroupEntry common_bg_entries[] = |
738 | { |
739 | { nullptr, 0, bd->renderResources.Uniforms, 0, MEMALIGN(sizeof(Uniforms), 16), 0, 0 }, |
740 | { nullptr, 1, 0, 0, 0, bd->renderResources.Sampler, 0 }, |
741 | }; |
742 | |
743 | WGPUBindGroupDescriptor common_bg_descriptor = {}; |
744 | common_bg_descriptor.layout = bg_layouts[0]; |
745 | common_bg_descriptor.entryCount = sizeof(common_bg_entries) / sizeof(WGPUBindGroupEntry); |
746 | common_bg_descriptor.entries = common_bg_entries; |
747 | bd->renderResources.CommonBindGroup = wgpuDeviceCreateBindGroup(bd->wgpuDevice, &common_bg_descriptor); |
748 | |
749 | WGPUBindGroup image_bind_group = ImGui_ImplWGPU_CreateImageBindGroup(bg_layouts[1], bd->renderResources.FontTextureView); |
750 | bd->renderResources.ImageBindGroup = image_bind_group; |
751 | bd->renderResources.ImageBindGroupLayout = bg_layouts[1]; |
752 | bd->renderResources.ImageBindGroups.SetVoidPtr(ImHashData(&bd->renderResources.FontTextureView, sizeof(ImTextureID)), image_bind_group); |
753 | |
754 | SafeRelease(vertex_shader_desc.module); |
755 | SafeRelease(pixel_shader_desc.module); |
756 | SafeRelease(graphics_pipeline_desc.layout); |
757 | SafeRelease(bg_layouts[0]); |
758 | |
759 | return true; |
760 | } |
761 | |
762 | void ImGui_ImplWGPU_InvalidateDeviceObjects() |
763 | { |
764 | ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); |
765 | if (!bd->wgpuDevice) |
766 | return; |
767 | |
768 | SafeRelease(bd->pipelineState); |
769 | SafeRelease(bd->renderResources); |
770 | |
771 | ImGuiIO& io = ImGui::GetIO(); |
772 | io.Fonts->SetTexID(0); // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. |
773 | |
774 | for (unsigned int i = 0; i < bd->numFramesInFlight; i++) |
775 | SafeRelease(res&: bd->pFrameResources[i]); |
776 | } |
777 | |
778 | bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info) |
779 | { |
780 | ImGuiIO& io = ImGui::GetIO(); |
781 | IMGUI_CHECKVERSION(); |
782 | IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!" ); |
783 | |
784 | // Setup backend capabilities flags |
785 | ImGui_ImplWGPU_Data* bd = IM_NEW(ImGui_ImplWGPU_Data)(); |
786 | io.BackendRendererUserData = (void*)bd; |
787 | #if defined(__EMSCRIPTEN__) |
788 | io.BackendRendererName = "imgui_impl_webgpu_emscripten" ; |
789 | #elif defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) |
790 | io.BackendRendererName = "imgui_impl_webgpu_dawn" ; |
791 | #elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) |
792 | io.BackendRendererName = "imgui_impl_webgpu_wgpu" ; |
793 | #else |
794 | io.BackendRendererName = "imgui_impl_webgpu" ; |
795 | #endif |
796 | io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. |
797 | |
798 | bd->initInfo = *init_info; |
799 | bd->wgpuDevice = init_info->Device; |
800 | bd->defaultQueue = wgpuDeviceGetQueue(bd->wgpuDevice); |
801 | bd->renderTargetFormat = init_info->RenderTargetFormat; |
802 | bd->depthStencilFormat = init_info->DepthStencilFormat; |
803 | bd->numFramesInFlight = init_info->NumFramesInFlight; |
804 | bd->frameIndex = UINT_MAX; |
805 | |
806 | bd->renderResources.FontTexture = nullptr; |
807 | bd->renderResources.FontTextureView = nullptr; |
808 | bd->renderResources.Sampler = nullptr; |
809 | bd->renderResources.Uniforms = nullptr; |
810 | bd->renderResources.CommonBindGroup = nullptr; |
811 | bd->renderResources.ImageBindGroups.Data.reserve(100); |
812 | bd->renderResources.ImageBindGroup = nullptr; |
813 | bd->renderResources.ImageBindGroupLayout = nullptr; |
814 | |
815 | // Create buffers with a default size (they will later be grown as needed) |
816 | bd->pFrameResources = new FrameResources[bd->numFramesInFlight]; |
817 | for (unsigned int i = 0; i < bd->numFramesInFlight; i++) |
818 | { |
819 | FrameResources* fr = &bd->pFrameResources[i]; |
820 | fr->IndexBuffer = nullptr; |
821 | fr->VertexBuffer = nullptr; |
822 | fr->IndexBufferHost = nullptr; |
823 | fr->VertexBufferHost = nullptr; |
824 | fr->IndexBufferSize = 10000; |
825 | fr->VertexBufferSize = 5000; |
826 | } |
827 | |
828 | return true; |
829 | } |
830 | |
831 | void ImGui_ImplWGPU_Shutdown() |
832 | { |
833 | ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); |
834 | IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?" ); |
835 | ImGuiIO& io = ImGui::GetIO(); |
836 | |
837 | ImGui_ImplWGPU_InvalidateDeviceObjects(); |
838 | delete[] bd->pFrameResources; |
839 | bd->pFrameResources = nullptr; |
840 | wgpuQueueRelease(bd->defaultQueue); |
841 | bd->wgpuDevice = nullptr; |
842 | bd->numFramesInFlight = 0; |
843 | bd->frameIndex = UINT_MAX; |
844 | |
845 | io.BackendRendererName = nullptr; |
846 | io.BackendRendererUserData = nullptr; |
847 | io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; |
848 | IM_DELETE(p: bd); |
849 | } |
850 | |
851 | void ImGui_ImplWGPU_NewFrame() |
852 | { |
853 | ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); |
854 | if (!bd->pipelineState) |
855 | ImGui_ImplWGPU_CreateDeviceObjects(); |
856 | } |
857 | |
858 | //----------------------------------------------------------------------------- |
859 | |
860 | #endif // #ifndef IMGUI_DISABLE |
861 | |