Examples: GLFW+WebGPU: update to latest specs and to work on Emscripten 4.0.10+ and latest Dawn-Native, WGPU-Native. (#8381, #8567, #8191, #7435)

This commit is contained in:
BrutPitt
2025-10-31 18:58:05 +01:00
committed by ocornut
parent c5b2a848fa
commit 778aed9966
6 changed files with 651 additions and 266 deletions

View File

@@ -91,13 +91,15 @@ Other Changes:
ImGui_ImplWGPU_GetDeviceLostReasonName(), ImGui_ImplWGPU_GetErrorTypeName(), ImGui_ImplWGPU_GetDeviceLostReasonName(), ImGui_ImplWGPU_GetErrorTypeName(),
ImGui_ImplWGPU_GetLogLevelName(). ImGui_ImplWGPU_GetLogLevelName().
- WebGPU: update to compile with Dawn and Emscripten's 4.0.10+ - WebGPU: update to compile with Dawn and Emscripten's 4.0.10+
'--use-port=emdawnwebgpu' ports. (#8381, #8898) [@brutpitt, @trbabb] '--use-port=emdawnwebgpu' ports. (#8381, #8898, #7435) [@brutpitt, @trbabb]
When using Emscripten 4.0.10+, backend now defaults to IMGUI_IMPL_WEBGPU_BACKEND_DAWN When using Emscripten 4.0.10+, backend now defaults to IMGUI_IMPL_WEBGPU_BACKEND_DAWN
instead of IMGUI_IMPL_WEBGPU_BACKEND_WGPU, if neither are specified. instead of IMGUI_IMPL_WEBGPU_BACKEND_WGPU, if neither are specified.
(note: examples application were not updated yet) (note: examples application were not updated yet)
- Win32: Revert 1.92.4 change of comparing dwPacketNumber, which prevents - Win32: Revert 1.92.4 change of comparing dwPacketNumber, which prevents
refreshing accurate gamepad info after focus-out + io.ClearInputKeys(). (#8556) refreshing accurate gamepad info after focus-out + io.ClearInputKeys(). (#8556)
- Examples: - Examples:
- GLFW+WebGPU: update example for latest specs, to work on Emscripten 4.0.10+,
latest Dawn-Native and WGPU-Native. (#8381, #8567, #8191, #7435) [@brutpitt]
- GLFW+WebGPU: removed unnecessary ImGui_ImplWGPU_InvalidateDeviceObjects() call - GLFW+WebGPU: removed unnecessary ImGui_ImplWGPU_InvalidateDeviceObjects() call
during surface resize. (#8381) during surface resize. (#8381)

View File

@@ -6,18 +6,30 @@
# * 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. cmake -B build -DIMGUI_WGPU_DIR=your_preferred_folder ("full path" or "relative" starting from current directory)
# 4. 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
# 3. emcmake cmake -G Ninja -B build # 3. emcmake cmake -G Ninja -B build
# (optional) -DIMGUI_EMSCRIPTEN_WEBGPU_FLAG="--use-port=path/to/emdawnwebgpu_package/emdawnwebgpu.port.py", see ReadMe.md
# 3. cmake --build build # 3. cmake --build build
# 4. emrun build/index.html # 4. emrun build/index.html
cmake_minimum_required(VERSION 3.10.2) cmake_minimum_required(VERSION 3.22) # Dawn requires CMake >= 3.22
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()
set(CMAKE_CXX_STANDARD 17) # Dawn requires C++17 set(CMAKE_CXX_STANDARD 17) # Dawn requires C++17
@@ -25,93 +37,172 @@ set(CMAKE_CXX_STANDARD 17) # Dawn requires C++17
# Dear ImGui # Dear ImGui
set(IMGUI_DIR ../../) set(IMGUI_DIR ../../)
# Libraries set(IMGUI_EXAMPLE_SOURCE_FILES
# Example code
main.cpp
# Dear ImGui 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(EMSCRIPTEN) if(EMSCRIPTEN)
if(EMSCRIPTEN_VERSION VERSION_GREATER_EQUAL "3.1.57") if(NOT IMGUI_EMSCRIPTEN_WEBGPU_FLAG) # if IMGUI_EMSCRIPTEN_WEBGPU_FLAG not used, set by current EMSCRIPTEN version
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)") if(EMSCRIPTEN_VERSION VERSION_GREATER_EQUAL "4.0.10")
else() 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")
# cannot use contrib.glfw3 prior to 3.1.57 else()
set(IMGUI_EMSCRIPTEN_GLFW3 "-sUSE_GLFW=3" CACHE STRING "Use -sUSE_GLFW=3 for GLFW implementation" FORCE) set(IMGUI_EMSCRIPTEN_WEBGPU_FLAG "-sUSE_WEBGPU=1" CACHE STRING "Use -sUSE_WEBGPU=1 for EMSCRIPTEN WGPU implementation")
endif() endif()
set(LIBRARIES glfw) else() # if IMGUI_EMSCRIPTEN_WEBGPU_FLAG used, check correct version
add_compile_options(-sDISABLE_EXCEPTION_CATCHING=1 -DIMGUI_DISABLE_FILE_FUNCTIONS=1 -DIMGUI_IMPL_WEBGPU_BACKEND_WGPU) if(EMSCRIPTEN_VERSION VERSION_LESS "4.0.10" AND "${IMGUI_EMSCRIPTEN_WEBGPU_FLAG}" MATCHES "emdawnwebgpu")
# it's necessary EMSCRIPTEN >= v4.0.10 (although "--use-port=path/to/emdawnwebgpu.port.py" is supported/tested from v4.0.8)
message(FATAL_ERROR "emdawnwebgpu needs EMSCRIPTEN version >= 4.0.10")
endif()
endif()
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)")
else() # 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)
endif()
set(LIBRARIES glfw)
add_compile_options(-sDISABLE_EXCEPTION_CATCHING=1 -DIMGUI_DISABLE_FILE_FUNCTIONS=1)
else() # Native/Desktop build
# Check DAWN/WGPU directory
if(NOT IMGUI_DAWN_DIR AND NOT IMGUI_WGPU_DIR) # if it's Native/Desktop build, IMGUI_DAWN_DIR or IMGUI_WGPU_DIR must be specified
message(FATAL_ERROR "Please specify the Dawn or WGPU base directory")
endif()
if(IMGUI_DAWN_DIR AND IMGUI_WGPU_DIR) # both IMGUI_DAWN_DIR and IMGUI_WGPU_DIR cannot be set
message(FATAL_ERROR "Please specify only one between Dawn / WGPU base directory")
endif()
if(APPLE) # Add SDL2 module to get Surface, with libs and file property for MacOS build
set_source_files_properties(${IMGUI_DIR}/backends/imgui_impl_wgpu.cpp PROPERTIES COMPILE_FLAGS "-x objective-c++")
# set(OS_LIBRARIES "-framework CoreFoundation -framework QuartzCore -framework Metal -framework MetalKit -framework Cocoa")
endif()
if(IMGUI_DAWN_DIR) # DAWN-Native build options
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)
set(DAWN_BUILD_MONOLITHIC_LIBRARY "STATIC" CACHE STRING "Build monolithic library: SHARED, STATIC, or 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(DAWN_FORCE_SYSTEM_COMPONENT_LOAD "Allow system component fallback" ON)
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" ON)
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_glfw glfw)
else() # WGPU-Native build options
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)
set(OS_LIBRARIES d3dcompiler ws2_32 userenv bcrypt ntdll opengl32 Propsys RuntimeObject)
elseif(UNIX AND NOT APPLE)
set(OS_LIBRARIES "-lm -ldl")
elseif(APPLE)
set(OS_LIBRARIES "-framework CoreFoundation -framework QuartzCore -framework Metal -framework MetalKit -framework Cocoa")
endif()
set(LIBRARIES glfw ${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
)
target_compile_definitions(${IMGUI_EXECUTABLE} PUBLIC "IMGUI_EXAMPLE_GLFW_WGPU")
# compiler option only for IMGUI_EXAMPLE_SOURCE_FILES
if (MSVC)
target_compile_options(${IMGUI_EXECUTABLE} PUBLIC /W4) # warning level 4
else() else()
# Dawn wgpu desktop target_compile_options(${IMGUI_EXECUTABLE} PUBLIC -Wall) # -Wextra -Wpedantic
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)
# 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()
add_subdirectory("${IMGUI_DAWN_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/dawn" EXCLUDE_FROM_ALL)
set(LIBRARIES webgpu_dawn webgpu_cpp webgpu_glfw glfw)
endif() endif()
add_executable(example_glfw_wgpu # In this example IMGUI_IMPL_WEBGPU_BACKEND_DAWN / IMGUI_IMPL_WEBGPU_BACKEND_WGPU internal define is set according to:
main.cpp # EMSCRIPTEN: by used FLAG
# backend files # --use-port=emdawnwebgpu --> IMGUI_IMPL_WEBGPU_BACKEND_DAWN defined
${IMGUI_DIR}/backends/imgui_impl_glfw.cpp # -sUSE_WEBGPU=1 --> IMGUI_IMPL_WEBGPU_BACKEND_WGPU defined
${IMGUI_DIR}/backends/imgui_impl_wgpu.cpp # NATIVE: by used SDK installation directory
# Dear ImGui files # if IMGUI_DAWN_DIR is valid --> IMGUI_IMPL_WEBGPU_BACKEND_DAWN defined
${IMGUI_DIR}/imgui.cpp # if IMGUI_WGPU_DIR is valid --> IMGUI_IMPL_WEBGPU_BACKEND_WGPU defined
${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()
target_include_directories(example_glfw_wgpu PUBLIC
${IMGUI_DIR}
${IMGUI_DIR}/backends
)
target_link_libraries(example_glfw_wgpu PUBLIC ${LIBRARIES}) if(NOT EMSCRIPTEN) # WegGPU-Native settings
# Set IMGUI_IMPL_WEBGPU_BACKEND_XXXX based on the SDK (directory) used
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 target_link_libraries(${IMGUI_EXECUTABLE} INTERFACE webgpu_cpp PUBLIC ${LIBRARIES})
if(EMSCRIPTEN) else() # Emscripten settings
if("${IMGUI_EMSCRIPTEN_GLFW3}" STREQUAL "--use-port=contrib.glfw3") set(CMAKE_EXECUTABLE_SUFFIX ".html")
target_compile_options(example_glfw_wgpu PUBLIC
"${IMGUI_EMSCRIPTEN_GLFW3}" if("${IMGUI_EMSCRIPTEN_GLFW3}" STREQUAL "--use-port=contrib.glfw3")
target_compile_options(${IMGUI_EXECUTABLE} PUBLIC "${IMGUI_EMSCRIPTEN_GLFW3}")
endif()
message(STATUS "Using ${IMGUI_EMSCRIPTEN_GLFW3} GLFW implementation")
if("${IMGUI_EMSCRIPTEN_WEBGPU_FLAG}" MATCHES "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}"
"-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"
) )
endif() set_target_properties(${IMGUI_EXECUTABLE} PROPERTIES OUTPUT_NAME "index")
message(STATUS "Using ${IMGUI_EMSCRIPTEN_GLFW3} GLFW implementation")
target_link_options(example_glfw_wgpu PRIVATE
"-sUSE_WEBGPU=1"
"${IMGUI_EMSCRIPTEN_GLFW3}"
"-sWASM=1"
"-sALLOW_MEMORY_GROWTH=1"
"-sNO_EXIT_RUNTIME=0"
"-sASSERTIONS=1"
"-sDISABLE_EXCEPTION_CATCHING=1"
"-sNO_FILESYSTEM=1"
)
set_target_properties(example_glfw_wgpu 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

@@ -6,8 +6,8 @@
# This Makefile assumes you have loaded emscripten's environment. # This Makefile assumes you have loaded emscripten's environment.
# (On Windows, you may need to execute emsdk_env.bat or encmdprompt.bat ahead) # (On Windows, you may need to execute emsdk_env.bat or encmdprompt.bat ahead)
# #
# Running `make` will produce three files: # Running `make -f Makefile.emscripten` will produce three files:
# - web/index.html (current stored in the repository) # - web/index.html
# - web/index.js # - web/index.js
# - web/index.wasm # - web/index.wasm
# #
@@ -16,7 +16,7 @@
CC = emcc CC = emcc
CXX = em++ CXX = em++
WEB_DIR = web WEB_DIR = web
EXE = $(WEB_DIR)/index.js EXE = $(WEB_DIR)/index.html
IMGUI_DIR = ../.. IMGUI_DIR = ../..
SOURCES = main.cpp 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)/imgui.cpp $(IMGUI_DIR)/imgui_demo.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp
@@ -34,15 +34,19 @@ EMS =
# ("EMS" options gets added to both CPPFLAGS and LDFLAGS, whereas some options are for linker only) # ("EMS" options gets added to both CPPFLAGS and LDFLAGS, whereas some options are for linker only)
# Note: For glfw, we use emscripten-glfw port (contrib.glfw3) instead of (-s USE_GLFW=3) to get a better support for High DPI displays. # Note: For glfw, we use emscripten-glfw port (contrib.glfw3) instead of (-s USE_GLFW=3) to get a better support for High DPI displays.
EMS += -s DISABLE_EXCEPTION_CATCHING=1 --use-port=contrib.glfw3 EMS += -s DISABLE_EXCEPTION_CATCHING=1 --use-port=contrib.glfw3
LDFLAGS += -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s NO_EXIT_RUNTIME=0 -s ASSERTIONS=1 LDFLAGS += -s WASM=1
LDFLAGS += -s ALLOW_MEMORY_GROWTH=1
LDFLAGS += -s ASYNCIFY=1
LDFLAGS += -s NO_EXIT_RUNTIME=0
LDFLAGS += -s ASSERTIONS=1
# (1) Using legacy WebGPU implementation (Emscripten < 4.0.10) # (1) Using legacy WebGPU implementation (Emscripten < 4.0.10)
EMS += -DIMGUI_IMPL_WEBGPU_BACKEND_WGPU #EMS += -DIMGUI_IMPL_WEBGPU_BACKEND_WGPU
LDFLAGS += -s USE_WEBGPU=1 #LDFLAGS += -s USE_WEBGPU=1
# or (2) Using newer Dawn-based WebGPU port (Emscripten >= 4.0.10) (UNSUPPORTED YET) # or (2) Using newer Dawn-based WebGPU port (Emscripten >= 4.0.10)
#EMS += --use-port=emdawnwebgpu EMS += --use-port=emdawnwebgpu
#LDFLAGS += --use-port=emdawnwebgpu LDFLAGS += --use-port=emdawnwebgpu
# Build as single file (binary text encoded in .html file) # Build as single file (binary text encoded in .html file)
#LDFLAGS += -sSINGLE_FILE #LDFLAGS += -sSINGLE_FILE
@@ -67,7 +71,7 @@ endif
CPPFLAGS += -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends CPPFLAGS += -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends
#CPPFLAGS += -g #CPPFLAGS += -g
CPPFLAGS += -Wall -Wformat -Os $(EMS) CPPFLAGS += -Wall -Wformat -Os $(EMS)
#LDFLAGS += --shell-file shell_minimal.html LDFLAGS += --shell-file ../libs/emscripten/shell_minimal.html
LDFLAGS += $(EMS) LDFLAGS += $(EMS)
##--------------------------------------------------------------------- ##---------------------------------------------------------------------

View File

@@ -1,5 +1,146 @@
## 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_glfw_wgpu[.exe]
* build/example_glfw_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. `cmake -B build -DIMGUI_WGPU_DIR=your_preferred_folder` ("full path" or "relative" starting from current directory)
4. `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:
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`
- (optional) `-DIMGUI_EMSCRIPTEN_WEBGPU_FLAG="--use-port=path/to/emdawnwebgpu_package/emdawnwebgpu.port.py"`, see below
4. `cmake --build build`
#### Sync Emscripten with latest Google Dawn:
If you want to sync Emscripten with latest DAWN release it's necessary to download the `port-emdawnwgpu-package` (released daily by Google) here:
https://github.com/google/dawn/releases
Unpack it in your preferred folder and to replace the step 3 with:
3. `emcmake cmake -DIMGUI_EMSCRIPTEN_WEBGPU_FLAG="--use-port=path/to/emdawnwebgpu_package/emdawnwebgpu.port.py" -G Ninja -B build`
**N.B.**
For the WASM code produced by Emscripten to work correctly, it will also be necessary to have the "corresponding" (or newer) version of Google Canary (nightly build for developers) that includes the latest changes
---
### CMake by step
#### Generate Dawn Native:
- `cmake -G Ninja -DIMGUI_DAWN_DIR=path_to_sdk_dir -B where_to_build_dir`
- Using `IMGUI_DAWN_DIR` set `IMGUI_IMPL_WEBGPU_BACKEND_DAWN` compiler define
#### Generate WGPU Native:
- `cmake -G Ninja -DIMGUI_WGPU_DIR=path_to_sdk_dir -B where_to_build_dir`
- Using `IMGUI_WGPU_DIR` set `IMGUI_IMPL_WEBGPU_BACKEND_WGPU` compiler define
#### Generate Emscripten:
- `emcmake cmake -G Ninja -B where_to_build_dir`\
CMake checks the EMSCRIPEN version then:
- if EMS >= 4.0.10 uses `--use-port=emdawnwebgpu` flag to build
- it set `IMGUI_IMPL_WEBGPU_BACKEND_DAWN` compiler define
- if EMS < 4.0.10 uses `-sUSE_WEBGPU=1` flag to build
- it set `IMGUI_IMPL_WEBGPU_BACKEND_WGPU` compiler define
#### Generate Emscripten forcing `-sUSE_WEBGPU=1` deprecated flag even with EMS >= 4.0.10
- `emcmake cmake -G Ninja -DIMGUI_EMSCRIPTEN_WEBGPU_FLAG="-sUSE_WEBGPU=1" -B where_to_build_dir`
- it set `IMGUI_IMPL_WEBGPU_BACKEND_WGPU` compiler define
#### Generate Emscripten using external WebGPU library (emdawnwebgpu_pkg)
- `emcmake cmake -G Ninja -DIMGUI_EMSCRIPTEN_WEBGPU_FLAG="--use-port=path_to_emdawnwebgpu_pkg" -B where_to_build_dir`
- it set `IMGUI_IMPL_WEBGPU_BACKEND_DAWN` compiler define
- *To use external WebGPU library it's necessary to have EMS >= 4.0.10 or the minimum requirements specified by the package:*
- https://github.com/google/dawn/releases
#### Build time
Once the procedure for the specific builder is generated, the build command is **always the same**:
- Build using CMake
- `cmake --build where_to_build_dir`
- It will use selected builder to build the example.
- Build explicitly:
- `cd where_to_build_dir`
- `ninja`
- This is the builder chosen during the generation phase
---
### CMake useful options
#### Generator types (alternative to **ninja** bulder):
- `-G Ninja` to build with __ninja__ builder
- `-G "Unix Makefiles"` to build with __make__ builder
- `-G "Visual Studio 17 2022" -A x64` to create a VS 2022 solution (.sln) file, Windows only
- **Native build only**
- Not **officially** supported to build Google Dawn
Example:
- using **make** instead **ninja**:
- `cmake -G "Unix Makefiles" -DIMGUI_DAWN_DIR=path_to_sdk_dir -B where_to_build_dir`
**Syntax is case sensitive and the "" are necessary in case of spaces between words*
#### Directories
- The directory path can be absolute or relative (starting from the current directory)
- It's necessary to use different `where_to_build_dir` for different CMake generations
#### Build type
The default build type is **Debug**
It is possible to use a different build type using:
- `-DCMAKE_BUILD_TYPE=Release`
- `-DCMAKE_BUILD_TYPE=MinSizeRel`
- `-DCMAKE_BUILD_TYPE=RelWithDebInfo`
Example:
- building **Release**:
- `cmake -G ninja -DIMGUI_WGPU_DIR=path_to_sdk_dir -DCMAKE_BUILD_TYPE=Release -B where_to_build_dir `
#### GLFW / SDL2 / SDL3 includes, libraries, search paths and package manager
Includes and libraries, by default, are searched in system/compiler paths (environment variables): you can add the path to your development tools to the environment variables without having to modify the `CMakeLists.txt` file.
- e.g. CLang search in path specified from the following environment variables:
- include files: CPATH, C_INCLUDE_PATH, CPLUS_INCLUDE_PATH
- library files: LIBRARY_PATH
If you are using a package manager (**vcpkg** / **conan** / ... ) you need/can to specify it, adding to cmake command:
- `-DCMAKE_TOOLCHAIN_FILE=path/to/package_manager.cmake`
Examples:
- using **vcpkg** package manager it's necessary adding:
- `-DCMAKE_TOOLCHAIN_FILE=<vcpkg_root_dir>/scripts/buildsystems/vcpkg.cmake`
- full cmake command using **vcpkg** package manager:
- `cmake -G Ninja -DIMGUI_DAWN_DIR=path_to_sdk_dir -DCMAKE_TOOLCHAIN_FILE=<vcpkg_root_dir>/scripts/buildsystems/vcpkg.cmake -B where_to_build_dir`
---
### 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.
@@ -10,6 +151,8 @@
- Requires recent Emscripten as WGPU is still a work-in-progress API. - Requires recent Emscripten as WGPU is still a work-in-progress API.
---
## How to Run ## How to Run
To run on a local machine: To run on a local machine:

View File

@@ -12,26 +12,31 @@
#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> #include <GLFW/glfw3.h>
// This example can also compile and run with Emscripten! See 'Makefile.emscripten' for details. // This example can also compile and run with Emscripten! See 'Makefile.emscripten' for details.
#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>
#include "../libs/emscripten/emscripten_mainloop_stub.h"
#else
#include <webgpu/webgpu_glfw.h>
#endif #endif
#include <webgpu/webgpu.h> #include <webgpu/webgpu.h>
#include <webgpu/webgpu_cpp.h> #include <webgpu/webgpu_cpp.h>
#include "../libs/emscripten/emscripten_mainloop_stub.h"
#else
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
#include <webgpu/webgpu_glfw.h>
#endif
#endif
// Data // Data
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_surface_width = 1280; static int wgpu_surface_width = 1280;
static int wgpu_surface_height = 800; static int wgpu_surface_height = 800;
@@ -43,33 +48,11 @@ 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*)
{
const char* error_type_lbl = "";
switch (error_type)
{
case WGPUErrorType_Validation: error_type_lbl = "Validation"; break;
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 ResizeSurface(int width, int height) static void ResizeSurface(int width, int height)
{ {
if (wgpu_swap_chain) wgpu_surface_configuration.width = wgpu_surface_width = width;
wgpuSwapChainRelease(wgpu_swap_chain); wgpu_surface_configuration.height = wgpu_surface_height = height;
wgpu_surface_width = width; wgpuSurfaceConfigure(wgpu_surface, &wgpu_surface_configuration);
wgpu_surface_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);
} }
// Main code // Main code
@@ -98,7 +81,7 @@ int main(int, char**)
glfwTerminate(); glfwTerminate();
return 1; return 1;
} }
ResizeSurface(wgpu_surface_width, wgpu_surface_height);
glfwShowWindow(window); glfwShowWindow(window);
// Setup Dear ImGui context // Setup Dear ImGui context
@@ -125,7 +108,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);
@@ -181,6 +164,23 @@ int main(int, char**)
if (width != wgpu_surface_width || height != wgpu_surface_height) if (width != wgpu_surface_width || height != wgpu_surface_height)
ResizeSurface(width, height); ResizeSurface(width, height);
// Check surface status for error. If texture is not optimal, try to reconfigure the surface.
WGPUSurfaceTexture surface_texture;
wgpuSurfaceGetCurrentTexture(wgpu_surface, &surface_texture);
if (ImGui_ImplWGPU_IsSurfaceStatusError(surface_texture.status))
{
fprintf(stderr, "Unrecoverable Surface Texture status=%#.8x\n", surface_texture.status);
abort();
}
if (ImGui_ImplWGPU_IsSurfaceStatusSubOptimal(surface_texture.status))
{
if (surface_texture.texture)
wgpuTextureRelease(surface_texture.texture);
if (width > 0 && height > 0)
ResizeSurface(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();
@@ -226,17 +226,21 @@ int main(int, char**)
// Rendering // Rendering
ImGui::Render(); ImGui::Render();
#ifndef __EMSCRIPTEN__ WGPUTextureViewDescriptor view_desc = {};
// Tick needs to be called in Dawn to display validation errors view_desc.format = wgpu_surface_configuration.format;
wgpuDeviceTick(wgpu_device); view_desc.dimension = WGPUTextureViewDimension_2D ;
#endif view_desc.mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED;
view_desc.arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED;
view_desc.aspect = WGPUTextureAspect_All;
WGPUTextureView texture_view = wgpuTextureCreateView(surface_texture.texture, &view_desc);
WGPURenderPassColorAttachment color_attachments = {}; 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 = texture_view;
WGPURenderPassDescriptor render_pass_desc = {}; WGPURenderPassDescriptor render_pass_desc = {};
render_pass_desc.colorAttachmentCount = 1; render_pass_desc.colorAttachmentCount = 1;
@@ -252,14 +256,16 @@ int main(int, char**)
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 wgpu_queue = wgpuDeviceGetQueue(wgpu_device);
wgpuQueueSubmit(wgpu_queue, 1, &cmd_buffer); wgpuQueueSubmit(wgpu_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(texture_view);
wgpuRenderPassEncoderRelease(pass); wgpuRenderPassEncoderRelease(pass);
wgpuCommandEncoderRelease(encoder); wgpuCommandEncoderRelease(encoder);
wgpuCommandBufferRelease(cmd_buffer); wgpuCommandBufferRelease(cmd_buffer);
@@ -273,75 +279,298 @@ 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 0;
} }
#ifndef __EMSCRIPTEN__ // GLFW helper to create a WebGPU surface, used only in WGPU-Native. DAWN-Native already has a built-in function
static WGPUAdapter RequestAdapter(WGPUInstance instance) // As of today (2025/10) 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.
auto onAdapterRequestEnded = [](WGPURequestAdapterStatus status, WGPUAdapter adapter, const char* message, void* pUserData) // Currently supported platforms: Windows / Linux (X11 and Wayland) / MacOS. Not necessary nor available with EMSCRIPTEN.
{ #if !defined(__EMSCRIPTEN__) && (defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN))
if (status == WGPURequestAdapterStatus_Success)
*(WGPUAdapter*)pUserData = adapter;
else
printf("Could not get WebGPU adapter: %s\n", message);
};
WGPUAdapter adapter;
wgpuInstanceRequestAdapter(instance, nullptr, onAdapterRequestEnded, (void*)&adapter);
return adapter;
}
static WGPUDevice RequestDevice(WGPUAdapter adapter) #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
#define GLFW_HAS_X11_OR_WAYLAND 1
#else
#define GLFW_HAS_X11_OR_WAYLAND 0
#endif
#ifdef _WIN32
#undef APIENTRY
#ifndef GLFW_EXPOSE_NATIVE_WIN32 // for glfwGetWin32Window()
#define GLFW_EXPOSE_NATIVE_WIN32
#endif
#elif defined(__APPLE__)
#ifndef GLFW_EXPOSE_NATIVE_COCOA // for glfwGetCocoaWindow()
#define GLFW_EXPOSE_NATIVE_COCOA
#endif
#elif GLFW_HAS_X11_OR_WAYLAND
#ifndef GLFW_EXPOSE_NATIVE_X11 // for glfwGetX11Display(), glfwGetX11Window() on Freedesktop (Linux, BSD, etc.)
#define GLFW_EXPOSE_NATIVE_X11
#endif
#ifndef GLFW_EXPOSE_NATIVE_WAYLAND
#if defined(__has_include) && __has_include(<wayland-client.h>)
#define GLFW_EXPOSE_NATIVE_WAYLAND
#endif
#endif
#endif
#include <GLFW/glfw3native.h>
#undef Status // X11 headers are leaking this.
WGPUSurface CreateWGPUSurface(const WGPUInstance& instance, GLFWwindow* window)
{ {
auto onDeviceRequestEnded = [](WGPURequestDeviceStatus status, WGPUDevice device, const char* message, void* pUserData) ImGui_ImplWGPU_CreateSurfaceInfo create_info = {};
create_info.Instance = instance;
#if defined(GLFW_EXPOSE_NATIVE_COCOA)
{ {
if (status == WGPURequestDeviceStatus_Success) create_info.System = "cocoa";
*(WGPUDevice*)pUserData = device; create_info.RawWindow = (void*)glfwGetCocoaWindow(window);
else return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info);
printf("Could not get WebGPU device: %s\n", message); }
}; #elif defined(GLFW_EXPOSE_NATIVE_WAYLAND)
WGPUDevice device; if (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND)
wgpuAdapterRequestDevice(adapter, nullptr, onDeviceRequestEnded, (void*)&device); {
return device; create_info.System = "wayland";
create_info.RawDisplay = (void*)glfwGetWaylandDisplay();
create_info.RawSurface = (void*)glfwGetWaylandWindow(window);
return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info);
}
#elif defined(GLFW_EXPOSE_NATIVE_X11)
if (glfwGetPlatform() == GLFW_PLATFORM_X11)
{
create_info.System = "x11";
create_info.RawWindow = (void*)glfwGetX11Window(window);
create_info.RawDisplay = (void*)glfwGetX11Display();
return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info);
}
#elif defined(GLFW_EXPOSE_NATIVE_WIN32)
{
create_info.System = "win32";
create_info.RawWindow = (void*)glfwGetWin32Window(window);
create_info.RawInstance = (void*)::GetModuleHandle(NULL);
return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info);
}
#else
#error "Unsupported WebGPU native platform!"
#endif
return nullptr;
} }
#endif #endif
#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
static WGPUAdapter RequestAdapter(wgpu::Instance& instance)
{
wgpu::Adapter acquired_adapter;
wgpu::RequestAdapterOptions adapter_options;
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;
}
acquired_adapter = std::move(adapter);
};
// Synchronously (wait until) acquire Adapter
wgpu::Future waitAdapterFunc { instance.RequestAdapter(&adapter_options, wgpu::CallbackMode::WaitAnyOnly, onRequestAdapter) };
wgpu::WaitStatus waitStatusAdapter = instance.WaitAny(waitAdapterFunc, UINT64_MAX);
IM_ASSERT(acquired_adapter != nullptr && waitStatusAdapter == wgpu::WaitStatus::Success && "Error on Adapter request");
return acquired_adapter.MoveToCHandle();
}
static WGPUDevice RequestDevice(wgpu::Instance& instance, wgpu::Adapter& adapter)
{
// Set device callback functions
wgpu::DeviceDescriptor device_desc;
device_desc.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous,
[](const wgpu::Device&, wgpu::DeviceLostReason type, wgpu::StringView msg) { fprintf(stderr, "%s error: %s\n", ImGui_ImplWGPU_GetDeviceLostReasonName((WGPUDeviceLostReason)type), msg.data); }
);
device_desc.SetUncapturedErrorCallback(
[](const wgpu::Device&, wgpu::ErrorType type, wgpu::StringView msg) { fprintf(stderr, "%s error: %s\n", ImGui_ImplWGPU_GetErrorTypeName((WGPUErrorType)type), msg.data); }
);
wgpu::Device acquired_device;
auto onRequestDevice = [&](wgpu::RequestDeviceStatus status, wgpu::Device local_device, wgpu::StringView message)
{
if (status != wgpu::RequestDeviceStatus::Success)
{
printf("Failed to get an device: %s\n", message.data);
return;
}
acquired_device = std::move(local_device);
};
// Synchronously (wait until) get Device
wgpu::Future waitDeviceFunc { adapter.RequestDevice(&device_desc, wgpu::CallbackMode::WaitAnyOnly, onRequestDevice) };
wgpu::WaitStatus waitStatusDevice = instance.WaitAny(waitDeviceFunc, UINT64_MAX);
IM_ASSERT(acquired_device != nullptr && waitStatusDevice == wgpu::WaitStatus::Success && "Error on Device request");
return acquired_device.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 RequestAdapter(WGPUInstance& instance)
{
WGPURequestAdapterOptions adapter_options = {};
WGPUAdapter local_adapter;
WGPURequestAdapterCallbackInfo adapterCallbackInfo = {};
adapterCallbackInfo.callback = handle_request_adapter;
adapterCallbackInfo.userdata1 = &local_adapter;
wgpuInstanceRequestAdapter(instance, &adapter_options, adapterCallbackInfo);
IM_ASSERT(local_adapter && "Error on Adapter request");
return local_adapter;
}
static WGPUDevice RequestDevice(WGPUAdapter& adapter)
{
WGPUDevice local_device;
WGPURequestDeviceCallbackInfo deviceCallbackInfo = {};
deviceCallbackInfo.callback = handle_request_device;
deviceCallbackInfo.userdata1 = &local_device;
wgpuAdapterRequestDevice(adapter, nullptr, deviceCallbackInfo);
IM_ASSERT(local_device && "Error on Device request");
return local_device;
}
#endif // __EMSCRIPTEN__
#endif // IMGUI_IMPL_WEBGPU_BACKEND_WGPU
static bool InitWGPU(GLFWwindow* window) static bool InitWGPU(GLFWwindow* window)
{ {
wgpu::Instance instance = wgpuCreateInstance(nullptr); 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 instance_desc = {};
static constexpr wgpu::InstanceFeatureName timedWaitAny = wgpu::InstanceFeatureName::TimedWaitAny;
instance_desc.requiredFeatureCount = 1;
instance_desc.requiredFeatures = &timedWaitAny;
wgpu::Instance instance = wgpu::CreateInstance(&instance_desc);
wgpu::Adapter adapter = RequestAdapter(instance);
ImGui_ImplWGPU_DebugPrintAdapterInfo(adapter.Get());
wgpu_device = RequestDevice(instance, adapter);
// Create the surface.
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
wgpu_device = emscripten_webgpu_get_device(); wgpu::EmscriptenSurfaceSourceCanvasHTMLSelector canvas_desc = {};
if (!wgpu_device)
return false;
wgpu::SurfaceDescriptorFromCanvasHTMLSelector canvas_desc = {};
canvas_desc.selector = "#canvas"; canvas_desc.selector = "#canvas";
wgpu::SurfaceDescriptor surface_desc = {}; wgpu::SurfaceDescriptor surface_desc = {};
surface_desc.nextInChain = &canvas_desc; surface_desc.nextInChain = &canvas_desc;
wgpu::Surface surface = instance.CreateSurface(&surface_desc); wgpu_surface = instance.CreateSurface(&surface_desc).MoveToCHandle();
wgpu::Adapter adapter = {};
wgpu_preferred_fmt = (WGPUTextureFormat)surface.GetPreferredFormat(adapter);
#else #else
WGPUAdapter adapter = RequestAdapter(instance.Get()); wgpu_surface = CreateWGPUSurface(instance.Get(), window);
if (!adapter)
return false;
wgpu_device = RequestDevice(adapter);
wgpu::Surface surface = wgpu::glfw::CreateSurfaceForWindow(instance, window);
if (!surface)
return false;
wgpu_preferred_fmt = WGPUTextureFormat_BGRA8Unorm;
#endif #endif
if (!wgpu_surface)
return false;
// Moving Dawn objects into WGPU handles // Moving Dawn objects into WGPU handles
wgpu_instance = instance.MoveToCHandle(); wgpu_instance = instance.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();
IM_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(
[](WGPULogLevel level, WGPUStringView msg, void* userdata) { fprintf(stderr, "%s: %.*s\n", ImGui_ImplWGPU_GetLogLevelName(level), (int)msg.length, msg.data); }, nullptr
);
wgpuSetLogLevel(WGPULogLevel_Warn);
WGPUAdapter adapter = RequestAdapter(wgpu_instance);
ImGui_ImplWGPU_DebugPrintAdapterInfo(adapter);
wgpu_device = RequestDevice(adapter);
// Create the surface.
wgpu_surface = CreateWGPUSurface(wgpu_instance, 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;
} }

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>