Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 28 additions & 38 deletions backends/imgui_impl_wgpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2025-07-23: Update to latest version of webgpu.h
// 2025-06-12: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. (#8465)
// 2025-02-26: Recreate image bind groups during render. (#8426, #8046, #7765, #8027) + Update for latest webgpu-native changes.
// 2024-10-14: Update Dawn support for change of string usages. (#8082, #8083)
Expand All @@ -44,16 +45,17 @@
// 2021-01-28: Initial version.

#include "imgui.h"
#include <cstdlib>

// When targeting native platforms (i.e. NOT emscripten), one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN
// or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be provided. See imgui_impl_wgpu.h for more details.
#ifndef __EMSCRIPTEN__
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) == defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
#error exactly one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be defined!
#endif
//#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) == defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
//#error exactly one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be defined!
//#endif
#else
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
#error neither IMGUI_IMPL_WEBGPU_BACKEND_DAWN nor IMGUI_IMPL_WEBGPU_BACKEND_WGPU may be defined if targeting emscripten!
//#error neither IMGUI_IMPL_WEBGPU_BACKEND_DAWN nor IMGUI_IMPL_WEBGPU_BACKEND_WGPU may be defined if targeting emscripten!
#endif
#endif

Expand Down Expand Up @@ -254,11 +256,11 @@ static void SafeRelease(FrameResources& res)
SafeRelease(res.VertexBufferHost);
}

static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const char* wgsl_source)
static WGPUShaderModule ImGui_ImplWGPU_CreateShaderModule(const char* wgsl_source)
{
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData();

#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK)
WGPUShaderSourceWGSL wgsl_desc = {};
wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
wgsl_desc.code = { wgsl_source, WGPU_STRLEN };
Expand All @@ -271,15 +273,7 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const c
WGPUShaderModuleDescriptor desc = {};
desc.nextInChain = reinterpret_cast<WGPUChainedStruct*>(&wgsl_desc);

WGPUProgrammableStageDescriptor stage_desc = {};
stage_desc.module = wgpuDeviceCreateShaderModule(bd->wgpuDevice, &desc);

#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
stage_desc.entryPoint = { "main", WGPU_STRLEN };
#else
stage_desc.entryPoint = "main";
#endif
return stage_desc;
return wgpuDeviceCreateShaderModule(bd->wgpuDevice, &desc);
}

static WGPUBindGroup ImGui_ImplWGPU_CreateImageBindGroup(WGPUBindGroupLayout layout, WGPUTextureView texture)
Expand Down Expand Up @@ -367,7 +361,7 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
// Avoid rendering when minimized
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (fb_width <= 0 || fb_height <= 0 || draw_data->CmdLists.Size == 0)
if (fb_width <= 0 || fb_height <= 0 || draw_data->CmdListsCount == 0)
return;

// Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
Expand Down Expand Up @@ -442,8 +436,9 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
// Upload vertex/index data into a single contiguous GPU buffer
ImDrawVert* vtx_dst = (ImDrawVert*)fr->VertexBufferHost;
ImDrawIdx* idx_dst = (ImDrawIdx*)fr->IndexBufferHost;
for (const ImDrawList* draw_list : draw_data->CmdLists)
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* draw_list = draw_data->CmdLists[n];
memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert));
memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx));
vtx_dst += draw_list->VtxBuffer.Size;
Expand All @@ -470,8 +465,9 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
int global_idx_offset = 0;
ImVec2 clip_scale = draw_data->FramebufferScale;
ImVec2 clip_off = draw_data->DisplayPos;
for (const ImDrawList* draw_list : draw_data->CmdLists)
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* draw_list = draw_data->CmdLists[n];
for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
Expand Down Expand Up @@ -510,7 +506,7 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
continue;

// Apply scissor/clipping rectangle, Draw
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));
//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));
wgpuRenderPassEncoderDrawIndexed(pass_encoder, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
}
}
Expand Down Expand Up @@ -560,11 +556,7 @@ void ImGui_ImplWGPU_UpdateTexture(ImTextureData* tex)

