Backends, Examples: WGPU: wip refactor. (8381)

Rebased and squashed 47 commits.
- ImGui WebGPU examples: removed swap-chain in favor of surfaceConfigure (8191)
- New SDL2 WGPU example
- Code optimization: removed the redundancies of assignment
- Changes: ImGui code style - Lambdas RequestAdapter and RequestDevice callbacks declarated as signature - Validation Layer callbacks moved from lambdas to standard functions
- Same changes of GLFW example: ImGui code style - Lambdas RequestAdapter and RequestDevice callbacks declarated as signature - Validation Layer callbacks moved from lambdas to standard functions
- Scrollbars inhibition
- Use of new direct CreateDevice function (w/o callback), added release of all resources used (not present in original example)
- If the O.S. is Linux/Unix check if Wayland is the current active session and set DAWN_USE_WAYLAND=ON option (otherwise is always OFF)
- example_glfw_wgpu: removed all workarounds
- example_sdl2_wgpu: same style and functionality as GLFW example
- sdl2wgpu tool to acquire surfaceDescriptor via SDL_syswm and to pass to wgpuInstanceCreateSurface to create WGPU Surface
- css style to avoid the scrollbar
- added `chek_surface_texture_status` function to check the `WGPUSurfaceTexture .status` and recreate the `Surface` in case of "not optimal" (bad) status.
- Changed comment reference to `emwgpudawn` an EMSCRIPTEN WGPU binding maintained by Google
- Adaptation to the last Google DAWN commit (1411699ba)
Adaptation to the last EMSCRIPTEN 4.0.10, using new "--use-port=emdawnwebgpu" compiler/linker flag
- Support for EMSCRIPTEN >= 4.0.10 (using "--use-port=emdawnwebgpu") and NEW support for WGPU-Native
- Finalized the support of WGPU-Native for MacOS and minimal code adjustment
- WebGPU examples - DAWN / WGPU native and EMSCRIPTEN - for GLFW/SDL2/SDL3 frameworks
- "index.html" no more necessary (now the common one is used), "sdl2wgpu.cpp" It has been moved and renamed (sdl2_wgpu.c), "sdl2wgpu.h" no more necessary
- added procedure for using CMake
- added procedure for using CMake
- Updated example_sdl3_wgpu build procedure for EMSCRIPTEN
- WGPU+GLFW: Helper to create a WebGPU surface for Native application. Used only with WGPU-Native SDK: DAWN-Native already has a built-in function
- WGPU+SDL2: helper to create a WebGPU surface (exclusively!) for Native/Desktop applications and available only together with WebGPU/WGPU backend
- WGPU+SDL3: helper to create a WebGPU surface (exclusively!) for Native/Desktop applications and available only together with WebGPU/WGPU backend
- WebGPU Helper functions and differentiation between the 4 compilation methods (via defines): Google DAWN (Native/Emscripten) / WGPU (Native/EMscripten)
- example_glfw_wgpu: ImGui_ImplGLFW_CreateWGPUSurface_Helper (new helper function in imgui_impl_glfw backend), check status and error callback functions in imgui_impl_wgpu backend
- example_sdl2_wgpu: ImGui_ImplSDL2_CreateWGPUSurface_Helper (new helper function in imgui_impl_sdl2 backend), check status and error callback functions in imgui_impl_wgpu backend
- example_sdl3_wgpu: ImGui_ImplSDL3_CreateWGPUSurface_Helper (new helper function in imgui_impl_sdl3 backend), check status and error callback functions in imgui_impl_wgpu backend
- Functions ImGui_ImplXXXX_CreateWGPUSurface_Helper were inserted into imgui_impl_xxxx (xxxx = GLFW / SDL2 / SDL3), and initialization has been integrated into every example: no more necessary, removed
This commit is contained in:
BrutPitt
2025-02-07 06:00:17 +01:00
committed by ocornut
parent cee40f8af9
commit 46ca8bc16f
20 changed files with 2585 additions and 290 deletions

View File

@@ -1049,6 +1049,97 @@ void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow* window, const char* c
} }
#endif // #ifdef EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 #endif // #ifdef EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3
// GLFW helper to create a WebGPU surface, used only in WGPU-Native, DAWN-Native already has a built-in function
// At current date (jun/2025) there is no "official" support in GLFW to create a surface for WebGPU backend
// This stub uses "low level" GLFW calls to acquire information from a specific Window Manager.
// Currently supported platforms: Windows / Linux (X11 and Wayland) / MacOS
// Not necessary/available with EMSCRIPTEN
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) && !defined(__EMSCRIPTEN__)
// GLFW native necessary to get information about current platform / Window Manager
#include <GLFW/glfw3native.h>
// MacOS specific: is necessary to compile with "-x objective-c++" flags
// (e.g. using cmake: set_source_files_properties(${IMGUI_DIR}/backends/imgui_impl_glfw.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++") )
#if defined(GLFW_EXPOSE_NATIVE_COCOA)
#include <Foundation/Foundation.h>
#include <QuartzCore/CAMetalLayer.h>
#endif
WGPUSurface ImGui_ImplGLFW_CreateWGPUSurface_Helper(WGPUInstance instance, GLFWwindow* window)
{
WGPUSurfaceDescriptor surfaceDescriptor = {};
WGPUChainedStruct chainedStruct = {};
WGPUSurface surface = {};
#if defined(GLFW_EXPOSE_NATIVE_COCOA)
{
id metal_layer = NULL;
NSWindow *ns_window = glfwGetCocoaWindow(window);
[ns_window.contentView setWantsLayer:YES];
metal_layer = [CAMetalLayer layer];
[ns_window.contentView setLayer:metal_layer];
chainedStruct.sType = WGPUSType_SurfaceSourceMetalLayer;
WGPUSurfaceSourceMetalLayer surfaceMetal = {};
surfaceMetal.chain = chainedStruct;
surfaceMetal.layer = metal_layer;
surfaceDescriptor.nextInChain = &surfaceMetal.chain;
surface = wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
}
#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) && defined(GLFW_EXPOSE_NATIVE_X11)
if (glfwGetPlatform() == GLFW_PLATFORM_X11) {
Display *x11_display = glfwGetX11Display();
Window x11_window = glfwGetX11Window(window);
chainedStruct.sType = WGPUSType_SurfaceSourceXlibWindow;
WGPUSurfaceSourceXlibWindow surfaceXlib = {};
surfaceXlib.chain = chainedStruct;
surfaceXlib.display = x11_display;
surfaceXlib.window = x11_window;
surfaceDescriptor.nextInChain = &surfaceXlib.chain;
surface = wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
}
if (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND) {
struct wl_display *wayland_display = glfwGetWaylandDisplay();
struct wl_surface *wayland_surface = glfwGetWaylandWindow(window);
chainedStruct.sType = WGPUSType_SurfaceSourceWaylandSurface;
WGPUSurfaceSourceWaylandSurface surfaceWayland = {};
surfaceWayland.chain = chainedStruct;
surfaceWayland.display = wayland_display;
surfaceWayland.surface = wayland_surface;
surfaceDescriptor.nextInChain = &surfaceWayland.chain;
surface = wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
}
#elif defined(GLFW_EXPOSE_NATIVE_WIN32)
{
HWND hwnd = glfwGetWin32Window(window);
HINSTANCE hinstance = GetModuleHandle(NULL);
chainedStruct.sType = WGPUSType_SurfaceSourceWindowsHWND;
WGPUSurfaceSourceWindowsHWND surfaceHWND = {};
surfaceHWND.chain = chainedStruct;
surfaceHWND.hinstance = hinstance;
surfaceHWND.hwnd = hwnd;
surfaceDescriptor.nextInChain = &surfaceHWND.chain;
surface = wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
}
#elif
#error "Unsupported GLFW/WebGPU native platform"
#endif
return surface;
}
#endif // defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) && !defined(__EMSCRIPTEN__)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#if defined(__clang__) #if defined(__clang__)

View File

@@ -66,5 +66,11 @@ IMGUI_IMPL_API void ImGui_ImplGlfw_Sleep(int milliseconds);
IMGUI_IMPL_API float ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window); IMGUI_IMPL_API float ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window);
IMGUI_IMPL_API float ImGui_ImplGlfw_GetContentScaleForMonitor(GLFWmonitor* monitor); IMGUI_IMPL_API float ImGui_ImplGlfw_GetContentScaleForMonitor(GLFWmonitor* monitor);
// GLFW helper to create a WebGPU surface for Native/Desktop applications: used only in WGPU-Native, DAWN-Native already has a built-in function
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) && !defined(__EMSCRIPTEN__)
#include <webgpu/webgpu.h>
WGPUSurface ImGui_ImplGLFW_CreateWGPUSurface_Helper(WGPUInstance instance, GLFWwindow* window);
#endif
#endif // #ifndef IMGUI_DISABLE #endif // #ifndef IMGUI_DISABLE

View File

@@ -899,6 +899,92 @@ void ImGui_ImplSDL2_NewFrame()
ImGui_ImplSDL2_UpdateGamepads(); ImGui_ImplSDL2_UpdateGamepads();
} }
// SDL2 helper to create a WebGPU surface (exclusively!) for Native/Desktop applications: available only together with WebGPU/WGPU backend
// At current date (jun/2025) there is no "official" support in SDL2 to create a surface for WebGPU backend
// This stub uses "low level" SDL2 calls to acquire information from a specific Window Manager.
// Currently supported platforms: Windows / Linux (X11 and Wayland) / MacOS
// Not necessary/available with EMSCRIPTEN
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) && !defined(__EMSCRIPTEN__)
#ifdef SDL_VIDEO_DRIVER_COCOA
// MacOS specific: is necessary to compile with "-x objective-c++" flags
// (e.g. using cmake: set_source_files_properties(${IMGUI_DIR}/backends/imgui_impl_sdl2.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++") )
#include <Cocoa/Cocoa.h>
#include <QuartzCore/CAMetalLayer.h>
#endif
WGPUSurface ImGui_ImplSDL2_CreateWGPUSurface_Helper(WGPUInstance instance, SDL_Window* window)
{
WGPUSurfaceDescriptor surfaceDescriptor = {};
WGPUChainedStruct chainedStruct = {};
SDL_SysWMinfo sysWMInfo;
SDL_VERSION(&sysWMInfo.version);
SDL_GetWindowWMInfo(window, &sysWMInfo);
WGPUSurface surface = {};
#if defined(SDL_VIDEO_DRIVER_WAYLAND) || defined(SDL_VIDEO_DRIVER_X11)
const char *vidDrv = SDL_GetHint(SDL_HINT_VIDEODRIVER);
if(!vidDrv) return NULL;
if(tolower(vidDrv[0])=='w' && tolower(vidDrv[1])=='a' && tolower(vidDrv[2])=='y' &&
tolower(vidDrv[3])=='l' && tolower(vidDrv[4])=='a' && tolower(vidDrv[5])=='n' && tolower(vidDrv[6])=='d') { // wayland
chainedStruct.sType = WGPUSType_SurfaceSourceWaylandSurface;
WGPUSurfaceSourceWaylandSurface surfaceWayland = {};
surfaceWayland.chain = chainedStruct;
surfaceWayland.display = sysWMInfo.info.wl.display;
surfaceWayland.surface = sysWMInfo.info.wl.surface;
surfaceDescriptor.nextInChain = &surfaceWayland.chain;
surface = wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
} else { // x11
chainedStruct.sType = WGPUSType_SurfaceSourceXlibWindow;
WGPUSurfaceSourceXlibWindow surfaceXlib = {};
surfaceXlib.chain = chainedStruct;
surfaceXlib.display = sysWMInfo.info.x11.display;
surfaceXlib.window = sysWMInfo.info.x11.window;
surfaceDescriptor.nextInChain = &surfaceXlib.chain;
surface = wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
}
#elif defined(SDL_VIDEO_DRIVER_WINDOWS)
{
chainedStruct.sType = WGPUSType_SurfaceSourceWindowsHWND;
WGPUSurfaceSourceWindowsHWND surfaceHWND = {};
surfaceHWND.chain = chainedStruct;
surfaceHWND.hinstance = sysWMInfo.info.win.hinstance;
surfaceHWND.hwnd = sysWMInfo.info.win.window;
surfaceDescriptor.nextInChain = &surfaceHWND.chain;
surface = wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
}
#elif defined(SDL_VIDEO_DRIVER_COCOA)
{
id metal_layer = [CAMetalLayer layer];
NSWindow *ns_window = sysWMInfo.info.cocoa.window;
[ns_window.contentView setWantsLayer:YES];
[ns_window.contentView setLayer:metal_layer];
chainedStruct.sType = WGPUSType_SurfaceSourceMetalLayer;
WGPUSurfaceSourceMetalLayer surfaceMetal = {};
surfaceMetal.chain = chainedStruct;
surfaceMetal.layer = metal_layer;
surfaceDescriptor.nextInChain = &surfaceMetal.chain;
surface = wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
}
#else
#error "Unsupported SDL2/WebGPU Backend"
#endif
return surface;
}
#endif //defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) && !defined(__EMSCRIPTEN__)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#if defined(__clang__) #if defined(__clang__)

View File

@@ -47,4 +47,10 @@ IMGUI_IMPL_API float ImGui_ImplSDL2_GetContentScaleForDisplay(int display_ind
enum ImGui_ImplSDL2_GamepadMode { ImGui_ImplSDL2_GamepadMode_AutoFirst, ImGui_ImplSDL2_GamepadMode_AutoAll, ImGui_ImplSDL2_GamepadMode_Manual }; enum ImGui_ImplSDL2_GamepadMode { ImGui_ImplSDL2_GamepadMode_AutoFirst, ImGui_ImplSDL2_GamepadMode_AutoAll, ImGui_ImplSDL2_GamepadMode_Manual };
IMGUI_IMPL_API void ImGui_ImplSDL2_SetGamepadMode(ImGui_ImplSDL2_GamepadMode mode, struct _SDL_GameController** manual_gamepads_array = nullptr, int manual_gamepads_count = -1); IMGUI_IMPL_API void ImGui_ImplSDL2_SetGamepadMode(ImGui_ImplSDL2_GamepadMode mode, struct _SDL_GameController** manual_gamepads_array = nullptr, int manual_gamepads_count = -1);
// SDL2 helper to create a WebGPU surface (exclusively!) for Native/Desktop applications: available only together with WebGPU/WGPU backend
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) && !defined(__EMSCRIPTEN__)
#include <webgpu/webgpu.h>
WGPUSurface ImGui_ImplSDL2_CreateWGPUSurface_Helper(WGPUInstance instance, SDL_Window* window);
#endif
#endif // #ifndef IMGUI_DISABLE #endif // #ifndef IMGUI_DISABLE

View File

@@ -839,6 +839,91 @@ void ImGui_ImplSDL3_NewFrame()
ImGui_ImplSDL3_UpdateGamepads(); ImGui_ImplSDL3_UpdateGamepads();
} }
// SDL3 helper to create a WebGPU surface (exclusively!) for Native/Desktop applications: available only together with WebGPU/WGPU backend
// At current date (jun/2025) there is no "official" support in SDL3 to create a surface for WebGPU backend
// This stub uses "low level" SDL3 calls to acquire information from a specific Window Manager.
// Currently supported platforms: Windows / Linux (X11 and Wayland) / MacOS
// Not necessary/available with EMSCRIPTEN
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) && !defined(__EMSCRIPTEN__)
#if defined(SDL_PLATFORM_MACOS)
// MacOS specific: is necessary to compile with "-x objective-c++" flags
// (e.g. using cmake: set_source_files_properties(${IMGUI_DIR}/backends/imgui_impl_sdl3.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++") )
# include <Cocoa/Cocoa.h>
# include <QuartzCore/CAMetalLayer.h>
#elif defined(SDL_PLATFORM_WIN32)
# include <windows.h>
#endif
WGPUSurface ImGui_ImplSDL3_CreateWGPUSurface_Helper(WGPUInstance instance, SDL_Window* window) {
SDL_PropertiesID propertiesID = SDL_GetWindowProperties(window);
WGPUSurfaceDescriptor surfaceDescriptor = {};
WGPUSurface surface = {};
#if defined(SDL_PLATFORM_MACOS)
{
id metal_layer = NULL;
NSWindow *ns_window = (__bridge NSWindow *)SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, NULL);
if (!ns_window) return NULL;
[ns_window.contentView setWantsLayer : YES];
metal_layer = [CAMetalLayer layer];
[ns_window.contentView setLayer : metal_layer];
WGPUSurfaceSourceMetalLayer surfaceMetal = {};
surfaceMetal.chain.sType = WGPUSType_SurfaceSourceMetalLayer;
surfaceMetal.layer = metal_layer;
surfaceDescriptor.nextInChain = &surfaceMetal.chain;
surface = wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
}
#elif defined(SDL_PLATFORM_LINUX)
if (!SDL_strcmp(SDL_GetCurrentVideoDriver(), "wayland")) {
void *w_display = SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER, NULL);
void *w_surface = SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER, NULL);
if (!w_display || !w_surface) return NULL;
WGPUSurfaceSourceWaylandSurface surfaceWayland = {};
surfaceWayland.chain.sType = WGPUSType_SurfaceSourceWaylandSurface;
surfaceWayland.display = w_display;
surfaceWayland.surface = w_surface;
surfaceDescriptor.nextInChain = &surfaceWayland.chain;
surface = wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
} else if (!SDL_strcmp(SDL_GetCurrentVideoDriver(), "x11")) {
void *x_display = SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, NULL);
uint64_t x_window = SDL_GetNumberProperty(propertiesID, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
if (!x_display || !x_window) return NULL;
WGPUSurfaceSourceXlibWindow surfaceXlib = {};
surfaceXlib.chain.sType = WGPUSType_SurfaceSourceXlibWindow;
surfaceXlib.display = x_display;
surfaceXlib.window = x_window;
surfaceDescriptor.nextInChain = &surfaceXlib.chain;
surface = wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
}
#elif defined(SDL_PLATFORM_WIN32)
{
HWND hwnd = (HWND)SDL_GetPointerProperty(propertiesID, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
if (!hwnd) return NULL;
HINSTANCE hinstance = GetModuleHandle(NULL);
WGPUSurfaceSourceWindowsHWND surfaceHWND = {};
surfaceHWND.chain.sType = WGPUSType_SurfaceSourceWindowsHWND;
surfaceHWND.hinstance = hinstance;
surfaceHWND.hwnd = hwnd;
surfaceDescriptor.nextInChain = &surfaceHWND.chain;
surface = wgpuInstanceCreateSurface(instance, &surfaceDescriptor);
}
#else
#error "Unsupported SDL3/WebGPU Backend"
#endif
return surface;
}
#endif // defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) && !defined(__EMSCRIPTEN__)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#if defined(__clang__) #if defined(__clang__)

