-
Notifications
You must be signed in to change notification settings - Fork 10.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add glfw wgpu desktop and multiviewport support #7132
Changes from 1 commit
6fda112
e7b5f87
e5cf10b
62cfa98
fff4391
ad666db
b271eff
82d92aa
e92aa28
c770f20
3de52f8
44c7dfc
0573513
c623669
04f4001
1ff90c5
fbf45ad
6b7358e
65dc67f
fd75685
fc570ac
286cd5b
3c435c0
da29b77
9d9ca37
adcc321
40df3db
0a1f5b9
868facf
085781f
8be48a4
38ddfb2
29ff159
f080228
cf4c10b
5c5ae80
37b37fc
976dc23
515b437
742e534
9638c28
25a492f
d3c3514
9a2b598
0bf134a
f959c41
e7712ff
4f9ba19
231cbee
1db579d
f790d51
76bc1b8
3caa79c
c1743ee
dad1689
fab96a6
daecfff
b475309
f9df6bf
80a5fdb
648278c
7b8107e
4cb0fe3
9ec299e
eba46cb
361432a
fa0120e
b555984
07e8ff9
b720c0f
da18fcb
baaaaea
b8a44b1
c895e98
913151c
a60387a
6ef4f67
50b2ff0
5717f0a
558c57a
9d6818d
49e70e6
ebb8d78
b30df88
4bb7567
fc4d818
6ebbecc
0c9c12c
0b30947
2f2d507
f5d1852
9a7f1b1
8d08e0b
ecdae77
6cd66dd
ec89c07
a3abb1f
80d21d0
69730ea
8ff415b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
Added backend flags and checks Added platform init function Added platform shutdown function Hooked render functions CreateWindow, DestroyWindow, SetWindowSize, RenderWindow, SwapBuffers
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,7 @@ | |
#include "imgui_impl_wgpu.h" | ||
#include <limits.h> | ||
#include <webgpu/webgpu.h> | ||
#include <memory> | ||
|
||
// Dear ImGui prototypes from imgui_internal.h | ||
extern ImGuiID ImHashData(const void* data_p, size_t data_size, ImU32 seed = 0); | ||
|
@@ -243,6 +244,18 @@ static void SafeRelease(FrameResources& res) | |
SafeRelease(res.IndexBufferHost); | ||
SafeRelease(res.VertexBufferHost); | ||
} | ||
static void SafeRelease(WGPUSwapChain& res) | ||
{ | ||
if (res) | ||
wgpuSwapChainRelease(res); | ||
res = nullptr; | ||
} | ||
static void SafeRelease(WGPUSurface& res) | ||
{ | ||
if (res) | ||
wgpuSurfaceRelease(res); | ||
res = nullptr; | ||
} | ||
|
||
static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const char* wgsl_source) | ||
{ | ||
|
@@ -724,6 +737,7 @@ bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextur | |
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. | ||
#ifndef __EMSCRIPTEN__ | ||
IM_ASSERT(instance == nullptr && "WGPUInstance required for multi viewport!"); | ||
io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // Enable multiviewport on desktop. | ||
#endif | ||
|
||
bd->wgpuInstance = instance; | ||
|
@@ -756,6 +770,9 @@ bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextur | |
fr->VertexBufferSize = 5000; | ||
} | ||
|
||
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) | ||
ImGui_ImplWGPU_InitPlatformInterface(); | ||
|
||
return true; | ||
} | ||
|
||
|
@@ -773,6 +790,10 @@ void ImGui_ImplWGPU_Shutdown() | |
bd->numFramesInFlight = 0; | ||
bd->frameIndex = UINT_MAX; | ||
|
||
// Clean up windows | ||
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) | ||
ImGui_ImplWGPU_ShutdownPlatformInterface(); | ||
|
||
io.BackendRendererName = nullptr; | ||
io.BackendRendererUserData = nullptr; | ||
io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; | ||
|
@@ -786,6 +807,147 @@ void ImGui_ImplWGPU_NewFrame() | |
ImGui_ImplWGPU_CreateDeviceObjects(); | ||
} | ||
|
||
//-------------------------------------------------------------------------------------------------------- | ||
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT | ||
// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously. | ||
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first.. | ||
//-------------------------------------------------------------------------------------------------------- | ||
|
||
|
||
static void ImGui_ImplWGPU_CreateWindow(ImGuiViewport* viewport) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably be moved out of the backend and user should provide this instead - there's no easy way to handle this for every platform/WebGPU impl. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 100% this is the main thing I was struggling with trying to fix, would it be just a callback that a user would need to assign to ? |
||
{ | ||
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); | ||
ImGui_ImplWGPU_ViewportData* vd = IM_NEW(ImGui_ImplWGPU_ViewportData)(); | ||
viewport->RendererUserData = vd; | ||
|
||
WGPUChainedStruct chainedDescriptor = {}; | ||
// ------ ISSUE unknown for linux and apple ------------- | ||
#ifdef _WIN32 | ||
chainedDescriptor.sType = WGPUSType_SurfaceDescriptorFromWindowsHWND; | ||
std::unique_ptr<WGPUSurfaceDescriptorFromWindowsHWND> surfaceDescFromHandle = std::make_unique<WGPUSurfaceDescriptorFromWindowsHWND>(); | ||
surfaceDescFromHandle->hwnd = viewport->PlatformHandleRaw; | ||
#elif defined(__APPLE__) | ||
chainedDescriptor.sType = WGPUSType_SurfaceDescriptorFromMetalLayer; | ||
std::unique_ptr<WGPUSurfaceDescriptorFromMetalLayer> surfaceDescFromHandle = std::make_unique<WGPUSurfaceDescriptorFromMetalLayer>(); | ||
NSWindow* ns_window = viewport->PlatformHandleRaw; | ||
[ns_window.contentView setWantsLayer : YES]; | ||
metal_layer = [CAMetalLayer layer]; | ||
[ns_window.contentView setLayer : metal_layer] ; | ||
surfaceDescFromHandle->layer = metal_layer; | ||
// #elif defined(DAWN_USE_WAYLAND) | ||
// struct wl_display* wayland_display = glfwGetWaylandDisplay(); | ||
// struct wl_surface* wayland_surface = glfwGetWaylandWindow(window); | ||
// chainedDescriptor.sType = WGPUSType_SurfaceDescriptorFromWindowsHWND; | ||
// std::unique_ptr<WGPUSurfaceDescriptorFromWaylandSurface> surfaceDescFromHandle = std::make_unique<WGPUSurfaceDescriptorFromWaylandSurface>(); | ||
// surfaceDescFromHandle->display = wayland_display; | ||
// surfaceDescFromHandle->surface = wayland_surface; | ||
// #else | ||
// Display* x11_display = glfwGetX11Display(); | ||
// Window x11_window = glfwGetX11Window((GLFWwindow*) viewport->PlatformHandle); | ||
// chainedDescriptor.sType = WGPUSType_SurfaceDescriptorFromXlibWindow; | ||
// std::unique_ptr<WGPUSurfaceDescriptorFromXlibWindow> surfaceDescFromHandle = std::make_unique<WGPUSurfaceDescriptorFromXlibWindow>(); | ||
// surfaceDescFromHandle->display = x11_display; | ||
// surfaceDescFromHandle->window = x11_window; | ||
#endif | ||
// ------------------------------------------------------ | ||
|
||
surfaceDescFromHandle->chain = chainedDescriptor; | ||
WGPUSurfaceDescriptor surfaceDescriptor = {}; | ||
surfaceDescriptor.nextInChain = &surfaceDescFromHandle->chain; | ||
|
||
vd->Window.surface = wgpuInstanceCreateSurface(bd->wgpuInstance, &surfaceDescriptor); | ||
|
||
WGPUSwapChainDescriptor swapDescriptor = {}; | ||
swapDescriptor.usage = WGPUTextureUsage_RenderAttachment; | ||
swapDescriptor.format = bd->renderTargetFormat; | ||
swapDescriptor.width = viewport->Size.x; | ||
swapDescriptor.height = viewport->Size.y; | ||
swapDescriptor.presentMode = WGPUPresentMode_Fifo; | ||
|
||
vd->Window.swapChain = wgpuDeviceCreateSwapChain(bd->wgpuDevice, vd->Window.surface, &swapDescriptor); | ||
} | ||
|
||
|
||
static void ImGui_ImplWGPU_DestroyWindow(ImGuiViewport* viewport) | ||
{ | ||
if (ImGui_ImplWGPU_ViewportData* vd = (ImGui_ImplWGPU_ViewportData*)viewport->RendererUserData) | ||
{ | ||
SafeRelease(vd->Window.swapChain); | ||
SafeRelease(vd->Window.surface); | ||
IM_DELETE(vd); | ||
} | ||
viewport->RendererUserData = nullptr; | ||
} | ||
|
||
|
||
static void ImGui_ImplWGPU_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) | ||
{ | ||
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); | ||
ImGui_ImplWGPU_ViewportData* vd = (ImGui_ImplWGPU_ViewportData*)viewport->RendererUserData; | ||
|
||
SafeRelease(vd->Window.swapChain); | ||
|
||
WGPUSwapChainDescriptor swapDescriptor = {}; | ||
swapDescriptor.usage = WGPUTextureUsage_RenderAttachment; | ||
swapDescriptor.format = bd->renderTargetFormat; | ||
swapDescriptor.width = size.x; | ||
swapDescriptor.height = size.y; | ||
swapDescriptor.presentMode = WGPUPresentMode_Fifo; | ||
|
||
vd->Window.swapChain = wgpuDeviceCreateSwapChain(bd->wgpuDevice, vd->Window.surface, &swapDescriptor); | ||
} | ||
|
||
static void ImGui_ImplWGPU_RenderWindow(ImGuiViewport* viewport, void*) | ||
{ | ||
ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); | ||
ImGui_ImplWGPU_ViewportData* vd = (ImGui_ImplWGPU_ViewportData*)viewport->RendererUserData; | ||
|
||
ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); | ||
WGPURenderPassColorAttachment color_attachments = {}; | ||
color_attachments.view = wgpuSwapChainGetCurrentTextureView(vd->Window.swapChain); | ||
color_attachments.loadOp = WGPULoadOp_Clear; | ||
color_attachments.storeOp = WGPUStoreOp_Store; | ||
color_attachments.clearValue = { clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't work without this: color_attachments.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is a new requirement imposed in a version of dawn that I had yet to work with, If that is required then putting it in should be done. |
||
|
||
WGPURenderPassDescriptor render_pass_desc = {}; | ||
render_pass_desc.colorAttachmentCount = 1; | ||
render_pass_desc.colorAttachments = &color_attachments; | ||
render_pass_desc.depthStencilAttachment = nullptr; | ||
|
||
WGPUCommandEncoderDescriptor enc_desc = {}; | ||
WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(bd->wgpuDevice, &enc_desc); | ||
|
||
WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &render_pass_desc); | ||
ImGui_ImplWGPU_RenderDrawData(viewport->DrawData, pass); | ||
wgpuRenderPassEncoderEnd(pass); | ||
|
||
WGPUCommandBufferDescriptor cmd_buffer_desc = {}; | ||
WGPUCommandBuffer cmd_buffer = wgpuCommandEncoderFinish(encoder, &cmd_buffer_desc); | ||
WGPUQueue queue = wgpuDeviceGetQueue(bd->wgpuDevice); | ||
wgpuQueueSubmit(queue, 1, &cmd_buffer); | ||
} | ||
|
||
static void ImGui_ImplWGPU_SwapBuffers(ImGuiViewport* viewport, void*) | ||
{ | ||
ImGui_ImplWGPU_ViewportData* vd = (ImGui_ImplWGPU_ViewportData*)viewport->RendererUserData; | ||
wgpuSwapChainPresent(vd->Window.swapChain); | ||
} | ||
|
||
void ImGui_ImplWGPU_InitPlatformInterface() | ||
{ | ||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); | ||
platform_io.Renderer_CreateWindow = ImGui_ImplWGPU_CreateWindow; | ||
platform_io.Renderer_DestroyWindow = ImGui_ImplWGPU_DestroyWindow; | ||
platform_io.Renderer_SetWindowSize = ImGui_ImplWGPU_SetWindowSize; | ||
platform_io.Renderer_RenderWindow = ImGui_ImplWGPU_RenderWindow; | ||
platform_io.Renderer_SwapBuffers = ImGui_ImplWGPU_SwapBuffers; | ||
} | ||
|
||
void ImGui_ImplWGPU_ShutdownPlatformInterface() | ||
{ | ||
ImGui::DestroyPlatformWindows(); | ||
} | ||
|
||
//----------------------------------------------------------------------------- | ||
|
||
#endif // #ifndef IMGUI_DISABLE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be removed if
ImGui_ImplWGPU_CreateWindow
is not included in the backend.