// Create texture
WGPUTextureDescriptor tex_desc = {};
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
tex_desc.label = { "Dear ImGui Texture", WGPU_STRLEN };
#else
tex_desc.label = "Dear ImGui Texture";
#endif
tex_desc.dimension = WGPUTextureDimension_2D;
tex_desc.size.width = tex->Width;
tex_desc.size.height = tex->Height;
Expand Down Expand Up @@ -696,14 +688,14 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
graphics_pipeline_desc.layout = wgpuDeviceCreatePipelineLayout(bd->wgpuDevice, &layout_desc);

// Create the vertex shader
WGPUProgrammableStageDescriptor vertex_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__shader_vert_wgsl);
graphics_pipeline_desc.vertex.module = vertex_shader_desc.module;
graphics_pipeline_desc.vertex.entryPoint = vertex_shader_desc.entryPoint;
WGPUShaderModule vertex_shader_module = ImGui_ImplWGPU_CreateShaderModule(__shader_vert_wgsl);
graphics_pipeline_desc.vertex.module = vertex_shader_module;
graphics_pipeline_desc.vertex.entryPoint = WGPUStringView{"main", sizeof("main") - 1};

// Vertex input configuration
WGPUVertexAttribute attribute_desc[] =
{
#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(WEBGPU_BACKEND_EMDAWNWEBGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK)
{ nullptr, WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, pos), 0 },
{ nullptr, WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, uv), 1 },
{ nullptr, WGPUVertexFormat_Unorm8x4, (uint64_t)offsetof(ImDrawVert, col), 2 },
Expand All @@ -724,7 +716,7 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
graphics_pipeline_desc.vertex.buffers = buffer_layouts;

// Create the pixel shader
WGPUProgrammableStageDescriptor pixel_shader_desc = ImGui_ImplWGPU_CreateShaderModule(__shader_frag_wgsl);
WGPUShaderModule pixel_shader_module = ImGui_ImplWGPU_CreateShaderModule(__shader_frag_wgsl);

// Create the blending setup
WGPUBlendState blend_state = {};
Expand All @@ -741,8 +733,8 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
color_state.writeMask = WGPUColorWriteMask_All;

WGPUFragmentState fragment_state = {};
fragment_state.module = pixel_shader_desc.module;
fragment_state.entryPoint = pixel_shader_desc.entryPoint;
fragment_state.module = pixel_shader_module;
fragment_state.entryPoint = WGPUStringView{"main", sizeof("main") - 1};
fragment_state.targetCount = 1;
fragment_state.targets = &color_state;

Expand All @@ -751,11 +743,7 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
// Create depth-stencil State
WGPUDepthStencilState depth_stencil_state = {};
depth_stencil_state.format = bd->depthStencilFormat;
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
depth_stencil_state.depthWriteEnabled = WGPUOptionalBool_False;
#else
depth_stencil_state.depthWriteEnabled = false;
#endif
depth_stencil_state.depthCompare = WGPUCompareFunction_Always;
depth_stencil_state.stencilFront.compare = WGPUCompareFunction_Always;
depth_stencil_state.stencilFront.failOp = WGPUStencilOperation_Keep;
Expand All @@ -770,7 +758,9 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
graphics_pipeline_desc.depthStencil = (bd->depthStencilFormat == WGPUTextureFormat_Undefined) ? nullptr : &depth_stencil_state;

bd->pipelineState = wgpuDeviceCreateRenderPipeline(bd->wgpuDevice, &graphics_pipeline_desc);

if(bd->pipelineState == nullptr){
abort();
}
ImGui_ImplWGPU_CreateUniformBuffer();

// Create sampler
Expand Down Expand Up @@ -798,8 +788,8 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
bd->renderResources.CommonBindGroup = wgpuDeviceCreateBindGroup(bd->wgpuDevice, &common_bg_descriptor);
bd->renderResources.ImageBindGroupLayout = bg_layouts[1];

SafeRelease(vertex_shader_desc.module);
SafeRelease(pixel_shader_desc.module);
SafeRelease(vertex_shader_module);
SafeRelease(pixel_shader_module);
SafeRelease(graphics_pipeline_desc.layout);
SafeRelease(bg_layouts[0]);