View File

@@ -44,4 +44,10 @@ IMGUI_IMPL_API bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event);
enum ImGui_ImplSDL3_GamepadMode { ImGui_ImplSDL3_GamepadMode_AutoFirst, ImGui_ImplSDL3_GamepadMode_AutoAll, ImGui_ImplSDL3_GamepadMode_Manual }; enum ImGui_ImplSDL3_GamepadMode { ImGui_ImplSDL3_GamepadMode_AutoFirst, ImGui_ImplSDL3_GamepadMode_AutoAll, ImGui_ImplSDL3_GamepadMode_Manual };
IMGUI_IMPL_API void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array = nullptr, int manual_gamepads_count = -1); IMGUI_IMPL_API void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array = nullptr, int manual_gamepads_count = -1);
// SDL3 helper to create a WebGPU surface for Native/Desktop applications: available only with WebGPU/WGPU backend
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) && !defined(__EMSCRIPTEN__)
#include <webgpu/webgpu.h>
WGPUSurface ImGui_ImplSDL3_CreateWGPUSurface_Helper(WGPUInstance instance, SDL_Window* window);
#endif
#endif // #ifndef IMGUI_DISABLE #endif // #ifndef IMGUI_DISABLE

View File

@@ -52,10 +52,12 @@
#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)
#error exactly one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be defined! #error exactly one of IMGUI_IMPL_WEBGPU_BACKEND_DAWN or IMGUI_IMPL_WEBGPU_BACKEND_WGPU must be defined!
#endif #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!
#endif #endif
// This condition is TRUE: when it's built with EMSCRIPTEN using -sUSE_WEBGPU=1 flag (deprecated from 4.0.10)
// This condition is FALSE for all other 3 cases: WGPU-Native, DAWN-Native or DAWN-EMSCRIPTEN (using --use-port=emdawnwebgpu flag)
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) && defined(__EMSCRIPTEN__)
#define IMGUI_IMPL_WEBGPU_BACKEND_WGPU_EMSCRIPTEN
#endif #endif
#ifndef IMGUI_DISABLE #ifndef IMGUI_DISABLE
@@ -63,6 +65,9 @@
#include <limits.h> #include <limits.h>
#include <webgpu/webgpu.h> #include <webgpu/webgpu.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN #ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN
// Dawn renamed WGPUProgrammableStageDescriptor to WGPUComputeState (see: https://github.com/webgpu-native/webgpu-headers/pull/413) // Dawn renamed WGPUProgrammableStageDescriptor to WGPUComputeState (see: https://github.com/webgpu-native/webgpu-headers/pull/413)
// Using type alias until WGPU adopts the same naming convention (#8369) // Using type alias until WGPU adopts the same naming convention (#8369)
@@ -259,7 +264,7 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const c
{ {
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); 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_WGPU_EMSCRIPTEN)
WGPUShaderSourceWGSL wgsl_desc = {}; WGPUShaderSourceWGSL wgsl_desc = {};
wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL; wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
wgsl_desc.code = { wgsl_source, WGPU_STRLEN }; wgsl_desc.code = { wgsl_source, WGPU_STRLEN };
@@ -275,7 +280,7 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const c
WGPUProgrammableStageDescriptor stage_desc = {}; WGPUProgrammableStageDescriptor stage_desc = {};
stage_desc.module = wgpuDeviceCreateShaderModule(bd->wgpuDevice, &desc); stage_desc.module = wgpuDeviceCreateShaderModule(bd->wgpuDevice, &desc);
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) #if !defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU_EMSCRIPTEN)
stage_desc.entryPoint = { "main", WGPU_STRLEN }; stage_desc.entryPoint = { "main", WGPU_STRLEN };
#else #else
stage_desc.entryPoint = "main"; stage_desc.entryPoint = "main";
@@ -399,7 +404,7 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
{ {
nullptr, nullptr,
"Dear ImGui Vertex buffer", "Dear ImGui Vertex buffer",
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) #if !defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU_EMSCRIPTEN)
WGPU_STRLEN, WGPU_STRLEN,
#endif #endif
WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex, WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex,
@@ -426,7 +431,7 @@ void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURenderPassEncoder
{ {
nullptr, nullptr,
"Dear ImGui Index buffer", "Dear ImGui Index buffer",
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) #if !defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU_EMSCRIPTEN)
WGPU_STRLEN, WGPU_STRLEN,
#endif #endif
WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index, WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index,
@@ -561,7 +566,7 @@ void ImGui_ImplWGPU_UpdateTexture(ImTextureData* tex)
// Create texture // Create texture
WGPUTextureDescriptor tex_desc = {}; WGPUTextureDescriptor tex_desc = {};
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) #if !defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU_EMSCRIPTEN)
tex_desc.label = { "Dear ImGui Texture", WGPU_STRLEN }; tex_desc.label = { "Dear ImGui Texture", WGPU_STRLEN };
#else #else
tex_desc.label = "Dear ImGui Texture"; tex_desc.label = "Dear ImGui Texture";
@@ -606,7 +611,7 @@ void ImGui_ImplWGPU_UpdateTexture(ImTextureData* tex)
// Update full texture or selected blocks. We only ever write to textures regions which have never been used before! // Update full texture or selected blocks. We only ever write to textures regions which have never been used before!
// This backend choose to use tex->UpdateRect but you can use tex->Updates[] to upload individual regions. // This backend choose to use tex->UpdateRect but you can use tex->Updates[] to upload individual regions.
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) #if !defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU_EMSCRIPTEN)
WGPUTexelCopyTextureInfo dst_view = {}; WGPUTexelCopyTextureInfo dst_view = {};
#else #else
WGPUImageCopyTexture dst_view = {}; WGPUImageCopyTexture dst_view = {};
@@ -615,7 +620,7 @@ void ImGui_ImplWGPU_UpdateTexture(ImTextureData* tex)
dst_view.mipLevel = 0; dst_view.mipLevel = 0;
dst_view.origin = { (uint32_t)upload_x, (uint32_t)upload_y, 0 }; dst_view.origin = { (uint32_t)upload_x, (uint32_t)upload_y, 0 };
dst_view.aspect = WGPUTextureAspect_All; dst_view.aspect = WGPUTextureAspect_All;
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) #if !defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU_EMSCRIPTEN)
WGPUTexelCopyBufferLayout layout = {}; WGPUTexelCopyBufferLayout layout = {};
#else #else
WGPUTextureDataLayout layout = {}; WGPUTextureDataLayout layout = {};
@@ -638,7 +643,7 @@ static void ImGui_ImplWGPU_CreateUniformBuffer()
{ {
nullptr, nullptr,
"Dear ImGui Uniform buffer", "Dear ImGui Uniform buffer",
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) #if !defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU_EMSCRIPTEN)
WGPU_STRLEN, WGPU_STRLEN,
#endif #endif
WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform, WGPUBufferUsage_CopyDst | WGPUBufferUsage_Uniform,
@@ -752,7 +757,7 @@ bool ImGui_ImplWGPU_CreateDeviceObjects()
// Create depth-stencil State // Create depth-stencil State
WGPUDepthStencilState depth_stencil_state = {}; WGPUDepthStencilState depth_stencil_state = {};
depth_stencil_state.format = bd->depthStencilFormat; depth_stencil_state.format = bd->depthStencilFormat;
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) #if !defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU_EMSCRIPTEN)
depth_stencil_state.depthWriteEnabled = WGPUOptionalBool_False; depth_stencil_state.depthWriteEnabled = WGPUOptionalBool_False;
#else #else
depth_stencil_state.depthWriteEnabled = false; depth_stencil_state.depthWriteEnabled = false;
@@ -834,14 +839,18 @@ bool ImGui_ImplWGPU_Init(ImGui_ImplWGPU_InitInfo* init_info)
// Setup backend capabilities flags // Setup backend capabilities flags
ImGui_ImplWGPU_Data* bd = IM_NEW(ImGui_ImplWGPU_Data)(); ImGui_ImplWGPU_Data* bd = IM_NEW(ImGui_ImplWGPU_Data)();
io.BackendRendererUserData = (void*)bd; io.BackendRendererUserData = (void*)bd;
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
#if defined(__EMSCRIPTEN__) #if defined(__EMSCRIPTEN__)
io.BackendRendererName = "imgui_impl_webgpu_emscripten"; io.BackendRendererName = "imgui_impl_webgpu_dawn_emscripten"; // compiled & linked using EMSCRIPTEN with "--use-port=emdawnwebgpu" flag
#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
io.BackendRendererName = "imgui_impl_webgpu_dawn";
#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
io.BackendRendererName = "imgui_impl_webgpu_wgpu";
#else #else
io.BackendRendererName = "imgui_impl_webgpu"; io.BackendRendererName = "imgui_impl_webgpu_dawn"; // DAWN-Native
#endif
#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
#if defined(__EMSCRIPTEN__)
io.BackendRendererName = "imgui_impl_webgpu_wgpu_emscripten"; // linked using EMSCRIPTEN with "-sUSE_WEBGPU=1" flag, deprecated from EMSCRIPTEN 4.0.10
#else
io.BackendRendererName = "imgui_impl_webgpu_wgpu"; // WGPU-Native
#endif
#endif #endif
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render. io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
@@ -906,6 +915,106 @@ void ImGui_ImplWGPU_NewFrame()
IM_ASSERT(0 && "ImGui_ImplWGPU_CreateDeviceObjects() failed!"); IM_ASSERT(0 && "ImGui_ImplWGPU_CreateDeviceObjects() failed!");
} }
// WebGPU Helpers
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
// DAWN Validation Layer callback: reason for device loss
void ImGui_ImplWGPU_DAWN_DeviceLostCallback_Helper(const wgpu::Device&, wgpu::DeviceLostReason reason, wgpu::StringView message)
{
const char* reasonName = "";
switch (reason) {
case wgpu::DeviceLostReason::Unknown: reasonName = "Unknown"; break;
case wgpu::DeviceLostReason::Destroyed: reasonName = "Destroyed"; break;
case wgpu::DeviceLostReason::CallbackCancelled: reasonName = "InstanceDropped"; break;
case wgpu::DeviceLostReason::FailedCreation: reasonName = "FailedCreation"; break;
default: reasonName = "UNREACHABLE"; break;
}
fprintf(stderr, "%s device message: %s\n", reasonName, message.data);
}
// DAWN Validation Layer callback: print error type
void ImGui_ImplWGPU_DAWN_ErrorCallback_Helper(const wgpu::Device&, wgpu::ErrorType type, wgpu::StringView message)
{
const char* errorTypeName = "";
switch (type) {
case wgpu::ErrorType::Validation: errorTypeName = "Validation"; break;
case wgpu::ErrorType::OutOfMemory: errorTypeName = "Out of memory"; break;
case wgpu::ErrorType::Unknown: errorTypeName = "Unknown"; break;
case wgpu::ErrorType::Internal: errorTypeName = "Internal"; break;
default: errorTypeName = "UNREACHABLE"; break;
}
fprintf(stderr, "%s error: %s\n", errorTypeName, message.data);
}
#elif !defined(__EMSCRIPTEN__)
// WGPU-Native LOG callback: print information based on request level
void ImGui_ImplWGPU_WGPU_LogCallback_Helper(WGPULogLevel level, WGPUStringView message, void *userdata)
{
const char *level_str = "";
switch (level) {
case WGPULogLevel_Error: level_str = "error"; break;
case WGPULogLevel_Warn: level_str = "warn"; break;
case WGPULogLevel_Info: level_str = "info"; break;
case WGPULogLevel_Debug: level_str = "debug"; break;
case WGPULogLevel_Trace: level_str = "trace"; break;
default: level_str = "unknown_level";
}
fprintf(stderr, "[wgpu] [%s] %.*s\n", level_str, (int) message.length, message.data);
}
#endif
/// Print Adapter info
///@param[in] adapter const WGPUAdapter & : reference to acquired and valid WGPUAdapter
///@note The function prints: "selected Adapter - drivers version, BackendType (#)"
void ImGui_ImplWGPU_PrintAdapterInfo_Helper(const WGPUAdapter &adapter)
{
WGPUAdapterInfo info = {};
wgpuAdapterGetInfo(adapter, &info);
#ifdef __EMSCRIPTEN__
printf("BackendType (%u)\n", info.backendType);
#else
printf("Using: %.*s - %.*s, BackendType (%u)\n", (int) info.device.length, info.device.data, (int) info.description.length, info.description.data, info.backendType);
#endif
}
/// Check if the Status of SurfaceTexture is Optimal
///@param[in] status WGPUSurfaceGetCurrentTextureStatus : current WGPUSurfaceTexture .status (value to check)
///@return true (bool) : SurfaceTexture have an optimal status and we can use it
///@return false (bool) : it's necessary to re-configure the SurfaceTexture
///@note : with "unrecoverable error" the program aborts
bool ImGui_ImplWGPU_CheckSurfaceTextureOptimalStatus_Helper(WGPUSurfaceGetCurrentTextureStatus status)
{
switch ( status )
{
#if defined(__EMSCRIPTEN__) && !defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
case WGPUSurfaceGetCurrentTextureStatus_Success:
return true;
#else
case WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal:
return true;
case WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal:
#endif
case WGPUSurfaceGetCurrentTextureStatus_Timeout:
case WGPUSurfaceGetCurrentTextureStatus_Outdated:
case WGPUSurfaceGetCurrentTextureStatus_Lost:
// if the status is NOT Optimal it's necessary try to reconfigure the surface
return false;
// Unrecoverable errors
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
case WGPUSurfaceGetCurrentTextureStatus_Error:
#else // IMGUI_IMPL_WEBGPU_BACKEND_WGPU
case WGPUSurfaceGetCurrentTextureStatus_OutOfMemory:
case WGPUSurfaceGetCurrentTextureStatus_DeviceLost:
#endif
case WGPUSurfaceGetCurrentTextureStatus_Force32:
// Fatal error
fprintf(stderr, "Unrecoverable Error Check Surface Texture status=%#.8x\n", status);
abort();
default: // should never be reached
fprintf(stderr, "Unexpected Error Check Surface Texture status=%#.8x\n", status);
abort();
}
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#endif // #ifndef IMGUI_DISABLE #endif // #ifndef IMGUI_DISABLE

View File

@@ -29,6 +29,14 @@
#include <webgpu/webgpu.h> #include <webgpu/webgpu.h>
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
// DAWN "wgpu::" classes (e.g. used in Validation Layers Callbacks)
#include <webgpu/webgpu_cpp.h>
#elif !defined(__EMSCRIPTEN__)
// WGPU-Native specific data structure (e.g. used in WGPULogLevel)
#include <webgpu/wgpu.h>
#endif
// Initialization data, for ImGui_ImplWGPU_Init() // Initialization data, for ImGui_ImplWGPU_Init()
struct ImGui_ImplWGPU_InitInfo struct ImGui_ImplWGPU_InitInfo
{ {
@@ -68,4 +76,20 @@ struct ImGui_ImplWGPU_RenderState
WGPURenderPassEncoder RenderPassEncoder; WGPURenderPassEncoder RenderPassEncoder;
}; };
// WebGPU Helpers
// Check if the Status of SurfaceTexture is Optimal
bool ImGui_ImplWGPU_CheckSurfaceTextureOptimalStatus_Helper(WGPUSurfaceGetCurrentTextureStatus status);
// Print Adapter info
void ImGui_ImplWGPU_PrintAdapterInfo_Helper(const WGPUAdapter &adapter);
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN) // DAWN both Native / EMSCRIPTEN
// DAWN Validation Layer callback: reason for device loss
void ImGui_ImplWGPU_DAWN_DeviceLostCallback_Helper(const wgpu::Device&, wgpu::DeviceLostReason reason, wgpu::StringView message);
// DAWN Validation Layer callback: print error type
void ImGui_ImplWGPU_DAWN_ErrorCallback_Helper(const wgpu::Device&, wgpu::ErrorType type, wgpu::StringView message);
#elif !defined(__EMSCRIPTEN__) // WGPU-Native
// WGPU-Native LOG callback: print information based on request level
void ImGui_ImplWGPU_WGPU_LogCallback_Helper(WGPULogLevel level, WGPUStringView message, void *userdata);
#endif
#endif // #ifndef IMGUI_DISABLE #endif // #ifndef IMGUI_DISABLE

142
examples/example_glfw_wgpu/CMakeLists.txt Normal file → Executable file
View File

