13#include "common/config.h"
15#if defined(EVENGINE_WEBGPU)
23#if defined(WIN32) && defined(min)
31#ifndef IMGUI_IMPL_WEBGPU_DEFAULT_FONT_SAMPLER
32#define IMGUI_IMPL_WEBGPU_DEFAULT_FONT_SAMPLER 0
39struct ImGui_ImplWGPU_Data {
40 WGPUDevice wgpuDevice =
nullptr;
41 WGPUTextureFormat rtFormat = WGPUTextureFormat_BGRA8Unorm;
43 WGPUQueue defaultQueue =
nullptr;
45 WGPUBindGroupLayout bindGroupLayout =
nullptr;
46 WGPUBindGroup bindGroup =
nullptr;
47 WGPUBuffer uniformBuffer =
nullptr;
48 uint32_t uniformBufferSize = 0;
49 WGPURenderPipeline renderPipeline =
nullptr;
51 WGPUTexture fontTexture =
nullptr;
52 WGPUTextureView fontTextureView =
nullptr;
53 WGPUSampler fontSampler =
nullptr;
55 int numFramesInFlight = 1;
61static ImGui_ImplWGPU_Data *ImGui_ImplWGPU_GetBackendData() {
62 return ImGui::GetCurrentContext() ? (ImGui_ImplWGPU_Data *)ImGui::GetIO().BackendRendererUserData
66static const char *ImGui_ImplWGPU_GetWGSLVertexShader() {
72 @location(0) position: vec2f,
73 @location(1) uv: vec2f,
74 @location(2) color: vec4f,
77 @builtin(position) position: vec4f,
78 @location(0) uv: vec2f,
79 @location(1) color: vec4f,
81@group(0) @binding(0) var<uniform> uniforms: Uniforms;
83fn vs_main(in: VertexInput) -> VertexOutput {
84 var out: VertexOutput;
85 out.position = uniforms.mvp * vec4f(in.position, 0.0, 1.0);
93static const char *ImGui_ImplWGPU_GetWGSLFragmentShader() {
96 @builtin(position) position: vec4f,
97 @location(0) uv: vec2f,
98 @location(1) color: vec4f,
100@group(0) @binding(1) var fontSampler: sampler;
101@group(0) @binding(2) var fontTexture: texture_2d<f32>;
103fn fs_main(in: VertexOutput) -> @location(0) vec4f {
104 return textureSample(fontTexture, fontSampler, in.uv) * in.color;
109static void ImGui_ImplWGPU_SetupRenderState(ImDrawData *draw_data,
110 WGPURenderPassEncoder pass_encoder) {
111 ImGui_ImplWGPU_Data *bd = ImGui_ImplWGPU_GetBackendData();
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;
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},
125 wgpuQueueWriteBuffer(bd->defaultQueue, bd->uniformBuffer, 0, mvp,
sizeof(mvp));
128 wgpuRenderPassEncoderSetPipeline(pass_encoder, bd->renderPipeline);
129 wgpuRenderPassEncoderSetBindGroup(pass_encoder, 0, bd->bindGroup, 0,
nullptr);
132static void ImGui_ImplWGPU_CreatePipeline() {
133 ImGui_ImplWGPU_Data *bd = ImGui_ImplWGPU_GetBackendData();
134 WGPUDevice
device = bd->wgpuDevice;
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);
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);
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);
172 WGPUPipelineLayoutDescriptor plDesc{};
173 plDesc.bindGroupLayoutCount = 1;
174 plDesc.bindGroupLayouts = &bd->bindGroupLayout;
175 WGPUPipelineLayout pipelineLayout = wgpuDeviceCreatePipelineLayout(
device, &plDesc);
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;
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;
202 WGPUColorTargetState colorTarget{};
203 colorTarget.format = bd->rtFormat;
204 colorTarget.blend = &blend;
205 colorTarget.writeMask = WGPUColorWriteMask_All;
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);
227static void ImGui_ImplWGPU_CreateUniformBuffer() {
228 ImGui_ImplWGPU_Data *bd = ImGui_ImplWGPU_GetBackendData();
229 WGPUDevice
device = bd->wgpuDevice;
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);
240 ImGuiIO &io = ImGui::GetIO();
241 ImGui_ImplWGPU_Data *bd = ImGui_ImplWGPU_GetBackendData();
242 WGPUDevice
device = bd->wgpuDevice;
244 unsigned char *pixels;
246 io.Fonts->GetTexDataAsRGBA32(&pixels, &
width, &
height, &size);
247 size_t stride =
static_cast<size_t>(
width) * 4;
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);
260 WGPUTexelCopyBufferLayout
layout{};
262 layout.bytesPerRow = stride;
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, ©Dst, pixels, stride *
height, &
layout, &extent);
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);
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);
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;
298 WGPUBindGroupDescriptor bgDesc{};
299 bgDesc.layout = bd->bindGroupLayout;
300 bgDesc.entryCount = 3;
301 bgDesc.entries = entries;
302 bd->bindGroup = wgpuDeviceCreateBindGroup(
device, &bgDesc);
304 io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->fontTextureView);
309 ImGui_ImplWGPU_Data *bd = ImGui_ImplWGPU_GetBackendData();
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);
323 ImGuiIO &io = ImGui::GetIO();
324 IM_ASSERT(io.BackendRendererUserData == NULL &&
"Already initialized a renderer backend!");
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;
332 bd->rtFormat = rt_format;
333 bd->numFramesInFlight = num_frames_in_flight;
334 bd->defaultQueue = wgpuDeviceGetQueue(
device);
336 ImGui_ImplWGPU_CreateUniformBuffer();
337 ImGui_ImplWGPU_CreatePipeline();
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;
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;
362 ImGui_ImplWGPU_Data *bd = ImGui_ImplWGPU_GetBackendData();
363 if (draw_data->CmdListsCount == 0)
return;
365 ImGui_ImplWGPU_SetupRenderState(draw_data, pass_encoder);
368 ImVec2 clip_off = draw_data->DisplayPos;
369 ImVec2 clip_scale = draw_data->FramebufferScale;
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;
375 for (
int n = 0;
n < draw_data->CmdListsCount;
n++) {
376 const ImDrawList *cmd_list = draw_data->CmdLists[
n];
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;
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);
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);
398 wgpuRenderPassEncoderSetVertexBuffer(pass_encoder, 0, vb, 0, vbSize);
399 wgpuRenderPassEncoderSetIndexBuffer(pass_encoder, ib,
400 sizeof(ImDrawIdx) == 2 ? WGPUIndexFormat_Uint16
401 : WGPUIndexFormat_Uint32,
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);
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;
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));
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);
446 wgpuRenderPassEncoderDrawIndexed(pass_encoder, pcmd->ElemCount, 1,
447 pcmd->IdxOffset, (int32_t)pcmd->VtxOffset, 0);
451 wgpuBufferRelease(vb);
452 wgpuBufferRelease(ib);
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()