载入中...
搜索中...
未找到
imgui_impl_wgpu.cpp
浏览该文件的文档.
1// dear imgui: Renderer Backend for WebGPU (wgpu), adapted for EVEngine's
2// vendored imgui v1.83. Uses the stable webgpu.h C API so the same file
3// compiles against Dawn (native) and Emscripten (browser).
4//
5// This is a self-contained renderer: imgui draws are issued into an existing
6// WGPURenderPassEncoder supplied by the host (EVEngine's present overlay).
7
8// The source scanner compiles every *.cpp under ui/imgui/ on all platforms;
9// keep this TU empty when the WebGPU backend is not active.
10// config.h must be included before the guard: EVENGINE_WEBGPU is defined by
11// config.h, not by a -D flag, so including it inside the guard would make this
12// TU always empty.
13#include "common/config.h"
14
15#if defined(EVENGINE_WEBGPU)
16
17#include "imgui_impl_wgpu.h"
18
19#include <limits.h>
20#include <string.h>
21
22// Avoid clashes with Windows headers.
23#if defined(WIN32) && defined(min)
24#undef min
25#undef max
26#endif
27
28//-------------------------------------------------------------------------
29// Configuration
30//-------------------------------------------------------------------------
31#ifndef IMGUI_IMPL_WEBGPU_DEFAULT_FONT_SAMPLER
32#define IMGUI_IMPL_WEBGPU_DEFAULT_FONT_SAMPLER 0
33#endif
34
35//-----------------------------------------------------------------------------
36// Implementation
37//-----------------------------------------------------------------------------
38
39struct ImGui_ImplWGPU_Data {
40 WGPUDevice wgpuDevice = nullptr;
41 WGPUTextureFormat rtFormat = WGPUTextureFormat_BGRA8Unorm;
42
43 WGPUQueue defaultQueue = nullptr;
44
45 WGPUBindGroupLayout bindGroupLayout = nullptr;
46 WGPUBindGroup bindGroup = nullptr;
47 WGPUBuffer uniformBuffer = nullptr;
48 uint32_t uniformBufferSize = 0;
49 WGPURenderPipeline renderPipeline = nullptr;
50
51 WGPUTexture fontTexture = nullptr;
52 WGPUTextureView fontTextureView = nullptr;
53 WGPUSampler fontSampler = nullptr;
54
55 int numFramesInFlight = 1;
56 int frameIndex = 0;
57};
58
59// Backend data stored in io.BackendRendererUserData to allow support for multiple
60// Dear ImGui contexts.
61static ImGui_ImplWGPU_Data *ImGui_ImplWGPU_GetBackendData() {
62 return ImGui::GetCurrentContext() ? (ImGui_ImplWGPU_Data *)ImGui::GetIO().BackendRendererUserData
63 : nullptr;
64}
65
66static const char *ImGui_ImplWGPU_GetWGSLVertexShader() {
67 return R"(
68struct Uniforms {
69 mvp: mat4x4f,
70};
71struct VertexInput {
72 @location(0) position: vec2f,
73 @location(1) uv: vec2f,
74 @location(2) color: vec4f,
75};
76struct VertexOutput {
77 @builtin(position) position: vec4f,
78 @location(0) uv: vec2f,
79 @location(1) color: vec4f,
80};
81@group(0) @binding(0) var<uniform> uniforms: Uniforms;
82@vertex
83fn vs_main(in: VertexInput) -> VertexOutput {
84 var out: VertexOutput;
85 out.position = uniforms.mvp * vec4f(in.position, 0.0, 1.0);
86 out.uv = in.uv;
87 out.color = in.color;
88 return out;
89}
90)";
91}
92
93static const char *ImGui_ImplWGPU_GetWGSLFragmentShader() {
94 return R"(
95struct VertexOutput {
96 @builtin(position) position: vec4f,
97 @location(0) uv: vec2f,
98 @location(1) color: vec4f,
99};
100@group(0) @binding(1) var fontSampler: sampler;
101@group(0) @binding(2) var fontTexture: texture_2d<f32>;
102@fragment
103fn fs_main(in: VertexOutput) -> @location(0) vec4f {
104 return textureSample(fontTexture, fontSampler, in.uv) * in.color;
105}
106)";
107}
108
109static void ImGui_ImplWGPU_SetupRenderState(ImDrawData *draw_data,
110 WGPURenderPassEncoder pass_encoder) {
111 ImGui_ImplWGPU_Data *bd = ImGui_ImplWGPU_GetBackendData();
112
113 // Setup orthographic projection matrix into our constant buffer.
114 {
115 float L = draw_data->DisplayPos.x;
116 float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
117 float T = draw_data->DisplayPos.y;
118 float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
119 float mvp[4][4] = {
120 {2.0f / (R - L), 0.0f, 0.0f, 0.0f},
121 {0.0f, 2.0f / (T - B), 0.0f, 0.0f},
122 {0.0f, 0.0f, 0.5f, 0.0f},
123 {(R + L) / (L - R), (T + B) / (B - T), 0.5f, 1.0f},
124 };
125 wgpuQueueWriteBuffer(bd->defaultQueue, bd->uniformBuffer, 0, mvp, sizeof(mvp));
126 }
127
128 wgpuRenderPassEncoderSetPipeline(pass_encoder, bd->renderPipeline);
129 wgpuRenderPassEncoderSetBindGroup(pass_encoder, 0, bd->bindGroup, 0, nullptr);
130}
131
132static void ImGui_ImplWGPU_CreatePipeline() {
133 ImGui_ImplWGPU_Data *bd = ImGui_ImplWGPU_GetBackendData();
134 WGPUDevice device = bd->wgpuDevice;
135
136 // Shader modules.
137 WGPUShaderSourceWGSL wgslVert{};
138 wgslVert.chain.sType = WGPUSType_ShaderSourceWGSL;
139 wgslVert.code = WGPUStringView{ImGui_ImplWGPU_GetWGSLVertexShader(),
140 strlen(ImGui_ImplWGPU_GetWGSLVertexShader())};
141 WGPUShaderModuleDescriptor vsDesc{};
142 vsDesc.nextInChain = &wgslVert.chain;
143 WGPUShaderModule vsModule = wgpuDeviceCreateShaderModule(device, &vsDesc);
144
145 WGPUShaderSourceWGSL wgslFrag{};
146 wgslFrag.chain.sType = WGPUSType_ShaderSourceWGSL;
147 wgslFrag.code = WGPUStringView{ImGui_ImplWGPU_GetWGSLFragmentShader(),
148 strlen(ImGui_ImplWGPU_GetWGSLFragmentShader())};
149 WGPUShaderModuleDescriptor fsDesc{};
150 fsDesc.nextInChain = &wgslFrag.chain;
151 WGPUShaderModule fsModule = wgpuDeviceCreateShaderModule(device, &fsDesc);
152
153 // Bind group layout.
154 WGPUBindGroupLayoutEntry entries[3] = {};
155 entries[0].binding = 0;
156 entries[0].visibility = WGPUShaderStage_Vertex;
157 entries[0].buffer.type = WGPUBufferBindingType_Uniform;
158 entries[0].buffer.minBindingSize = 64;
159 entries[1].binding = 1;
160 entries[1].visibility = WGPUShaderStage_Fragment;
161 entries[1].sampler.type = WGPUSamplerBindingType_Filtering;
162 entries[2].binding = 2;
163 entries[2].visibility = WGPUShaderStage_Fragment;
164 entries[2].texture.sampleType = WGPUTextureSampleType_Float;
165 entries[2].texture.viewDimension = WGPUTextureViewDimension_2D;
166 WGPUBindGroupLayoutDescriptor bglDesc{};
167 bglDesc.entryCount = 3;
168 bglDesc.entries = entries;
169 bd->bindGroupLayout = wgpuDeviceCreateBindGroupLayout(device, &bglDesc);
170
171 // Pipeline layout.
172 WGPUPipelineLayoutDescriptor plDesc{};
173 plDesc.bindGroupLayoutCount = 1;
174 plDesc.bindGroupLayouts = &bd->bindGroupLayout;
175 WGPUPipelineLayout pipelineLayout = wgpuDeviceCreatePipelineLayout(device, &plDesc);
176
177 // Vertex layout: ImDrawVert { vec2 pos; vec2 uv; u32 col; }
178 WGPUVertexAttribute attrs[3] = {};
179 attrs[0].format = WGPUVertexFormat_Float32x2;
180 attrs[0].offset = IM_OFFSETOF(ImDrawVert, pos);
181 attrs[0].shaderLocation = 0;
182 attrs[1].format = WGPUVertexFormat_Float32x2;
183 attrs[1].offset = IM_OFFSETOF(ImDrawVert, uv);
184 attrs[1].shaderLocation = 1;
185 attrs[2].format = WGPUVertexFormat_Unorm8x4;
186 attrs[2].offset = IM_OFFSETOF(ImDrawVert, col);
187 attrs[2].shaderLocation = 2;
188 WGPUVertexBufferLayout vbLayout{};
189 vbLayout.arrayStride = sizeof(ImDrawVert);
190 vbLayout.stepMode = WGPUVertexStepMode_Vertex;
191 vbLayout.attributeCount = 3;
192 vbLayout.attributes = attrs;
193
194 WGPUBlendState blend{};
195 blend.color.srcFactor = WGPUBlendFactor_SrcAlpha;
196 blend.color.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha;
197 blend.color.operation = WGPUBlendOperation_Add;
198 blend.alpha.srcFactor = WGPUBlendFactor_One;
199 blend.alpha.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha;
200 blend.alpha.operation = WGPUBlendOperation_Add;
201
202 WGPUColorTargetState colorTarget{};
203 colorTarget.format = bd->rtFormat;
204 colorTarget.blend = &blend;
205 colorTarget.writeMask = WGPUColorWriteMask_All;
206
207 WGPURenderPipelineDescriptor desc{};
208 desc.layout = pipelineLayout;
209 desc.vertex.module = vsModule;
210 desc.vertex.entryPoint = WGPUStringView{"vs_main", 6};
211 desc.vertex.bufferCount = 1;
212 desc.vertex.buffers = &vbLayout;
213 desc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
214 desc.primitive.frontFace = WGPUFrontFace_CCW;
215 desc.primitive.cullMode = WGPUCullMode_None;
216 desc.primitive.stripIndexFormat = WGPUIndexFormat_Undefined;
217 WGPUFragmentState fragState{};
218 fragState.module = fsModule;
219 fragState.entryPoint = WGPUStringView{"fs_main", 6};
220 fragState.targetCount = 1;
221 fragState.targets = &colorTarget;
222 desc.fragment = &fragState;
223 desc.multisample.count = 1;
224 bd->renderPipeline = wgpuDeviceCreateRenderPipeline(device, &desc);
225}
226
227static void ImGui_ImplWGPU_CreateUniformBuffer() {
228 ImGui_ImplWGPU_Data *bd = ImGui_ImplWGPU_GetBackendData();
229 WGPUDevice device = bd->wgpuDevice;
230
231 bd->uniformBufferSize = 64;
232 WGPUBufferDescriptor desc{};
233 desc.size = bd->uniformBufferSize;
234 desc.usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst;
235 desc.mappedAtCreation = false;
236 bd->uniformBuffer = wgpuDeviceCreateBuffer(device, &desc);
237}
238
240 ImGuiIO &io = ImGui::GetIO();
241 ImGui_ImplWGPU_Data *bd = ImGui_ImplWGPU_GetBackendData();
242 WGPUDevice device = bd->wgpuDevice;
243
244 unsigned char *pixels;
245 int width, height, size;
246 io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &size);
247 size_t stride = static_cast<size_t>(width) * 4;
248
249 WGPUTextureDescriptor texDesc{};
250 texDesc.dimension = WGPUTextureDimension_2D;
251 texDesc.size.width = width;
252 texDesc.size.height = height;
253 texDesc.size.depthOrArrayLayers = 1;
254 texDesc.sampleCount = 1;
255 texDesc.format = WGPUTextureFormat_RGBA8Unorm;
256 texDesc.mipLevelCount = 1;
257 texDesc.usage = WGPUTextureUsage_TextureBinding | WGPUTextureUsage_CopyDst;
258 bd->fontTexture = wgpuDeviceCreateTexture(device, &texDesc);
259
260 WGPUTexelCopyBufferLayout layout{};
261 layout.offset = 0;
262 layout.bytesPerRow = stride;
263 layout.rowsPerImage = height;
264 WGPUTexelCopyTextureInfo copyDst{};
265 copyDst.texture = bd->fontTexture;
266 copyDst.mipLevel = 0;
267 copyDst.aspect = WGPUTextureAspect_All;
268 WGPUExtent3D extent{static_cast<uint32_t>(width), static_cast<uint32_t>(height), 1};
269 wgpuQueueWriteTexture(bd->defaultQueue, &copyDst, pixels, stride * height, &layout, &extent);
270
271 WGPUTextureViewDescriptor viewDesc{};
272 viewDesc.format = WGPUTextureFormat_RGBA8Unorm;
273 viewDesc.dimension = WGPUTextureViewDimension_2D;
274 viewDesc.baseMipLevel = 0;
275 viewDesc.mipLevelCount = 1;
276 viewDesc.baseArrayLayer = 0;
277 viewDesc.arrayLayerCount = 1;
278 bd->fontTextureView = wgpuTextureCreateView(bd->fontTexture, &viewDesc);
279
280 WGPUSamplerDescriptor samplerDesc{};
281 samplerDesc.minFilter = WGPUFilterMode_Linear;
282 samplerDesc.magFilter = WGPUFilterMode_Linear;
283 samplerDesc.mipmapFilter = WGPUMipmapFilterMode_Linear;
284 samplerDesc.addressModeU = WGPUAddressMode_ClampToEdge;
285 samplerDesc.addressModeV = WGPUAddressMode_ClampToEdge;
286 samplerDesc.addressModeW = WGPUAddressMode_ClampToEdge;
287 bd->fontSampler = wgpuDeviceCreateSampler(device, &samplerDesc);
288
289 WGPUBindGroupEntry entries[3] = {};
290 entries[0].binding = 0;
291 entries[0].buffer = bd->uniformBuffer;
292 entries[0].size = bd->uniformBufferSize;
293 entries[1].binding = 1;
294 entries[1].sampler = bd->fontSampler;
295 entries[2].binding = 2;
296 entries[2].textureView = bd->fontTextureView;
297
298 WGPUBindGroupDescriptor bgDesc{};
299 bgDesc.layout = bd->bindGroupLayout;
300 bgDesc.entryCount = 3;
301 bgDesc.entries = entries;
302 bd->bindGroup = wgpuDeviceCreateBindGroup(device, &bgDesc);
303
304 io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->fontTextureView);
305 return true;
306}
307
309 ImGui_ImplWGPU_Data *bd = ImGui_ImplWGPU_GetBackendData();
310 if (!bd) return;
311 if (bd->fontTexture) { wgpuTextureRelease(bd->fontTexture); bd->fontTexture = nullptr; }
312 if (bd->fontTextureView) { wgpuTextureViewRelease(bd->fontTextureView); bd->fontTextureView = nullptr; }
313 if (bd->fontSampler) { wgpuSamplerRelease(bd->fontSampler); bd->fontSampler = nullptr; }
314 if (bd->bindGroup) { wgpuBindGroupRelease(bd->bindGroup); bd->bindGroup = nullptr; }
315 if (bd->bindGroupLayout) { wgpuBindGroupLayoutRelease(bd->bindGroupLayout); bd->bindGroupLayout = nullptr; }
316 if (bd->uniformBuffer) { wgpuBufferRelease(bd->uniformBuffer); bd->uniformBuffer = nullptr; }
317 if (bd->renderPipeline) { wgpuRenderPipelineRelease(bd->renderPipeline); bd->renderPipeline = nullptr; }
318 if (ImGui::GetCurrentContext() && ImGui::GetIO().Fonts)
319 ImGui::GetIO().Fonts->SetTexID(0);
320}
321
322bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format) {
323 ImGuiIO &io = ImGui::GetIO();
324 IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!");
325
326 ImGui_ImplWGPU_Data *bd = IM_NEW(ImGui_ImplWGPU_Data)();
327 io.BackendRendererUserData = (void *)bd;
328 io.BackendRendererName = "imgui_impl_webgpu";
329 io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;
330
331 bd->wgpuDevice = device;
332 bd->rtFormat = rt_format;
333 bd->numFramesInFlight = num_frames_in_flight;
334 bd->defaultQueue = wgpuDeviceGetQueue(device);
335
336 ImGui_ImplWGPU_CreateUniformBuffer();
337 ImGui_ImplWGPU_CreatePipeline();
339
340 return true;
341}
342
344 ImGui_ImplWGPU_Data *bd = ImGui_ImplWGPU_GetBackendData();
345 IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?");
347 if (bd->defaultQueue) { wgpuQueueRelease(bd->defaultQueue); bd->defaultQueue = nullptr; }
348 if (bd->wgpuDevice) { wgpuDeviceRelease(bd->wgpuDevice); bd->wgpuDevice = nullptr; }
349 ImGuiIO &io = ImGui::GetIO();
350 io.BackendRendererName = NULL;
351 io.BackendRendererUserData = NULL;
352 IM_DELETE(bd);
353}
354
356 ImGui_ImplWGPU_Data *bd = ImGui_ImplWGPU_GetBackendData();
357 IM_ASSERT(bd != NULL && "Did you call ImGui_ImplWGPU_Init()?");
358 bd->frameIndex = (bd->frameIndex + 1) % bd->numFramesInFlight;
359}
360
361void ImGui_ImplWGPU_RenderDrawData(ImDrawData *draw_data, WGPURenderPassEncoder pass_encoder) {
362 ImGui_ImplWGPU_Data *bd = ImGui_ImplWGPU_GetBackendData();
363 if (draw_data->CmdListsCount == 0) return;
364
365 ImGui_ImplWGPU_SetupRenderState(draw_data, pass_encoder);
366
367 // Will project scissor/clipping rectangles into framebuffer space.
368 ImVec2 clip_off = draw_data->DisplayPos;
369 ImVec2 clip_scale = draw_data->FramebufferScale;
370
371 const float fb_width = draw_data->DisplaySize.x * clip_scale.x;
372 const float fb_height = draw_data->DisplaySize.y * clip_scale.y;
373 if (fb_width <= 0.0f || fb_height <= 0.0f) return;
374
375 for (int n = 0; n < draw_data->CmdListsCount; n++) {
376 const ImDrawList *cmd_list = draw_data->CmdLists[n];
377
378 // Create and upload vertex/index buffers.
379 size_t vbSize = (size_t)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert);
380 size_t ibSize = (size_t)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx);
381 WGPUBuffer vb = nullptr;
382 WGPUBuffer ib = nullptr;
383
384 WGPUBufferDescriptor vbDesc{};
385 vbDesc.size = vbSize;
386 vbDesc.usage = WGPUBufferUsage_Vertex | WGPUBufferUsage_CopyDst;
387 vbDesc.mappedAtCreation = false;
388 vb = wgpuDeviceCreateBuffer(bd->wgpuDevice, &vbDesc);
389 wgpuQueueWriteBuffer(bd->defaultQueue, vb, 0, cmd_list->VtxBuffer.Data, vbSize);
390
391 WGPUBufferDescriptor ibDesc{};
392 ibDesc.size = ibSize;
393 ibDesc.usage = WGPUBufferUsage_Index | WGPUBufferUsage_CopyDst;
394 ibDesc.mappedAtCreation = false;
395 ib = wgpuDeviceCreateBuffer(bd->wgpuDevice, &ibDesc);
396 wgpuQueueWriteBuffer(bd->defaultQueue, ib, 0, cmd_list->IdxBuffer.Data, ibSize);
397
398 wgpuRenderPassEncoderSetVertexBuffer(pass_encoder, 0, vb, 0, vbSize);
399 wgpuRenderPassEncoderSetIndexBuffer(pass_encoder, ib,
400 sizeof(ImDrawIdx) == 2 ? WGPUIndexFormat_Uint16
401 : WGPUIndexFormat_Uint32,
402 0, ibSize);
403
404 for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) {
405 const ImDrawCmd *pcmd = &cmd_list->CmdBuffer[cmd_i];
406 if (pcmd->UserCallback != NULL) {
407 pcmd->UserCallback(cmd_list, pcmd);
408 } else {
409 // Project scissor/clipping rectangles into framebuffer space.
410 ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x,
411 (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
412 ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x,
413 (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
414 if (clip_min.x < 0.0f) clip_min.x = 0.0f;
415 if (clip_min.y < 0.0f) clip_min.y = 0.0f;
416 if (clip_max.x > fb_width) clip_max.x = fb_width;
417 if (clip_max.y > fb_height) clip_max.y = fb_height;
418 if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue;
419
420 wgpuRenderPassEncoderSetScissorRect(pass_encoder,
421 (uint32_t)clip_min.x, (uint32_t)clip_min.y,
422 (uint32_t)(clip_max.x - clip_min.x),
423 (uint32_t)(clip_max.y - clip_min.y));
424
425 // Bind font texture when the texture id changes.
426 WGPUTextureView fontTextureView =
427 (WGPUTextureView)(intptr_t)pcmd->TextureId;
428 if (fontTextureView != bd->fontTextureView) {
429 WGPUBindGroupEntry entries[3] = {};
430 entries[0].binding = 0;
431 entries[0].buffer = bd->uniformBuffer;
432 entries[0].size = bd->uniformBufferSize;
433 entries[1].binding = 1;
434 entries[1].sampler = bd->fontSampler;
435 entries[2].binding = 2;
436 entries[2].textureView = fontTextureView;
437 WGPUBindGroupDescriptor bgDesc{};
438 bgDesc.layout = bd->bindGroupLayout;
439 bgDesc.entryCount = 3;
440 bgDesc.entries = entries;
441 WGPUBindGroup bg = wgpuDeviceCreateBindGroup(bd->wgpuDevice, &bgDesc);
442 wgpuRenderPassEncoderSetBindGroup(pass_encoder, 0, bg, 0, nullptr);
443 wgpuBindGroupRelease(bg);
444 }
445
446 wgpuRenderPassEncoderDrawIndexed(pass_encoder, pcmd->ElemCount, 1,
447 pcmd->IdxOffset, (int32_t)pcmd->VtxOffset, 0);
448 }
449 }
450
451 wgpuBufferRelease(vb);
452 wgpuBufferRelease(ib);
453 }
454}
455
456#endif // EVENGINE_WEBGPU
std::string layout
vkb::Device & device
float height
Definition Grass.cpp:235
glm::vec3 n
Definition Grass.cpp:64
int width
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()