@@ -6,6 +6,16 @@
# * build/Debug/example_glfw_wgpu[.exe] # * build/Debug/example_glfw_wgpu[.exe]
# * build/example_glfw_wgpu[.exe] # * build/example_glfw_wgpu[.exe]
# Building for desktop (WGPU-Native) with WGPU-Native:
# 1. download WGPU-Native autogenerated binary modules for your platform/compiler from: https://github.com/gfx-rs/wgpu-native/releases
# 2. unzip the downloaded file in your_preferred_folder
# 3. move into your_preferred_folder (e.g. typing: `cd your_preferred_folder`)
# 4. cmake -B build -DIMGUI_WGPU_DIR=your_preferred_folder ("full path" or "relative" starting from current directory)
# 5. cmake --build build
# The resulting binary will be found at one of the following locations:
# * build/Debug/example_glfw_wgpu[.exe]
# * build/example_glfw_wgpu[.exe]
# Building for Emscripten: # Building for Emscripten:
# 1. Install Emscripten SDK following the instructions: https://emscripten.org/docs/getting_started/downloads.html # 1. Install Emscripten SDK following the instructions: https://emscripten.org/docs/getting_started/downloads.html
# 2. Install Ninja build system # 2. Install Ninja build system
@@ -16,6 +26,8 @@
cmake_minimum_required(VERSION 3.10.2) cmake_minimum_required(VERSION 3.10.2)
project(imgui_example_glfw_wgpu C CXX) project(imgui_example_glfw_wgpu C CXX)
set(IMGUI_EXECUTABLE example_glfw_wgpu)
if(NOT CMAKE_BUILD_TYPE) if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE) set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE)
endif() endif()
@@ -25,17 +37,50 @@ set(CMAKE_CXX_STANDARD 17) # Dawn requires C++17
# Dear ImGui # Dear ImGui
set(IMGUI_DIR ../../) set(IMGUI_DIR ../../)
# ImGui example commons source files
set(IMGUI_EXAMPLE_SOURCE_FILES
main.cpp
# backend files
${IMGUI_DIR}/backends/imgui_impl_glfw.cpp
${IMGUI_DIR}/backends/imgui_impl_wgpu.cpp
# Dear ImGui files
${IMGUI_DIR}/imgui.cpp
${IMGUI_DIR}/imgui_draw.cpp
${IMGUI_DIR}/imgui_demo.cpp
${IMGUI_DIR}/imgui_tables.cpp
${IMGUI_DIR}/imgui_widgets.cpp)
# Libraries # Libraries
if(EMSCRIPTEN) if(EMSCRIPTEN)
if(EMSCRIPTEN_VERSION VERSION_GREATER_EQUAL "4.0.10")
set(IMGUI_EMSCRIPTEN_WEBGPU_FLAG "--use-port=emdawnwebgpu" CACHE STRING "Choose between --use-port=emdawnwebgpu (Dawn implementation of EMSCRIPTEN) and -sUSE_WEBGPU=1 (WGPU implementation of EMSCRIPTEN, deprecated in 4.0.10): default to --use-port=emdawnwebgpu for EMSCRIPTEN >= 4.0.10")
else()
set(IMGUI_EMSCRIPTEN_WEBGPU_FLAG "-sUSE_WEBGPU=1" CACHE STRING "Use -sUSE_WEBGPU=1 for EMSCRIPTEN WGPU implementation" FORCE)
endif()
if(EMSCRIPTEN_VERSION VERSION_GREATER_EQUAL "3.1.57") if(EMSCRIPTEN_VERSION VERSION_GREATER_EQUAL "3.1.57")
set(IMGUI_EMSCRIPTEN_GLFW3 "--use-port=contrib.glfw3" CACHE STRING "Choose between --use-port=contrib.glfw3 and -sUSE_GLFW=3 for GLFW implementation (default to --use-port=contrib.glfw3)") set(IMGUI_EMSCRIPTEN_GLFW3 "--use-port=contrib.glfw3" CACHE STRING "Choose between --use-port=contrib.glfw3 and -sUSE_GLFW=3 for GLFW implementation (default to --use-port=contrib.glfw3)")
else() else()
# cannot use contrib.glfw3 prior to 3.1.57 # cannot use contrib.glfw3 prior to 3.1.57
set(IMGUI_EMSCRIPTEN_GLFW3 "-sUSE_GLFW=3" CACHE STRING "Use -sUSE_GLFW=3 for GLFW implementation" FORCE) set(IMGUI_EMSCRIPTEN_GLFW3 "-sUSE_GLFW=3" CACHE STRING "Use -sUSE_GLFW=3 for GLFW implementation" FORCE)
endif() endif()
set(LIBRARIES glfw) set(LIBRARIES glfw)
add_compile_options(-sDISABLE_EXCEPTION_CATCHING=1 -DIMGUI_DISABLE_FILE_FUNCTIONS=1) add_compile_options(-sDISABLE_EXCEPTION_CATCHING=1 -DIMGUI_DISABLE_FILE_FUNCTIONS=1)
else() else()
# if it's Native/Desktop build, IMGUI_DAWN_DIR or IMGUI_WGPU_DIR must be specified
if (NOT IMGUI_DAWN_DIR AND NOT IMGUI_WGPU_DIR)
message(FATAL_ERROR "Please specify the Dawn or WGPU base directory")
endif()
# IMGUI_DAWN_DIR and IMGUI_WGPU_DIR both cannot be set
if (IMGUI_DAWN_DIR AND IMGUI_WGPU_DIR)
message(FATAL_ERROR "Please specify only one of Dawn / WGPU base directory")
endif()
# Native DAWN build settings
if(IMGUI_DAWN_DIR)
# Dawn wgpu desktop # Dawn wgpu desktop
set(DAWN_FETCH_DEPENDENCIES ON) set(DAWN_FETCH_DEPENDENCIES ON)
set(IMGUI_DAWN_DIR CACHE PATH "Path to Dawn repository") set(IMGUI_DAWN_DIR CACHE PATH "Path to Dawn repository")
@@ -61,57 +106,96 @@ else()
option(TINT_BUILD_SPV_WRITER "Build the SPIR-V output writer" OFF) option(TINT_BUILD_SPV_WRITER "Build the SPIR-V output writer" OFF)
option(TINT_BUILD_WGSL_WRITER "Build the WGSL output writer" ON) option(TINT_BUILD_WGSL_WRITER "Build the WGSL output writer" ON)
endif() endif()
# check if WAYLAND is the current Session Type and enable DAWN_USE_WAYLAND Wayland option @compile time
# You can override this using: cmake -DDAWN_USE_WAYLAND=X (X = ON | OFF)
if(LINUX)
if ($ENV{XDG_SESSION_TYPE} MATCHES wayland)
option(DAWN_USE_WAYLAND "Enable support for Wayland surface" ON)
endif()
endif()
add_subdirectory("${IMGUI_DAWN_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/dawn" EXCLUDE_FROM_ALL) add_subdirectory("${IMGUI_DAWN_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/dawn" EXCLUDE_FROM_ALL)
set(LIBRARIES webgpu_dawn webgpu_cpp webgpu_glfw glfw) set(LIBRARIES webgpu_dawn webgpu_cpp webgpu_glfw glfw)
else()
# Native WGPU build settings
find_package(glfw3 CONFIG)
set(WGPU_NATIVE_LIB_DIR ${IMGUI_WGPU_DIR}/lib)
find_library(WGPU_LIBRARY NAMES libwgpu_native.a wgpu_native.lib wgpu_native
HINTS ${WGPU_NATIVE_LIB_DIR} REQUIRED)
if (WIN32)
add_definitions(-DGLFW_EXPOSE_NATIVE_WIN32)
set(OS_LIBRARIES d3dcompiler ws2_32 userenv bcrypt ntdll opengl32 Propsys RuntimeObject)
elseif(UNIX AND NOT APPLE)
add_definitions(-DGLFW_EXPOSE_NATIVE_X11)
add_definitions(-DGLFW_EXPOSE_NATIVE_WAYLAND)
set(OS_LIBRARIES "-lm -ldl")
elseif(APPLE)
add_definitions(-DGLFW_EXPOSE_NATIVE_COCOA)
set_source_files_properties(${IMGUI_DIR}/backends/imgui_impl_glfw.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++")
set(OS_LIBRARIES "-framework CoreFoundation -framework QuartzCore -framework Metal -framework MetalKit -framework Cocoa")
endif() endif()
add_executable(example_glfw_wgpu set(LIBRARIES glfw ${WGPU_LIBRARY} ${OS_LIBRARIES})
main.cpp
# backend files
${IMGUI_DIR}/backends/imgui_impl_glfw.cpp
${IMGUI_DIR}/backends/imgui_impl_wgpu.cpp
# Dear ImGui files
${IMGUI_DIR}/imgui.cpp
${IMGUI_DIR}/imgui_draw.cpp
${IMGUI_DIR}/imgui_demo.cpp
${IMGUI_DIR}/imgui_tables.cpp
${IMGUI_DIR}/imgui_widgets.cpp
)
IF(NOT EMSCRIPTEN)
target_compile_definitions(example_glfw_wgpu PUBLIC
"IMGUI_IMPL_WEBGPU_BACKEND_DAWN"
)
endif() endif()
target_include_directories(example_glfw_wgpu PUBLIC endif()
add_executable(${IMGUI_EXECUTABLE} ${IMGUI_EXAMPLE_SOURCE_FILES})
target_include_directories(${IMGUI_EXECUTABLE} PUBLIC
${IMGUI_DIR} ${IMGUI_DIR}
${IMGUI_DIR}/backends ${IMGUI_DIR}/backends
) )
target_link_libraries(example_glfw_wgpu PUBLIC ${LIBRARIES}) target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_EXAMPLE_GLFW_WGPU")
# In this example IMGUI_IMPL_WEBGPU_BACKEND_DAWN / IMGUI_IMPL_WEBGPU_BACKEND_WGPU internal define is set according to:
# EMSCRIPTEN: by used FLAG
# --use-port=emdawnwebgpu --> IMGUI_IMPL_WEBGPU_BACKEND_DAWN defined
# -sUSE_WEBGPU=1 --> IMGUI_IMPL_WEBGPU_BACKEND_WGPU defined
# NATIVE: by used SDK installation directory
# if IMGUI_DAWN_DIR is valid --> IMGUI_IMPL_WEBGPU_BACKEND_DAWN defined
# if IMGUI_WGPU_DIR is valid --> IMGUI_IMPL_WEBGPU_BACKEND_WGPU defined
# Native settings
if(NOT EMSCRIPTEN)
if(IMGUI_DAWN_DIR)
target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_DAWN")
else()
target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGPU")
target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGPU_DIR}/include)
endif()
target_link_libraries(${IMGUI_EXECUTABLE} PUBLIC ${LIBRARIES})
# Emscripten settings # Emscripten settings
if(EMSCRIPTEN) else()
set(CMAKE_EXECUTABLE_SUFFIX ".html")
if("${IMGUI_EMSCRIPTEN_GLFW3}" STREQUAL "--use-port=contrib.glfw3") if("${IMGUI_EMSCRIPTEN_GLFW3}" STREQUAL "--use-port=contrib.glfw3")
target_compile_options(example_glfw_wgpu PUBLIC target_compile_options(${IMGUI_EXECUTABLE} PUBLIC "${IMGUI_EMSCRIPTEN_GLFW3}")
"${IMGUI_EMSCRIPTEN_GLFW3}"
)
endif() endif()
message(STATUS "Using ${IMGUI_EMSCRIPTEN_GLFW3} GLFW implementation") message(STATUS "Using ${IMGUI_EMSCRIPTEN_GLFW3} GLFW implementation")
target_link_options(example_glfw_wgpu PRIVATE
"-sUSE_WEBGPU=1" if("${IMGUI_EMSCRIPTEN_WEBGPU_FLAG}" STREQUAL "--use-port=emdawnwebgpu")
target_compile_options(${IMGUI_EXECUTABLE} PUBLIC "${IMGUI_EMSCRIPTEN_WEBGPU_FLAG}")
target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_DAWN")
else()
target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGPU")
endif()
message(STATUS "Using ${IMGUI_EMSCRIPTEN_WEBGPU_FLAG} WebGPU implementation")
target_link_options(${IMGUI_EXECUTABLE} PRIVATE
"${IMGUI_EMSCRIPTEN_WEBGPU_FLAG}"
"${IMGUI_EMSCRIPTEN_GLFW3}" "${IMGUI_EMSCRIPTEN_GLFW3}"
"-sWASM=1" "-sWASM=1"
"-sASYNCIFY=1"
"-sALLOW_MEMORY_GROWTH=1" "-sALLOW_MEMORY_GROWTH=1"
"-sNO_EXIT_RUNTIME=0" "-sNO_EXIT_RUNTIME=0"
"-sASSERTIONS=1" "-sASSERTIONS=1"
"-sDISABLE_EXCEPTION_CATCHING=1" "-sDISABLE_EXCEPTION_CATCHING=1"
"-sNO_FILESYSTEM=1" "-sNO_FILESYSTEM=1"
"--shell-file=${CMAKE_CURRENT_LIST_DIR}/../libs/emscripten/shell_minimal.html"
) )
set_target_properties(example_glfw_wgpu PROPERTIES OUTPUT_NAME "index") set_target_properties(${IMGUI_EXECUTABLE} PROPERTIES OUTPUT_NAME "index")
# copy our custom index.html to build directory
add_custom_command(TARGET example_glfw_wgpu POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_LIST_DIR}/web/index.html" $<TARGET_FILE_DIR:example_glfw_wgpu>
)
endif() endif()

View File

@@ -1,5 +1,39 @@
## How to Build ## How to Build
### Using CMake
#### Building for desktop (WebGPU-native) with Google Dawn:
1. `git clone https://github.com/google/dawn dawn`
2. `cmake -B build -DIMGUI_DAWN_DIR=dawn`
3. `cmake --build build`
The resulting binary will be found at one of the following locations:
* build/Debug/example_sdl2_wgpu[.exe]
* build/example_sdl2_wgpu[.exe]
#### Building for desktop (WebGPU-Native) with WGPU:
1. download WGPU-Native autogenerated binary modules for your platform/compiler from: https://github.com/gfx-rs/wgpu-native/releases
2. unzip the downloaded file in `your_preferred_folder`
3. move into `your_preferred_folder` (e.g. typing: `cd your_preferred_folder`)
4. `cmake -B build -DIMGUI_WGPU_DIR=your_preferred_folder` ("full path" or "relative" starting from current directory)
5. `cmake --build build`
The resulting binary will be found at one of the following locations:
* build/Debug/example_sdl2_wgpu[.exe]
* build/example_sdl2_wgpu[.exe]
#### Building for Emscripten:
1. Install Emscripten SDK following the instructions: https://emscripten.org/docs/getting_started/downloads.html
2. Install Ninja build system
3. `emcmake cmake -G Ninja -B build`
4. `cmake --build build`
To run:
- `emrun build/index.html`
or
- `python -m http.server` then open WGPU browser with url: `http://localhost:8000/build`
### Using makefile
- 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 - 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. - 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.

View File

