main.cpp 23 KB

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