main.cpp 24 KB

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