@@ -12,54 +12,55 @@
#include "imgui_impl_glfw.h" #include "imgui_impl_glfw.h"
#include "imgui_impl_wgpu.h" #include "imgui_impl_wgpu.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <GLFW/glfw3.h>
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
#include <emscripten.h> #include <emscripten.h>
#include <emscripten/html5.h> #include <emscripten/html5.h>
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
#include <emscripten/html5_webgpu.h> #include <emscripten/html5_webgpu.h>
#else
#include <webgpu/webgpu_glfw.h>
#endif #endif
#include <GLFW/glfw3.h>
#include <webgpu/webgpu.h> #include <webgpu/webgpu.h>
#include <webgpu/webgpu_cpp.h> #include <webgpu/webgpu_cpp.h>
// This example can also compile and run with Emscripten! See 'Makefile.emscripten' for details.
#ifdef __EMSCRIPTEN__
#include "../libs/emscripten/emscripten_mainloop_stub.h" #include "../libs/emscripten/emscripten_mainloop_stub.h"
#else
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
#include <webgpu/webgpu_glfw.h>
#endif
#endif #endif
// Global WebGPU required states // Global WebGPU required states
static WGPUInstance wgpu_instance = nullptr; static WGPUInstance wgpu_instance = nullptr;
static WGPUDevice wgpu_device = nullptr; static WGPUDevice wgpu_device = nullptr;
static WGPUSurface wgpu_surface = nullptr; static WGPUSurface wgpu_surface = nullptr;
static WGPUTextureFormat wgpu_preferred_fmt = WGPUTextureFormat_RGBA8Unorm; static WGPUQueue wgpu_queue = nullptr;
static WGPUSwapChain wgpu_swap_chain = nullptr; static WGPUSurfaceConfiguration wgpu_surface_configuration {};
static int wgpu_swap_chain_width = 1280; static int wgpu_surface_width = 1280;
static int wgpu_swap_chain_height = 800; static int wgpu_surface_height = 800;
// Forward declarations // Forward declarations
static bool InitWGPU(GLFWwindow* window); static bool InitWGPU(void* window);
static void CreateSwapChain(int width, int height);
static void glfw_error_callback(int error, const char* description) static void glfw_error_callback(int error, const char* description)
{ {
printf("GLFW Error %d: %s\n", error, description); printf("GLFW Error %d: %s\n", error, description);
} }
static void wgpu_error_callback(WGPUErrorType error_type, const char* message, void*) static void ResizeSurface(int width, int height)
{ {
const char* error_type_lbl = ""; wgpu_surface_configuration.width = wgpu_surface_width = width;
switch (error_type) wgpu_surface_configuration.height = wgpu_surface_height = height;
{
case WGPUErrorType_Validation: error_type_lbl = "Validation"; break; wgpuSurfaceConfigure(wgpu_surface, &wgpu_surface_configuration);
case WGPUErrorType_OutOfMemory: error_type_lbl = "Out of memory"; break;
case WGPUErrorType_Unknown: error_type_lbl = "Unknown"; break;
case WGPUErrorType_DeviceLost: error_type_lbl = "Device lost"; break;
default: error_type_lbl = "Unknown";
} }
printf("%s error: %s\n", error_type_lbl, message);
static void ReleaseTextureAndConfigureSurface(WGPUTexture &texture, int fb_width, int fb_height)
{
if (texture)
wgpuTextureRelease(texture);
if ( fb_width > 0 && fb_height > 0 )
ResizeSurface(fb_width, fb_height);
} }
// Main code // Main code
@@ -67,24 +68,23 @@ int main(int, char**)
{ {
glfwSetErrorCallback(glfw_error_callback); glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit()) if (!glfwInit())
return 1; return EXIT_FAILURE;
// Make sure GLFW does not initialize any graphics context. // Make sure GLFW does not initialize any graphics context.
// This needs to be done explicitly later. // This needs to be done explicitly later.
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
GLFWwindow* window = glfwCreateWindow(wgpu_swap_chain_width, wgpu_swap_chain_height, "Dear ImGui GLFW+WebGPU example", nullptr, nullptr); GLFWwindow* window = glfwCreateWindow(wgpu_surface_width, wgpu_surface_height, "Dear ImGui GLFW+WebGPU example", nullptr, nullptr);
if (window == nullptr) if (window == nullptr)
return 1; return EXIT_FAILURE;
// Initialize the WebGPU environment // Initialize the WebGPU environment
if (!InitWGPU(window)) if (!InitWGPU(window))
{ {
if (window)
glfwDestroyWindow(window); glfwDestroyWindow(window);
glfwTerminate(); glfwTerminate();
return 1; return EXIT_FAILURE;
} }
CreateSwapChain(wgpu_swap_chain_width, wgpu_swap_chain_height);
glfwShowWindow(window); glfwShowWindow(window);
// Setup Dear ImGui context // Setup Dear ImGui context
@@ -106,7 +106,7 @@ int main(int, char**)
ImGui_ImplWGPU_InitInfo init_info; ImGui_ImplWGPU_InitInfo init_info;
init_info.Device = wgpu_device; init_info.Device = wgpu_device;
init_info.NumFramesInFlight = 3; init_info.NumFramesInFlight = 3;
init_info.RenderTargetFormat = wgpu_preferred_fmt; init_info.RenderTargetFormat = wgpu_surface_configuration.format;
init_info.DepthStencilFormat = WGPUTextureFormat_Undefined; init_info.DepthStencilFormat = WGPUTextureFormat_Undefined;
ImGui_ImplWGPU_Init(&init_info); ImGui_ImplWGPU_Init(&init_info);
@@ -114,19 +114,19 @@ int main(int, char**)
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
// - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
// - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
// - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
// - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
// - Read 'docs/FONTS.md' for more instructions and details. If you like the default font but want it to scale better, consider using the 'ProggyVector' from the same author! // - Read 'docs/FONTS.md' for more instructions and details. If you like the default font but want it to scale better, consider using the 'ProggyVector' from the same author!
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
// - Emscripten allows preloading a file or folder to be accessible at runtime. See Makefile for details. // - Emscripten allows preloading a file or folder to be accessible at runtime. See Makefile for details.
//io.Fonts->AddFontDefault(); //io.Fonts->AddFontDefault();
//style.FontSizeBase = 20.0f;
#ifndef IMGUI_DISABLE_FILE_FUNCTIONS #ifndef IMGUI_DISABLE_FILE_FUNCTIONS
//io.Fonts->AddFontFromFileTTF("fonts/segoeui.ttf"); //io.Fonts->AddFontFromFileTTF("fonts/segoeui.ttf", 18.0f);
//io.Fonts->AddFontFromFileTTF("fonts/DroidSans.ttf"); //io.Fonts->AddFontFromFileTTF("fonts/DroidSans.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("fonts/Roboto-Medium.ttf"); //io.Fonts->AddFontFromFileTTF("fonts/Roboto-Medium.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("fonts/Cousine-Regular.ttf"); //io.Fonts->AddFontFromFileTTF("fonts/Cousine-Regular.ttf", 15.0f);
//io.Fonts->AddFontFromFileTTF("fonts/ProggyTiny.ttf"); //io.Fonts->AddFontFromFileTTF("fonts/ProggyTiny.ttf", 10.0f);
//ImFont* font = io.Fonts->AddFontFromFileTTF("fonts/ArialUni.ttf"); //ImFont* font = io.Fonts->AddFontFromFileTTF("fonts/ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());
//IM_ASSERT(font != nullptr); //IM_ASSERT(font != nullptr);
#endif #endif
@@ -160,13 +160,22 @@ int main(int, char**)
// React to changes in screen size // React to changes in screen size
int width, height; int width, height;
glfwGetFramebufferSize((GLFWwindow*)window, &width, &height); glfwGetFramebufferSize((GLFWwindow*)window, &width, &height);
if (width != wgpu_swap_chain_width || height != wgpu_swap_chain_height) if (width != wgpu_surface_width || height != wgpu_surface_height)
{ {
ImGui_ImplWGPU_InvalidateDeviceObjects(); ImGui_ImplWGPU_InvalidateDeviceObjects();
CreateSwapChain(width, height); ResizeSurface(width, height);
ImGui_ImplWGPU_CreateDeviceObjects(); ImGui_ImplWGPU_CreateDeviceObjects();
} }
WGPUSurfaceTexture surfaceTexture;
wgpuSurfaceGetCurrentTexture(wgpu_surface, &surfaceTexture);
// Check SurfaceTexture status, if NOT optimal status we try to re-configure Surface
if (!ImGui_ImplWGPU_CheckSurfaceTextureOptimalStatus_Helper(surfaceTexture.status)) {
ReleaseTextureAndConfigureSurface(surfaceTexture.texture, width, height);
continue;
}
// Start the Dear ImGui frame // Start the Dear ImGui frame
ImGui_ImplWGPU_NewFrame(); ImGui_ImplWGPU_NewFrame();
ImGui_ImplGlfw_NewFrame(); ImGui_ImplGlfw_NewFrame();
@@ -212,19 +221,24 @@ int main(int, char**)
// Rendering // Rendering
ImGui::Render(); ImGui::Render();
#ifndef __EMSCRIPTEN__ WGPUTextureViewDescriptor viewDescriptor {};
// Tick needs to be called in Dawn to display validation errors viewDescriptor.format = wgpu_surface_configuration.format;
wgpuDeviceTick(wgpu_device); viewDescriptor.dimension = WGPUTextureViewDimension_2D ;
#endif viewDescriptor.mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED;
viewDescriptor.arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED;
viewDescriptor.aspect = WGPUTextureAspect_All;
WGPURenderPassColorAttachment color_attachments = {}; WGPUTextureView textureView = wgpuTextureCreateView(surfaceTexture.texture, &viewDescriptor);
WGPURenderPassColorAttachment color_attachments {};
color_attachments.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; color_attachments.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
color_attachments.loadOp = WGPULoadOp_Clear; color_attachments.loadOp = WGPULoadOp_Clear;
color_attachments.storeOp = WGPUStoreOp_Store; color_attachments.storeOp = WGPUStoreOp_Store;
color_attachments.clearValue = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w }; color_attachments.clearValue = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w };
color_attachments.view = wgpuSwapChainGetCurrentTextureView(wgpu_swap_chain); color_attachments.view = textureView;
WGPURenderPassDescriptor render_pass_desc = {}; WGPURenderPassDescriptor render_pass_desc {};
render_pass_desc.colorAttachmentCount = 1; render_pass_desc.colorAttachmentCount = 1;
render_pass_desc.colorAttachments = &color_attachments; render_pass_desc.colorAttachments = &color_attachments;
render_pass_desc.depthStencilAttachment = nullptr; render_pass_desc.depthStencilAttachment = nullptr;
@@ -236,16 +250,18 @@ int main(int, char**)
ImGui_ImplWGPU_RenderDrawData(ImGui::GetDrawData(), pass); ImGui_ImplWGPU_RenderDrawData(ImGui::GetDrawData(), pass);
wgpuRenderPassEncoderEnd(pass); wgpuRenderPassEncoderEnd(pass);
WGPUCommandBufferDescriptor cmd_buffer_desc = {}; WGPUCommandBufferDescriptor cmd_buffer_desc {};
WGPUCommandBuffer cmd_buffer = wgpuCommandEncoderFinish(encoder, &cmd_buffer_desc); WGPUCommandBuffer cmd_buffer = wgpuCommandEncoderFinish(encoder, &cmd_buffer_desc);
WGPUQueue queue = wgpuDeviceGetQueue(wgpu_device); wgpuQueueSubmit(wgpu_queue, 1, &cmd_buffer);
wgpuQueueSubmit(queue, 1, &cmd_buffer);
#ifndef __EMSCRIPTEN__ #ifndef __EMSCRIPTEN__
wgpuSwapChainPresent(wgpu_swap_chain); wgpuSurfacePresent(wgpu_surface);
// Tick needs to be called in Dawn to display validation errors
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
wgpuDeviceTick(wgpu_device);
#endif #endif
#endif
wgpuTextureViewRelease(color_attachments.view); wgpuTextureViewRelease(textureView);
wgpuRenderPassEncoderRelease(pass); wgpuRenderPassEncoderRelease(pass);
wgpuCommandEncoderRelease(encoder); wgpuCommandEncoderRelease(encoder);
wgpuCommandBufferRelease(cmd_buffer); wgpuCommandBufferRelease(cmd_buffer);
@@ -259,92 +275,222 @@ int main(int, char**)
ImGui_ImplGlfw_Shutdown(); ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext(); ImGui::DestroyContext();
wgpuSurfaceUnconfigure(wgpu_surface);
wgpuSurfaceRelease(wgpu_surface);
wgpuQueueRelease(wgpu_queue);
wgpuDeviceRelease(wgpu_device);
wgpuInstanceRelease(wgpu_instance);
glfwDestroyWindow(window); glfwDestroyWindow(window);
glfwTerminate(); glfwTerminate();
return 0; return EXIT_SUCCESS;
} }
#ifndef __EMSCRIPTEN__
static WGPUAdapter RequestAdapter(WGPUInstance instance) #if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
static WGPUAdapter GetAdapter(wgpu::Instance &instance)
{ {
auto onAdapterRequestEnded = [](WGPURequestAdapterStatus status, WGPUAdapter adapter, const char* message, void* pUserData) wgpu::Adapter acquiredAdapter;
wgpu::RequestAdapterOptions adapterOptions;
auto onRequestAdapter = [&](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter, wgpu::StringView message) {
if (status != wgpu::RequestAdapterStatus::Success)
{
printf("Failed to get an adapter: %s\n", message.data);
return;
}
acquiredAdapter = std::move(adapter);
};
// Synchronously (wait until) acquire Adapter
wgpu::Future waitAdapterFunc { instance.RequestAdapter(&adapterOptions, wgpu::CallbackMode::WaitAnyOnly, onRequestAdapter) };
wgpu::WaitStatus waitStatusAdapter = instance.WaitAny(waitAdapterFunc, UINT64_MAX);
assert(acquiredAdapter != nullptr && waitStatusAdapter == wgpu::WaitStatus::Success && "Error on Adapter request");
#ifndef NDEBUG
ImGui_ImplWGPU_PrintAdapterInfo_Helper(acquiredAdapter.Get());
#endif
return acquiredAdapter.MoveToCHandle();
}
static WGPUDevice GetDevice(wgpu::Instance &instance, wgpu::Adapter &adapter)
{
// Set device callback functions
wgpu::DeviceDescriptor deviceDesc;
deviceDesc.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous, ImGui_ImplWGPU_DAWN_DeviceLostCallback_Helper);
deviceDesc.SetUncapturedErrorCallback(ImGui_ImplWGPU_DAWN_ErrorCallback_Helper);
wgpu::Device acquiredDevice;
auto onRequestDevice = [&](wgpu::RequestDeviceStatus status, wgpu::Device localDevice, wgpu::StringView message) {
if (status != wgpu::RequestDeviceStatus::Success)
{
printf("Failed to get an device: %s\n", message.data);
return;
}
acquiredDevice = std::move(localDevice);
};
// Synchronously (wait until) get Device
wgpu::Future waitDeviceFunc { adapter.RequestDevice(&deviceDesc, wgpu::CallbackMode::WaitAnyOnly, onRequestDevice) };
wgpu::WaitStatus waitStatusDevice = instance.WaitAny(waitDeviceFunc, UINT64_MAX);
assert(acquiredDevice != nullptr && waitStatusDevice == wgpu::WaitStatus::Success && "Error on Device request");
return acquiredDevice.MoveToCHandle();
}
#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
#ifdef __EMSCRIPTEN__
// Adapter and device initialization via JS
EM_ASYNC_JS( void, getAdapterAndDeviceViaJS, (),
{
if (!navigator.gpu) throw Error("WebGPU not supported.");
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
Module.preinitializedWebGPUDevice = device;
} );
#else // __EMSCRIPTEN__
static void handle_request_adapter(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void *userdata1, void *userdata2)
{ {
if (status == WGPURequestAdapterStatus_Success) if (status == WGPURequestAdapterStatus_Success)
*(WGPUAdapter*)(pUserData) = adapter; {
WGPUAdapter *extAdapter = (WGPUAdapter *) userdata1;
*extAdapter = adapter;
}
else else
printf("Could not get WebGPU adapter: %s\n", message); printf("Request_adapter status=%#.8x message=%.*s\n", status, (int) message.length, message.data);
};
WGPUAdapter adapter;
wgpuInstanceRequestAdapter(instance, nullptr, onAdapterRequestEnded, (void*)&adapter);
return adapter;
} }
static WGPUDevice RequestDevice(WGPUAdapter& adapter) static void handle_request_device(WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView message, void *userdata1, void *userdata2)
{
auto onDeviceRequestEnded = [](WGPURequestDeviceStatus status, WGPUDevice device, const char* message, void* pUserData)
{ {
if (status == WGPURequestDeviceStatus_Success) if (status == WGPURequestDeviceStatus_Success)
*(WGPUDevice*)(pUserData) = device;
else
printf("Could not get WebGPU device: %s\n", message);
};
WGPUDevice device;
wgpuAdapterRequestDevice(adapter, nullptr, onDeviceRequestEnded, (void*)&device);
return device;
}
#endif
static bool InitWGPU(GLFWwindow* window)
{ {
wgpu::Instance instance = wgpuCreateInstance(nullptr); WGPUDevice *extDevice = (WGPUDevice *) userdata1;
*extDevice = device;
}
else
printf("Request_device status=%#.8x message=%.*s\n", status, (int) message.length, message.data);
}
#ifdef __EMSCRIPTEN__ static WGPUAdapter GetAdapter(WGPUInstance &instance)
wgpu_device = emscripten_webgpu_get_device(); {
if (!wgpu_device) WGPURequestAdapterOptions adapterOptions = {};
return false;
#else static WGPUAdapter localAdapter;
WGPUAdapter adapter = RequestAdapter(instance.Get()); WGPURequestAdapterCallbackInfo adapterCallbackInfo = {};
if (!adapter) adapterCallbackInfo.callback = handle_request_adapter;
return false; adapterCallbackInfo.userdata1 = &localAdapter;
wgpu_device = RequestDevice(adapter);
wgpuInstanceRequestAdapter(wgpu_instance, &adapterOptions, adapterCallbackInfo);
assert(localAdapter && "Error on Adapter request");
#ifndef NDEBUG
ImGui_ImplWGPU_PrintAdapterInfo_Helper(localAdapter);
#endif #endif
#ifdef __EMSCRIPTEN__ return localAdapter;
wgpu::SurfaceDescriptorFromCanvasHTMLSelector html_surface_desc = {}; }
html_surface_desc.selector = "#canvas";
wgpu::SurfaceDescriptor surface_desc = {};
surface_desc.nextInChain = &html_surface_desc;
wgpu::Surface surface = instance.CreateSurface(&surface_desc);
wgpu::Adapter adapter = {}; static WGPUDevice GetDevice(WGPUAdapter &adapter)
wgpu_preferred_fmt = (WGPUTextureFormat)surface.GetPreferredFormat(adapter); {
static WGPUDevice localDevice;
WGPURequestDeviceCallbackInfo deviceCallbackInfo = {};
deviceCallbackInfo.callback = handle_request_device;
deviceCallbackInfo.userdata1 = &localDevice;
wgpuAdapterRequestDevice(adapter, NULL, deviceCallbackInfo);
assert(localDevice && "Error on Device request");
return localDevice;
}
#endif // __EMSCRIPTEN__
#endif // IMGUI_IMPL_WEBGPU_BACKEND_WGPU
static bool InitWGPU(void* window)
{
WGPUTextureFormat preferred_fmt = WGPUTextureFormat_Undefined; // acquired from SurfaceCapabilities
// Google DAWN backend: Adapter and Device acquisition, Surface creation
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
wgpu::InstanceDescriptor instanceDescriptor = {};
instanceDescriptor.capabilities.timedWaitAnyEnable = true;
wgpu::Instance instance = wgpu::CreateInstance(&instanceDescriptor);
wgpu::Adapter adapter { GetAdapter(instance) };
wgpu_device = GetDevice(instance, adapter);
// Create the surface.
#ifdef __EMSCRIPTEN__
wgpu::EmscriptenSurfaceSourceCanvasHTMLSelector canvasDesc{};
canvasDesc.selector = "#canvas";
wgpu::SurfaceDescriptor surfaceDesc = {};
surfaceDesc.nextInChain = &canvasDesc;
wgpu::Surface surface = instance.CreateSurface(&surfaceDesc);
#else #else
wgpu::Surface surface = wgpu::glfw::CreateSurfaceForWindow(instance, window); wgpu::Surface surface = wgpu::glfw::CreateSurfaceForWindow(instance, (GLFWwindow *) window);
#endif
if (!surface) if (!surface)
return false; return false;
wgpu_preferred_fmt = WGPUTextureFormat_BGRA8Unorm;
#endif
// Moving Dawn objects into WGPU handles
wgpu_instance = instance.MoveToCHandle(); wgpu_instance = instance.MoveToCHandle();
wgpu_surface = surface.MoveToCHandle(); wgpu_surface = surface.MoveToCHandle();
wgpuDeviceSetUncapturedErrorCallback(wgpu_device, wgpu_error_callback, nullptr); WGPUSurfaceCapabilities surface_capabilities = {};
wgpuSurfaceGetCapabilities(wgpu_surface, adapter.Get(), &surface_capabilities);
preferred_fmt = surface_capabilities.formats[0];
// WGPU backend: Adapter and Device acquisition, Surface creation
#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
wgpu_instance = wgpuCreateInstance(nullptr);
#ifdef __EMSCRIPTEN__
getAdapterAndDeviceViaJS();
wgpu_device = emscripten_webgpu_get_device();
assert(wgpu_device != nullptr && "Error creating the Device");
WGPUSurfaceDescriptorFromCanvasHTMLSelector html_surface_desc = {};
html_surface_desc.chain.sType = WGPUSType_SurfaceDescriptorFromCanvasHTMLSelector;
html_surface_desc.selector = "#canvas";
WGPUSurfaceDescriptor surface_desc = {};
surface_desc.nextInChain = &html_surface_desc.chain;
// Create the surface.
wgpu_surface = wgpuInstanceCreateSurface(wgpu_instance, &surface_desc);
preferred_fmt = wgpuSurfaceGetPreferredFormat(wgpu_surface, {} /* adapter */);
#else // __EMSCRIPTEN__
wgpuSetLogCallback(ImGui_ImplWGPU_WGPU_LogCallback_Helper, NULL);
wgpuSetLogLevel(WGPULogLevel_Warn);
static WGPUAdapter adapter = GetAdapter(wgpu_instance);
wgpu_device = GetDevice(adapter);
// Create the surface.
wgpu_surface = ImGui_ImplGLFW_CreateWGPUSurface_Helper( wgpu_instance, (GLFWwindow*) window);
if (!wgpu_surface)
return false;
WGPUSurfaceCapabilities surface_capabilities = {};
wgpuSurfaceGetCapabilities(wgpu_surface, adapter, &surface_capabilities);
preferred_fmt = surface_capabilities.formats[0];
#endif // __EMSCRIPTEN__
#endif // IMGUI_IMPL_WEBGPU_BACKEND_WGPU
wgpu_surface_configuration.presentMode = WGPUPresentMode_Fifo;
wgpu_surface_configuration.alphaMode = WGPUCompositeAlphaMode_Auto;
wgpu_surface_configuration.usage = WGPUTextureUsage_RenderAttachment;
wgpu_surface_configuration.width = wgpu_surface_width;
wgpu_surface_configuration.height = wgpu_surface_height;
wgpu_surface_configuration.device = wgpu_device;
wgpu_surface_configuration.format = preferred_fmt;
wgpuSurfaceConfigure(wgpu_surface, &wgpu_surface_configuration);
wgpu_queue = wgpuDeviceGetQueue(wgpu_device);
return true; return true;
} }
static void CreateSwapChain(int width, int height)
{
if (wgpu_swap_chain)
wgpuSwapChainRelease(wgpu_swap_chain);
wgpu_swap_chain_width = width;
wgpu_swap_chain_height = height;
WGPUSwapChainDescriptor swap_chain_desc = {};
swap_chain_desc.usage = WGPUTextureUsage_RenderAttachment;
swap_chain_desc.format = wgpu_preferred_fmt;
swap_chain_desc.width = width;
swap_chain_desc.height = height;
swap_chain_desc.presentMode = WGPUPresentMode_Fifo;
wgpu_swap_chain = wgpuDeviceCreateSwapChain(wgpu_device, wgpu_surface, &swap_chain_desc);
}

