From 4955f4b7855a8bec3fe0b31c8f62d4737ad5da53 Mon Sep 17 00:00:00 2001 From: Michael Martz Date: Tue, 5 Apr 2022 13:42:20 -0700 Subject: [PATCH 1/4] Fixed issue #2600 Each window created in imgui_impl_win32.cpp now creates an openGL context which shares its display list space with the primary window's. IMGUI_IMPL_WIN32_OPENGL3 must be defined to use this fix. --- backends/imgui_impl_win32.cpp | 83 ++++++++++++++++++++++++++++++++++- backends/imgui_impl_win32.h | 4 ++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 89ef37a2780c..932debebf1e7 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -82,6 +82,8 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); static void ImGui_ImplWin32_InitPlatformInterface(); static void ImGui_ImplWin32_ShutdownPlatformInterface(); static void ImGui_ImplWin32_UpdateMonitors(); +static void ImGui_ImplWin32_SetWindowFocus(ImGuiViewport* viewport); +static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport); struct ImGui_ImplWin32_Data { @@ -96,6 +98,10 @@ struct ImGui_ImplWin32_Data bool WantUpdateHasGamepad; bool WantUpdateMonitors; +#ifdef IMGUI_IMPL_WIN32_OPENGL3 + HGLRC HgLrc; +#endif //IMGUI_IMPL_WIN32_OPENGL3 + #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD HMODULE XInputDLL; PFN_XInputGetCapabilities XInputGetCapabilities; @@ -115,7 +121,11 @@ static ImGui_ImplWin32_Data* ImGui_ImplWin32_GetBackendData() } // Functions +#ifdef IMGUI_IMPL_WIN32_OPENGL3 +bool ImGui_ImplWin32_Init(void* hwnd, void* hglrc) +#else bool ImGui_ImplWin32_Init(void* hwnd) +#endif { ImGuiIO& io = ImGui::GetIO(); IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!"); @@ -142,6 +152,10 @@ bool ImGui_ImplWin32_Init(void* hwnd) bd->Time = perf_counter; bd->LastMouseCursor = ImGuiMouseCursor_COUNT; +#ifdef IMGUI_IMPL_WIN32_OPENGL3 + bd->HgLrc = (HGLRC)hglrc; +#endif //IMGUI_IMPL_WIN32_OPENGL3 + // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (void*)bd->hWnd; @@ -846,7 +860,23 @@ struct ImGui_ImplWin32_ViewportData DWORD DwStyle; DWORD DwExStyle; - ImGui_ImplWin32_ViewportData() { Hwnd = NULL; HwndOwned = false; DwStyle = DwExStyle = 0; } +#ifdef IMGUI_IMPL_WIN32_OPENGL3 + HDC Hdc; + HGLRC HgLrc; +#endif //IMGUI_IMPL_WIN32_OPENGL3 + + ImGui_ImplWin32_ViewportData() + { + Hwnd = NULL; + HwndOwned = false; + DwStyle = 0; + DwExStyle = 0; + +#ifdef IMGUI_IMPL_WIN32_OPENGL3 + Hdc = NULL; + HgLrc = NULL; +#endif //IMGUI_IMPL_WIN32_OPENGL3 + } ~ImGui_ImplWin32_ViewportData() { IM_ASSERT(Hwnd == NULL); } }; @@ -888,6 +918,27 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) vd->HwndOwned = true; viewport->PlatformRequestResize = false; viewport->PlatformHandle = viewport->PlatformHandleRaw = vd->Hwnd; + +#ifdef IMGUI_IMPL_WIN32_OPENGL3 + vd->Hdc = GetDC(vd->Hwnd); + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + + // Set pixel format to match base window + HDC Hdc = GetDC(bd->hWnd); + int pf = GetPixelFormat(Hdc); + PIXELFORMATDESCRIPTOR pfd; + DescribePixelFormat(Hdc, pf, sizeof(pfd), &pfd); + SetPixelFormat(vd->Hdc, pf, &pfd); + + // Create OpenGL context for new window + vd->HgLrc = wglCreateContext(vd->Hdc); + wglMakeCurrent(vd->Hdc, vd->HgLrc); + wglShareLists(bd->HgLrc, vd->HgLrc); + + // Present new window + ImGui_ImplWin32_SetWindowFocus(viewport); + ImGui_ImplWin32_ShowWindow(viewport); +#endif //IMGUI_IMPL_WIN32_OPENGL3 } static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport) @@ -902,7 +953,13 @@ static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport) ::SetCapture(bd->hWnd); } if (vd->Hwnd && vd->HwndOwned) + { + #ifdef IMGUI_IMPL_WIN32_OPENGL3 + wglDeleteContext(vd->HgLrc); + #endif //IMGUI_IMPL_WIN32_OPENGL3 + ::DestroyWindow(vd->Hwnd); + } vd->Hwnd = NULL; IM_DELETE(vd); } @@ -1062,6 +1119,21 @@ static void ImGui_ImplWin32_OnChangedViewport(ImGuiViewport* viewport) #endif } +#ifdef IMGUI_IMPL_WIN32_OPENGL3 +static void ImGui_ImplWin32_RenderWindow(ImGuiViewport* viewport, void*) +{ + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + wglMakeCurrent(vd->Hdc, vd->HgLrc); +} + +static void ImGui_ImplWin32_SwapBuffers(ImGuiViewport* viewport, void*) +{ + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + wglMakeCurrent(vd->Hdc, vd->HgLrc); + SwapBuffers(vd->Hdc); +} +#endif //IMGUI_IMPL_WIN32_OPENGL3 + static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) @@ -1071,6 +1143,10 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, { switch (msg) { +#ifdef IMGUI_IMPL_WIN32_OPENGL3 + case WM_ERASEBKGND: + return 0; // Prevent flickering on window invalidation +#endif //IMGUI_IMPL_WIN32_OPENGL3 case WM_CLOSE: viewport->PlatformRequestClose = true; return 0; @@ -1134,6 +1210,11 @@ static void ImGui_ImplWin32_InitPlatformInterface() platform_io.Platform_UpdateWindow = ImGui_ImplWin32_UpdateWindow; platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; // FIXME-DPI platform_io.Platform_OnChangedViewport = ImGui_ImplWin32_OnChangedViewport; // FIXME-DPI + +#ifdef IMGUI_IMPL_WIN32_OPENGL3 + platform_io.Platform_RenderWindow = ImGui_ImplWin32_RenderWindow; + platform_io.Platform_SwapBuffers = ImGui_ImplWin32_SwapBuffers; +#endif //IMGUI_IMPL_WIN32_OPENGL3 // Register main window handle (which is owned by the main application, not by us) // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. diff --git a/backends/imgui_impl_win32.h b/backends/imgui_impl_win32.h index 3778c3220191..3fa48adb2877 100644 --- a/backends/imgui_impl_win32.h +++ b/backends/imgui_impl_win32.h @@ -16,7 +16,11 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API +#ifdef IMGUI_IMPL_WIN32_OPENGL3 +IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd, void* hglrc); +#else IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); +#endif IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); From 31aedf762064f2ff4f22b2ba0e17c38c3bf2dd2d Mon Sep 17 00:00:00 2001 From: Michael Martz Date: Tue, 5 Apr 2022 22:53:46 -0700 Subject: [PATCH 2/4] Refined fix for issue #2600 ImGui_ImplWin32_Init() now optionally accepts a render context handle, and IMGUI_IMPL_WIN32_OPENGL3 is no longer a necessary define. --- backends/imgui_impl_win32.cpp | 129 +++++++++++++++++++--------------- backends/imgui_impl_win32.h | 6 +- 2 files changed, 72 insertions(+), 63 deletions(-) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 932debebf1e7..7ae32d0ce0bd 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -23,6 +23,14 @@ #include #include +// Stuff for loading WGL functions explicitly +#include // atexit() +static HMODULE libgl; +HGLRC (WINAPI* _wglCreateContext)(HDC); +BOOL (WINAPI* _wglDeleteContext)(HGLRC); +BOOL (WINAPI* _wglMakeCurrent)(HDC, HGLRC); +BOOL (WINAPI* _wglShareLists)(HGLRC, HGLRC); + // Configuration flags to add in your imconfig.h file: //#define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD // Disable gamepad support. This was meaningful before <1.81 but we now load XInput dynamically so the option is now less relevant. @@ -88,6 +96,8 @@ static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport); struct ImGui_ImplWin32_Data { HWND hWnd; + HDC Hdc; + HGLRC HgLrc; HWND MouseHwnd; bool MouseTracked; int MouseButtonsDown; @@ -98,10 +108,6 @@ struct ImGui_ImplWin32_Data bool WantUpdateHasGamepad; bool WantUpdateMonitors; -#ifdef IMGUI_IMPL_WIN32_OPENGL3 - HGLRC HgLrc; -#endif //IMGUI_IMPL_WIN32_OPENGL3 - #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD HMODULE XInputDLL; PFN_XInputGetCapabilities XInputGetCapabilities; @@ -121,11 +127,7 @@ static ImGui_ImplWin32_Data* ImGui_ImplWin32_GetBackendData() } // Functions -#ifdef IMGUI_IMPL_WIN32_OPENGL3 bool ImGui_ImplWin32_Init(void* hwnd, void* hglrc) -#else -bool ImGui_ImplWin32_Init(void* hwnd) -#endif { ImGuiIO& io = ImGui::GetIO(); IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!"); @@ -136,6 +138,25 @@ bool ImGui_ImplWin32_Init(void* hwnd) if (!::QueryPerformanceCounter((LARGE_INTEGER*)&perf_counter)) return false; + // Load WGL symbols explicitly + // This is needed to allow compilation if and when non-OpenGL APIs are used with Win32. + // The function pointer names are preceded with an underscore ("_") to prevent duplicate + // symbols in projects where OpenGL32.dll is linked implicitly. + if (hglrc) + { + libgl = LoadLibraryA("opengl32.dll"); + IM_ASSERT(libgl); + _wglCreateContext = (HGLRC(WINAPI*)(HDC))GetProcAddress(libgl, "wglCreateContext"); + _wglDeleteContext = (BOOL(WINAPI*)(HGLRC))GetProcAddress(libgl, "wglDeleteContext"); + _wglMakeCurrent = (BOOL(WINAPI*)(HDC, HGLRC))GetProcAddress(libgl, "wglMakeCurrent"); + _wglShareLists = (BOOL(WINAPI*)(HGLRC, HGLRC))GetProcAddress(libgl, "wglShareLists"); + IM_ASSERT(_wglCreateContext); + IM_ASSERT(_wglDeleteContext); + IM_ASSERT(_wglMakeCurrent); + IM_ASSERT(_wglShareLists); + atexit([](){ FreeLibrary(libgl); }); + } + // Setup backend capabilities flags ImGui_ImplWin32_Data* bd = IM_NEW(ImGui_ImplWin32_Data)(); io.BackendPlatformUserData = (void*)bd; @@ -146,16 +167,14 @@ bool ImGui_ImplWin32_Init(void* hwnd) io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional) bd->hWnd = (HWND)hwnd; + bd->Hdc = GetDC(bd->hWnd); + bd->HgLrc = (HGLRC)hglrc; // OpenGL bd->WantUpdateHasGamepad = true; bd->WantUpdateMonitors = true; bd->TicksPerSecond = perf_frequency; bd->Time = perf_counter; bd->LastMouseCursor = ImGuiMouseCursor_COUNT; -#ifdef IMGUI_IMPL_WIN32_OPENGL3 - bd->HgLrc = (HGLRC)hglrc; -#endif //IMGUI_IMPL_WIN32_OPENGL3 - // Our mouse update function expect PlatformHandle to be filled for the main viewport ImGuiViewport* main_viewport = ImGui::GetMainViewport(); main_viewport->PlatformHandle = main_viewport->PlatformHandleRaw = (void*)bd->hWnd; @@ -857,25 +876,19 @@ struct ImGui_ImplWin32_ViewportData { HWND Hwnd; bool HwndOwned; - DWORD DwStyle; - DWORD DwExStyle; - -#ifdef IMGUI_IMPL_WIN32_OPENGL3 HDC Hdc; HGLRC HgLrc; -#endif //IMGUI_IMPL_WIN32_OPENGL3 + DWORD DwStyle; + DWORD DwExStyle; ImGui_ImplWin32_ViewportData() { Hwnd = NULL; HwndOwned = false; - DwStyle = 0; - DwExStyle = 0; - -#ifdef IMGUI_IMPL_WIN32_OPENGL3 Hdc = NULL; HgLrc = NULL; -#endif //IMGUI_IMPL_WIN32_OPENGL3 + DwStyle = 0; + DwExStyle = 0; } ~ImGui_ImplWin32_ViewportData() { IM_ASSERT(Hwnd == NULL); } }; @@ -916,29 +929,28 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, // Window area parent_window, NULL, ::GetModuleHandle(NULL), NULL); // Parent window, Menu, Instance, Param vd->HwndOwned = true; + vd->Hdc = GetDC(vd->Hwnd); viewport->PlatformRequestResize = false; viewport->PlatformHandle = viewport->PlatformHandleRaw = vd->Hwnd; -#ifdef IMGUI_IMPL_WIN32_OPENGL3 - vd->Hdc = GetDC(vd->Hwnd); ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); - - // Set pixel format to match base window - HDC Hdc = GetDC(bd->hWnd); - int pf = GetPixelFormat(Hdc); - PIXELFORMATDESCRIPTOR pfd; - DescribePixelFormat(Hdc, pf, sizeof(pfd), &pfd); - SetPixelFormat(vd->Hdc, pf, &pfd); - - // Create OpenGL context for new window - vd->HgLrc = wglCreateContext(vd->Hdc); - wglMakeCurrent(vd->Hdc, vd->HgLrc); - wglShareLists(bd->HgLrc, vd->HgLrc); - - // Present new window - ImGui_ImplWin32_SetWindowFocus(viewport); - ImGui_ImplWin32_ShowWindow(viewport); -#endif //IMGUI_IMPL_WIN32_OPENGL3 + if (bd->HgLrc) // OpenGL + { + // Use the main window's pixel format + int pf = GetPixelFormat(bd->Hdc); + PIXELFORMATDESCRIPTOR pfd; + DescribePixelFormat(bd->Hdc, pf, sizeof(pfd), &pfd); + SetPixelFormat(vd->Hdc, pf, &pfd); + + // Create OpenGL context and share main context's lists with it + vd->HgLrc = _wglCreateContext(vd->Hdc); + _wglMakeCurrent(vd->Hdc, vd->HgLrc); + _wglShareLists(bd->HgLrc, vd->HgLrc); + + // Present window + ImGui_ImplWin32_SetWindowFocus(viewport); + ImGui_ImplWin32_ShowWindow(viewport); + } } static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport) @@ -954,9 +966,8 @@ static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport) } if (vd->Hwnd && vd->HwndOwned) { - #ifdef IMGUI_IMPL_WIN32_OPENGL3 - wglDeleteContext(vd->HgLrc); - #endif //IMGUI_IMPL_WIN32_OPENGL3 + if (vd->HgLrc) + _wglDeleteContext(vd->HgLrc); ::DestroyWindow(vd->Hwnd); } @@ -1119,20 +1130,20 @@ static void ImGui_ImplWin32_OnChangedViewport(ImGuiViewport* viewport) #endif } -#ifdef IMGUI_IMPL_WIN32_OPENGL3 -static void ImGui_ImplWin32_RenderWindow(ImGuiViewport* viewport, void*) +static void ImGui_ImplWin32_OGL_RenderWindow(ImGuiViewport* viewport, void*) { ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; - wglMakeCurrent(vd->Hdc, vd->HgLrc); + IM_ASSERT(vd->HgLrc); // ImGui_ImplWin32_OGL_RenderWindow() is only registered if API is OpenGL + _wglMakeCurrent(vd->Hdc, vd->HgLrc); } -static void ImGui_ImplWin32_SwapBuffers(ImGuiViewport* viewport, void*) +static void ImGui_ImplWin32_OGL_SwapBuffers(ImGuiViewport* viewport, void*) { ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; - wglMakeCurrent(vd->Hdc, vd->HgLrc); + IM_ASSERT(vd->HgLrc); // ImGui_ImplWin32_OGL_SwapBuffers() is only registered if API is OpenGL + _wglMakeCurrent(vd->Hdc, vd->HgLrc); SwapBuffers(vd->Hdc); } -#endif //IMGUI_IMPL_WIN32_OPENGL3 static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { @@ -1143,10 +1154,9 @@ static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, { switch (msg) { -#ifdef IMGUI_IMPL_WIN32_OPENGL3 case WM_ERASEBKGND: - return 0; // Prevent flickering on window invalidation -#endif //IMGUI_IMPL_WIN32_OPENGL3 + // Prevent flickering on window invalidation + return TRUE; case WM_CLOSE: viewport->PlatformRequestClose = true; return 0; @@ -1211,18 +1221,21 @@ static void ImGui_ImplWin32_InitPlatformInterface() platform_io.Platform_GetWindowDpiScale = ImGui_ImplWin32_GetWindowDpiScale; // FIXME-DPI platform_io.Platform_OnChangedViewport = ImGui_ImplWin32_OnChangedViewport; // FIXME-DPI -#ifdef IMGUI_IMPL_WIN32_OPENGL3 - platform_io.Platform_RenderWindow = ImGui_ImplWin32_RenderWindow; - platform_io.Platform_SwapBuffers = ImGui_ImplWin32_SwapBuffers; -#endif //IMGUI_IMPL_WIN32_OPENGL3 + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + if (bd->HgLrc) // OpenGL + { + platform_io.Platform_RenderWindow = ImGui_ImplWin32_OGL_RenderWindow; + platform_io.Platform_SwapBuffers = ImGui_ImplWin32_OGL_SwapBuffers; + } // Register main window handle (which is owned by the main application, not by us) // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports. ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); ImGui_ImplWin32_ViewportData* vd = IM_NEW(ImGui_ImplWin32_ViewportData)(); vd->Hwnd = bd->hWnd; vd->HwndOwned = false; + vd->Hdc = bd->Hdc; + vd->HgLrc = bd->HgLrc; // OpenGL main_viewport->PlatformUserData = vd; main_viewport->PlatformHandle = (void*)bd->hWnd; } diff --git a/backends/imgui_impl_win32.h b/backends/imgui_impl_win32.h index 3fa48adb2877..9c79bb072b39 100644 --- a/backends/imgui_impl_win32.h +++ b/backends/imgui_impl_win32.h @@ -16,11 +16,7 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API -#ifdef IMGUI_IMPL_WIN32_OPENGL3 -IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd, void* hglrc); -#else -IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); -#endif +IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd, void* hglrc = NULL); IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); From 1c9488d748eb7092036503bed470eb56d02e9a30 Mon Sep 17 00:00:00 2001 From: Michael Martz Date: Wed, 13 Apr 2022 12:39:44 -0700 Subject: [PATCH 3/4] Resolved multiple issues with fix#2600 Device Contexts are no longer maintained for the application lifetime. ::GetDC() is now never called when using DirectX backend. Removed lambda callback and brought WGL function loading into conformance with XInput DLL loading. --- backends/imgui_impl_opengl3.cpp | 8 ++ backends/imgui_impl_win32.cpp | 159 ++++++++++++++++++++------------ backends/imgui_impl_win32.h | 3 +- imgui.h | 2 + 4 files changed, 113 insertions(+), 59 deletions(-) diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 4633e24d92d2..d426367ade0a 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -825,6 +825,11 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects() static void ImGui_ImplOpenGL3_RenderWindow(ImGuiViewport* viewport, void*) { + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + + if (platform_io.Platform_PushOglContext) + platform_io.Platform_PushOglContext(viewport); + if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) { ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); @@ -832,6 +837,9 @@ static void ImGui_ImplOpenGL3_RenderWindow(ImGuiViewport* viewport, void*) glClear(GL_COLOR_BUFFER_BIT); } ImGui_ImplOpenGL3_RenderDrawData(viewport->DrawData); + + if (platform_io.Platform_PopOglContext) + platform_io.Platform_PopOglContext(viewport); } static void ImGui_ImplOpenGL3_InitPlatformInterface() diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 7ae32d0ce0bd..4388f5144d54 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -24,12 +24,12 @@ #include // Stuff for loading WGL functions explicitly -#include // atexit() -static HMODULE libgl; -HGLRC (WINAPI* _wglCreateContext)(HDC); -BOOL (WINAPI* _wglDeleteContext)(HGLRC); -BOOL (WINAPI* _wglMakeCurrent)(HDC, HGLRC); -BOOL (WINAPI* _wglShareLists)(HGLRC, HGLRC); +static HGLRC (WINAPI* _wglCreateContext)(HDC); +static BOOL (WINAPI* _wglDeleteContext)(HGLRC); +static BOOL (WINAPI* _wglMakeCurrent)(HDC, HGLRC); +static BOOL (WINAPI* _wglShareLists)(HGLRC, HGLRC); +static HGLRC (WINAPI* _wglGetCurrentContext)(VOID); +static HDC (WINAPI* _wglGetCurrentDC)(VOID); // Configuration flags to add in your imconfig.h file: //#define IMGUI_IMPL_WIN32_DISABLE_GAMEPAD // Disable gamepad support. This was meaningful before <1.81 but we now load XInput dynamically so the option is now less relevant. @@ -96,8 +96,7 @@ static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport); struct ImGui_ImplWin32_Data { HWND hWnd; - HDC Hdc; - HGLRC HgLrc; + HGLRC OpenGlContext; HWND MouseHwnd; bool MouseTracked; int MouseButtonsDown; @@ -108,6 +107,11 @@ struct ImGui_ImplWin32_Data bool WantUpdateHasGamepad; bool WantUpdateMonitors; + // OpenGL + HMODULE OglDLL; + HDC stashedDeviceContext; + HGLRC stashedOpenGlContext; + #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD HMODULE XInputDLL; PFN_XInputGetCapabilities XInputGetCapabilities; @@ -127,7 +131,12 @@ static ImGui_ImplWin32_Data* ImGui_ImplWin32_GetBackendData() } // Functions -bool ImGui_ImplWin32_Init(void* hwnd, void* hglrc) +bool ImGui_ImplWin32_Init(void* hwnd) +{ + return ImGui_ImplWin32_InitForOpenGL(hwnd, NULL); +} + +bool ImGui_ImplWin32_InitForOpenGL(void* hwnd, void* hglrc) { ImGuiIO& io = ImGui::GetIO(); IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!"); @@ -138,25 +147,6 @@ bool ImGui_ImplWin32_Init(void* hwnd, void* hglrc) if (!::QueryPerformanceCounter((LARGE_INTEGER*)&perf_counter)) return false; - // Load WGL symbols explicitly - // This is needed to allow compilation if and when non-OpenGL APIs are used with Win32. - // The function pointer names are preceded with an underscore ("_") to prevent duplicate - // symbols in projects where OpenGL32.dll is linked implicitly. - if (hglrc) - { - libgl = LoadLibraryA("opengl32.dll"); - IM_ASSERT(libgl); - _wglCreateContext = (HGLRC(WINAPI*)(HDC))GetProcAddress(libgl, "wglCreateContext"); - _wglDeleteContext = (BOOL(WINAPI*)(HGLRC))GetProcAddress(libgl, "wglDeleteContext"); - _wglMakeCurrent = (BOOL(WINAPI*)(HDC, HGLRC))GetProcAddress(libgl, "wglMakeCurrent"); - _wglShareLists = (BOOL(WINAPI*)(HGLRC, HGLRC))GetProcAddress(libgl, "wglShareLists"); - IM_ASSERT(_wglCreateContext); - IM_ASSERT(_wglDeleteContext); - IM_ASSERT(_wglMakeCurrent); - IM_ASSERT(_wglShareLists); - atexit([](){ FreeLibrary(libgl); }); - } - // Setup backend capabilities flags ImGui_ImplWin32_Data* bd = IM_NEW(ImGui_ImplWin32_Data)(); io.BackendPlatformUserData = (void*)bd; @@ -167,8 +157,7 @@ bool ImGui_ImplWin32_Init(void* hwnd, void* hglrc) io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can call io.AddMouseViewportEvent() with correct data (optional) bd->hWnd = (HWND)hwnd; - bd->Hdc = GetDC(bd->hWnd); - bd->HgLrc = (HGLRC)hglrc; // OpenGL + bd->OpenGlContext = (HGLRC)hglrc; bd->WantUpdateHasGamepad = true; bd->WantUpdateMonitors = true; bd->TicksPerSecond = perf_frequency; @@ -181,6 +170,28 @@ bool ImGui_ImplWin32_Init(void* hwnd, void* hglrc) if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) ImGui_ImplWin32_InitPlatformInterface(); + // Dynamically load WGL symbols + // This is needed to allow compilation if and when non-OpenGL APIs are used with Win32. + // The function pointer names are preceded with an underscore ("_") to prevent duplicate + // symbols in projects where OpenGL32.dll is linked implicitly. + if (hglrc && !(bd->OglDLL)) + { + bd->OglDLL = ::LoadLibraryA("opengl32.dll"); + IM_ASSERT(bd->OglDLL); + _wglCreateContext = (HGLRC(WINAPI*)(HDC))GetProcAddress(bd->OglDLL, "wglCreateContext"); + _wglDeleteContext = (BOOL(WINAPI*)(HGLRC))GetProcAddress(bd->OglDLL, "wglDeleteContext"); + _wglMakeCurrent = (BOOL(WINAPI*)(HDC, HGLRC))GetProcAddress(bd->OglDLL, "wglMakeCurrent"); + _wglShareLists = (BOOL(WINAPI*)(HGLRC, HGLRC))GetProcAddress(bd->OglDLL, "wglShareLists"); + _wglGetCurrentContext = (HGLRC(WINAPI*)(VOID))GetProcAddress(bd->OglDLL, "wglGetCurrentContext"); + _wglGetCurrentDC = (HDC(WINAPI*)(VOID))GetProcAddress(bd->OglDLL, "wglGetCurrentDC"); + IM_ASSERT(_wglCreateContext); + IM_ASSERT(_wglDeleteContext); + IM_ASSERT(_wglMakeCurrent); + IM_ASSERT(_wglShareLists); + IM_ASSERT(_wglGetCurrentContext); + IM_ASSERT(_wglGetCurrentDC); + } + // Dynamically load XInput library #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD const char* xinput_dll_names[] = @@ -212,6 +223,10 @@ void ImGui_ImplWin32_Shutdown() ImGui_ImplWin32_ShutdownPlatformInterface(); + // Unload OpenGL library + if (bd->OglDLL) + ::FreeLibrary(bd->OglDLL); + // Unload XInput library #ifndef IMGUI_IMPL_WIN32_DISABLE_GAMEPAD if (bd->XInputDLL) @@ -876,8 +891,7 @@ struct ImGui_ImplWin32_ViewportData { HWND Hwnd; bool HwndOwned; - HDC Hdc; - HGLRC HgLrc; + HGLRC OpenGlContext; DWORD DwStyle; DWORD DwExStyle; @@ -885,8 +899,7 @@ struct ImGui_ImplWin32_ViewportData { Hwnd = NULL; HwndOwned = false; - Hdc = NULL; - HgLrc = NULL; + OpenGlContext = NULL; DwStyle = 0; DwExStyle = 0; } @@ -929,27 +942,39 @@ static void ImGui_ImplWin32_CreateWindow(ImGuiViewport* viewport) rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, // Window area parent_window, NULL, ::GetModuleHandle(NULL), NULL); // Parent window, Menu, Instance, Param vd->HwndOwned = true; - vd->Hdc = GetDC(vd->Hwnd); viewport->PlatformRequestResize = false; viewport->PlatformHandle = viewport->PlatformHandleRaw = vd->Hwnd; ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); - if (bd->HgLrc) // OpenGL + if (bd->OpenGlContext) { + HDC previousDC = _wglGetCurrentDC(); + HGLRC previousRC = _wglGetCurrentContext(); + HDC newDC = ::GetDC(vd->Hwnd); + // Use the main window's pixel format - int pf = GetPixelFormat(bd->Hdc); + int pf; PIXELFORMATDESCRIPTOR pfd; - DescribePixelFormat(bd->Hdc, pf, sizeof(pfd), &pfd); - SetPixelFormat(vd->Hdc, pf, &pfd); + if (previousDC) + { + pf = ::GetPixelFormat(previousDC); + ::DescribePixelFormat(previousDC, pf, sizeof(pfd), &pfd); + } + else + { + HDC mainDC = ::GetDC(bd->hWnd); + pf = ::GetPixelFormat(mainDC); + ::DescribePixelFormat(mainDC, pf, sizeof(pfd), &pfd); + ::ReleaseDC(bd->hWnd, mainDC); + } + ::SetPixelFormat(newDC, pf, &pfd); // Create OpenGL context and share main context's lists with it - vd->HgLrc = _wglCreateContext(vd->Hdc); - _wglMakeCurrent(vd->Hdc, vd->HgLrc); - _wglShareLists(bd->HgLrc, vd->HgLrc); - - // Present window - ImGui_ImplWin32_SetWindowFocus(viewport); - ImGui_ImplWin32_ShowWindow(viewport); + vd->OpenGlContext = _wglCreateContext(newDC); + _wglMakeCurrent(newDC, vd->OpenGlContext); + _wglShareLists(bd->OpenGlContext, vd->OpenGlContext); + _wglMakeCurrent(previousDC, previousRC); + ::ReleaseDC(vd->Hwnd, newDC); } } @@ -966,8 +991,8 @@ static void ImGui_ImplWin32_DestroyWindow(ImGuiViewport* viewport) } if (vd->Hwnd && vd->HwndOwned) { - if (vd->HgLrc) - _wglDeleteContext(vd->HgLrc); + if (vd->OpenGlContext) + _wglDeleteContext(vd->OpenGlContext); ::DestroyWindow(vd->Hwnd); } @@ -1130,19 +1155,37 @@ static void ImGui_ImplWin32_OnChangedViewport(ImGuiViewport* viewport) #endif } -static void ImGui_ImplWin32_OGL_RenderWindow(ImGuiViewport* viewport, void*) +static void ImGui_ImplWin32_PushOglContext(ImGuiViewport* viewport) +{ + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; + IM_ASSERT(vd->OpenGlContext); // ImGui_ImplWin32_PushOglContext() is only registered if API is OpenGL + + bd->stashedDeviceContext = _wglGetCurrentDC(); + bd->stashedOpenGlContext = _wglGetCurrentContext(); + _wglMakeCurrent(::GetDC(vd->Hwnd), vd->OpenGlContext); +} + +static void ImGui_ImplWin32_PopOglContext(ImGuiViewport* viewport) { + ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; - IM_ASSERT(vd->HgLrc); // ImGui_ImplWin32_OGL_RenderWindow() is only registered if API is OpenGL - _wglMakeCurrent(vd->Hdc, vd->HgLrc); + IM_ASSERT(vd->OpenGlContext); // ImGui_ImplWin32_PopOglContext() is only registered if API is OpenGL + + IM_ASSERT(_wglGetCurrentContext() == vd->OpenGlContext); + HDC vd_dc = _wglGetCurrentDC(); + _wglMakeCurrent(bd->stashedDeviceContext, bd->stashedOpenGlContext); + ::ReleaseDC(vd->Hwnd, vd_dc); } static void ImGui_ImplWin32_OGL_SwapBuffers(ImGuiViewport* viewport, void*) { ImGui_ImplWin32_ViewportData* vd = (ImGui_ImplWin32_ViewportData*)viewport->PlatformUserData; - IM_ASSERT(vd->HgLrc); // ImGui_ImplWin32_OGL_SwapBuffers() is only registered if API is OpenGL - _wglMakeCurrent(vd->Hdc, vd->HgLrc); - SwapBuffers(vd->Hdc); + IM_ASSERT(vd->OpenGlContext); // ImGui_ImplWin32_OGL_SwapBuffers() is only registered if API is OpenGL + + ImGui_ImplWin32_PushOglContext(viewport); + ::SwapBuffers(_wglGetCurrentDC()); + ImGui_ImplWin32_PopOglContext(viewport); } static LRESULT CALLBACK ImGui_ImplWin32_WndProcHandler_PlatformWindow(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) @@ -1222,9 +1265,10 @@ static void ImGui_ImplWin32_InitPlatformInterface() platform_io.Platform_OnChangedViewport = ImGui_ImplWin32_OnChangedViewport; // FIXME-DPI ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); - if (bd->HgLrc) // OpenGL + if (bd->OpenGlContext) // OpenGL { - platform_io.Platform_RenderWindow = ImGui_ImplWin32_OGL_RenderWindow; + platform_io.Platform_PushOglContext = ImGui_ImplWin32_PushOglContext; + platform_io.Platform_PopOglContext = ImGui_ImplWin32_PopOglContext; platform_io.Platform_SwapBuffers = ImGui_ImplWin32_OGL_SwapBuffers; } @@ -1234,8 +1278,7 @@ static void ImGui_ImplWin32_InitPlatformInterface() ImGui_ImplWin32_ViewportData* vd = IM_NEW(ImGui_ImplWin32_ViewportData)(); vd->Hwnd = bd->hWnd; vd->HwndOwned = false; - vd->Hdc = bd->Hdc; - vd->HgLrc = bd->HgLrc; // OpenGL + vd->OpenGlContext = bd->OpenGlContext; main_viewport->PlatformUserData = vd; main_viewport->PlatformHandle = (void*)bd->hWnd; } diff --git a/backends/imgui_impl_win32.h b/backends/imgui_impl_win32.h index 9c79bb072b39..05409853b9e7 100644 --- a/backends/imgui_impl_win32.h +++ b/backends/imgui_impl_win32.h @@ -16,7 +16,8 @@ #pragma once #include "imgui.h" // IMGUI_IMPL_API -IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd, void* hglrc = NULL); +IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); +IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd, void* hglrc); IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); diff --git a/imgui.h b/imgui.h index 184ca324ddaf..168a8d95f97e 100644 --- a/imgui.h +++ b/imgui.h @@ -3154,6 +3154,8 @@ struct ImGuiPlatformIO float (*Platform_GetWindowDpiScale)(ImGuiViewport* vp); // N . . . . // (Optional) [BETA] FIXME-DPI: DPI handling: Return DPI scale for this viewport. 1.0f = 96 DPI. void (*Platform_OnChangedViewport)(ImGuiViewport* vp); // . F . . . // (Optional) [BETA] FIXME-DPI: DPI handling: Called during Begin() every time the viewport we are outputting into changes, so backend has a chance to swap fonts to adjust style. int (*Platform_CreateVkSurface)(ImGuiViewport* vp, ImU64 vk_inst, const void* vk_allocators, ImU64* out_vk_surface); // (Optional) For a Vulkan Renderer to call into Platform code (since the surface creation needs to tie them both). + void (*Platform_PushOglContext)(ImGuiViewport* vp); // . . . R . // (Optional) For an OpenGL Renderer to make a platform window's rendering context current for rendering + void (*Platform_PopOglContext)(ImGuiViewport* vp); // . . . R . // (Optional) For an OpenGL Renderer to revert to the previous rendering context after rendering a platform window // (Optional) Renderer functions (e.g. DirectX, OpenGL, Vulkan) void (*Renderer_CreateWindow)(ImGuiViewport* vp); // . . U . . // Create swap chain, frame buffers etc. (called after Platform_CreateWindow) From 113abc056e4c5091e408641a360917b4e5606302 Mon Sep 17 00:00:00 2001 From: Michael Martz Date: Wed, 13 Apr 2022 18:52:03 -0700 Subject: [PATCH 4/4] Removed unused forward declarations in Win32 impl. --- backends/imgui_impl_win32.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 4388f5144d54..5865b94d3dca 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -90,8 +90,6 @@ typedef DWORD (WINAPI *PFN_XInputGetState)(DWORD, XINPUT_STATE*); static void ImGui_ImplWin32_InitPlatformInterface(); static void ImGui_ImplWin32_ShutdownPlatformInterface(); static void ImGui_ImplWin32_UpdateMonitors(); -static void ImGui_ImplWin32_SetWindowFocus(ImGuiViewport* viewport); -static void ImGui_ImplWin32_ShowWindow(ImGuiViewport* viewport); struct ImGui_ImplWin32_Data {