WebGPU + SDL3. Bug or not? #9530
Closed
ivan-enzhaev
started this conversation in
New Users Build/Link/Run issues ONLY!
Replies: 4 comments 1 reply
|
Please don’t post in Discussions, it is very clearly stated everywhere. if you want to provide changes to such an evolving backend please specify:
|
0 replies
|
Let's target Desktop only, keeping Tools:
set(CMAKE_BUILD_TYPE "Debug")
cmake_minimum_required(VERSION 3.21)
project(imgui-webgpu-sdl3-cpp-mingw)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# -----------------------------------------------------------------------------
# ImGui Path & Source Definitions
# -----------------------------------------------------------------------------
set(IMGUI_DIR "C:/libs/imgui-1.92.9b")
set(IMGUI_SOURCES
${IMGUI_DIR}/imgui.cpp
${IMGUI_DIR}/imgui_draw.cpp
${IMGUI_DIR}/imgui_tables.cpp
${IMGUI_DIR}/imgui_widgets.cpp
${IMGUI_DIR}/backends/imgui_impl_sdl3.cpp
${IMGUI_DIR}/backends/imgui_impl_wgpu.cpp
)
# -----------------------------------------------------------------------------
# Define Project Source Files
# -----------------------------------------------------------------------------
set(PROJECT_SOURCES
src/main.cpp
${IMGUI_SOURCES}
)
# -----------------------------------------------------------------------------
# Target & Includes
# -----------------------------------------------------------------------------
add_executable(app)
target_sources(app PRIVATE ${PROJECT_SOURCES})
target_include_directories(app PRIVATE
${IMGUI_DIR}
${IMGUI_DIR}/backends
)
# -----------------------------------------------------------------------------
# Windows / MinGW Desktop Dependencies & Linking
# -----------------------------------------------------------------------------
add_compile_definitions(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
add_library(SDL3_Static STATIC IMPORTED)
set_target_properties(SDL3_Static PROPERTIES
IMPORTED_LOCATION "C:/libs/sdl3-3.4.14-mingw/lib/libSDL3.a"
INTERFACE_INCLUDE_DIRECTORIES "C:/libs/sdl3-3.4.14-mingw/include"
INTERFACE_LINK_LIBRARIES "winmm;imm32;version;setupapi;dinput8"
)
add_library(wgpu_native STATIC IMPORTED)
set_target_properties(wgpu_native PROPERTIES
IMPORTED_LOCATION "C:/libs/wgpu-29.0.1.1-mingw/lib/libwgpu_native.a"
INTERFACE_INCLUDE_DIRECTORIES "C:/libs/wgpu-29.0.1.1-mingw/include"
INTERFACE_LINK_LIBRARIES "ws2_32;userenv;ntdll;bcrypt;d3d12;dxgi"
)
target_link_libraries(app PRIVATE
SDL3_Static
wgpu_native
)
target_compile_options(app PRIVATE -Os)
target_link_options(app PRIVATE -static -s)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
target_link_options(app PRIVATE -mconsole)
else()
target_link_options(app PRIVATE -mwindows)
endif()src/main.cpp #define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <webgpu/webgpu.h>
#include "imgui.h"
#include "imgui_impl_sdl3.h"
#include "imgui_impl_wgpu.h"
#if defined(SDL_PLATFORM_WIN32)
#include <windows.h>
#endif
static SDL_Window *window = NULL;
static WGPUInstance instance = NULL;
static WGPUAdapter adapter = NULL;
static WGPUDevice device = NULL;
static WGPUQueue queue = NULL;
static WGPUSurface surface = NULL;
static WGPUSurfaceConfiguration config = { 0 };
static WGPURenderPipeline pipeline = NULL;
static WGPUBuffer uniform_buffer = NULL;
static WGPUBindGroup bind_group = NULL;
static bool is_configured = false;
static bool need_reconfigure = false;
static bool imgui_initialized = false;
static float triangle_color[3] = { 1.0f, 0.5f, 0.0f };
static float scale_val = 1.0f;
struct Uniforms {
float scale;
float aspect_ratio;
float padding[2];
float color[4];
};
static const int WIN_WIDTH = 1280;
static const int WIN_HEIGHT = 720;
const char *shader_code =
"struct Uniforms {\n"
" scale: f32,\n"
" aspect_ratio: f32,\n"
" padding: vec2f,\n"
" color: vec4f,\n"
"};\n"
"@group(0) @binding(0) var<uniform> u: Uniforms;\n\n"
"@vertex\n"
"fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4f {\n"
" var pos = array<vec2f, 3>(\n"
" vec2f(0.0, 0.5),\n"
" vec2f(-0.5, -0.5),\n"
" vec2f(0.5, -0.5)\n"
" );\n"
" var p = pos[in_vertex_index] * u.scale;\n"
" return vec4f(p.x, p.y * u.aspect_ratio, 0.0, 1.0);\n"
"}\n\n"
"@fragment\n"
"fn fs_main() -> @location(0) vec4f {\n"
" return u.color;\n"
"}\n";
#ifdef __cplusplus
static inline WGPUStringView WGPU_STR(const char *s) {
WGPUStringView view;
view.data = s;
view.length = (s != NULL) ? SDL_strlen(s) : WGPU_STRLEN;
return view;
}
#else
#define WGPU_STR(s) (WGPUStringView){ .data = s, .length = (s != NULL) ? SDL_strlen(s) : WGPU_STRLEN }
#endif
static WGPUSurface CreateWGPUSurface(WGPUInstance inst, SDL_Window *win) {
#if defined(SDL_PLATFORM_WIN32)
SDL_PropertiesID props = SDL_GetWindowProperties(win);
HWND hwnd = (HWND)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
HINSTANCE hinstance = (HINSTANCE)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_INSTANCE_POINTER, NULL);
WGPUSurfaceSourceWindowsHWND hwndSource = {
.chain = { .sType = WGPUSType_SurfaceSourceWindowsHWND },
.hinstance = hinstance,
.hwnd = hwnd
};
WGPUSurfaceDescriptor desc = { .nextInChain = (WGPUChainedStruct *)&hwndSource };
return wgpuInstanceCreateSurface(inst, &desc);
#else
return NULL;
#endif
}
void handle_device_request(WGPURequestDeviceStatus status, WGPUDevice res, WGPUStringView message, void *userdata1, void *userdata2) {
device = res;
}
void handle_adapter_request(WGPURequestAdapterStatus status, WGPUAdapter res, WGPUStringView message, void *userdata1, void *userdata2) {
adapter = res;
WGPURequestDeviceCallbackInfo deviceCallbackInfo = {
.mode = WGPUCallbackMode_AllowSpontaneous,
.callback = handle_device_request
};
wgpuAdapterRequestDevice(adapter, NULL, deviceCallbackInfo);
}
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) {
SDL_SetHint(SDL_HINT_MAIN_CALLBACK_RATE, "60");
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "1");
SDL_Init(SDL_INIT_VIDEO);
window = SDL_CreateWindow("WebGPU Triangle + ImGui", WIN_WIDTH, WIN_HEIGHT, SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY);
instance = wgpuCreateInstance(NULL);
surface = CreateWGPUSurface(instance, window);
WGPURequestAdapterOptions opt = { .compatibleSurface = surface };
WGPURequestAdapterCallbackInfo adapterCallbackInfo = {
.mode = WGPUCallbackMode_AllowSpontaneous,
.callback = (WGPURequestAdapterCallback)handle_adapter_request
};
wgpuInstanceRequestAdapter(instance, &opt, adapterCallbackInfo);
while (adapter == NULL || device == NULL) {
SDL_Delay(1);
}
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
ImGui::StyleColorsDark();
float scale = SDL_GetWindowDisplayScale(window);
if (scale <= 0.0f) scale = 1.0f;
ImGui::GetStyle().ScaleAllSizes(scale);
ImFontConfig font_cfg;
font_cfg.SizePixels = 15.0f * scale;
io.Fonts->AddFontDefault(&font_cfg);
ImGui_ImplSDL3_InitForOther(window);
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
ImGui_ImplSDL3_ProcessEvent(event);
if (event->type == SDL_EVENT_QUIT) {
return SDL_APP_SUCCESS;
}
if (event->type == SDL_EVENT_WINDOW_RESIZED || event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) {
need_reconfigure = true;
}
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppIterate(void *appstate) {
if (!is_configured || need_reconfigure) {
int w = 0, h = 0;
SDL_GetWindowSizeInPixels(window, &w, &h);
config.width = (uint32_t)w;
config.height = (uint32_t)h;
if (!is_configured) {
queue = wgpuDeviceGetQueue(device);
WGPUSurfaceCapabilities caps = { 0 };
wgpuSurfaceGetCapabilities(surface, adapter, &caps);
WGPUTextureFormat surface_format = WGPUTextureFormat_Undefined;
for (size_t i = 0; i < caps.formatCount; ++i) {
if (caps.formats[i] == WGPUTextureFormat_BGRA8Unorm) {
surface_format = caps.formats[i];
break;
}
if (caps.formats[i] == WGPUTextureFormat_RGBA8Unorm) {
surface_format = caps.formats[i];
}
}
if (surface_format == WGPUTextureFormat_Undefined) {
surface_format = caps.formats[0];
}
WGPUPresentMode present_mode = (caps.presentModeCount > 0) ? caps.presentModes[0] : WGPUPresentMode_Fifo;
config.device = device;
config.format = surface_format;
config.usage = WGPUTextureUsage_RenderAttachment;
config.presentMode = present_mode;
wgpuSurfaceCapabilitiesFreeMembers(caps);
WGPUBufferDescriptor buffer_desc = {};
buffer_desc.size = sizeof(Uniforms);
buffer_desc.usage = WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst;
uniform_buffer = wgpuDeviceCreateBuffer(device, &buffer_desc);
WGPUBindGroupLayoutEntry bgl_entry = {};
bgl_entry.binding = 0;
bgl_entry.visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
bgl_entry.buffer.type = WGPUBufferBindingType_Uniform;
WGPUBindGroupLayoutDescriptor bgl_desc = {};
bgl_desc.entryCount = 1;
bgl_desc.entries = &bgl_entry;
WGPUBindGroupLayout bind_group_layout = wgpuDeviceCreateBindGroupLayout(device, &bgl_desc);
WGPUBindGroupEntry bg_entry = {};
bg_entry.binding = 0;
bg_entry.buffer = uniform_buffer;
bg_entry.size = sizeof(Uniforms);
WGPUBindGroupDescriptor bg_desc = {};
bg_desc.layout = bind_group_layout;
bg_desc.entryCount = 1;
bg_desc.entries = &bg_entry;
bind_group = wgpuDeviceCreateBindGroup(device, &bg_desc);
WGPUPipelineLayoutDescriptor pipeline_layout_desc = {};
pipeline_layout_desc.bindGroupLayoutCount = 1;
pipeline_layout_desc.bindGroupLayouts = &bind_group_layout;
WGPUPipelineLayout pipeline_layout = wgpuDeviceCreatePipelineLayout(device, &pipeline_layout_desc);
WGPUShaderSourceWGSL wgsl_desc = { 0 };
wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL;
wgsl_desc.code = WGPU_STR(shader_code);
WGPUShaderModuleDescriptor shader_desc = { 0 };
shader_desc.nextInChain = &wgsl_desc.chain;
WGPUShaderModule shader_module = wgpuDeviceCreateShaderModule(device, &shader_desc);
WGPUColorTargetState color_target = {
.format = config.format,
.writeMask = WGPUColorWriteMask_All
};
WGPUFragmentState fragment_state = {
.module = shader_module,
.entryPoint = WGPU_STR("fs_main"),
.targetCount = 1,
.targets = &color_target
};
WGPURenderPipelineDescriptor pipeline_desc = { 0 };
pipeline_desc.layout = pipeline_layout;
pipeline_desc.vertex.module = shader_module;
pipeline_desc.vertex.entryPoint = WGPU_STR("vs_main");
pipeline_desc.fragment = &fragment_state;
pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
pipeline_desc.multisample.count = 1;
pipeline_desc.multisample.mask = 0xFFFFFFFF;
pipeline = wgpuDeviceCreateRenderPipeline(device, &pipeline_desc);
wgpuBindGroupLayoutRelease(bind_group_layout);
wgpuPipelineLayoutRelease(pipeline_layout);
wgpuShaderModuleRelease(shader_module);
ImGui_ImplWGPU_InitInfo init_info = {};
init_info.Device = device;
init_info.NumFramesInFlight = 3;
init_info.RenderTargetFormat = config.format;
init_info.DepthStencilFormat = WGPUTextureFormat_Undefined;
ImGui_ImplWGPU_Init(&init_info);
imgui_initialized = true;
is_configured = true;
}
wgpuSurfaceConfigure(surface, &config);
need_reconfigure = false;
}
WGPUSurfaceTexture surfaceTexture = { 0 };
wgpuSurfaceGetCurrentTexture(surface, &surfaceTexture);
if (!surfaceTexture.texture) {
return SDL_APP_CONTINUE;
}
ImGui_ImplWGPU_NewFrame();
ImGui_ImplSDL3_NewFrame();
ImGui::NewFrame();
ImGuiIO &io = ImGui::GetIO();
float scale = SDL_GetWindowDisplayScale(window);
if (scale <= 0.0f) scale = 1.0f;
float max_card_width = 360.0f * scale;
float desired_width = (io.DisplaySize.x * 0.90f < max_card_width) ? io.DisplaySize.x * 0.90f : max_card_width;
float pos_x = (io.DisplaySize.x - desired_width) * 0.5f;
float pos_y = 20.0f * scale;
ImGui::SetNextWindowPos(ImVec2(pos_x, pos_y), ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(desired_width, 0.0f), ImGuiCond_Always);
ImGui::Begin("WebGPU Controls", nullptr, ImGuiWindowFlags_NoSavedSettings);
ImGui::PushItemWidth(-FLT_MIN);
ImGui::Text("Scale");
ImGui::SliderFloat("##Scale", &scale_val, 0.1f, 2.0f);
ImGui::Spacing();
ImGui::Text("Triangle Color");
ImGui::ColorEdit3("##Triangle Color", triangle_color);
ImGui::PopItemWidth();
ImGui::Spacing();
if (ImGui::Button("Reset Color", ImVec2(-FLT_MIN, 0.0f))) {
triangle_color[0] = 1.0f;
triangle_color[1] = 0.5f;
triangle_color[2] = 0.0f;
}
ImGui::End();
int w_pixels = 0, h_pixels = 0;
SDL_GetWindowSizeInPixels(window, &w_pixels, &h_pixels);
float aspect = (float)w_pixels / (float)h_pixels;
Uniforms uniforms = {};
uniforms.scale = scale_val;
uniforms.aspect_ratio = aspect;
uniforms.color[0] = triangle_color[0];
uniforms.color[1] = triangle_color[1];
uniforms.color[2] = triangle_color[2];
uniforms.color[3] = 1.0f;
wgpuQueueWriteBuffer(queue, uniform_buffer, 0, &uniforms, sizeof(Uniforms));
WGPUTextureView view = wgpuTextureCreateView(surfaceTexture.texture, NULL);
WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, NULL);
WGPURenderPassColorAttachment colorAttachment = {
.nextInChain = NULL,
.view = view,
.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED,
.resolveTarget = NULL,
.loadOp = WGPULoadOp_Clear,
.storeOp = WGPUStoreOp_Store,
.clearValue = { 0.15, 0.15, 0.18, 1.0 }
};
WGPURenderPassDescriptor renderPassDesc = {
.colorAttachmentCount = 1,
.colorAttachments = &colorAttachment
};
WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &renderPassDesc);
wgpuRenderPassEncoderSetPipeline(pass, pipeline);
wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group, 0, NULL);
wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0);
ImGui::Render();
ImGui_ImplWGPU_RenderDrawData(ImGui::GetDrawData(), pass);
wgpuRenderPassEncoderEnd(pass);
WGPUCommandBuffer commandBuffer = wgpuCommandEncoderFinish(encoder, NULL);
wgpuQueueSubmit(queue, 1, &commandBuffer);
wgpuSurfacePresent(surface);
wgpuCommandBufferRelease(commandBuffer);
wgpuRenderPassEncoderRelease(pass);
wgpuCommandEncoderRelease(encoder);
wgpuTextureViewRelease(view);
wgpuTextureRelease(surfaceTexture.texture);
return SDL_APP_CONTINUE;
}
void SDL_AppQuit(void *appstate, SDL_AppResult result) {
if (imgui_initialized) {
ImGui_ImplWGPU_Shutdown();
}
ImGui_ImplSDL3_Shutdown();
ImGui::DestroyContext();
if (bind_group) wgpuBindGroupRelease(bind_group);
if (uniform_buffer) wgpuBufferRelease(uniform_buffer);
if (pipeline) wgpuRenderPipelineRelease(pipeline);
if (surface) wgpuSurfaceUnconfigure(surface);
if (queue) wgpuQueueRelease(queue);
if (device) wgpuDeviceRelease(device);
if (adapter) wgpuAdapterRelease(adapter);
if (surface) wgpuSurfaceRelease(surface);
if (instance) wgpuInstanceRelease(instance);
if (window) SDL_DestroyWindow(window);
SDL_Quit();
}Errors: Project: imgui-webgpu-sdl3-cpp-mingw.zip |
0 replies
|
Implemented your fix as 6d1f88f. Thank you very much! |
1 reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Dear ImGuisupports WebGPU! It is very cool! I've built my app below for Wasm, EXE, and APK - it would not be possible with Vulkan to build for all three platforms from a single codebase.View the Wasm-demo in the browser
Use this QR code to run the Wasm-demo above in your mobile browser:
Source code: imgui-webgpu-sdl3-cpp
But it does not work without of these changes in the
imgui_impl_wgpu.cppfile:Replace this code:
With this one:
Replace this code:
WGPUVertexAttribute attribute_desc[] = { #if defined IMGUI_IMPL_WEBGPU_BACKEND_DAWN || defined IMGUI_IMPL_WEBGPU_BACKEND_WGVK { nullptr, WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, pos), 0 }, { nullptr, WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, uv), 1 }, { nullptr, WGPUVertexFormat_Unorm8x4, (uint64_t)offsetof(ImDrawVert, col), 2 }, #else { WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, pos), 0 }, { WGPUVertexFormat_Float32x2, (uint64_t)offsetof(ImDrawVert, uv), 1 }, { WGPUVertexFormat_Unorm8x4, (uint64_t)offsetof(ImDrawVert, col), 2 }, #endif };With this one:
All reactions