View File

@@ -1,84 +0,0 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"/>
<title>Dear ImGui Emscripten+GLFW+WebGPU example</title>
<style>
body { margin: 0; background-color: black }
.emscripten {
position: absolute;
top: 0px;
left: 0px;
margin: 0px;
border: 0;
width: 100%;
height: 100%;
overflow: hidden;
display: block;
image-rendering: optimizeSpeed;
image-rendering: -moz-crisp-edges;
image-rendering: -o-crisp-edges;
image-rendering: -webkit-optimize-contrast;
image-rendering: optimize-contrast;
image-rendering: crisp-edges;
image-rendering: pixelated;
-ms-interpolation-mode: nearest-neighbor;
}
</style>
</head>
<body>
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
<script type='text/javascript'>
var Module;
(async () => {
Module = {
preRun: [],
postRun: [],
print: (function() {
return function(text) {
text = Array.prototype.slice.call(arguments).join(' ');
console.log(text);
};
})(),
printErr: function(text) {
text = Array.prototype.slice.call(arguments).join(' ');
console.error(text);
},
canvas: (function() {
var canvas = document.getElementById('canvas');
//canvas.addEventListener("webglcontextlost", function(e) { alert('FIXME: WebGL context lost, please reload the page'); e.preventDefault(); }, false);
return canvas;
})(),
setStatus: function(text) {
console.log("status: " + text);
},
monitorRunDependencies: function(left) {
// no run dependencies to log
}
};
window.onerror = function() {
console.log("onerror: " + event);
};
// Initialize the graphics adapter
{
if (!navigator.gpu) {
throw Error("WebGPU not supported.");
}
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
Module.preinitializedWebGPUDevice = device;
}
{
const js = document.createElement('script');
js.async = true;
js.src = "index.js";
document.body.appendChild(js);
}
})();
</script>
</body>
</html>

View File

@@ -0,0 +1,194 @@
# Building for desktop (WebGPU-native) with Dawn:
# 1. git clone https://github.com/google/dawn dawn
# 2. cmake -B build -DIMGUI_DAWN_DIR=dawn
# 3. cmake --build build
# The resulting binary will be found at one of the following locations:
# * build/Debug/example_sdl2_wgpu[.exe]
# * build/example_sdl2_wgpu[.exe]
# Building for desktop (WGPU-Native) with WGPU-Native:
# 1. download WGPU-Native autogenerated binary modules for your platform/compiler from: https://github.com/gfx-rs/wgpu-native/releases
# 2. unzip the downloaded file in your_preferred_folder
# 3. move into your_preferred_folder (e.g. typing: `cd your_preferred_folder`)
# 4. cmake -B build -DIMGUI_WGPU_DIR=your_preferred_folder ("full path" or "relative" starting from current directory)
# 5. cmake --build build
# The resulting binary will be found at one of the following locations:
# * build/Debug/example_sdl2_wgpu[.exe]
# * build/example_sdl2_wgpu[.exe]
# Building for Emscripten:
# 1. Install Emscripten SDK following the instructions: https://emscripten.org/docs/getting_started/downloads.html
# 2. Install Ninja build system
# 3. emcmake cmake -G Ninja -B build
# 3. cmake --build build
# 4. emrun build/index.html
cmake_minimum_required(VERSION 3.10.2)
project(imgui_example_sdl2_wgpu C CXX)
set(IMGUI_EXECUTABLE example_sdl2_wgpu)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE)
endif()
set(CMAKE_CXX_STANDARD 17) # Dawn requires C++17
# Dear ImGui
set(IMGUI_DIR ../../)
# ImGui example commons source files
set(IMGUI_EXAMPLE_SOURCE_FILES
main.cpp
# backend files
${IMGUI_DIR}/backends/imgui_impl_sdl2.cpp
${IMGUI_DIR}/backends/imgui_impl_wgpu.cpp
# Dear ImGui files
${IMGUI_DIR}/imgui.cpp
${IMGUI_DIR}/imgui_draw.cpp
${IMGUI_DIR}/imgui_demo.cpp
${IMGUI_DIR}/imgui_tables.cpp
${IMGUI_DIR}/imgui_widgets.cpp)
# SDL_MAIN_HANDLED
find_package(SDL2 REQUIRED)
# Libraries
if(EMSCRIPTEN)
if(EMSCRIPTEN_VERSION VERSION_GREATER_EQUAL "4.0.10")
set(IMGUI_EMSCRIPTEN_WEBGPU_FLAG "--use-port=emdawnwebgpu" CACHE STRING "Choose between --use-port=emdawnwebgpu (Dawn implementation of EMSCRIPTEN) and -sUSE_WEBGPU=1 (WGPU implementation of EMSCRIPTEN, deprecated in 4.0.10): default to --use-port=emdawnwebgpu for EMSCRIPTEN >= 4.0.10")
else()
set(IMGUI_EMSCRIPTEN_WEBGPU_FLAG "-sUSE_WEBGPU=1" CACHE STRING "Use -sUSE_WEBGPU=1 for EMSCRIPTEN WGPU implementation" FORCE)
endif()
add_compile_options(-sDISABLE_EXCEPTION_CATCHING=1 -DIMGUI_DISABLE_FILE_FUNCTIONS=1)
else()
# if it's Native/Desktop build, IMGUI_DAWN_DIR or IMGUI_WGPU_DIR must be specified
if (NOT IMGUI_DAWN_DIR AND NOT IMGUI_WGPU_DIR)
message(FATAL_ERROR "Please specify the Dawn or WGPU base directory")
endif()
# IMGUI_DAWN_DIR and IMGUI_WGPU_DIR both cannot be set
if (IMGUI_DAWN_DIR AND IMGUI_WGPU_DIR)
message(FATAL_ERROR "Please specify only one of Dawn / WGPU base directory")
endif()
# Add SDL2 module to get Surface, with libs and file property for MacOS build
if(APPLE)
set_source_files_properties(${IMGUI_DIR}/backends/imgui_impl_sdl2.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++")
set(OS_LIBRARIES "-framework CoreFoundation -framework QuartzCore -framework Metal -framework MetalKit -framework Cocoa")
endif()
# Native DAWN build settings
if(IMGUI_DAWN_DIR)
# Dawn wgpu desktop
set(DAWN_FETCH_DEPENDENCIES ON)
set(IMGUI_DAWN_DIR CACHE PATH "Path to Dawn repository")
if (NOT IMGUI_DAWN_DIR)
message(FATAL_ERROR "Please specify the Dawn repository by setting IMGUI_DAWN_DIR")
endif()
option(DAWN_FETCH_DEPENDENCIES "Use fetch_dawn_dependencies.py as an alternative to using depot_tools" ON)
# disable buildin GLFW in DAWN when we use SDL2 / SDL3 build
option(DAWN_USE_GLFW OFF)
# Dawn builds many things by default - disable things we don't need
option(DAWN_BUILD_SAMPLES "Enables building Dawn's samples" OFF)
option(TINT_BUILD_CMD_TOOLS "Build the Tint command line tools" OFF)
option(TINT_BUILD_DOCS "Build documentation" OFF)
option(TINT_BUILD_TESTS "Build tests" OFF)
if (NOT APPLE)
option(TINT_BUILD_MSL_WRITER "Build the MSL output writer" OFF)
endif()
if(WIN32)
option(TINT_BUILD_SPV_READER "Build the SPIR-V input reader" OFF)
option(TINT_BUILD_WGSL_READER "Build the WGSL input reader" ON)
option(TINT_BUILD_GLSL_WRITER "Build the GLSL output writer" OFF)
option(TINT_BUILD_GLSL_VALIDATOR "Build the GLSL output validator" OFF)
option(TINT_BUILD_SPV_WRITER "Build the SPIR-V output writer" OFF)
option(TINT_BUILD_WGSL_WRITER "Build the WGSL output writer" ON)
endif()
# check if WAYLAND is the current Session Type and enable DAWN_USE_WAYLAND Wayland option @compile time
# You can override this using: cmake -DDAWN_USE_WAYLAND=X (X = ON | OFF)
if(LINUX)
if ($ENV{XDG_SESSION_TYPE} MATCHES wayland)
option(DAWN_USE_WAYLAND "Enable support for Wayland surface" ON)
endif()
endif()
add_subdirectory("${IMGUI_DAWN_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/dawn" EXCLUDE_FROM_ALL)
set(LIBRARIES webgpu_dawn webgpu_cpp ${OS_LIBRARIES})
else()
# Native WGPU build settings
set(WGPU_NATIVE_LIB_DIR ${IMGUI_WGPU_DIR}/lib)
find_library(WGPU_LIBRARY NAMES libwgpu_native.a wgpu_native.lib wgpu_native
HINTS ${WGPU_NATIVE_LIB_DIR} REQUIRED)
if (WIN32)
set(OS_LIBRARIES d3dcompiler ws2_32 userenv bcrypt ntdll opengl32 Propsys RuntimeObject)
elseif(UNIX AND NOT APPLE)
set(OS_LIBRARIES "-lm -ldl")
endif()
set(LIBRARIES ${WGPU_LIBRARY} ${OS_LIBRARIES})
endif()
endif()
add_executable(${IMGUI_EXECUTABLE} ${IMGUI_EXAMPLE_SOURCE_FILES})
target_include_directories(${IMGUI_EXECUTABLE} PUBLIC
${IMGUI_DIR}
${IMGUI_DIR}/backends
${SDL2_INCLUDE_DIRS}
)
target_link_libraries(${IMGUI_EXECUTABLE} PUBLIC ${LIBRARIES} ${SDL2_LIBRARIES})
target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_EXAMPLE_SDL2_WGPU")
# IMGUI_IMPL_WEBGPU_BACKEND_DAWN/WGPU internal define is set according to:
# EMSCRIPTEN: by used FLAG
# --use-port=emdawnwebgpu --> IMGUI_IMPL_WEBGPU_BACKEND_DAWN enabled (+EMSCRIPTEN)
# -sUSE_WEBGPU=1 --> IMGUI_IMPL_WEBGPU_BACKEND_WGPU enabled (+EMSCRIPTEN)
# NATIVE: by used SDK installation directory
# if IMGUI_DAWN_DIR is valid --> IMGUI_IMPL_WEBGPU_BACKEND_DAWN enabled
# if IMGUI_WGPU_DIR is valid --> IMGUI_IMPL_WEBGPU_BACKEND_WGPU enabled
# Native settings
IF(NOT EMSCRIPTEN)
if(IMGUI_DAWN_DIR)
target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_DAWN")
else()
target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGPU")
target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGPU_DIR}/include)
endif()
# Emscripten settings
else()
set(CMAKE_EXECUTABLE_SUFFIX ".html")
if("${IMGUI_EMSCRIPTEN_WEBGPU_FLAG}" STREQUAL "--use-port=emdawnwebgpu")
target_compile_options(${IMGUI_EXECUTABLE} PUBLIC "${IMGUI_EMSCRIPTEN_WEBGPU_FLAG}")
target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_DAWN")
else()
target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGPU")
endif()
message(STATUS "Using ${IMGUI_EMSCRIPTEN_WEBGPU_FLAG} WebGPU implementation")
target_compile_options(${IMGUI_EXECUTABLE} PUBLIC "-sUSE_SDL=2" )
target_link_options(${IMGUI_EXECUTABLE} PRIVATE
"${IMGUI_EMSCRIPTEN_WEBGPU_FLAG}"
"-sUSE_SDL=2"
"-sWASM=1"
"-sASYNCIFY=1"
"-sALLOW_MEMORY_GROWTH=1"
"-sNO_EXIT_RUNTIME=0"
"-sASSERTIONS=1"
"-sDISABLE_EXCEPTION_CATCHING=1"
"-sNO_FILESYSTEM=1"
"--shell-file=${CMAKE_CURRENT_LIST_DIR}/../libs/emscripten/shell_minimal.html"
)
set_target_properties(${IMGUI_EXECUTABLE} PROPERTIES OUTPUT_NAME "index")
endif()

View File

