-
-
Notifications
You must be signed in to change notification settings - Fork 10.4k
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
WIN32 + OpenGL 3: Allow WndProcHandler to receive contexts as argument so they do not rely in global variables and becomes thread safe #8069
Conversation
…t so they do not rely in global variables This allows to render multiple independent windows with their own context from one thread. This is mandatory, for example, to host ImGui windows from Windows Presentation Foundation (WPF), which results in a very powerful tool
Maybe I'm missing something, but is there a reason you can't just call
Dear ImGui is not thread safe in general. If your Dear ImGui UI thread and OS UI threads are separate you're going to have race conditions between one thread updating (IIRC you can actually get away with protecting It doesn't seem like it, but if your only goal is to have multiple contexts across multiple threads then it's generally recommended to make the context variable a thread local. |
Hello David, thank you for answeing. So current set up is: I want to render one (or more) instances of ImGui from WPF (C# .NET Core). So there are two threads involved, both created from the C# side: the Render thread that does all the ImGui (NewFrame, Begin, etc) calls, and the WPF UI thread that owns the Win32 window and hence receives the notifications and does the WndProc calls. So I cannot set the current context in the WndProc callback since the render thread might be in the middle of a NewFrame/EndFrame operation (it crashes, leading to ImGui exceptions) I cannot use the lock/mutex either since is C# who manages the threads and not C++ (leads to segfault in a very simple hello world program). So far the only solution I've found (it works amazingly) is to pass the context as a input parameter to the ImGui function. Cheers! Juan |
If you #define GImGui to be a TLS variable you may use a different one per thread.
The problem is that you are sitting on a thin line of luck / undefined behavior. At it happens, the IO functions are currently not accessing the global context variable (see #6895 (comment)) but it has never been a thorough guarantee. I see that I could add a patch similar to your to reduce access to this from
I can make a tweaked version of your patch for master, but static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Allow secondary viewport WndProc to be called regardless of current context
ImGuiContext* hwnd_ctx = (ImGuiContext*)::GetPropA(hWnd, "IMGUI_CONTEXT");
ImGuiContext* prev_ctx = ImGui::GetCurrentContext();
if (hwnd_ctx != prev_ctx && hwnd_ctx != NULL)
ImGui::SetCurrentContext(hwnd_ctx);
LRESULT result = 0;
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
result = true;
else if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hWnd))
{
switch (msg)
{
case WM_CLOSE:
viewport->PlatformRequestClose = true;
break;
case WM_MOVE:
viewport->PlatformRequestMove = true;
break;
case WM_SIZE:
viewport->PlatformRequestResize = true;
break;
case WM_MOUSEACTIVATE:
if (viewport->Flags & ImGuiViewportFlags_NoFocusOnClick)
result = MA_NOACTIVATE;
break;
case WM_NCHITTEST:
// Let mouse pass-through the window. This will allow the backend to call io.AddMouseViewportEvent() correctly. (which is optional).
// The ImGuiViewportFlags_NoInputs flag is set while dragging a viewport, as want to detect the window behind the one we are dragging.
// If you cannot easily access those viewport flags from your windowing/event code: you may manually synchronize its state e.g. in
// your main loop after calling UpdatePlatformWindows(). Iterate all viewports/platform windows and pass the flag to your windowing system.
if (viewport->Flags & ImGuiViewportFlags_NoInputs)
result = HTTRANSPARENT;
break;
}
}
if (result == 0)
result = DefWindowProc(hWnd, msg, wParam, lParam);
if (hwnd_ctx != prev_ctx && hwnd_ctx != NULL)
ImGui::SetCurrentContext(prev_ctx);
return result;
} We could remove the Get/SetCurrentContext(), but the problem here is that we cannot use And of course we can reimplement it using I am attaching my version of the patch (it's 80% the same as yours, doesn't use imgui_internal.h by calling |
…tformWindow() to be easier to rework in a parallal friendly way. (#8069)
I'm caving in because I think we need a playground for this and I'd rather it be the win32 backend. I have pushed 3b8c7d0 (master) + fedf45c (docking) + ee1decc (docking) To be clear: I am not comfortable with this and it is undocumented, but it should help getting moving forward. For your use case you will need to extern/forward declare |
Hello Omar, Thank you very much for the update. It works perfectly!! Cheers, Juan |
Plot twist: it introduced a crash #8162 which I am now fixing. |
Hello,
I would like to first thank you for the hard work you've put in this very useful project.
I propose a small edit on the interfaces of the Win32 WndProc handler, to accept an arbitrary ImGui Context as input so they do not rely on the current context and it is thread safe. This allows to render multiple independent windows with their own context from one thread. This is mandatory, for example, to host ImGui windows from Windows Presentation Foundation (WPF), which results in a very powerful tool.
Here I attach a screenshot of the resulting UI you can get by combining WPF and ImGui. It renders 4 fully independent ImGui applications, using a single auxiliary thread (e.g. a RenderThread).
I need this change to make it to work since at the time the WndProc function is called, it is possible that the RenderThread is currently drawing its frame, and the WndProc method would access an incorrect ImGuiIO or BackendData.
Thank you again.
Juan