Expand Down Expand Up @@ -905,4 +895,4 @@ void ImGui_ImplWGPU_NewFrame()

//-----------------------------------------------------------------------------

#endif // #ifndef IMGUI_DISABLE
#endif // #ifndef IMGUI_DISABLE
5 changes: 2 additions & 3 deletions examples/example_glfw_wgpu/Makefile.emscripten
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ EMS =

# ("EMS" options gets added to both CPPFLAGS and LDFLAGS, whereas some options are for linker only)
# Note: For glfw, we use emscripten-glfw port (contrib.glfw3) instead of (-s USE_GLFW=3) to get a better support for High DPI displays.
EMS += -s DISABLE_EXCEPTION_CATCHING=1 --use-port=contrib.glfw3
LDFLAGS += -s USE_WEBGPU=1
LDFLAGS += -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s NO_EXIT_RUNTIME=0 -s ASSERTIONS=1
EMS += -DWEBGPU_BACKEND_EMDAWNWEBGPU -DIMGUI_IMPL_WEBGPU_BACKEND_DAWN -sDISABLE_EXCEPTION_CATCHING=1 --use-port=contrib.glfw3 --use-port=emdawnwebgpu
LDFLAGS += -sASYNCIFY=2 -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s NO_EXIT_RUNTIME=0 -s ASSERTIONS=1

# Build as single file (binary text encoded in .html file)
#LDFLAGS += -sSINGLE_FILE
Expand Down
31 changes: 29 additions & 2 deletions examples/example_glfw_wgpu/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
## How to Build

### Desktop Builds

- Download and compile one of the three WebGPU on desktop implementations:
- [Dawn](https://dawn.googlesource.com/dawn)
- [WGPU](https://github.com/gfx-rs/wgpu-native) (requires Rust)
- [WGVK](https://github.com/manuel5975p/WGVK) (Lightweight, Vulkan only)


Once compiled, imgui's backend code can be compiled and linked to the webgpu implementation library, as an example to WGVK on X11:

```
g++ -o example -D_GLFW_X11 -DIMGUI_IMPL_WEBGPU_BACKEND_DAWN imgui_demo.cpp imgui.cpp imgui_draw.cpp imgui_widgets.cpp imgui_tables.cpp examples/example_glfw_wgpu/main.cpp backends/imgui_impl_wgpu.cpp backends/imgui_impl_glfw.cpp -I . -I backends -I <path_to_WGVK>/include/ <path_to_WGVK>/build/libwgvk.a
```

This command is assumed to be run from imgui's root directory.

Explanation of the options:
- `-D_GLFW_X11` required to expose X11 handles. Replace with `-D_GLFW_WAYLAND` for wayland or `-D_GLFW_WIN32` for windows
- `-DIMGUI_IMPL_WEBGPU_BACKEND_DAWN` because WGVK mimics dawn
- `-I . -I backends` Include paths for imgui
- `-I <path_to_WGVK>/include/` include path for `<webgpu/webgpu.h>`

### Web Builds
- You need to install Emscripten from https://emscripten.org/docs/getting_started/downloads.html, and have the environment variables set, as described in https://emscripten.org/docs/getting_started/downloads.html#installation-instructions

- Depending on your configuration, in Windows you may need to run `emsdk/emsdk_env.bat` in your console to access the Emscripten command-line tools.
Expand All @@ -10,10 +33,14 @@

- Requires recent Emscripten as WGPU is still a work-in-progress API.

## How to Run
## How to run Web Builds

To run on a local machine:
- Make sure your browse supports WGPU and it is enabled. WGPU is still WIP not enabled by default in most browser.
- For Chrome:
- Enable the experimental flags JSPI and WebGPU in chrome://flags/
- For Firefox:
- Enable the experimental flags wasm_js_promise_integration and WebGPU in about:config

- `make serve` will use Python3 to spawn a local webserver, you can then browse http://localhost:8000 to access your build.
- Otherwise, generally you will need a local webserver:
- Quoting [https://emscripten.org/docs/getting_started](https://emscripten.org/docs/getting_started/Tutorial.html#generating-html):<br>
Expand Down
Loading