@@ -0,0 +1,91 @@
#
# Makefile to use with emscripten
# See https://emscripten.org/docs/getting_started/downloads.html
# for installation instructions.
#
# This Makefile assumes you have loaded emscripten's environment.
# (On Windows, you may need to execute emsdk_env.bat or encmdprompt.bat ahead)
#
# Running `make` will produce three files:
# - web/index.html (current stored in the repository)
# - web/index.js
# - web/index.wasm
#
# All three are needed to run the demo.
CC = emcc
CXX = em++
WEB_DIR = web
EXE = $(WEB_DIR)/index.js
IMGUI_DIR = ../..
SOURCES = main.cpp
SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp
SOURCES += $(IMGUI_DIR)/backends/imgui_impl_glfw.cpp $(IMGUI_DIR)/backends/imgui_impl_wgpu.cpp
OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES))))
UNAME_S := $(shell uname -s)
CPPFLAGS =
LDFLAGS =
EMS =
##---------------------------------------------------------------------
## EMSCRIPTEN OPTIONS
##---------------------------------------------------------------------
# ("EMS" options gets added to both CPPFLAGS and LDFLAGS, whereas some options are for linker only)
EMS += -s DISABLE_EXCEPTION_CATCHING=1
LDFLAGS += -s USE_GLFW=3 -s USE_WEBGPU=1
LDFLAGS += -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
# Emscripten allows preloading a file or folder to be accessible at runtime.
# The Makefile for this example project suggests embedding the misc/fonts/ folder into our application, it will then be accessible as "/fonts"
# See documentation for more details: https://emscripten.org/docs/porting/files/packaging_files.html
# (Default value is 0. Set to 1 to enable file-system and include the misc/fonts/ folder as part of the build.)
USE_FILE_SYSTEM ?= 0
ifeq ($(USE_FILE_SYSTEM), 0)
LDFLAGS += -s NO_FILESYSTEM=1
CPPFLAGS += -DIMGUI_DISABLE_FILE_FUNCTIONS
endif
ifeq ($(USE_FILE_SYSTEM), 1)
LDFLAGS += --no-heap-copy --preload-file ../../misc/fonts@/fonts
endif
##---------------------------------------------------------------------
## FINAL BUILD FLAGS
##---------------------------------------------------------------------
CPPFLAGS += -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends
#CPPFLAGS += -g
CPPFLAGS += -Wall -Wformat -Os $(EMS)
#LDFLAGS += --shell-file shell_minimal.html
LDFLAGS += $(EMS)
##---------------------------------------------------------------------
## BUILD RULES
##---------------------------------------------------------------------
%.o:%.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
%.o:$(IMGUI_DIR)/%.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
%.o:$(IMGUI_DIR)/backends/%.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
all: $(EXE)
@echo Build complete for $(EXE)
$(WEB_DIR):
mkdir $@
serve: all
python3 -m http.server -d $(WEB_DIR)
$(EXE): $(OBJS) $(WEB_DIR)
$(CXX) -o $@ $(OBJS) $(LDFLAGS)
clean:
rm -f $(EXE) $(OBJS) $(WEB_DIR)/*.js $(WEB_DIR)/*.wasm $(WEB_DIR)/*.wasm.pre

View File

@@ -0,0 +1,58 @@
## How to Build
### Using CMake
#### Building for desktop (WebGPU-native) with Google Dawn:
1. `git clone https://github.com/google/dawn dawn`
2. `cmake -B build -DIMGUI_DAWN_DIR=dawn`
3. `cmake --build build`
The resulting binary will be found at one of the following locations:
* build/Debug/example_sdl2_wgpu[.exe]
* build/example_sdl2_wgpu[.exe]
#### Building for desktop (WebGPU-Native) with WGPU:
1. download WGPU-Native autogenerated binary modules for your platform/compiler from: https://github.com/gfx-rs/wgpu-native/releases
2. unzip the downloaded file in `your_preferred_folder`
3. move into `your_preferred_folder` (e.g. typing: `cd your_preferred_folder`)
4. `cmake -B build -DIMGUI_WGPU_DIR=your_preferred_folder` ("full path" or "relative" starting from current directory)
5. `cmake --build build`
The resulting binary will be found at one of the following locations:
* build/Debug/example_sdl2_wgpu[.exe]
* build/example_sdl2_wgpu[.exe]
#### Building for Emscripten:
1. Install Emscripten SDK following the instructions: https://emscripten.org/docs/getting_started/downloads.html
2. Install Ninja build system
3. `emcmake cmake -G Ninja -B build`
4. `cmake --build build`
To run:
- `emrun build/index.html`
or
- `python -m http.server` then open WGPU browser with url: `http://localhost:8000/build`
### Using makefile
- 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.
- You may also refer to our [Continuous Integration setup](https://github.com/ocornut/imgui/tree/master/.github/workflows) for Emscripten setup.
- Then build using `make -f Makefile.emscripten` while in the `example_glfw_wgpu/` directory.
- Requires recent Emscripten as WGPU is still a work-in-progress API.
## How to Run
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.
- `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>
_"Unfortunately several browsers (including Chrome, Safari, and Internet Explorer) do not support file:// [XHR](https://emscripten.org/docs/site/glossary.html#term-xhr) requests, and cant load extra files needed by the HTML (like a .wasm file, or packaged file data as mentioned lower down). For these browsers youll need to serve the files using a [local webserver](https://emscripten.org/docs/getting_started/FAQ.html#faq-local-webserver) and then open http://localhost:8000/hello.html."_
- Emscripten SDK has a handy `emrun` command: `emrun web/example_glfw_wgpu.html --browser firefox` which will spawn a temporary local webserver (in Firefox). See https://emscripten.org/docs/compiling/Running-html-files-with-emrun.html for details.
- You may use Python 3 builtin webserver: `python -m http.server -d web` (this is what `make serve` uses).
- You may use Python 2 builtin webserver: `cd web && python -m SimpleHTTPServer`.
- If you are accessing the files over a network, certain browsers, such as Firefox, will restrict Gamepad API access to secure contexts only (e.g. https only).

View File

@@ -0,0 +1,492 @@
// Dear ImGui: standalone example application for using GLFW + WebGPU
// - Emscripten is supported for publishing on web. See https://emscripten.org.
// - Dawn is used as a WebGPU implementation on desktop.
// Learn about Dear ImGui:
// - FAQ https://dearimgui.com/faq
// - Getting Started https://dearimgui.com/getting-started
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
// - Introduction, links and more at the top of imgui.cpp
#include "imgui.h"
#include "imgui_impl_sdl2.h"
#include "imgui_impl_wgpu.h"
#include <stdio.h>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#include <emscripten/html5.h>
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
#include <emscripten/html5_webgpu.h>
#endif
#include "../libs/emscripten/emscripten_mainloop_stub.h"
#endif
#include <SDL.h>
#include <webgpu/webgpu.h>
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
#include <webgpu/webgpu_cpp.h>
#endif
// This example can also compile and run with Emscripten! See 'Makefile.emscripten' for details.
#ifdef __EMSCRIPTEN__
#endif
// Global WebGPU required states
WGPUInstance wgpu_instance = nullptr;
WGPUDevice wgpu_device = nullptr;
WGPUSurface wgpu_surface = nullptr;
WGPUQueue wgpu_queue = nullptr;
WGPUSurfaceConfiguration wgpu_surface_configuration {};
int wgpu_surface_width = 1280;
int wgpu_surface_height = 720;
// Forward declarations
static bool InitWGPU(void* window);
static void ResizeSurface(int width, int height)
{
wgpu_surface_configuration.width = wgpu_surface_width = width;
wgpu_surface_configuration.height = wgpu_surface_height = height;
wgpuSurfaceConfigure( wgpu_surface, (WGPUSurfaceConfiguration *) &wgpu_surface_configuration );
}
static void ReleaseTextureAndConfigureSurface(WGPUTexture &texture, int fb_width, int fb_height)
{
if (texture)
wgpuTextureRelease(texture);
if ( fb_width > 0 && fb_height > 0 )
ResizeSurface(fb_width, fb_height);
}
// Main code
int main(int, char**)
{
#if defined(__linux__)
// it's necessary to specify "x11" or "wayland": default is "x11" it works also in wayland
SDL_SetHint(SDL_HINT_VIDEODRIVER, "x11");
// or comment the previous line and export SDL_VIDEODRIVER environment variable:
// export SDL_VIDEODRIVER=wayland (to set wayland session type)
// export SDL_VIDEODRIVER=$XDG_SESSION_TYPE (to get current session type from WM: x11 | wayland)
#endif
// Init SDL
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *window = SDL_CreateWindow("Dear ImGui SDL2+WebGPU example", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, wgpu_surface_width, wgpu_surface_height, SDL_WINDOW_RESIZABLE);
// Initialize WGPU
InitWGPU(window);
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
// Setup Dear ImGui style
ImGui::StyleColorsDark();
//ImGui::StyleColorsLight();
// Setup Platform/Renderer backends
ImGui_ImplSDL2_InitForOther(window);
ImGui_ImplWGPU_InitInfo init_info;
init_info.Device = wgpu_device;
init_info.NumFramesInFlight = 3;
init_info.RenderTargetFormat = wgpu_surface_configuration.format;
init_info.DepthStencilFormat = WGPUTextureFormat_Undefined;
ImGui_ImplWGPU_Init(&init_info);
// Load Fonts
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
// - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
// - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
// - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
// - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
// - Read 'docs/FONTS.md' for more instructions and details.
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
// - Emscripten allows preloading a file or folder to be accessible at runtime. See Makefile for details.
//io.Fonts->AddFontDefault();
#ifndef IMGUI_DISABLE_FILE_FUNCTIONS
//io.Fonts->AddFontFromFileTTF("fonts/segoeui.ttf", 18.0f);
//io.Fonts->AddFontFromFileTTF("fonts/DroidSans.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("fonts/Roboto-Medium.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("fonts/Cousine-Regular.ttf", 15.0f);
//io.Fonts->AddFontFromFileTTF("fonts/ProggyTiny.ttf", 10.0f);
//ImFont* font = io.Fonts->AddFontFromFileTTF("fonts/ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());
//IM_ASSERT(font != nullptr);
#endif
// Our state
bool show_demo_window = true;
bool show_another_window = false;
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
SDL_Event event;
bool canCloseWindow = false;
// Main loop
#ifdef __EMSCRIPTEN__
// For an Emscripten build we are disabling file-system access, so let's not attempt to do a fopen() of the imgui.ini file.
// You may manually call LoadIniSettingsFromMemory() to load settings from your own storage.
io.IniFilename = nullptr;
EMSCRIPTEN_MAINLOOP_BEGIN
#else
while (!canCloseWindow)
#endif
{
while (SDL_PollEvent(&event))
{
ImGui_ImplSDL2_ProcessEvent(&event);
if (event.type == SDL_QUIT ||
(event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE &&
event.window.windowID == SDL_GetWindowID(window)))
canCloseWindow = true;
}
// Poll and handle events (inputs, window resize, etc.)
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
// React to changes in screen size
int width, height;
SDL_GetWindowSize(window, &width, &height);
if (width != wgpu_surface_width || height != wgpu_surface_height)
{
ImGui_ImplWGPU_InvalidateDeviceObjects();
ResizeSurface(width, height);
ImGui_ImplWGPU_CreateDeviceObjects();
//continue;
}
WGPUSurfaceTexture surfaceTexture;
wgpuSurfaceGetCurrentTexture(wgpu_surface, &surfaceTexture);
// Check SurfaceTexture status, if NOT optimal status we try to re-configure Surface
if (!ImGui_ImplWGPU_CheckSurfaceTextureOptimalStatus_Helper(surfaceTexture.status)) {
ReleaseTextureAndConfigureSurface(surfaceTexture.texture, width, height);
continue;
}
// Start the Dear ImGui frame
ImGui_ImplWGPU_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
// 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
if (show_demo_window)
ImGui::ShowDemoWindow(&show_demo_window);
// 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
{
static float f = 0.0f;
static int counter = 0;
ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state
ImGui::Checkbox("Another Window", &show_another_window);
ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
counter++;
ImGui::SameLine();
ImGui::Text("counter = %d", counter);
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
ImGui::End();
}
// 3. Show another simple window.
if (show_another_window)
{
ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
ImGui::Text("Hello from another window!");
if (ImGui::Button("Close Me"))
show_another_window = false;
ImGui::End();
}
// Rendering
ImGui::Render();
WGPUTextureViewDescriptor viewDescriptor {};
viewDescriptor.format = wgpu_surface_configuration.format;
viewDescriptor.dimension = WGPUTextureViewDimension_2D;
viewDescriptor.mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED;
viewDescriptor.arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED;
viewDescriptor.aspect = WGPUTextureAspect_All;
WGPUTextureView textureView = wgpuTextureCreateView(surfaceTexture.texture, &viewDescriptor);
WGPURenderPassColorAttachment color_attachments {};
color_attachments.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
color_attachments.loadOp = WGPULoadOp_Clear;
color_attachments.storeOp = WGPUStoreOp_Store;
color_attachments.clearValue = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w };
color_attachments.view = textureView;
WGPURenderPassDescriptor render_pass_desc {};
render_pass_desc.colorAttachmentCount = 1;
render_pass_desc.colorAttachments = &color_attachments;
render_pass_desc.depthStencilAttachment = nullptr;
WGPUCommandEncoderDescriptor enc_desc {};
WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(wgpu_device, &enc_desc);
WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &render_pass_desc);
ImGui_ImplWGPU_RenderDrawData(ImGui::GetDrawData(), pass);
wgpuRenderPassEncoderEnd(pass);
WGPUCommandBufferDescriptor cmd_buffer_desc {};
WGPUCommandBuffer cmd_buffer = wgpuCommandEncoderFinish(encoder, &cmd_buffer_desc);
wgpuQueueSubmit(wgpu_queue, 1, &cmd_buffer);
#ifndef __EMSCRIPTEN__
wgpuSurfacePresent(wgpu_surface);
// Tick needs to be called in Dawn to display validation errors
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
wgpuDeviceTick(wgpu_device);
#endif
#endif
wgpuTextureViewRelease(textureView);
wgpuRenderPassEncoderRelease(pass);
wgpuCommandEncoderRelease(encoder);
wgpuCommandBufferRelease(cmd_buffer);
}
#ifdef __EMSCRIPTEN__
EMSCRIPTEN_MAINLOOP_END;
#endif
// Cleanup
ImGui_ImplWGPU_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
wgpuSurfaceUnconfigure(wgpu_surface);
wgpuSurfaceRelease(wgpu_surface);
wgpuQueueRelease(wgpu_queue);
wgpuDeviceRelease(wgpu_device);
wgpuInstanceRelease(wgpu_instance);
// Terminate SDL
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
}
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
static WGPUAdapter GetAdapter(wgpu::Instance &instance)
{
wgpu::Adapter acquiredAdapter;
wgpu::RequestAdapterOptions adapterOptions;
auto onRequestAdapter = [&](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter, wgpu::StringView message) {
if (status != wgpu::RequestAdapterStatus::Success)
{
printf("Failed to get an adapter: %s\n", message.data);
return;
}
acquiredAdapter = std::move(adapter);
};
// Synchronously (wait until) acquire Adapter
wgpu::Future waitAdapterFunc { instance.RequestAdapter(&adapterOptions, wgpu::CallbackMode::WaitAnyOnly, onRequestAdapter) };
wgpu::WaitStatus waitStatusAdapter = instance.WaitAny(waitAdapterFunc, UINT64_MAX);
assert(acquiredAdapter != nullptr && waitStatusAdapter == wgpu::WaitStatus::Success && "Error on Adapter request");
#ifndef NDEBUG
ImGui_ImplWGPU_PrintAdapterInfo_Helper(acquiredAdapter.Get());
#endif
return acquiredAdapter.MoveToCHandle();
}
static WGPUDevice GetDevice(wgpu::Instance &instance, wgpu::Adapter &adapter)
{
// Set device callback functions
wgpu::DeviceDescriptor deviceDesc;
deviceDesc.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous, ImGui_ImplWGPU_DAWN_DeviceLostCallback_Helper);
deviceDesc.SetUncapturedErrorCallback(ImGui_ImplWGPU_DAWN_ErrorCallback_Helper);
wgpu::Device acquiredDevice;
auto onRequestDevice = [&](wgpu::RequestDeviceStatus status, wgpu::Device localDevice, wgpu::StringView message) {
if (status != wgpu::RequestDeviceStatus::Success)
{
printf("Failed to get an device: %s\n", message.data);
return;
}
acquiredDevice = std::move(localDevice);
};
// Synchronously (wait until) get Device
wgpu::Future waitDeviceFunc { adapter.RequestDevice(&deviceDesc, wgpu::CallbackMode::WaitAnyOnly, onRequestDevice) };
wgpu::WaitStatus waitStatusDevice = instance.WaitAny(waitDeviceFunc, UINT64_MAX);
assert(acquiredDevice != nullptr && waitStatusDevice == wgpu::WaitStatus::Success && "Error on Device request");
return acquiredDevice.MoveToCHandle();
}
#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
#ifdef __EMSCRIPTEN__
// Adapter and device initialization via JS
EM_ASYNC_JS( void, getAdapterAndDeviceViaJS, (),
{
if (!navigator.gpu) throw Error("WebGPU not supported.");
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
Module.preinitializedWebGPUDevice = device;
} );
#else // __EMSCRIPTEN__
static void handle_request_adapter(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void *userdata1, void *userdata2)
{
if (status == WGPURequestAdapterStatus_Success)
{
WGPUAdapter *extAdapter = (WGPUAdapter *) userdata1;
*extAdapter = adapter;
}
else
printf("Request_adapter status=%#.8x message=%.*s\n", status, (int) message.length, message.data);
}
static void handle_request_device(WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView message, void *userdata1, void *userdata2)
{
if (status == WGPURequestDeviceStatus_Success)
{
WGPUDevice *extDevice = (WGPUDevice *) userdata1;
*extDevice = device;
}
else
printf("Request_device status=%#.8x message=%.*s\n", status, (int) message.length, message.data);
}
static WGPUAdapter GetAdapter(WGPUInstance &instance)
{
WGPURequestAdapterOptions adapterOptions = {};
static WGPUAdapter localAdapter;
WGPURequestAdapterCallbackInfo adapterCallbackInfo = {};
adapterCallbackInfo.callback = handle_request_adapter;
adapterCallbackInfo.userdata1 = &localAdapter;
wgpuInstanceRequestAdapter(wgpu_instance, &adapterOptions, adapterCallbackInfo);
assert(localAdapter && "Error on Adapter request");
#ifndef NDEBUG
ImGui_ImplWGPU_PrintAdapterInfo_Helper(localAdapter);
#endif
return localAdapter;
}
static WGPUDevice GetDevice(WGPUAdapter &adapter)
{
static WGPUDevice localDevice;
WGPURequestDeviceCallbackInfo deviceCallbackInfo = {};
deviceCallbackInfo.callback = handle_request_device;
deviceCallbackInfo.userdata1 = &localDevice;
wgpuAdapterRequestDevice(adapter, NULL, deviceCallbackInfo);
assert(localDevice && "Error on Device request");
return localDevice;
}
#endif // __EMSCRIPTEN__
#endif // IMGUI_IMPL_WEBGPU_BACKEND_WGPU
static bool InitWGPU(void* window)
{
WGPUTextureFormat preferred_fmt = WGPUTextureFormat_Undefined; // acquired from SurfaceCapabilities
// Google DAWN backend: Adapter and Device acquisition, Surface creation
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
wgpu::InstanceDescriptor instanceDescriptor = {};
instanceDescriptor.capabilities.timedWaitAnyEnable = true;
wgpu::Instance instance = wgpu::CreateInstance(&instanceDescriptor);
wgpu::Adapter adapter { GetAdapter(instance) };
wgpu_device = GetDevice(instance, adapter);
// Create the surface.
#ifdef __EMSCRIPTEN__
wgpu::EmscriptenSurfaceSourceCanvasHTMLSelector canvasDesc{};
canvasDesc.selector = "#canvas";
wgpu::SurfaceDescriptor surfaceDesc = {};
surfaceDesc.nextInChain = &canvasDesc;
wgpu::Surface surface = instance.CreateSurface(&surfaceDesc) ;
#else
wgpu::Surface surface = ImGui_ImplSDL2_CreateWGPUSurface_Helper(instance.Get(), (SDL_Window *) window);
#endif
if (!surface)
return false;
// Moving Dawn objects into WGPU handles
wgpu_instance = instance.MoveToCHandle();
wgpu_surface = surface.MoveToCHandle();
WGPUSurfaceCapabilities surface_capabilities = {};
wgpuSurfaceGetCapabilities(wgpu_surface, adapter.Get(), &surface_capabilities);
preferred_fmt = surface_capabilities.formats[0];
// WGPU backend: Adapter and Device acquisition, Surface creation
#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
wgpu_instance = wgpuCreateInstance(nullptr);
#ifdef __EMSCRIPTEN__
getAdapterAndDeviceViaJS();
wgpu_device = emscripten_webgpu_get_device();
assert(wgpu_device != nullptr && "Error creating the Device");
WGPUSurfaceDescriptorFromCanvasHTMLSelector html_surface_desc = {};
html_surface_desc.chain.sType = WGPUSType_SurfaceDescriptorFromCanvasHTMLSelector;
html_surface_desc.selector = "#canvas";
WGPUSurfaceDescriptor surface_desc = {};
surface_desc.nextInChain = &html_surface_desc.chain;
// Create the surface.
wgpu_surface = wgpuInstanceCreateSurface(wgpu_instance, &surface_desc);
preferred_fmt = wgpuSurfaceGetPreferredFormat(wgpu_surface, {} /* adapter */);
#else // __EMSCRIPTEN__
wgpuSetLogCallback(ImGui_ImplWGPU_WGPU_LogCallback_Helper, NULL);
wgpuSetLogLevel(WGPULogLevel_Warn);
static WGPUAdapter adapter = GetAdapter(wgpu_instance);
wgpu_device = GetDevice(adapter);
// Create the surface.
wgpu_surface = ImGui_ImplSDL2_CreateWGPUSurface_Helper(wgpu_instance, (SDL_Window *) window);
if (!wgpu_surface)
return false;
WGPUSurfaceCapabilities surface_capabilities = {};
wgpuSurfaceGetCapabilities(wgpu_surface, adapter, &surface_capabilities);
preferred_fmt = surface_capabilities.formats[0];
#endif // __EMSCRIPTEN__
#endif // IMGUI_IMPL_WEBGPU_BACKEND_WGPU
wgpu_surface_configuration.presentMode = WGPUPresentMode_Fifo;
wgpu_surface_configuration.alphaMode = WGPUCompositeAlphaMode_Auto;
wgpu_surface_configuration.usage = WGPUTextureUsage_RenderAttachment;
wgpu_surface_configuration.width = wgpu_surface_width;
wgpu_surface_configuration.height = wgpu_surface_height;
wgpu_surface_configuration.device = wgpu_device;
wgpu_surface_configuration.format = preferred_fmt;
wgpuSurfaceConfigure(wgpu_surface, &wgpu_surface_configuration);
wgpu_queue = wgpuDeviceGetQueue(wgpu_device);
return true;
}

