main.cpp 24 KB

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