Skip to content
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

Closed
wants to merge 100 commits into from

Conversation

Zelif
Copy link

@Zelif Zelif commented Dec 15, 2023

Adding Multi viewport to the desktop version of WGPU

When using dawn/wgpu-native have the multi viewport enabled and rendering (fixes: #7110 )

Items required to get the wgpu backend working:

  • Enable the backend setting and pluming
  • Create required render functions
    • Create Window - needs work
    • Render Window
    • Swap Window
    • Set Window size
    • Destroy Window
  • Store WGPU instance for surface creation
  • Store WGPU surface for swapchain creation per veiwport
  • Store WGPU swapchain for rendering per veiwport
  • Add safe release functiosn for surface and swapchain
  • Disable any multi viewport parts if emscripten is required
  • Make sure wgpu-native compiles
  • Make sure makefile works as well (include a desktop wgpu lib)
  • Track down memory leak when window is undocked (potential in render window ->
    color_attachments.view = wgpuSwapChainGetCurrentTextureView(vd->Window.swapChain);)

Create window is the major area that still needs work due to surface creation being platform dependent. I was looking at the way vulkan handles it and there is a special function inside the glfw/sdl/etc backends (CreateVkSurface) I assume this is the approach that would be required going forward?

Example changes

Example now works on desktop and all emscripten stuff is behind preprocessor directives.

Added CMakeLists.txt and a CMakePresets.json, the latter doesn't have to be used but is handy (only works with CMake 3.19+), adding the location to a clone of the dawn repo is required in CMakeLists.txt:30. I am yet to test out wgpu-native vs dawn as of yet. Or if there is anywhere that has static builds to link against for dawn that could be included. (I am unsure how you would want this included)

The make file will need to be updated but I do not know my way around them, and as I only know how to get dawn working with CMake that is what I used.

Firefox/WGPU-Native
Google/Dawn

Video of it working:
https://github.com/ocornut/imgui/assets/666945/875315c9-5b61-42b6-b8ed-a31bc8179050

Hopefully this helps :D

Changed ImGui_ImplWGPU_Init function by adding instance
Added Safe release for WGPU structures (SwapChain/Surface)
Added ImGui_ImplWGPUH_Window struct required for each window
Check for required empty instance when using multiveiwport
Added backend flags and checks
Added platform init function
Added platform shutdown function
Hooked render functions CreateWindow, DestroyWindow, SetWindowSize, RenderWindow, SwapBuffers
Fixed missing Instance
Fixed null check and moved it when using it
Fix emscripten build in unused area
Added cmake lists to compile dawn along with the example
Added cmake presets (for anyone one using cmake 3.19+) to configure easier
Split emscripten only sections off
Added callbacks for getting device & adapter
Added multi viewport code
Added swapchain creation function
Added temporary canvas resize on creation until ocornut#6751 is merged in
(Refreshing will resize the canvas)
Removed hardcoded dawn location
@Zelif Zelif marked this pull request as draft December 15, 2023 19:06
@ocornut ocornut added the web label Jan 9, 2024
@ocornut
Copy link
Owner

ocornut commented Jan 22, 2024

Hello,

Very interested in getting even-basic desktop support in.

Could you squash/rebase this and try to separate it into two commits?

  • one commit to compile example on desktop
  • one commit to add multi-viewport support
    If multi-viewport support is going to be complicated I would suggest aiming at merging the first part ahead of time.

Feedback:

    1. What is the justification for using <memory> ? We typically don't rely on STL. It seems like all the std::unique_ptr<>/make_unique<> cruft could be changed into regular pointer?
    1. What is the justification for changing e.g. WGPUDevice to e.g. wgpu::Device ? What is the difference?
    1. Emscripten abstraction can use the same emscripten_mainloop_stub.h EMSCRIPTEN_MAINLOOP_BEGIN EMSCRIPTEN_MAINLOOP_END mechanism we use in e.g. example_glfw_opengl3, for consistency.

ocornut added a commit that referenced this pull request Jan 22, 2024
…compatible Desktop examples, as aiming to make this suppot desktop eventually.

Also aimed at reducing diff for #7132 tho this will lead in conflict.
@ocornut
Copy link
Owner

ocornut commented Jan 22, 2024

FYI i have pushed some WebGPU related changes which will conflict with this (but ultimately simplify the diff).

  • e3c7ff9 Examples: Emscripten+WebGPU: slightly refactor like other Emscripten compatible Desktop examples, as aiming to make this support desktop eventually. -> this include some of the cruft from this PR and will be the most breaking part, so I checked and it is relatively easy to resolve the conflicts they are pretty shallow.
  • 831d42c WebGPU: ImGui_ImplWGPU_Init() now takes a ImGui_ImplWGPU_InitInfo structure instead of variety of parameters, allowing for easier further changes. (WebGPU pipeline is hardcoded to not use multisampling #7240) --> This will conflict with your addition of a parameter to ImGui_ImplWGPU_Init() but should be obvious to fix.
  • 5fc0a36: WebGPU: added ImGui_ImplWGPU_InitInfo::PipelineMultisampleState. (WebGPU pipeline is hardcoded to not use multisampling #7240) -> I believe this shouldn't conflict once the previous one is merged.

To proceed I suggest that:

  • you first merge docking branch just before those 3 changes.
  • then you successively cherry-pick those 3 commits.
  • now you have an up to date branch and we can easily squash/rebase into a neat 1 or 2 commits.
    Let me know if you need help with any of this.

@waywardmonkeys
Copy link
Contributor

I'm working with the wgpu-native folks to make it easier to use it with cmake, so that'll be of help perhaps to you in the future.

But a couple of notes from reading through this:

  • It'd be nice if everything used the C API / names rather than some using C and some using C++. That might be a good thing to do as a separate PR to clean up what's in the code now and then fix the merge conflicts? (I'm willing to do the change to what's in the code now...)
  • The swap chain API is not in wgpu-native and has been merged with the surface instead, so that part of things will need some work. There's an open issue for Dawn to make the same changes in API (with a patch being developed): https://bugs.chromium.org/p/dawn/issues/detail?id=2320&q=swapchain&can=2

@Zelif
Copy link
Author

Zelif commented Feb 1, 2024

@ocornut sorry for disappearing, just have a contract close to completion, that I need to take care of. I will sort those changes within the coming week or 2.

Surface creation is still something I am having issues with cross platform. That will require a bit of work to figure out.

@waywardmonkeys Awesome to hear about the cmake stuff and conflicts with swapchain. I had no idea that was coming. I will have to read up on it. The mix between the C and C++ was to get around surface creation stuff for half of it and getting the device and adapter callbacks. I have kept the implementation of webgpu as the C api only to keep it more inline, but the example has the C and C++ stuff. Tho it still has swapchain stuff in the dawn implementation that i used. So we will need to convert that.

What would the best approach be for aligning this? Pre processor for wgpu-native and dawn for how to use swapchain? But that would break when dawn changes it. My guess will be make it the way wgpu-native uses it and maybe release it once dawn releases that change.

I will try sort out some stuff today around the merge from docking, feel free to sort out the swapchain/surface reconciliation issue any help there is much appreciated :D

Copy link
Contributor

@eliasdaler eliasdaler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested with Dawn e24033c.

Had to make a change specified below for it to work.

This one:

color_attachments.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;

Also when resizing the window, it crashes for me on Windows 10 and D3D12 backend here

@@ -72,8 +74,22 @@ struct Uniforms
float Gamma;
};

// Forward declarations for multi-viewport support
static void ImGui_ImplWGPU_InitPlatformInterface();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ocornut, maybe it's worth renaming everything from WGPU to WebGPU? "wgpu" is a name of the Rust implementation and I see "WebGPU" being used everywhere (except for C API, which I find unfortunate).

Otherwise people might think that this backend can only be used with wgpu and not with Dawn.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had chosen to go with WGPU purely based on how the C API was naming things, so yeah WebGPU is fine to go with as well.

Comment on lines 86 to 87
ImGui_ImplWGPU_ViewportData() { }
~ImGui_ImplWGPU_ViewportData() { }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These constructor and destructors are unnecessary and can be removed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was a hang over of when I did have objects in there, yup should have been deleted

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 };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't work without this:

color_attachments.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;

Copy link
Author

Choose a reason for hiding this comment

The 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.

@@ -22,7 +22,7 @@

#include <webgpu/webgpu.h>

IMGUI_IMPL_API bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format, WGPUTextureFormat depth_format = WGPUTextureFormat_Undefined);
IMGUI_IMPL_API bool ImGui_ImplWGPU_Init(WGPUDevice device, int num_frames_in_flight, WGPUTextureFormat rt_format, WGPUTextureFormat depth_format = WGPUTextureFormat_Undefined, WGPUInstance instance = nullptr);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably needs a comment which will explain why "instance" is optional here (so that people don't need to read the implementation to know that).

@@ -31,4 +31,17 @@ IMGUI_IMPL_API void ImGui_ImplWGPU_RenderDrawData(ImDrawData* draw_data, WGPURen
IMGUI_IMPL_API void ImGui_ImplWGPU_InvalidateDeviceObjects();
IMGUI_IMPL_API bool ImGui_ImplWGPU_CreateDeviceObjects();

// Helper structure to hold the data needed by one rendering context into one OS window
// (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's not meant to be used by other people, then maybe let's move it into the example's main.cpp?

Actually, it would be pretty handy since you'll be able to use Dawn's webgpu_glfw.h + CreateSurfaceForWindow function that they provide.

@@ -216,22 +283,28 @@ static void MainLoopStep(void* window)
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 };
color_attachments.view = wgpuSwapChainGetCurrentTextureView(wgpu_swap_chain);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed here too:

color_attachments.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;

static int wgpu_swap_chain_height = 0;
static wgpu::Instance wgpu_instance = nullptr;
static wgpu::Surface wgpu_surface = nullptr;
static wgpu::Device wgpu_device = nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As others have said, there's no need to use C++ wrappers here since they don't simplify much in this example.

{
// Async Get Instance, Adapter and device
// Init and run everything once these have been obtained.
InitWGPU([](wgpu::Device device) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This type of code is pretty hard to follow. Would be a bit easier to understand this, imo (note that wgpu_instance should be changed to a C type too):

int main(int, char**)
{
    wgpu_instance = wgpuCreateInstance(nullptr);
    WGPUAdapter adapter = requestAdapter(wgpu_instance);
    wgpu_device = requestDevice(adapter);

    SetupAndRun();
    return 0;
}

static WGPUAdapter requestAdapter(WGPUInstance instance)
{
    auto onAdapterRequestEnded = [](WGPURequestAdapterStatus status,
        WGPUAdapter adapter,
        char const* message,
        void* pUserData) {
            if (status == WGPURequestAdapterStatus_Success) {
                *static_cast<WGPUAdapter*>(pUserData) = adapter;
            }
            else {
                printf("Could not get WebGPU adapter: %s\n", message);
            }
        };

    WGPUAdapter adapter;
    wgpuInstanceRequestAdapter(instance, nullptr, onAdapterRequestEnded, (void*)&adapter);
    return adapter;
}

static WGPUDevice requestDevice(WGPUAdapter& adapter)
{
    auto onDeviceRequestEnded = [](WGPURequestDeviceStatus status,
        WGPUDevice device,
        char const* message,
        void* pUserData) {
            if (status == WGPURequestDeviceStatus_Success) {
                *static_cast<WGPUDevice*>(pUserData) = device;
            }
            else {
                printf("Could not get WebGPU device: %s\n", message);
            }
        };

    WGPUDevice device;
    wgpuAdapterRequestDevice(adapter, nullptr, onDeviceRequestEnded, (void*)&device);
    return device;
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup that looks way better

//--------------------------------------------------------------------------------------------------------


static void ImGui_ImplWGPU_CreateWindow(ImGuiViewport* viewport)
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Author

Choose a reason for hiding this comment

The 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 ?

@@ -38,6 +39,7 @@
#include "imgui_impl_wgpu.h"
#include <limits.h>
#include <webgpu/webgpu.h>
#include <memory>
Copy link
Contributor

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.

@eliasdaler
Copy link
Contributor

@Zelif
After some discussion with Omar, we have decided that it might be better to split this PR in at least two parts for ease of reviewing/testing. One part should be just about the desktop integration. The other one should be about adding support for multiple viewports and should be merged after the first one is done.

If you're currently busy with other work, I can do this myself and credit your contributions.

(P.S. I would also make some of the changes I've proposed myself, especially about C API usage and taming the callbacks in main.cpp)

@Zelif
Copy link
Author

Zelif commented Mar 1, 2024

@eliasdaler
That would be great, I have been bogged down too much to contribute any further for a while now(always moving goal posts are fun :( ) Feel free to change it how ever you see fit, I don't need any credit for this, rather see it working than myself taking on extra load right now and failing. Originally I didn't think I would have got this far with it as wgpu is new for me and C++ is rusty.

The changes with swapchain I have yet to read up on and unsure if they have made their way to Dawn yet. I never got around to testing wgpu-native with it either.

What is the best way to move forward ? Do I add you or anyone else to my fork or did we want to move this to a new PR with someone else in control. I'm fine with anything wish I had time to sort it out.

thedmd and others added 2 commits March 1, 2024 21:06
Extracted from 2023/12/29 post.
WIP add PathFillConcave(), AddConcavePolyFilled()
* remove use of 'auto'
* IsConvex -> ImPathIsConvex
* Triangulator -> ImTriangulator
* ImTriangulator: split declaration from definition, ImTriangulator can be put in the header if necessary
* ImTriangulator: Add node list flip to reverse winding order and handle degenerate cases
* ImTriangulator: Remove _HeapStorage, always require scratch buffer to be provided
* ImTriangulator: Use ImTriangleContainsPoint
* AddConcavePolyFilled: Clone AddConvexPolyFilled and use triangulator
* AddConcavePolyFilled: Remove ImDrawListEx_AddPolyFilled_xxx
* AddConcavePolyFilled: Use _Data->TempBuffer in triangulator
* AddConcavePolyFilled:
…ornut#760)

- Simplify and compact some code. Shallow tweaks.
- Add comments.
- Add concave shape demo.
- Remove coarse culling.
- Remove nested types to match coding style and for consistent type nams when translated to other languages.
- Merged ClassifyNode() and ReclassifyNode().
- Extracted ImTriangleIsClockwise().
- Hold copy of points inside nodes instead of pointing to them.
@eliasdaler
Copy link
Contributor

eliasdaler commented Mar 3, 2024

What is the best way to move forward ? Do I add you or anyone else to my fork or did we want to move this to a new PR with someone else in control. I'm fine with anything wish I had time to sort it out.

No problem, life happens, don't feel bad about it. I've abandoned quite a few PRs in my life myself and it's okay. :)

I'll start a new PR from my fork sometime later.

@Zelif
Copy link
Author

Zelif commented Mar 3, 2024

@eliasdaler
Cheers, would be a load off my mind it has been bugging me not having time to spend time on it, when the creation of the instance and requiring of the adapter & device has been done. The index file can be removed and we can use the template one from:
libs/emscripten/shell_minimal.html

The only difference that was being done was the instance, adapter and was being captured in the html. (this was being swapped over in another pull request here: #7151
but that one never made it out.

Seems the depth stencil issue has already been resolved via:
#7232

gan74 and others added 27 commits April 24, 2024 19:40
…e closer to each others + better packed ImGuiNavItemData
…ure so other fields layout are not affected by it (ocornut#7534)

This is essentially a misleading grace feature allowing a build mistake to be made, as we technically are more flexible now. BUT if we reintroduce a need we may more harshly move it to the top of the structure to detect issues.
# Conflicts:
#	backends/imgui_impl_vulkan.cpp
#	backends/imgui_impl_win32.cpp
…s stored outside the scope". Technically not used outside that scope, but best to play nice.
…indow::Pipeline (ocornut#6325, ocornut#6305, ocornut#7398, ocornut#3459, ocornut#3253, ocornut#3522)

As this is currently unused and misleading. Next commit will add a separate pipeline for secondary viewport.
…cornut#6325, ocornut#6305, ocornut#7398, ocornut#3459, ocornut#3253, ocornut#3522)

Edited from original commit: moved ImGui_ImplVulkan_CreatePipeline() call from ImGui_ImplVulkanH_CreateOrResizeWindow() to ImGui_ImplVulkan_CreateWindow().
No functional changes.
…cornut#6861, ocornut#2884, followed by rename 94da584)

Since negative windows can never be visibile in master it didn't show as a difference.
# Conflicts:
#	imgui.cpp
#	imgui.h
…y one when window is located on a monitor with negative coordinates. (ocornut#6861, ocornut#2884)
- Added instance to init model
- Added Present mode for any new window to adhere by
- Added Multiviewport render and update code
- Changed Texture format and Present mode to select the first available mode for capabilities (some surface creation such as laptops might not support FiFo)
Added cmake lists to compile dawn along with the example
Added cmake presets (for anyone one using cmake 3.19+) to configure easier
Split emscripten only sections off
Added callbacks for getting device & adapter
Added multi viewport code
Added swapchain creation function
Added temporary canvas resize on creation until ocornut#6751 is merged in
(Refreshing will resize the canvas)
Removed hardcoded dawn location
Removed temp resize code
Split the main function up to show init fuctions
Removed temp code in index
@Zelif
Copy link
Author

Zelif commented May 4, 2024

Welp I messed that up(I will have to fix the history of this PR or start another?),
@eliasdaler @ocornut
I had some time today to revise multiviewport until I fix what I did with the merge, the changes found here:
https://github.com/Zelif/imgui/tree/origin/features/multiviewport-wgpu
It has 2 commits the changes for multiviewport and then the change to the example.
Removed the swapchain stuff and changed to surface configuration, and in the example get the preferred format and present mode. This was due to surface config not allowing me to set FiFo on my integrated GPU (yet was working when I controlled the swapchain).
This should align more with the wgpu-native library since they do not use swapchain.

This still has the issue of surface creation which @eliasdaler said to change to a callback that a user creates. I am unsure how to structure this, but should hopefully help.

EDIT: I started another PR that focuses on just the multiviewport stuff #7557
Probably close this one off as the other desktop parts are already been handled. :D

@Zelif Zelif mentioned this pull request May 4, 2024
2 tasks
@Zelif Zelif closed this May 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet