23.3 KB、560行 生 Blame 履歴
1// Dear ImGui: standalone example application for GLFW + WebGPU
2// - Emscripten is supported for publishing on web. See https://emscripten.org.
3// - Dawn is used as a WebGPU implementation on desktop.
4
5// Learn about Dear ImGui:
6// - FAQ https://dearimgui.com/faq
7// - Getting Started https://dearimgui.com/getting-started
8// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
9// - Introduction, links and more at the top of imgui.cpp
10
11#include "imgui.h"
12#include "imgui_impl_glfw.h"
13#include "imgui_impl_wgpu.h"
14#include <stdio.h>
15#include <stdlib.h>
16#include <GLFW/glfw3.h>
17
18// This example can also compile and run with Emscripten! See 'Makefile.emscripten' for details.
19#ifdef __EMSCRIPTEN__
20#include <emscripten.h>
21#include <emscripten/html5.h>
22#include "../libs/emscripten/emscripten_mainloop_stub.h"
23#endif
24
25#include <webgpu/webgpu.h>
26#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
27#include <webgpu/webgpu_cpp.h>
28#endif
29
30// Data
31static WGPUInstance wgpu_instance = nullptr;
32static WGPUDevice wgpu_device = nullptr;
33static WGPUSurface wgpu_surface = nullptr;
34static WGPUQueue wgpu_queue = nullptr;
35static WGPUSurfaceConfiguration wgpu_surface_configuration = {};
36static int wgpu_surface_width = 1280;
37static int wgpu_surface_height = 800;
38
39// Forward declarations
40static bool InitWGPU(GLFWwindow* window);
41WGPUSurface CreateWGPUSurface(const WGPUInstance& instance, GLFWwindow* window);
42
43static void glfw_error_callback(int error, const char* description)
44{
45 printf("GLFW Error %d: %s\n", error, description);
46}
47
48static void ResizeSurface(int width, int height)
49{
50 wgpu_surface_configuration.width = wgpu_surface_width = width;
51 wgpu_surface_configuration.height = wgpu_surface_height = height;
52 wgpuSurfaceConfigure(wgpu_surface, &wgpu_surface_configuration);
53}
54
55// Main code
56int main(int, char**)
57{
58 glfwSetErrorCallback(glfw_error_callback);
59 if (!glfwInit())
60 return 1;
61
62 // Make sure GLFW does not initialize any graphics context.
63 // This needs to be done explicitly later.
64 glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
65
66 // Create window
67 float main_scale = ImGui_ImplGlfw_GetContentScaleForMonitor(glfwGetPrimaryMonitor()); // Valid on GLFW 3.3+ only
68 wgpu_surface_width = (int)(wgpu_surface_width * main_scale);
69 wgpu_surface_height = (int)(wgpu_surface_height * main_scale);
70 GLFWwindow* window = glfwCreateWindow(wgpu_surface_width, wgpu_surface_height, "Dear ImGui GLFW+WebGPU example", nullptr, nullptr);
71 if (window == nullptr)
72 return 1;
73
74 // Initialize the WebGPU environment
75 if (!InitWGPU(window))
76 {
77 glfwDestroyWindow(window);
78 glfwTerminate();
79 return 1;
80 }
81
82 glfwShowWindow(window);
83
84 // Setup Dear ImGui context
85 IMGUI_CHECKVERSION();
86 ImGui::CreateContext();
87 ImGuiIO& io = ImGui::GetIO(); (void)io;
88 io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
89 io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
90 io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
91
92 // Setup Dear ImGui style
93 ImGui::StyleColorsDark();
94 //ImGui::StyleColorsLight();
95
96 // Setup scaling
97 ImGuiStyle& style = ImGui::GetStyle();
98 style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again)
99 style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor)
100
101 // Setup Platform/Renderer backends
102 ImGui_ImplGlfw_InitForOther(window, true);
103#ifdef __EMSCRIPTEN__
104 ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas");
105#endif
106 ImGui_ImplWGPU_InitInfo init_info;
107 init_info.Device = wgpu_device;
108 init_info.NumFramesInFlight = 3;
109 init_info.RenderTargetFormat = wgpu_surface_configuration.format;
110 init_info.DepthStencilFormat = WGPUTextureFormat_Undefined;
111 ImGui_ImplWGPU_Init(&init_info);
112
113 // Load Fonts
114 // - If fonts are not explicitly loaded, Dear ImGui will select an embedded font: either AddFontDefaultVector() or AddFontDefaultBitmap().
115 // This selection is based on (style.FontSizeBase * style.FontScaleMain * style.FontScaleDpi) reaching a small threshold.
116 // - You can load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
117 // - If a file cannot be loaded, AddFont functions will return a nullptr. Please handle those errors in your code (e.g. use an assertion, display an error and quit).
118 // - Read 'docs/FONTS.md' for more instructions and details.
119 // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use FreeType for higher quality font rendering.
120 // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
121 // - Our Emscripten build process allows embedding fonts to be accessible at runtime from the "fonts/" folder. See Makefile.emscripten for details.
122 //style.FontSizeBase = 20.0f;
123 //io.Fonts->AddFontDefaultVector();
124 //io.Fonts->AddFontDefaultBitmap();
125#ifndef IMGUI_DISABLE_FILE_FUNCTIONS
126 //io.Fonts->AddFontFromFileTTF("fonts/segoeui.ttf");
127 //io.Fonts->AddFontFromFileTTF("fonts/DroidSans.ttf");
128 //io.Fonts->AddFontFromFileTTF("fonts/Roboto-Medium.ttf");
129 //io.Fonts->AddFontFromFileTTF("fonts/Cousine-Regular.ttf");
130 //ImFont* font = io.Fonts->AddFontFromFileTTF("fonts/ArialUni.ttf");
131 //IM_ASSERT(font != nullptr);
132#endif
133
134 // Our state
135 bool show_demo_window = true;
136 bool show_another_window = false;
137 ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
138
139 // Main loop
140#ifdef __EMSCRIPTEN__
141 // For an Emscripten build we are disabling file-system access, so let's not attempt to do a fopen() of the imgui.ini file.
142 // You may manually call LoadIniSettingsFromMemory() to load settings from your own storage.
143 io.IniFilename = nullptr;
144 EMSCRIPTEN_MAINLOOP_BEGIN
145#else
146 while (!glfwWindowShouldClose(window))
147#endif
148 {
149 // Poll and handle events (inputs, window resize, etc.)
150 // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
151 // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
152 // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
153 // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
154 glfwPollEvents();
155 if (glfwGetWindowAttrib(window, GLFW_ICONIFIED) != 0)
156 {
157 ImGui_ImplGlfw_Sleep(10);
158 continue;
159 }
160
161 // React to changes in screen size
162 int width, height;
163 glfwGetFramebufferSize((GLFWwindow*)window, &width, &height);
164 if (width != wgpu_surface_width || height != wgpu_surface_height)
165 ResizeSurface(width, height);
166
167 // Check surface status for error. If texture is not optimal, try to reconfigure the surface.
168 WGPUSurfaceTexture surface_texture;
169 wgpuSurfaceGetCurrentTexture(wgpu_surface, &surface_texture);
170 if (ImGui_ImplWGPU_IsSurfaceStatusError(surface_texture.status))
171 {
172 fprintf(stderr, "Unrecoverable Surface Texture status=%#.8x\n", surface_texture.status);
173 abort();
174 }
175 if (ImGui_ImplWGPU_IsSurfaceStatusSubOptimal(surface_texture.status))
176 {
177 if (surface_texture.texture)
178 wgpuTextureRelease(surface_texture.texture);
179 if (width > 0 && height > 0)
180 ResizeSurface(width, height);
181 continue;
182 }
183
184 // Start the Dear ImGui frame
185 ImGui_ImplWGPU_NewFrame();
186 ImGui_ImplGlfw_NewFrame();
187 ImGui::NewFrame();
188
189 // 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!).
190 if (show_demo_window)
191 ImGui::ShowDemoWindow(&show_demo_window);
192
193 // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
194 {
195 static float f = 0.0f;
196 static int counter = 0;
197
198 ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
199
200 ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
201 ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state
202 ImGui::Checkbox("Another Window", &show_another_window);
203
204 ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
205 ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
206
207 if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
208 counter++;
209 ImGui::SameLine();
210 ImGui::Text("counter = %d", counter);
211
212 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
213 ImGui::End();
214 }
215
216 // 3. Show another simple window.
217 if (show_another_window)
218 {
219 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)
220 ImGui::Text("Hello from another window!");
221 if (ImGui::Button("Close Me"))
222 show_another_window = false;
223 ImGui::End();
224 }
225
226 // Rendering
227 ImGui::Render();
228
229 WGPUTextureViewDescriptor view_desc = {};
230 view_desc.format = wgpu_surface_configuration.format;
231 view_desc.dimension = WGPUTextureViewDimension_2D ;
232 view_desc.mipLevelCount = WGPU_MIP_LEVEL_COUNT_UNDEFINED;
233 view_desc.arrayLayerCount = WGPU_ARRAY_LAYER_COUNT_UNDEFINED;
234 view_desc.aspect = WGPUTextureAspect_All;
235
236 WGPUTextureView texture_view = wgpuTextureCreateView(surface_texture.texture, &view_desc);
237
238 WGPURenderPassColorAttachment color_attachments = {};
239 color_attachments.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
240 color_attachments.loadOp = WGPULoadOp_Clear;
241 color_attachments.storeOp = WGPUStoreOp_Store;
242 color_attachments.clearValue = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w };
243 color_attachments.view = texture_view;
244
245 WGPURenderPassDescriptor render_pass_desc = {};
246 render_pass_desc.colorAttachmentCount = 1;
247 render_pass_desc.colorAttachments = &color_attachments;
248 render_pass_desc.depthStencilAttachment = nullptr;
249
250 WGPUCommandEncoderDescriptor enc_desc = {};
251 WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(wgpu_device, &enc_desc);
252
253 WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &render_pass_desc);
254 ImGui_ImplWGPU_RenderDrawData(ImGui::GetDrawData(), pass);
255 wgpuRenderPassEncoderEnd(pass);
256
257 WGPUCommandBufferDescriptor cmd_buffer_desc = {};
258 WGPUCommandBuffer cmd_buffer = wgpuCommandEncoderFinish(encoder, &cmd_buffer_desc);
259 wgpuQueueSubmit(wgpu_queue, 1, &cmd_buffer);
260
261#ifndef __EMSCRIPTEN__
262 wgpuSurfacePresent(wgpu_surface);
263 // Tick needs to be called in Dawn to display validation errors
264#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
265 wgpuDeviceTick(wgpu_device);
266#endif
267#endif
268 wgpuTextureViewRelease(texture_view);
269 wgpuRenderPassEncoderRelease(pass);
270 wgpuCommandEncoderRelease(encoder);
271 wgpuCommandBufferRelease(cmd_buffer);
272 }
273#ifdef __EMSCRIPTEN__
274 EMSCRIPTEN_MAINLOOP_END;
275#endif
276
277 // Cleanup
278 ImGui_ImplWGPU_Shutdown();
279 ImGui_ImplGlfw_Shutdown();
280 ImGui::DestroyContext();
281
282 wgpuSurfaceUnconfigure(wgpu_surface);
283 wgpuSurfaceRelease(wgpu_surface);
284 wgpuQueueRelease(wgpu_queue);
285 wgpuDeviceRelease(wgpu_device);
286 wgpuInstanceRelease(wgpu_instance);
287
288 glfwDestroyWindow(window);
289 glfwTerminate();
290
291 return 0;
292}
293
294#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
295static WGPUAdapter RequestAdapter(wgpu::Instance& instance)
296{
297 wgpu::Adapter acquired_adapter;
298 wgpu::RequestAdapterOptions adapter_options;
299 auto onRequestAdapter = [&](wgpu::RequestAdapterStatus status, wgpu::Adapter adapter, wgpu::StringView message)
300 {
301 if (status != wgpu::RequestAdapterStatus::Success)
302 {
303 printf("Failed to get an adapter: %s\n", message.data);
304 return;
305 }
306 acquired_adapter = std::move(adapter);
307 };
308
309 // Synchronously (wait until) acquire Adapter
310 wgpu::Future waitAdapterFunc { instance.RequestAdapter(&adapter_options, wgpu::CallbackMode::WaitAnyOnly, onRequestAdapter) };
311 wgpu::WaitStatus waitStatusAdapter = instance.WaitAny(waitAdapterFunc, UINT64_MAX);
312 IM_ASSERT(acquired_adapter != nullptr && waitStatusAdapter == wgpu::WaitStatus::Success && "Error on Adapter request");
313 return acquired_adapter.MoveToCHandle();
314}
315
316static WGPUDevice RequestDevice(wgpu::Instance& instance, wgpu::Adapter& adapter)
317{
318 // Set device callback functions
319 wgpu::DeviceDescriptor device_desc;
320 device_desc.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous,
321 [](const wgpu::Device&, wgpu::DeviceLostReason type, wgpu::StringView msg) { fprintf(stderr, "%s error: %s\n", ImGui_ImplWGPU_GetDeviceLostReasonName((WGPUDeviceLostReason)type), msg.data); }
322 );
323 device_desc.SetUncapturedErrorCallback(
324 [](const wgpu::Device&, wgpu::ErrorType type, wgpu::StringView msg) { fprintf(stderr, "%s error: %s\n", ImGui_ImplWGPU_GetErrorTypeName((WGPUErrorType)type), msg.data); }
325 );
326
327 wgpu::Device acquired_device;
328 auto onRequestDevice = [&](wgpu::RequestDeviceStatus status, wgpu::Device local_device, wgpu::StringView message)
329 {
330 if (status != wgpu::RequestDeviceStatus::Success)
331 {
332 printf("Failed to get an device: %s\n", message.data);
333 return;
334 }
335 acquired_device = std::move(local_device);
336 };
337
338 // Synchronously (wait until) get Device
339 wgpu::Future waitDeviceFunc { adapter.RequestDevice(&device_desc, wgpu::CallbackMode::WaitAnyOnly, onRequestDevice) };
340 wgpu::WaitStatus waitStatusDevice = instance.WaitAny(waitDeviceFunc, UINT64_MAX);
341 IM_ASSERT(acquired_device != nullptr && waitStatusDevice == wgpu::WaitStatus::Success && "Error on Device request");
342 return acquired_device.MoveToCHandle();
343}
344#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK)
345static void handle_request_adapter(WGPURequestAdapterStatus status, WGPUAdapter adapter, WGPUStringView message, void* userdata1, void* userdata2)
346{
347 IM_UNUSED(userdata2);
348 if (status == WGPURequestAdapterStatus_Success)
349 {
350 WGPUAdapter* extAdapter = (WGPUAdapter*)userdata1;
351 *extAdapter = adapter;
352 }
353 else
354 {
355 printf("Request_adapter status=%#.8x message=%.*s\n", status, (int) message.length, message.data);
356 }
357}
358
359static void handle_request_device(WGPURequestDeviceStatus status, WGPUDevice device, WGPUStringView message, void* userdata1, void* userdata2)
360{
361 IM_UNUSED(userdata2);
362 if (status == WGPURequestDeviceStatus_Success)
363 {
364 WGPUDevice* extDevice = (WGPUDevice*)userdata1;
365 *extDevice = device;
366 }
367 else
368 {
369 printf("Request_device status=%#.8x message=%.*s\n", status, (int) message.length, message.data);
370 }
371}
372
373static WGPUAdapter RequestAdapter(WGPUInstance& instance)
374{
375 WGPURequestAdapterOptions adapter_options = {};
376
377 WGPUAdapter local_adapter = nullptr;
378 WGPURequestAdapterCallbackInfo adapterCallbackInfo = {};
379 adapterCallbackInfo.mode = WGPUCallbackMode_WaitAnyOnly;
380 adapterCallbackInfo.callback = handle_request_adapter;
381 adapterCallbackInfo.userdata1 = &local_adapter;
382
383 WGPUFuture future = wgpuInstanceRequestAdapter(instance, &adapter_options, adapterCallbackInfo);
384 WGPUFutureWaitInfo waitInfo = { future, false };
385 wgpuInstanceWaitAny(instance, 1, &waitInfo, ~0ull);
386 IM_ASSERT(local_adapter && "Error on Adapter request");
387 return local_adapter;
388}
389
390static WGPUDevice RequestDevice(WGPUInstance& instance, WGPUAdapter& adapter)
391{
392 WGPUDevice local_device = nullptr;
393 WGPURequestDeviceCallbackInfo deviceCallbackInfo = {};
394 deviceCallbackInfo.mode = WGPUCallbackMode_WaitAnyOnly;
395 deviceCallbackInfo.callback = handle_request_device;
396 deviceCallbackInfo.userdata1 = &local_device;
397 WGPUFuture future = wgpuAdapterRequestDevice(adapter, nullptr, deviceCallbackInfo);
398 WGPUFutureWaitInfo waitInfo = { future, false };
399 wgpuInstanceWaitAny(instance, 1, &waitInfo, ~0ull);
400 IM_ASSERT(local_device && "Error on Device request");
401 return local_device;
402}
403#endif // IMGUI_IMPL_WEBGPU_BACKEND_WGPU
404
405bool InitWGPU(GLFWwindow* window)
406{
407 WGPUTextureFormat preferred_fmt = WGPUTextureFormat_Undefined; // acquired from SurfaceCapabilities
408
409 // Google DAWN backend: Adapter and Device acquisition, Surface creation
410#if defined(IMGUI_IMPL_WEBGPU_BACKEND_DAWN)
411 wgpu::InstanceDescriptor instance_desc = {};
412 static constexpr wgpu::InstanceFeatureName timedWaitAny = wgpu::InstanceFeatureName::TimedWaitAny;
413 instance_desc.requiredFeatureCount = 1;
414 instance_desc.requiredFeatures = &timedWaitAny;
415 wgpu::Instance instance = wgpu::CreateInstance(&instance_desc);
416
417 wgpu::Adapter adapter = RequestAdapter(instance);
418 ImGui_ImplWGPU_DebugPrintAdapterInfo(adapter.Get());
419
420 wgpu_device = RequestDevice(instance, adapter);
421
422 // Create the surface.
423#ifdef __EMSCRIPTEN__
424 wgpu::EmscriptenSurfaceSourceCanvasHTMLSelector canvas_desc = {};
425 canvas_desc.selector = "#canvas";
426
427 wgpu::SurfaceDescriptor surface_desc = {};
428 surface_desc.nextInChain = &canvas_desc;
429 wgpu_surface = instance.CreateSurface(&surface_desc).MoveToCHandle();
430#else
431 wgpu_surface = CreateWGPUSurface(instance.Get(), window);
432#endif
433 if (!wgpu_surface)
434 return false;
435
436 // Moving Dawn objects into WGPU handles
437 wgpu_instance = instance.MoveToCHandle();
438
439 WGPUSurfaceCapabilities surface_capabilities = {};
440 wgpuSurfaceGetCapabilities(wgpu_surface, adapter.Get(), &surface_capabilities);
441
442 preferred_fmt = surface_capabilities.formats[0];
443
444 // WGPU backend: Adapter and Device acquisition, Surface creation
445#elif defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU) || defined(IMGUI_IMPL_WEBGPU_BACKEND_WGVK)
446 WGPUInstanceDescriptor instanceDesc = {};
447 WGPUInstanceFeatureName timedWaitAny = WGPUInstanceFeatureName_TimedWaitAny;
448 instanceDesc.requiredFeatureCount = 1;
449 instanceDesc.requiredFeatures = &timedWaitAny;
450 wgpu_instance = wgpuCreateInstance(&instanceDesc);
451
452#if defined(IMGUI_IMPL_WEBGPU_BACKEND_WGPU)
453 wgpuSetLogCallback(
454 [](WGPULogLevel level, WGPUStringView msg, void* userdata) { fprintf(stderr, "%s: %.*s\n", ImGui_ImplWGPU_GetLogLevelName(level), (int)msg.length, msg.data); }, nullptr
455 );
456 wgpuSetLogLevel(WGPULogLevel_Warn);
457#endif
458
459 WGPUAdapter adapter = RequestAdapter(wgpu_instance);
460 ImGui_ImplWGPU_DebugPrintAdapterInfo(adapter);
461
462 wgpu_device = RequestDevice(wgpu_instance, adapter);
463
464 // Create the surface.
465 wgpu_surface = CreateWGPUSurface(wgpu_instance, window);
466 if (!wgpu_surface)
467 return false;
468
469 WGPUSurfaceCapabilities surface_capabilities = {};
470 wgpuSurfaceGetCapabilities(wgpu_surface, adapter, &surface_capabilities);
471
472 preferred_fmt = surface_capabilities.formats[0];
473#endif // IMGUI_IMPL_WEBGPU_BACKEND_WGPU
474
475 wgpu_surface_configuration.presentMode = WGPUPresentMode_Fifo;
476 wgpu_surface_configuration.alphaMode = WGPUCompositeAlphaMode_Auto;
477 wgpu_surface_configuration.usage = WGPUTextureUsage_RenderAttachment;
478 wgpu_surface_configuration.width = wgpu_surface_width;
479 wgpu_surface_configuration.height = wgpu_surface_height;
480 wgpu_surface_configuration.device = wgpu_device;
481 wgpu_surface_configuration.format = preferred_fmt;
482
483 wgpuSurfaceConfigure(wgpu_surface, &wgpu_surface_configuration);
484 wgpu_queue = wgpuDeviceGetQueue(wgpu_device);
485
486 return true;
487}
488
489// GLFW helper to create a WebGPU surface, used only in WGPU-Native. DAWN-Native already has a built-in function
490// As of today (2025/10) there is no "official" support in GLFW to create a surface for WebGPU backend
491// This stub uses "low level" GLFW calls to acquire information from a specific Window Manager.
492// Currently supported platforms: Windows / Linux (X11 and Wayland) / MacOS. Not necessary nor available with EMSCRIPTEN.
493#ifndef __EMSCRIPTEN__
494
495#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
496#define GLFW_HAS_X11_OR_WAYLAND 1
497#else
498#define GLFW_HAS_X11_OR_WAYLAND 0
499#endif
500#ifdef _WIN32
501#undef APIENTRY
502#ifndef GLFW_EXPOSE_NATIVE_WIN32 // for glfwGetWin32Window()
503#define GLFW_EXPOSE_NATIVE_WIN32
504#endif
505#elif defined(__APPLE__)
506#ifndef GLFW_EXPOSE_NATIVE_COCOA // for glfwGetCocoaWindow()
507#define GLFW_EXPOSE_NATIVE_COCOA
508#endif
509#elif GLFW_HAS_X11_OR_WAYLAND
510#ifndef GLFW_EXPOSE_NATIVE_X11 // for glfwGetX11Display(), glfwGetX11Window() on Freedesktop (Linux, BSD, etc.)
511#define GLFW_EXPOSE_NATIVE_X11
512#endif
513#ifndef GLFW_EXPOSE_NATIVE_WAYLAND
514#if defined(__has_include) && __has_include(<wayland-client.h>)
515#define GLFW_EXPOSE_NATIVE_WAYLAND
516#endif
517#endif
518#endif
519#include <GLFW/glfw3native.h>
520#undef Status // X11 headers are leaking this and also 'Success', 'Always', 'None', all used in DAWN api. Add #undef if necessary.
521
522WGPUSurface CreateWGPUSurface(const WGPUInstance& instance, GLFWwindow* window)
523{
524 ImGui_ImplWGPU_CreateSurfaceInfo create_info = {};
525 create_info.Instance = instance;
526#if defined(GLFW_EXPOSE_NATIVE_COCOA)
527 {
528 create_info.System = "cocoa";
529 create_info.RawWindow = (void*)glfwGetCocoaWindow(window);
530 return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info);
531 }
532#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND)
533 if (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND)
534 {
535 create_info.System = "wayland";
536 create_info.RawDisplay = (void*)glfwGetWaylandDisplay();
537 create_info.RawSurface = (void*)glfwGetWaylandWindow(window);
538 return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info);
539 }
540#elif defined(GLFW_EXPOSE_NATIVE_X11)
541 if (glfwGetPlatform() == GLFW_PLATFORM_X11)
542 {
543 create_info.System = "x11";
544 create_info.RawWindow = (void*)glfwGetX11Window(window);
545 create_info.RawDisplay = (void*)glfwGetX11Display();
546 return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info);
547 }
548#elif defined(GLFW_EXPOSE_NATIVE_WIN32)
549 {
550 create_info.System = "win32";
551 create_info.RawWindow = (void*)glfwGetWin32Window(window);
552 create_info.RawInstance = (void*)::GetModuleHandle(NULL);
553 return ImGui_ImplWGPU_CreateWGPUSurfaceHelper(&create_info);
554 }
555#else
556#error "Unsupported WebGPU native platform!"
557 return nullptr;
558#endif
559}
560#endif // #ifndef __EMSCRIPTEN__