main.cpp 24 KB

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