main.cpp 24 KB

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