View File

@@ -0,0 +1,214 @@
# Building for desktop (WebGPU-native) with Dawn:
# 1. git clone https://github.com/google/dawn dawn
# 2. cmake -B build -DIMGUI_DAWN_DIR=dawn
# 3. cmake --build build
# The resulting binary will be found at one of the following locations:
# * build/Debug/example_sdl3_wgpu[.exe]
# * build/example_sdl3_wgpu[.exe]
# Building for desktop (WGPU-Native) with WGPU-Native:
# 1. download WGPU-Native autogenerated binary modules for your platform/compiler from: https://github.com/gfx-rs/wgpu-native/releases
# 2. unzip the downloaded file in your_preferred_folder
# 3. move into your_preferred_folder (e.g. typing: `cd your_preferred_folder`)
# 4. cmake -B build -DIMGUI_WGPU_DIR=your_preferred_folder ("full path" or "relative" starting from current directory)
# 5. cmake --build build
# The resulting binary will be found at one of the following locations:
# * build/Debug/example_sdl3_wgpu[.exe]
# * build/example_sdl3_wgpu[.exe]
# Building for Emscripten:
# At current date (jun/2025) there is no official support for SDL3 in EMSCRIPTEN,
# and its use in EMSCRIPTEN is also in the experimental phase.
# To use SDL3 with EMSCRIPTEN you need to download the SDL3 repository (source code)
# and build the libraries for EMSCRIPTEN following the official page:
# https://wiki.libsdl.org/SDL3/README-emscripten#building-sdlemscripten
#
# the SDL3 library will be built in `your_SDL3_folder/build`
#
# 1. Install Emscripten SDK following the instructions: https://emscripten.org/docs/getting_started/downloads.html
# 2. Install Ninja build system
# 3. emcmake cmake -DIMGUI_SDL3_EMSCRIPTEN_DIR=your_SDL3_folder -G Ninja -B build
# 3. cmake --build build
# 4. emrun build/index.html
cmake_minimum_required(VERSION 3.10.2)
project(imgui_example_sdl3_wgpu C CXX)
set(IMGUI_EXECUTABLE example_sdl3_wgpu)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE)
endif()
set(CMAKE_CXX_STANDARD 17) # Dawn requires C++17
# Dear ImGui
set(IMGUI_DIR ../../)
# ImGui example commons source files
set(IMGUI_EXAMPLE_SOURCE_FILES
main.cpp
# backend files
${IMGUI_DIR}/backends/imgui_impl_sdl3.cpp
${IMGUI_DIR}/backends/imgui_impl_wgpu.cpp
# Dear ImGui files
${IMGUI_DIR}/imgui.cpp
${IMGUI_DIR}/imgui_draw.cpp
${IMGUI_DIR}/imgui_demo.cpp
${IMGUI_DIR}/imgui_tables.cpp
${IMGUI_DIR}/imgui_widgets.cpp)
# Libraries
if(EMSCRIPTEN)
if(EMSCRIPTEN_VERSION VERSION_GREATER_EQUAL "4.0.10")
set(IMGUI_EMSCRIPTEN_WEBGPU_FLAG "--use-port=emdawnwebgpu" CACHE STRING "Choose between --use-port=emdawnwebgpu (Dawn implementation of EMSCRIPTEN) and -sUSE_WEBGPU=1 (WGPU implementation of EMSCRIPTEN, deprecated in 4.0.10): default to --use-port=emdawnwebgpu for EMSCRIPTEN >= 4.0.10")
else()
set(IMGUI_EMSCRIPTEN_WEBGPU_FLAG "-sUSE_WEBGPU=1" CACHE STRING "Use -sUSE_WEBGPU=1 for EMSCRIPTEN WGPU implementation" FORCE)
endif()
add_compile_options(-sDISABLE_EXCEPTION_CATCHING=1 -DIMGUI_DISABLE_FILE_FUNCTIONS=1)
else()
find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3)
# if it's Native/Desktop build, IMGUI_DAWN_DIR or IMGUI_WGPU_DIR must be specified
if (NOT IMGUI_DAWN_DIR AND NOT IMGUI_WGPU_DIR)
message(FATAL_ERROR "Please specify the Dawn or WGPU base directory")
endif()
# IMGUI_DAWN_DIR and IMGUI_WGPU_DIR both cannot be set
if (IMGUI_DAWN_DIR AND IMGUI_WGPU_DIR)
message(FATAL_ERROR "Please specify only one of Dawn / WGPU base directory")
endif()
if(APPLE)
set_source_files_properties(${IMGUI_DIR}/backends/imgui_impl_sdl3.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++")
set(OS_LIBRARIES "-framework CoreFoundation -framework QuartzCore -framework Metal -framework MetalKit -framework Cocoa")
endif()
# Native DAWN build settings
if(IMGUI_DAWN_DIR)
# Dawn wgpu desktop
set(DAWN_FETCH_DEPENDENCIES ON)
set(IMGUI_DAWN_DIR CACHE PATH "Path to Dawn repository")
if (NOT IMGUI_DAWN_DIR)
message(FATAL_ERROR "Please specify the Dawn repository by setting IMGUI_DAWN_DIR")
endif()
# disable buildin GLFW in DAWN when we use SDL2 / SDL3
option(DAWN_USE_GLFW OFF)
option(DAWN_FETCH_DEPENDENCIES "Use fetch_dawn_dependencies.py as an alternative to using depot_tools" ON)
# Dawn builds many things by default - disable things we don't need
option(DAWN_BUILD_SAMPLES "Enables building Dawn's samples" OFF)
option(TINT_BUILD_CMD_TOOLS "Build the Tint command line tools" OFF)
option(TINT_BUILD_DOCS "Build documentation" OFF)
option(TINT_BUILD_TESTS "Build tests" OFF)
if (NOT APPLE)
option(TINT_BUILD_MSL_WRITER "Build the MSL output writer" OFF)
endif()
if(WIN32)
option(TINT_BUILD_SPV_READER "Build the SPIR-V input reader" OFF)
option(TINT_BUILD_WGSL_READER "Build the WGSL input reader" ON)
option(TINT_BUILD_GLSL_WRITER "Build the GLSL output writer" OFF)
option(TINT_BUILD_GLSL_VALIDATOR "Build the GLSL output validator" OFF)
option(TINT_BUILD_SPV_WRITER "Build the SPIR-V output writer" OFF)
option(TINT_BUILD_WGSL_WRITER "Build the WGSL output writer" ON)
endif()
# check if WAYLAND is the current Session Type and enable DAWN_USE_WAYLAND Wayland option @compile time
# You can override this using: cmake -DDAWN_USE_WAYLAND=X (X = ON | OFF)
if(LINUX)
if ($ENV{XDG_SESSION_TYPE} MATCHES wayland)
option(DAWN_USE_WAYLAND "Enable support for Wayland surface" ON)
endif()
endif()
add_subdirectory("${IMGUI_DAWN_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/dawn" EXCLUDE_FROM_ALL)
set(LIBRARIES webgpu_dawn webgpu_cpp ${OS_LIBRARIES})
else()
# Native WGPU build settings
set(WGPU_NATIVE_LIB_DIR ${IMGUI_WGPU_DIR}/lib)
find_library(WGPU_LIBRARY NAMES libwgpu_native.a wgpu_native.lib wgpu_native
HINTS ${WGPU_NATIVE_LIB_DIR} REQUIRED)
if (WIN32)
set(OS_LIBRARIES d3dcompiler ws2_32 userenv bcrypt ntdll opengl32 Propsys RuntimeObject)
elseif(UNIX AND NOT APPLE)
set(OS_LIBRARIES "-lm -ldl")
endif()
set(LIBRARIES ${WGPU_LIBRARY} ${OS_LIBRARIES})
endif()
endif()
add_executable(${IMGUI_EXECUTABLE} ${IMGUI_EXAMPLE_SOURCE_FILES})
target_include_directories(${IMGUI_EXECUTABLE} PUBLIC
${IMGUI_DIR}
${IMGUI_DIR}/backends
${SDL3_INCLUDE_DIRS}
)
target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_EXAMPLE_SDL3_WGPU")
# IMGUI_IMPL_WEBGPU_BACKEND_DAWN/WGPU internal define is set according to:
# EMSCRIPTEN: by used FLAG
# --use-port=emdawnwebgpu --> IMGUI_IMPL_WEBGPU_BACKEND_DAWN enabled (+EMSCRIPTEN)
# -sUSE_WEBGPU=1 --> IMGUI_IMPL_WEBGPU_BACKEND_WGPU enabled (+EMSCRIPTEN)
# NATIVE: by used SDK installation directory
# if IMGUI_DAWN_DIR is valid --> IMGUI_IMPL_WEBGPU_BACKEND_DAWN enabled
# if IMGUI_WGPU_DIR is valid --> IMGUI_IMPL_WEBGPU_BACKEND_WGPU enabled
# Native settings
IF(NOT EMSCRIPTEN)
target_link_libraries(${IMGUI_EXECUTABLE} PUBLIC ${LIBRARIES} SDL3::SDL3)
if(IMGUI_DAWN_DIR)
target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_DAWN")
else()
target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGPU")
target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_WGPU_DIR}/include)
endif()
# Emscripten settings
else()
set(CMAKE_EXECUTABLE_SUFFIX ".html")
if(NOT IMGUI_SDL3_EMSCRIPTEN_DIR)
message(FATAL_ERROR "aaaaa")
else()
target_include_directories(${IMGUI_EXECUTABLE} PUBLIC ${IMGUI_SDL3_EMSCRIPTEN_DIR}/include)
find_library(SDL_EMS_LIBRARY NAMES libSDL3.a libSDL3.lib libSDL3 SDL3.a SDL3.lib SDL3
PATHS ${IMGUI_SDL3_EMSCRIPTEN_DIR}/build REQUIRED NO_CMAKE_FIND_ROOT_PATH)
target_link_libraries(${IMGUI_EXECUTABLE} PUBLIC ${SDL_EMS_LIBRARY})
endif()
if("${IMGUI_EMSCRIPTEN_WEBGPU_FLAG}" STREQUAL "--use-port=emdawnwebgpu")
target_compile_options(${IMGUI_EXECUTABLE} PUBLIC "${IMGUI_EMSCRIPTEN_WEBGPU_FLAG}")
target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_DAWN")
else()
target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_IMPL_WEBGPU_BACKEND_WGPU")
endif()
message(STATUS "Using ${IMGUI_EMSCRIPTEN_WEBGPU_FLAG} WebGPU implementation")
target_compile_options(${IMGUI_EXECUTABLE} PUBLIC "-sUSE_SDL=2" )
target_link_options(${IMGUI_EXECUTABLE} PRIVATE
"${IMGUI_EMSCRIPTEN_WEBGPU_FLAG}"
#"-sUSE_SDL=3" # There is currently no official support internally at emscripten
"-sWASM=1"
"-sASYNCIFY=1"
"-sALLOW_MEMORY_GROWTH=1"
"-sNO_EXIT_RUNTIME=0"
"-sASSERTIONS=1"
"-sDISABLE_EXCEPTION_CATCHING=1"
#"-sNO_FILESYSTEM=1" # sdl3 integrates a filesystem, you need to disable it at SDL3 build time
"--shell-file=${CMAKE_CURRENT_LIST_DIR}/../libs/emscripten/shell_minimal.html"
)
set_target_properties(${IMGUI_EXECUTABLE} PROPERTIES OUTPUT_NAME "index")
endif()
file(WRITE ${CMAKE_SOURCE_DIR}/.idea/.name ${PROJECT_NAME}) # used to rename a Project in clion (run once)

View File

