From 0a6c5bc2347a27e4922281353ee6cb56cd66fe28 Mon Sep 17 00:00:00 2001 From: Gilad Reich Date: Wed, 6 Mar 2019 21:34:37 +0100 Subject: [PATCH] Examples: DirectX9: Added support for multi-viewport (#2394) --- examples/example_win32_directx9/main.cpp | 31 ++++++ examples/imgui_impl_dx9.cpp | 123 ++++++++++++++++++++++- examples/imgui_impl_dx9.h | 1 + 3 files changed, 154 insertions(+), 1 deletion(-) diff --git a/examples/example_win32_directx9/main.cpp b/examples/example_win32_directx9/main.cpp index 6b5e731e..9833ca93 100644 --- a/examples/example_win32_directx9/main.cpp +++ b/examples/example_win32_directx9/main.cpp @@ -49,6 +49,10 @@ void ResetDevice() ImGui_ImplDX9_CreateDeviceObjects(); } +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 // From Windows SDK 8.1+ headers +#endif + extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { @@ -72,12 +76,23 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) case WM_DESTROY: PostQuitMessage(0); return 0; + case WM_DPICHANGED: + if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) + { + //const int dpi = HIWORD(wParam); + //printf("WM_DPICHANGED to %d (%.0f%%)\n", dpi, (float)dpi / 96.0f * 100.0f); + const RECT* suggested_rect = (RECT*)lParam; + ::SetWindowPos(hWnd, NULL, suggested_rect->left, suggested_rect->top, suggested_rect->right - suggested_rect->left, suggested_rect->bottom - suggested_rect->top, SWP_NOZORDER | SWP_NOACTIVATE); + } + break; } return DefWindowProc(hWnd, msg, wParam, lParam); } int main(int, char**) { + ImGui_ImplWin32_EnableDpiAwareness(); + // Create application window WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, _T("ImGui Example"), NULL }; RegisterClassEx(&wc); @@ -109,6 +124,14 @@ int main(int, char**) ImGui::StyleColorsDark(); //ImGui::StyleColorsClassic(); + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + // Setup Platform/Renderer bindings ImGui_ImplWin32_Init(hwnd); ImGui_ImplDX9_Init(g_pd3dDevice); @@ -205,6 +228,14 @@ int main(int, char**) ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData()); g_pd3dDevice->EndScene(); } + + // Update and Render additional Platform Windows + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + } + HRESULT result = g_pd3dDevice->Present(NULL, NULL, NULL, NULL); // Handle loss of D3D9 device diff --git a/examples/imgui_impl_dx9.cpp b/examples/imgui_impl_dx9.cpp index 378524e0..e4f46850 100644 --- a/examples/imgui_impl_dx9.cpp +++ b/examples/imgui_impl_dx9.cpp @@ -3,6 +3,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp. @@ -10,6 +11,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2018-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. // 2019-01-16: Misc: Disabled fog before drawing UI's. Fixes issue #2288. // 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. // 2018-06-08: Misc: Extracted imgui_impl_dx9.cpp/.h away from the old combined DX9+Win32 example. @@ -41,6 +43,10 @@ struct CUSTOMVERTEX }; #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1) +// Forward Declarations +static void ImGui_ImplDX9_InitPlatformInterface(); +static void ImGui_ImplDX9_ShutdownPlatformInterface(); + // Render function. // (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) @@ -203,16 +209,22 @@ void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) bool ImGui_ImplDX9_Init(IDirect3DDevice9* device) { ImGuiIO& io = ImGui::GetIO(); + io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional) io.BackendRendererName = "imgui_impl_dx9"; g_pd3dDevice = device; + + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + ImGui_ImplDX9_InitPlatformInterface(); + return true; } void ImGui_ImplDX9_Shutdown() { + ImGui_ImplDX9_ShutdownPlatformInterface(); ImGui_ImplDX9_InvalidateDeviceObjects(); - g_pd3dDevice = NULL; + if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; } } static bool ImGui_ImplDX9_CreateFontsTexture() @@ -278,3 +290,112 @@ void ImGui_ImplDX9_NewFrame() if (!g_FontTexture) ImGui_ImplDX9_CreateDeviceObjects(); } + +//-------------------------------------------------------------------------------------------------------- +// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT +// This is an _advanced_ and _optional_ feature, allowing the back-end 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.. +//-------------------------------------------------------------------------------------------------------- + +struct ImGuiViewportDataDx9 +{ + IDirect3DSwapChain9* SwapChain; + D3DPRESENT_PARAMETERS d3dpp; + + ImGuiViewportDataDx9() { SwapChain = NULL; ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS)); } + ~ImGuiViewportDataDx9() { IM_ASSERT(SwapChain == NULL); } +}; + +static void ImGui_ImplDX9_CreateWindow(ImGuiViewport* viewport) +{ + ImGuiViewportDataDx9* data = IM_NEW(ImGuiViewportDataDx9)(); + viewport->RendererUserData = data; + + HWND hWnd = (HWND)viewport->PlatformHandle; + IM_ASSERT(hWnd != 0); + + ZeroMemory(&data->d3dpp, sizeof(D3DPRESENT_PARAMETERS)); + data->d3dpp.Windowed = TRUE; + data->d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + data->d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; + data->d3dpp.hDeviceWindow = hWnd; + data->d3dpp.EnableAutoDepthStencil = TRUE; + data->d3dpp.AutoDepthStencilFormat = D3DFMT_D16; + data->d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // Present without vsync + + g_pd3dDevice->CreateAdditionalSwapChain(&data->d3dpp, &data->SwapChain); + IM_ASSERT(data->SwapChain != NULL); +} + +static void ImGui_ImplDX9_DestroyWindow(ImGuiViewport* viewport) +{ + // The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it. + if (ImGuiViewportDataDx9* data = (ImGuiViewportDataDx9*)viewport->RendererUserData) + { + if (data->SwapChain) + data->SwapChain->Release(); + data->SwapChain = NULL; + ZeroMemory(&data->d3dpp, sizeof(D3DPRESENT_PARAMETERS)); + IM_DELETE(data); + } + viewport->RendererUserData = NULL; +} + +static void ImGui_ImplDX9_SetWindowSize(ImGuiViewport* viewport, ImVec2 size) +{ + ImGuiViewportDataDx9* data = (ImGuiViewportDataDx9*)viewport->RendererUserData; + if (data->SwapChain) + { + data->SwapChain->Release(); + data->SwapChain = NULL; + data->d3dpp.BackBufferWidth = (UINT)size.x; + data->d3dpp.BackBufferHeight = (UINT)size.y; + g_pd3dDevice->CreateAdditionalSwapChain(&data->d3dpp, &data->SwapChain); + } +} + +static void ImGui_ImplDX9_RenderWindow(ImGuiViewport* viewport, void*) +{ + ImGuiViewportDataDx9* data = (ImGuiViewportDataDx9*)viewport->RendererUserData; + ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); + + LPDIRECT3DSURFACE9 render_target = NULL; + LPDIRECT3DSURFACE9 last_render_target = NULL; + data->SwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &render_target); + g_pd3dDevice->GetRenderTarget(0, &last_render_target); + g_pd3dDevice->SetRenderTarget(0, render_target); + + if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear)) + { + D3DCOLOR clear_col_dx = D3DCOLOR_RGBA((int)(clear_color.x*255.0f), (int)(clear_color.y*255.0f), (int)(clear_color.z*255.0f), (int)(clear_color.w*255.0f)); + g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, clear_col_dx, 1.0f, 0); + } + + ImGui_ImplDX9_RenderDrawData(viewport->DrawData); + + // Restore render target + g_pd3dDevice->SetRenderTarget(0, last_render_target); + render_target->Release(); + last_render_target->Release(); +} + +static void ImGui_ImplDX9_SwapBuffers(ImGuiViewport* viewport, void*) +{ + ImGuiViewportDataDx9* data = (ImGuiViewportDataDx9*)viewport->RendererUserData; + data->SwapChain->Present(NULL, NULL, data->d3dpp.hDeviceWindow, NULL, NULL); +} + +static void ImGui_ImplDX9_InitPlatformInterface() +{ + ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); + platform_io.Renderer_CreateWindow = ImGui_ImplDX9_CreateWindow; + platform_io.Renderer_DestroyWindow = ImGui_ImplDX9_DestroyWindow; + platform_io.Renderer_SetWindowSize = ImGui_ImplDX9_SetWindowSize; + platform_io.Renderer_RenderWindow = ImGui_ImplDX9_RenderWindow; + platform_io.Renderer_SwapBuffers = ImGui_ImplDX9_SwapBuffers; +} + +static void ImGui_ImplDX9_ShutdownPlatformInterface() +{ + ImGui::DestroyPlatformWindows(); +} diff --git a/examples/imgui_impl_dx9.h b/examples/imgui_impl_dx9.h index 95902f74..86953684 100644 --- a/examples/imgui_impl_dx9.h +++ b/examples/imgui_impl_dx9.h @@ -3,6 +3,7 @@ // Implemented features: // [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. +// [X] Renderer: Multi-viewport support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'. // You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. // If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.