@@ -0,0 +1,91 @@
#
# Makefile to use with emscripten
# See https://emscripten.org/docs/getting_started/downloads.html
# for installation instructions.
#
# This Makefile assumes you have loaded emscripten's environment.
# (On Windows, you may need to execute emsdk_env.bat or encmdprompt.bat ahead)
#
# Running `make` will produce three files:
# - web/index.html (current stored in the repository)
# - web/index.js
# - web/index.wasm
#
# All three are needed to run the demo.
CC = emcc
CXX = em++
WEB_DIR = web
EXE = $(WEB_DIR)/index.js
IMGUI_DIR = ../..
SOURCES = main.cpp
SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp
SOURCES += $(IMGUI_DIR)/backends/imgui_impl_glfw.cpp $(IMGUI_DIR)/backends/imgui_impl_wgpu.cpp
OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES))))
UNAME_S := $(shell uname -s)
CPPFLAGS =
LDFLAGS =
EMS =
##---------------------------------------------------------------------
## EMSCRIPTEN OPTIONS
##---------------------------------------------------------------------
# ("EMS" options gets added to both CPPFLAGS and LDFLAGS, whereas some options are for linker only)
EMS += -s DISABLE_EXCEPTION_CATCHING=1
LDFLAGS += -s USE_GLFW=3 -s USE_WEBGPU=1
LDFLAGS += -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
# Emscripten allows preloading a file or folder to be accessible at runtime.
# The Makefile for this example project suggests embedding the misc/fonts/ folder into our application, it will then be accessible as "/fonts"
# See documentation for more details: https://emscripten.org/docs/porting/files/packaging_files.html
# (Default value is 0. Set to 1 to enable file-system and include the misc/fonts/ folder as part of the build.)
USE_FILE_SYSTEM ?= 0
ifeq ($(USE_FILE_SYSTEM), 0)
LDFLAGS += -s NO_FILESYSTEM=1
CPPFLAGS += -DIMGUI_DISABLE_FILE_FUNCTIONS
endif
ifeq ($(USE_FILE_SYSTEM), 1)
LDFLAGS += --no-heap-copy --preload-file ../../misc/fonts@/fonts
endif
##---------------------------------------------------------------------
## FINAL BUILD FLAGS
##---------------------------------------------------------------------
CPPFLAGS += -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends
#CPPFLAGS += -g
CPPFLAGS += -Wall -Wformat -Os $(EMS)
#LDFLAGS += --shell-file shell_minimal.html
LDFLAGS += $(EMS)
##---------------------------------------------------------------------
## BUILD RULES
##---------------------------------------------------------------------
%.o:%.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
%.o:$(IMGUI_DIR)/%.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
%.o:$(IMGUI_DIR)/backends/%.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
all: $(EXE)
@echo Build complete for $(EXE)
$(WEB_DIR):
mkdir $@
serve: all
python3 -m http.server -d $(WEB_DIR)
$(EXE): $(OBJS) $(WEB_DIR)
$(CXX) -o $@ $(OBJS) $(LDFLAGS)
clean:
rm -f $(EXE) $(OBJS) $(WEB_DIR)/*.js $(WEB_DIR)/*.wasm $(WEB_DIR)/*.wasm.pre

View File

@@ -0,0 +1,63 @@
## How to Build
### Using CMake
#### Building for desktop (WebGPU-native) with Google Dawn:
1. `git clone https://github.com/google/dawn dawn`
2. `cmake -B build -DIMGUI_DAWN_DIR=dawn`
3. `cmake --build build`
The resulting binary will be found at one of the following locations:
* build/Debug/example_sdl2_wgpu[.exe]
* build/example_sdl2_wgpu[.exe]
#### Building for desktop (WebGPU-Native) with WGPU:
1. download WGPU-Native autogenerated binary modules for your platform/compiler from: https://github.com/gfx-rs/wgpu-native/releases
2. unzip the downloaded file in `your_preferred_folder`
3. move into `your_preferred_folder` (e.g. typing: `cd your_preferred_folder`)
4. `cmake -B build -DIMGUI_WGPU_DIR=your_preferred_folder` ("full path" or "relative" starting from current directory)
5. `cmake --build build`
The resulting binary will be found at one of the following locations:
* build/Debug/example_sdl2_wgpu[.exe]
* build/example_sdl2_wgpu[.exe]
#### Building for Emscripten:
At current date (Jul/2025) there is no official support for SDL3 in EMSCRIPTEN, and its use in EMSCRIPTEN is also in the experimental phase.
To use SDL3 with EMSCRIPTEN you need to download the SDL3 repository (source code) and build the libraries for EMSCRIPTEN following the official page:
https://wiki.libsdl.org/SDL3/README-emscripten#building-sdlemscripten
- the SDL3 library will be built in `your_SDL3_folder/build`
1. Install Emscripten SDK following the instructions: https://emscripten.org/docs/getting_started/downloads.html
2. Install Ninja build system
3. `emcmake cmake -DIMGUI_SDL3_EMSCRIPTEN_DIR=your_SDL3_folder -G Ninja -B build`
4. `cmake --build build`
To run:
- `emrun build/index.html`
or
- `python -m http.server` then open WGPU browser with url: `http://localhost:8000/build`
### Using makefile
- 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.
- You may also refer to our [Continuous Integration setup](https://github.com/ocornut/imgui/tree/master/.github/workflows) for Emscripten setup.
- Then build using `make -f Makefile.emscripten` while in the `example_glfw_wgpu/` directory.
- Requires recent Emscripten as WGPU is still a work-in-progress API.
## How to Run
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.
- `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>
_"Unfortunately several browsers (including Chrome, Safari, and Internet Explorer) do not support file:// [XHR](https://emscripten.org/docs/site/glossary.html#term-xhr) requests, and cant load extra files needed by the HTML (like a .wasm file, or packaged file data as mentioned lower down). For these browsers youll need to serve the files using a [local webserver](https://emscripten.org/docs/getting_started/FAQ.html#faq-local-webserver) and then open http://localhost:8000/hello.html."_
- Emscripten SDK has a handy `emrun` command: `emrun web/example_glfw_wgpu.html --browser firefox` which will spawn a temporary local webserver (in Firefox). See https://emscripten.org/docs/compiling/Running-html-files-with-emrun.html for details.
- You may use Python 3 builtin webserver: `python -m http.server -d web` (this is what `make serve` uses).
- You may use Python 2 builtin webserver: `cd web && python -m SimpleHTTPServer`.
- If you are accessing the files over a network, certain browsers, such as Firefox, will restrict Gamepad API access to secure contexts only (e.g. https only).

View File

@@ -0,0 +1,499 @@
// Dear ImGui: standalone example application for using GLFW + WebGPU
// - Emscripten is supported for publishing on web. See https://emscripten.org.
// - Dawn is used as a WebGPU implementation on desktop.
// Learn about Dear ImGui:
// - FAQ https://dearimgui.com/faq
// - Getting Started https://dearimgui.com/getting-started
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
// - Introduction, links and more at the top of imgui.cpp
#include "imgui.h"
#include "imgui_impl_sdl3.h"
#include "imgui_impl_wgpu.h"
#include <stdio.h>
#include <stdlib.h>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#include <emscripten/html5.h>
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
#include <emscripten/html5_webgpu.h>
#endif
#include "../libs/emscripten/emscripten_mainloop_stub.h"
#endif
#include <SDL3/SDL.h>
#include <webgpu/webgpu.h>
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
#include <webgpu/webgpu_cpp.h>
#endif
// This example can also compile and run with Emscripten! See 'Makefile.emscripten' for details.
#ifdef __EMSCRIPTEN__
#endif
// Global WebGPU required states
WGPUInstance wgpu_instance = nullptr;
WGPUDevice wgpu_device = nullptr;
WGPUSurface wgpu_surface = nullptr;
WGPUQueue wgpu_queue = nullptr;
WGPUSurfaceConfiguration wgpu_surface_configuration {};
int wgpu_surface_width = 1280;
int wgpu_surface_height = 720;
// Forward declarations
static bool InitWGPU(void* window);
void ResizeSurface(int width, int height)
{
wgpu_surface_configuration.width = wgpu_surface_width = width;
wgpu_surface_configuration.height = wgpu_surface_height = height;
wgpuSurfaceConfigure( wgpu_surface, (WGPUSurfaceConfiguration *) &wgpu_surface_configuration );
}
static void ReleaseTextureAndConfigureSurface(WGPUTexture &texture, int fb_width, int fb_height)
{
if (texture)
wgpuTextureRelease(texture);
if ( fb_width > 0 && fb_height > 0 )
ResizeSurface(fb_width, fb_height);
}
// Main code
int main(int, char**)
{
#if defined(__linux__)
// SDL3 default is "x11" (it works also in "wayland"), uncomment the line below to use "wayland"
// SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "wayland");
#endif
// Init SDL
if (!SDL_Init(SDL_INIT_VIDEO ))
{
printf("Error: SDL_Init(): %s\n", SDL_GetError());
return EXIT_FAILURE;
}
SDL_WindowFlags window_flags = SDL_WINDOW_RESIZABLE;
SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL3+WebGPU example", wgpu_surface_width, wgpu_surface_height, window_flags);
if (window == nullptr)
{
printf("Error: SDL_CreateWindow(): %s\n", SDL_GetError());
return EXIT_FAILURE;
}
// Initialize WGPU
InitWGPU(window);
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
// Setup Dear ImGui style
ImGui::StyleColorsDark();
//ImGui::StyleColorsLight();
// Setup Platform/Renderer backends
ImGui_ImplSDL3_InitForOther(window);
ImGui_ImplWGPU_InitInfo init_info;
init_info.Device = wgpu_device;
init_info.NumFramesInFlight = 3;
init_info.RenderTargetFormat = wgpu_surface_configuration.format;
init_info.DepthStencilFormat = WGPUTextureFormat_Undefined;
ImGui_ImplWGPU_Init(&init_info);
// Load Fonts
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
// - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
// - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
// - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
// - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
// - Read 'docs/FONTS.md' for more instructions and details.
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
// - Emscripten allows preloading a file or folder to be accessible at runtime. See Makefile for details.
//io.Fonts->AddFontDefault();
#ifndef IMGUI_DISABLE_FILE_FUNCTIONS
//io.Fonts->AddFontFromFileTTF("fonts/segoeui.ttf", 18.0f);
//io.Fonts->AddFontFromFileTTF("fonts/DroidSans.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("fonts/Roboto-Medium.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("fonts/Cousine-Regular.ttf", 15.0f);
//io.Fonts->AddFontFromFileTTF("fonts/ProggyTiny.ttf", 10.0f);
//ImFont* font = io.Fonts->AddFontFromFileTTF("fonts/ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());
//IM_ASSERT(font != nullptr);
#endif
// Our state
bool show_demo_window = true;
bool show_another_window = false;
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
SDL_Event event;
bool canCloseWindow = false;
// Main loop
#ifdef __EMSCRIPTEN__
// For an Emscripten build we are disabling file-system access, so let's not attempt to do a fopen() of the imgui.ini file.
// You may manually call LoadIniSettingsFromMemory() to load settings from your own storage.
io.IniFilename = nullptr;
EMSCRIPTEN_MAINLOOP_BEGIN
#else
while (!canCloseWindow)
#endif
{
while (SDL_PollEvent(&event))
{
ImGui_ImplSDL3_ProcessEvent(&event);
if (event.type == SDL_EVENT_QUIT ||
(event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED && event.window.windowID == SDL_GetWindowID(window)))
canCloseWindow = true;
}
// Poll and handle events (inputs, window resize, etc.)
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
// React to changes in screen size
int width, height;
SDL_GetWindowSize(window, &width, &height);
if (width != wgpu_surface_width || height != wgpu_surface_height)
{
ImGui_ImplWGPU_InvalidateDeviceObjects();
ResizeSurface(width, height);
ImGui_ImplWGPU_CreateDeviceObjects();
//continue;
}
WGPUSurfaceTexture surfaceTexture;
wgpuSurfaceGetCurrentTexture(wgpu_surface, &surfaceTexture);
// Check SurfaceTexture status, if NOT optimal status we try to re-configure Surface
if (!ImGui_ImplWGPU_CheckSurfaceTextureOptimalStatus_Helper(surfaceTexture.status)) {
ReleaseTextureAndConfigureSurface(surfaceTexture.texture, width, height);
continue;
}
// Start the Dear ImGui frame
ImGui_ImplWGPU_NewFrame();
ImGui_ImplSDL3_NewFrame();
ImGui::NewFrame();
// 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
if (show_demo_window)
ImGui::ShowDemoWindow(&show_demo_window);
// 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
{
static float f = 0.0f;
static int counter = 0;
ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state
ImGui::Checkbox("Another Window", &show_another_window);
ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
counter++;
ImGui::SameLine();
ImGui::Text("counter = %d", counter);
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
ImGui::End();
}
// 3. Show another simple window.
if (show_another_window)
{
ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
ImGui::Text("Hello from another window!");
if (ImGui::Button("Close Me"))
show_another_window = false;
ImGui::End();
}
// Rendering
ImGui::Render();
WGPUTextureViewDescriptor viewDescriptor {};
viewDescriptor.format = wgpu_surface_configuration.format;
viewDescriptor.dimension = WGPUTextureViewDimension_2D;
viewDescriptor.mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED;
viewDescriptor.arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED;
viewDescriptor.aspect = WGPUTextureAspect_All;
WGPUTextureView textureView = wgpuTextureCreateView(surfaceTexture.texture, &viewDescriptor);
WGPURenderPassColorAttachment color_attachments {};
color_attachments.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
color_attachments.loadOp = WGPULoadOp_Clear;
color_attachments.storeOp = WGPUStoreOp_Store;
color_attachments.clearValue = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w };
color_attachments.view = textureView;
WGPURenderPassDescriptor render_pass_desc {};
render_pass_desc.colorAttachmentCount = 1;
render_pass_desc.colorAttachments = &color_attachments;
render_pass_desc.depthStencilAttachment = nullptr;
WGPUCommandEncoderDescriptor enc_desc {};
WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(wgpu_device, &enc_desc);
WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &render_pass_desc);
ImGui_ImplWGPU_RenderDrawData(ImGui::GetDrawData(), pass);
wgpuRenderPassEncoderEnd(pass);
WGPUCommandBufferDescriptor cmd_buffer_desc {};
WGPUCommandBuffer cmd_buffer = wgpuCommandEncoderFinish(encoder, &cmd_buffer_desc);
wgpuQueueSubmit(wgpu_queue, 1, &cmd_buffer);
#ifndef __EMSCRIPTEN__
wgpuSurfacePresent(wgpu_surface);
// Tick needs to be called in Dawn to display validation errors
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
wgpuDeviceTick(wgpu_device);
#endif
#endif
wgpuTextureViewRelease(textureView);
wgpuRenderPassEncoderRelease(pass);
wgpuCommandEncoderRelease(encoder);
wgpuCommandBufferRelease(cmd_buffer);
}
#ifdef __EMSCRIPTEN__
EMSCRIPTEN_MAINLOOP_END;
#endif
// Cleanup
ImGui_ImplWGPU_Shutdown();
ImGui_ImplSDL3_Shutdown();
ImGui::DestroyContext();
wgpuSurfaceUnconfigure(wgpu_surface);
wgpuSurfaceRelease(wgpu_surface);
wgpuQueueRelease(wgpu_queue);
wgpuDeviceRelease(wgpu_device);
wgpuInstanceRelease(wgpu_instance);
// Terminate SDL
SDL_DestroyWindow(window);
SDL_Quit();
return EXIT_SUCCESS;
}
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
static WGPUAdapter GetAdapter(wgpu::Instance &instance)
{
wgpu::Adapter acquiredAdapter;
wgpu::RequestAdapterOptions adapterOptions;
auto onRequestAdapter = [&](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter, wgpu::StringView message) {
if (status != wgpu::RequestAdapterStatus::Success)
{
printf("Failed to get an adapter: %s\n", message.data);
return;
}
acquiredAdapter = std::move(adapter);
};
// Synchronously (wait until) acquire Adapter
wgpu::Future waitAdapterFunc { instance.RequestAdapter(&adapterOptions, wgpu::CallbackMode::WaitAnyOnly, onRequestAdapter) };
wgpu::WaitStatus waitStatusAdapter = instance.WaitAny(waitAdapterFunc, UINT64_MAX);
assert(acquiredAdapter != nullptr && waitStatusAdapter == wgpu::WaitStatus::Success && "Error on Adapter request");
#ifndef NDEBUG
ImGui_ImplWGPU_PrintAdapterInfo_Helper(acquiredAdapter.Get());
#endif
return acquiredAdapter.MoveToCHandle();
}
static WGPUDevice GetDevice(wgpu::Instance &instance, wgpu::Adapter &adapter)
{
// Set device callback functions
wgpu::DeviceDescriptor deviceDesc;
deviceDesc.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous, ImGui_ImplWGPU_DAWN_DeviceLostCallback_Helper);
deviceDesc.SetUncapturedErrorCallback(ImGui_ImplWGPU_DAWN_ErrorCallback_Helper);
wgpu::Device acquiredDevice;
auto onRequestDevice = [&](wgpu::RequestDeviceStatus status, wgpu::Device localDevice, wgpu::StringView message) {
if (status != wgpu::RequestDeviceStatus::Success)
{
printf("Failed to get an device: %s\n", message.data);
return;
}
acquiredDevice = std::move(localDevice);
};
// Synchronously (wait until) get Device
wgpu::Future waitDeviceFunc { adapter.RequestDevice(&deviceDesc, wgpu::CallbackMode::WaitAnyOnly, onRequestDevice) };
wgpu::WaitStatus waitStatusDevice = instance.WaitAny(waitDeviceFunc, UINT64_MAX);
assert(acquiredDevice != nullptr && waitStatusDevice == wgpu::WaitStatus::Success && "Error on Device request");
return acquiredDevice.MoveToCHandle();
}
#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
#ifdef __EMSCRIPTEN__
// Adapter and device initialization via JS
EM_ASYNC_JS( void, getAdapterAndDeviceViaJS, (),
{
if (!navigator.gpu) throw Error("WebGPU not supported.");
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
Module.preinitializedWebGPUDevice = device;
} );
#else // __EMSCRIPTEN__
static void handle_request_adapter(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void *userdata1, void *userdata2)
{
if (status == WGPURequestAdapterStatus_Success)
{
WGPUAdapter *extAdapter = (WGPUAdapter *) userdata1;
*extAdapter = adapter;
}
else
printf("Request_adapter status=%#.8x message=%.*s\n", status, (int) message.length, message.data);
}
static void handle_request_device(WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView message, void *userdata1, void *userdata2)
{
if (status == WGPURequestDeviceStatus_Success)
{
WGPUDevice *extDevice = (WGPUDevice *) userdata1;
*extDevice = device;
}
else
printf("Request_device status=%#.8x message=%.*s\n", status, (int) message.length, message.data);
}
static WGPUAdapter GetAdapter(WGPUInstance &instance)
{
WGPURequestAdapterOptions adapterOptions = {};
static WGPUAdapter localAdapter;
WGPURequestAdapterCallbackInfo adapterCallbackInfo = {};
adapterCallbackInfo.callback = handle_request_adapter;
adapterCallbackInfo.userdata1 = &localAdapter;
wgpuInstanceRequestAdapter(wgpu_instance, &adapterOptions, adapterCallbackInfo);
assert(localAdapter && "Error on Adapter request");
#ifndef NDEBUG
ImGui_ImplWGPU_PrintAdapterInfo_Helper(localAdapter);
#endif
return localAdapter;
}
static WGPUDevice GetDevice(WGPUAdapter &adapter)
{
static WGPUDevice localDevice;
WGPURequestDeviceCallbackInfo deviceCallbackInfo = {};
deviceCallbackInfo.callback = handle_request_device;
deviceCallbackInfo.userdata1 = &localDevice;
wgpuAdapterRequestDevice(adapter, NULL, deviceCallbackInfo);
assert(localDevice && "Error on Device request");
return localDevice;
}
#endif // __EMSCRIPTEN__
#endif // IMGUI_IMPL_WEBGPU_BACKEND_WGPU
static bool InitWGPU(void* window)
{
WGPUTextureFormat preferred_fmt = WGPUTextureFormat_Undefined; // acquired from SurfaceCapabilities
// Google DAWN backend: Adapter and Device acquisition, Surface creation
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
wgpu::InstanceDescriptor instanceDescriptor = {};
instanceDescriptor.capabilities.timedWaitAnyEnable = true;
wgpu::Instance instance = wgpu::CreateInstance(&instanceDescriptor);
wgpu::Adapter adapter { GetAdapter(instance) };
wgpu_device = GetDevice(instance, adapter);
// Create the surface.
#ifdef __EMSCRIPTEN__
wgpu::EmscriptenSurfaceSourceCanvasHTMLSelector canvasDesc{};
canvasDesc.selector = "#canvas";
wgpu::SurfaceDescriptor surfaceDesc = {};
surfaceDesc.nextInChain = &canvasDesc;
wgpu::Surface surface = instance.CreateSurface(&surfaceDesc) ;
#else
wgpu::Surface surface = ImGui_ImplSDL3_CreateWGPUSurface_Helper(instance.Get(), (SDL_Window *) window);
#endif
if (!surface)
return false;
// Moving Dawn objects into WGPU handles
wgpu_instance = instance.MoveToCHandle();
wgpu_surface = surface.MoveToCHandle();
WGPUSurfaceCapabilities surface_capabilities = {};
wgpuSurfaceGetCapabilities(wgpu_surface, adapter.Get(), &surface_capabilities);
preferred_fmt = surface_capabilities.formats[0];
// WGPU backend: Adapter and Device acquisition, Surface creation
#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
wgpu_instance = wgpuCreateInstance(nullptr);
#ifdef __EMSCRIPTEN__
getAdapterAndDeviceViaJS();
wgpu_device = emscripten_webgpu_get_device();
assert(wgpu_device != nullptr && "Error creating the Device");
WGPUSurfaceDescriptorFromCanvasHTMLSelector html_surface_desc = {};
html_surface_desc.chain.sType = WGPUSType_SurfaceDescriptorFromCanvasHTMLSelector;
html_surface_desc.selector = "#canvas";
WGPUSurfaceDescriptor surface_desc = {};
surface_desc.nextInChain = &html_surface_desc.chain;
// Create the surface.
wgpu_surface = wgpuInstanceCreateSurface(wgpu_instance, &surface_desc);
preferred_fmt = wgpuSurfaceGetPreferredFormat(wgpu_surface, {} /* adapter */);
#else // __EMSCRIPTEN__
wgpuSetLogCallback(ImGui_ImplWGPU_WGPU_LogCallback_Helper, NULL);
wgpuSetLogLevel(WGPULogLevel_Warn);
static WGPUAdapter adapter = GetAdapter(wgpu_instance);
wgpu_device = GetDevice(adapter);
// Create the surface.
wgpu_surface = ImGui_ImplSDL3_CreateWGPUSurface_Helper(wgpu_instance, (SDL_Window *) window);
if (!wgpu_surface)
return false;
WGPUSurfaceCapabilities surface_capabilities = {};
wgpuSurfaceGetCapabilities(wgpu_surface, adapter, &surface_capabilities);
preferred_fmt = surface_capabilities.formats[0];
#endif // __EMSCRIPTEN__
#endif // IMGUI_IMPL_WEBGPU_BACKEND_WGPU
wgpu_surface_configuration.presentMode = WGPUPresentMode_Fifo;
wgpu_surface_configuration.alphaMode = WGPUCompositeAlphaMode_Auto;
wgpu_surface_configuration.usage = WGPUTextureUsage_RenderAttachment;
wgpu_surface_configuration.width = wgpu_surface_width;
wgpu_surface_configuration.height = wgpu_surface_height;
wgpu_surface_configuration.device = wgpu_device;
wgpu_surface_configuration.format = preferred_fmt;
wgpuSurfaceConfigure(wgpu_surface, &wgpu_surface_configuration);
wgpu_queue = wgpuDeviceGetQueue(wgpu_device);
return true;
}