diff --git a/backends/imgui_impl_dx12.cpp b/backends/imgui_impl_dx12.cpp index 9d6b1263f..07cf3f562 100644 --- a/backends/imgui_impl_dx12.cpp +++ b/backends/imgui_impl_dx12.cpp @@ -101,9 +101,15 @@ struct ImGui_ImplDX12_Data DXGI_FORMAT RTVFormat; DXGI_FORMAT DSVFormat; ID3D12DescriptorHeap* pd3dSrvDescHeap; + ID3D12Fence* Fence; + UINT64 FenceLastSignaledValue; + HANDLE FenceEvent; UINT numFramesInFlight; bool tearingSupport; + ID3D12CommandAllocator* pTexCmdAllocator; + ID3D12GraphicsCommandList* pTexCmdList; + ImGui_ImplDX12_RenderBuffers* pFrameResources; UINT frameIndex; @@ -131,6 +137,7 @@ struct ImGui_ImplDX12_RenderBuffers // Buffers used for secondary viewports created by the multi-viewports systems struct ImGui_ImplDX12_FrameContext { + UINT64 FenceValue; ID3D12CommandAllocator* CommandAllocator; ID3D12Resource* RenderTarget; D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetCpuDescriptors; @@ -146,9 +153,7 @@ struct ImGui_ImplDX12_ViewportData ID3D12GraphicsCommandList* CommandList; ID3D12DescriptorHeap* RtvDescHeap; IDXGISwapChain3* SwapChain; - ID3D12Fence* Fence; - UINT64 FenceSignaledValue; - HANDLE FenceEvent; + HANDLE SwapChainWaitableObject; UINT NumFramesInFlight; ImGui_ImplDX12_FrameContext* FrameCtx; @@ -162,16 +167,15 @@ struct ImGui_ImplDX12_ViewportData CommandList = nullptr; RtvDescHeap = nullptr; SwapChain = nullptr; - Fence = nullptr; - FenceSignaledValue = 0; - FenceEvent = nullptr; + SwapChainWaitableObject = nullptr; NumFramesInFlight = num_frames_in_flight; FrameCtx = new ImGui_ImplDX12_FrameContext[NumFramesInFlight]; - FrameIndex = UINT_MAX; + FrameIndex = 0; FrameRenderBuffers = new ImGui_ImplDX12_RenderBuffers[NumFramesInFlight]; for (UINT i = 0; i < NumFramesInFlight; ++i) { + FrameCtx[i].FenceValue = 0; FrameCtx[i].CommandAllocator = nullptr; FrameCtx[i].RenderTarget = nullptr; @@ -187,8 +191,7 @@ struct ImGui_ImplDX12_ViewportData IM_ASSERT(CommandQueue == nullptr && CommandList == nullptr); IM_ASSERT(RtvDescHeap == nullptr); IM_ASSERT(SwapChain == nullptr); - IM_ASSERT(Fence == nullptr); - IM_ASSERT(FenceEvent == nullptr); + IM_ASSERT(SwapChainWaitableObject == nullptr); for (UINT i = 0; i < NumFramesInFlight; ++i) { @@ -533,29 +536,15 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex) props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - // FIXME-OPT: Can upload buffer be reused? + // FIXME-OPT: Could upload buffer be kept around, reused, and grown only when needed? Would that be worth it? ID3D12Resource* uploadBuffer = nullptr; HRESULT hr = bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uploadBuffer)); IM_ASSERT(SUCCEEDED(hr)); - // Create temporary command list and execute immediately - ID3D12Fence* fence = nullptr; - hr = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)); - IM_ASSERT(SUCCEEDED(hr)); - - HANDLE event = ::CreateEvent(0, 0, 0, 0); - IM_ASSERT(event != nullptr); - - // FIXME-OPT: Create once and reuse? - ID3D12CommandAllocator* cmdAlloc = nullptr; - hr = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc)); - IM_ASSERT(SUCCEEDED(hr)); - - // FIXME-OPT: Can be use the one from user? (pass ID3D12GraphicsCommandList* to ImGui_ImplDX12_UpdateTextures) - ID3D12GraphicsCommandList* cmdList = nullptr; - hr = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, nullptr, IID_PPV_ARGS(&cmdList)); - IM_ASSERT(SUCCEEDED(hr)); + bd->pTexCmdAllocator->Reset(); + bd->pTexCmdList->Reset(bd->pTexCmdAllocator, nullptr); + ID3D12GraphicsCommandList* cmdList = bd->pTexCmdList; // Copy to upload buffer void* mapped = nullptr; @@ -610,20 +599,16 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex) ID3D12CommandQueue* cmdQueue = bd->pCommandQueue; cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmdList); - hr = cmdQueue->Signal(fence, 1); + hr = cmdQueue->Signal(bd->Fence, ++bd->FenceLastSignaledValue); IM_ASSERT(SUCCEEDED(hr)); // FIXME-OPT: Suboptimal? // - To remove this may need to create NumFramesInFlight x ImGui_ImplDX12_FrameContext in backend data (mimick docking version) // - Store per-frame in flight: upload buffer? // - Where do cmdList and cmdAlloc fit? - fence->SetEventOnCompletion(1, event); - ::WaitForSingleObject(event, INFINITE); + bd->Fence->SetEventOnCompletion(bd->FenceLastSignaledValue, bd->FenceEvent); + ::WaitForSingleObject(bd->FenceEvent, INFINITE); - cmdList->Release(); - cmdAlloc->Release(); - ::CloseHandle(event); - fence->Release(); uploadBuffer->Release(); tex->SetStatus(ImTextureStatus_OK); } @@ -861,6 +846,20 @@ bool ImGui_ImplDX12_CreateDeviceObjects() if (result_pipeline_state != S_OK) return false; + // Create command allocator and command list for ImGui_ImplDX12_UpdateTexture() + HRESULT hr = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&bd->pTexCmdAllocator)); + IM_ASSERT(SUCCEEDED(hr)); + hr = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, bd->pTexCmdAllocator, nullptr, IID_PPV_ARGS(&bd->pTexCmdList)); + IM_ASSERT(SUCCEEDED(hr)); + hr = bd->pTexCmdList->Close(); + IM_ASSERT(SUCCEEDED(hr)); + + // Create fence. + hr = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&bd->Fence)); + IM_ASSERT(hr == S_OK); + bd->FenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); + IM_ASSERT(bd->FenceEvent != nullptr); + return true; } @@ -882,6 +881,11 @@ void ImGui_ImplDX12_InvalidateDeviceObjects() bd->commandQueueOwned = false; SafeRelease(bd->pRootSignature); SafeRelease(bd->pPipelineState); + SafeRelease(bd->pTexCmdList); + SafeRelease(bd->pTexCmdAllocator); + SafeRelease(bd->Fence); + CloseHandle(bd->FenceEvent); + bd->FenceEvent = nullptr; // Destroy all textures for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) @@ -1039,18 +1043,12 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle; IM_ASSERT(hwnd != 0); - vd->FrameIndex = UINT_MAX; - - // Create command queue. - D3D12_COMMAND_QUEUE_DESC queue_desc = {}; - queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; - queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; - - HRESULT res = S_OK; - res = bd->pd3dDevice->CreateCommandQueue(&queue_desc, IID_PPV_ARGS(&vd->CommandQueue)); - IM_ASSERT(res == S_OK); + // Use shared command queue from init info + vd->FrameIndex = 0; + vd->CommandQueue = bd->pCommandQueue; // Create command allocator. + HRESULT res = S_OK; for (UINT i = 0; i < bd->numFramesInFlight; ++i) { res = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&vd->FrameCtx[i].CommandAllocator)); @@ -1062,13 +1060,6 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) IM_ASSERT(res == S_OK); vd->CommandList->Close(); - // Create fence. - res = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&vd->Fence)); - IM_ASSERT(res == S_OK); - - vd->FenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); - IM_ASSERT(vd->FenceEvent != nullptr); - // Create swap chain // FIXME-VIEWPORT: May want to copy/inherit swap chain settings from the user/application. DXGI_SWAP_CHAIN_DESC1 sd1; @@ -1084,6 +1075,7 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) sd1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; sd1.Scaling = DXGI_SCALING_NONE; sd1.Stereo = FALSE; + sd1.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; IDXGIFactory5* dxgi_factory = nullptr; res = ::CreateDXGIFactory1(IID_PPV_ARGS(&dxgi_factory)); @@ -1108,7 +1100,7 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) swap_chain->QueryInterface(IID_PPV_ARGS(&vd->SwapChain)); swap_chain->Release(); - // Create the render targets + // Create the render targets and waitable object if (vd->SwapChain) { D3D12_DESCRIPTOR_HEAP_DESC desc = {}; @@ -1136,6 +1128,10 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) bd->pd3dDevice->CreateRenderTargetView(back_buffer, nullptr, vd->FrameCtx[i].RenderTargetCpuDescriptors); vd->FrameCtx[i].RenderTarget = back_buffer; } + + hr = vd->SwapChain->SetMaximumFrameLatency(bd->numFramesInFlight); + IM_ASSERT(hr == S_OK); + vd->SwapChainWaitableObject = vd->SwapChain->GetFrameLatencyWaitableObject(); } for (UINT i = 0; i < bd->numFramesInFlight; i++) @@ -1144,16 +1140,31 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport) static void ImGui_WaitForPendingOperations(ImGui_ImplDX12_ViewportData* vd) { - HRESULT hr = S_FALSE; - if (vd && vd->CommandQueue && vd->Fence && vd->FenceEvent) + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + HRESULT hr = vd->CommandQueue->Signal(bd->Fence, ++bd->FenceLastSignaledValue); + IM_ASSERT(hr == S_OK); + + hr = bd->Fence->SetEventOnCompletion(bd->FenceLastSignaledValue, bd->FenceEvent); + IM_ASSERT(hr == S_OK); + ::WaitForSingleObject(bd->FenceEvent, INFINITE); +} + +static ImGui_ImplDX12_FrameContext* ImGui_WaitForNextFrameContext(ImGui_ImplDX12_ViewportData* vd) +{ + ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); + ImGui_ImplDX12_FrameContext* frame_context = &vd->FrameCtx[vd->FrameIndex % vd->NumFramesInFlight]; + if (bd->Fence->GetCompletedValue() < frame_context->FenceValue) { - hr = vd->CommandQueue->Signal(vd->Fence, ++vd->FenceSignaledValue); + HRESULT hr = bd->Fence->SetEventOnCompletion(frame_context->FenceValue, bd->FenceEvent); IM_ASSERT(hr == S_OK); - ::WaitForSingleObject(vd->FenceEvent, 0); // Reset any forgotten waits - hr = vd->Fence->SetEventOnCompletion(vd->FenceSignaledValue, vd->FenceEvent); - IM_ASSERT(hr == S_OK); - ::WaitForSingleObject(vd->FenceEvent, INFINITE); + HANDLE waitableObjects[] = { vd->SwapChainWaitableObject, bd->FenceEvent }; + ::WaitForMultipleObjects(2, waitableObjects, TRUE, INFINITE); } + else + { + ::WaitForSingleObject(vd->SwapChainWaitableObject, INFINITE); + } + return frame_context; } static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) @@ -1164,13 +1175,12 @@ static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport) { ImGui_WaitForPendingOperations(vd); - SafeRelease(vd->CommandQueue); + vd->CommandQueue = nullptr; + ::CloseHandle(vd->SwapChainWaitableObject); + vd->SwapChainWaitableObject = nullptr; SafeRelease(vd->CommandList); SafeRelease(vd->SwapChain); SafeRelease(vd->RtvDescHeap); - SafeRelease(vd->Fence); - ::CloseHandle(vd->FenceEvent); - vd->FenceEvent = nullptr; for (UINT i = 0; i < bd->numFramesInFlight; i++) { @@ -1213,7 +1223,7 @@ static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*) ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData; - ImGui_ImplDX12_FrameContext* frame_context = &vd->FrameCtx[vd->FrameIndex % bd->numFramesInFlight]; + ImGui_ImplDX12_FrameContext* frame_context = ImGui_WaitForNextFrameContext(vd); UINT back_buffer_idx = vd->SwapChain->GetCurrentBackBufferIndex(); const ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); @@ -1243,9 +1253,11 @@ static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*) cmd_list->ResourceBarrier(1, &barrier); cmd_list->Close(); - vd->CommandQueue->Wait(vd->Fence, vd->FenceSignaledValue); vd->CommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmd_list); - vd->CommandQueue->Signal(vd->Fence, ++vd->FenceSignaledValue); + + HRESULT hr = vd->CommandQueue->Signal(bd->Fence, ++bd->FenceLastSignaledValue); + IM_ASSERT(hr == S_OK); + frame_context->FenceValue = bd->FenceLastSignaledValue; } static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport, void*) @@ -1254,8 +1266,7 @@ static void ImGui_ImplDX12_SwapBuffers(ImGuiViewport* viewport, void*) ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData; vd->SwapChain->Present(0, bd->tearingSupport ? DXGI_PRESENT_ALLOW_TEARING : 0); - while (vd->Fence->GetCompletedValue() < vd->FenceSignaledValue) - ::SwitchToThread(); + vd->FrameIndex++; } void ImGui_ImplDX12_InitMultiViewportSupport() diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 905f86488..31da48289 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -116,6 +116,11 @@ #endif // GLFW +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) +#define GLFW_HAS_X11_OR_WAYLAND 1 +#else +#define GLFW_HAS_X11_OR_WAYLAND 0 +#endif #include #ifdef _WIN32 #undef APIENTRY @@ -128,8 +133,8 @@ #define GLFW_EXPOSE_NATIVE_COCOA #endif #include -#elif !defined(__EMSCRIPTEN__) -#ifndef GLFW_EXPOSE_NATIVE_X11 // for glfwGetX11Window() on Freedesktop (Linux, BSD, etc.) +#elif GLFW_HAS_X11_OR_WAYLAND +#ifndef GLFW_EXPOSE_NATIVE_X11 // for glfwGetX11Display(), glfwGetX11Window() on Freedesktop (Linux, BSD, etc.) #define GLFW_EXPOSE_NATIVE_X11 #include #endif @@ -184,11 +189,6 @@ #define GLFW_HAS_GETKEYNAME (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwGetKeyName() #define GLFW_HAS_GETERROR (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetError() #define GLFW_HAS_GETPLATFORM (GLFW_VERSION_COMBINED >= 3400) // 3.4+ glfwGetPlatform() -#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) -#define GLFW_HAS_X11_OR_WAYLAND 1 -#else -#define GLFW_HAS_X11_OR_WAYLAND 0 -#endif // Map GLFWWindow* to ImGuiContext*. // - Would be simpler if we could use glfwSetWindowUserPointer()/glfwGetWindowUserPointer(), but this is a single and shared resource. diff --git a/backends/imgui_impl_vulkan.cpp b/backends/imgui_impl_vulkan.cpp index 2624f5210..95e7d06de 100644 --- a/backends/imgui_impl_vulkan.cpp +++ b/backends/imgui_impl_vulkan.cpp @@ -1322,8 +1322,6 @@ bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info) IM_ASSERT(info->DescriptorPoolSize > 0); if (info->UseDynamicRendering) IM_ASSERT(info->PipelineInfoMain.RenderPass == VK_NULL_HANDLE && info->PipelineInfoForViewports.RenderPass == VK_NULL_HANDLE); - else if (info->PipelineInfoForViewports.RenderPass == NULL) - info->PipelineInfoForViewports.RenderPass = info->PipelineInfoMain.RenderPass; bd->VulkanInitInfo = *info; @@ -1683,7 +1681,7 @@ void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, V info.imageFormat = wd->SurfaceFormat.format; info.imageColorSpace = wd->SurfaceFormat.colorSpace; info.imageArrayLayers = 1; - info.imageUsage = (image_usage != 0) ? image_usage : VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | image_usage; info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // Assume that graphics family == present family info.preTransform = (cap.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR : cap.currentTransform; info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; @@ -2014,7 +2012,7 @@ static void ImGui_ImplVulkan_CreateWindow(ImGuiViewport* viewport) } else { - IM_ASSERT(pipeline_info->RenderPass != VK_NULL_HANDLE && "Did you set ImGui_ImplVulkan_InitInfo::PipelineInfoForViewports.RenderPass?"); // Since 1.92.4 it is required. + pipeline_info->RenderPass = wd->RenderPass; } #endif bd->PipelineForViewports = ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, VK_NULL_HANDLE, &v->PipelineInfoForViewports); diff --git a/backends/imgui_impl_vulkan.h b/backends/imgui_impl_vulkan.h index 44b8b8bae..ad89cb9e4 100644 --- a/backends/imgui_impl_vulkan.h +++ b/backends/imgui_impl_vulkan.h @@ -69,8 +69,10 @@ // Specify settings to create pipeline and swapchain struct ImGui_ImplVulkan_PipelineInfo { - // For Main and Secondary viewports + // For Main viewport only VkRenderPass RenderPass; // Ignored if using dynamic rendering + + // For Main and Secondary viewports uint32_t Subpass; // VkSampleCountFlagBits MSAASamples = {}; // 0 defaults to VK_SAMPLE_COUNT_1_BIT #ifdef IMGUI_IMPL_VULKAN_HAS_DYNAMIC_RENDERING @@ -78,7 +80,7 @@ struct ImGui_ImplVulkan_PipelineInfo #endif // For Secondary viewports only (created/managed by backend) - VkImageUsageFlags SwapChainImageUsage; // 0 defaults to VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT. Add e.g. VK_IMAGE_USAGE_TRANSFER_SRC_BIT if you need to capture from viewports. + VkImageUsageFlags SwapChainImageUsage; // Extra flags for vkCreateSwapchainKHR() calls for secondary viewports. We automatically add VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT. You can add e.g. VK_IMAGE_USAGE_TRANSFER_SRC_BIT if you need to capture from viewports. }; // Initialization data, for ImGui_ImplVulkan_Init() diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ece3cbdc0..e4f190071 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -56,16 +56,18 @@ Breaking Changes: (introduced very recently and only used by `ImGui_ImplVulkan_CreateMainPipeline()` so it should not affect many users). (#8110, #8111) - Backends: Vulkan: helper ImGui_ImplVulkanH_CreateOrResizeWindow() added a - `VkImageUsageFlags image_usage` argument. - It was previously hardcoded to `VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT` and defaults - to that when the value is 0. In theory the function is an internal helper but - since it's used by our examples some may have used it. (#8946, #8111, #8686) + `VkImageUsageFlags image_usage` argument to specific extra flags for calls to + vkCreateSwapchainKHR() done for secondary viewports. We automatically add + `VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT`. In theory the function is an internal + helper but since it's used by our examples some may have used it. (#8946, #8111, #8686) Other Changes: - Windows: added lower-right resize grip on child windows using both ImGuiChildFlags_ResizeX and ImGuiChildFlags_ResizeY flags. (#8501) [@aleksijuvani] The grip is not visible before hovering to reduce clutter. +- InputText: fixed single-line InputText() not applying fine character clipping + properly (regression in 1.92.3). (#8967) [@Cyphall] - IO: added ImGuiPlatformIO::ClearPlatformHandlers(), ClearRendererHandlers() helpers to null all handlers. (#8945, #2769) - Misc: Debuggers: added type formatters for the LLDB debuggers (e.g. Xcode, @@ -75,8 +77,13 @@ Other Changes: - Backends: all backends call ImGuiPlatformIO::ClearPlatformHandlers() and ClearRendererHandlers() on shutdown, so as not to leave function pointers which may be dangling when using backend in e.g. DLL. (#8945, #2769) +- Backends: DirectX12: reuse a command list and allocator for texture uploads instead + of recreating them each time. (#8963, #8465) [@RT2Code] +- Backends: DirectX12: Rework synchronization logic. (#8961) [@RT2Code] - Backends: OpenGL3: fixed GL loader to work on Haiku OS which does not support `RTLD_NOLOAD`. (#8952) [@Xottab-DUTY, @threedeyes] +- Backends: GLFW: fixed build on platform that are neither Windows, macOS or + known Unixes (Regression in 1.92.3). (#8969, #8920, #8921) [@oktonion] - Backends: SDL2,SDL3: avoid using the SDL_GetGlobalMouseState() path when one of our window is hovered, as the event data is reliable and enough in this case. - Fix mouse coordinates issue in fullscreen apps with macOS notch. (#7919, #7786) @@ -89,6 +96,7 @@ Other Changes: - Examples: SDL2+DirectX11: Try WARP software driver if hardware driver is not available. (#5924, #5562) - Examples: SDL3+DirectX11: Added SDL3+DirectX11 example. (#8956, #8957) [@tomaz82] +- Examples: Win32+DirectX12: Rework synchronization logic. (#8961) [@RT2Code] - Examples: made examples's main.cpp consistent with returning 1 on error. Docking+Viewports Branch: diff --git a/examples/example_glfw_vulkan/main.cpp b/examples/example_glfw_vulkan/main.cpp index bf4f9c51f..01e47fdad 100644 --- a/examples/example_glfw_vulkan/main.cpp +++ b/examples/example_glfw_vulkan/main.cpp @@ -54,7 +54,6 @@ static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; static ImGui_ImplVulkanH_Window g_MainWindowData; static uint32_t g_MinImageCount = 2; static bool g_SwapChainRebuild = false; -static VkImageUsageFlags g_SwapChainImageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; static void glfw_error_callback(int error, const char* description) { @@ -240,7 +239,7 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface // Create SwapChain, RenderPass, Framebuffer, etc. IM_ASSERT(g_MinImageCount >= 2); - ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount, g_SwapChainImageUsage); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount, 0); } static void CleanupVulkan() @@ -470,7 +469,7 @@ int main(int, char**) if (fb_width > 0 && fb_height > 0 && (g_SwapChainRebuild || g_MainWindowData.Width != fb_width || g_MainWindowData.Height != fb_height)) { ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount); - ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount, g_SwapChainImageUsage); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount, 0); g_MainWindowData.FrameIndex = 0; g_SwapChainRebuild = false; } diff --git a/examples/example_sdl2_vulkan/main.cpp b/examples/example_sdl2_vulkan/main.cpp index 352034026..52ebe6dd9 100644 --- a/examples/example_sdl2_vulkan/main.cpp +++ b/examples/example_sdl2_vulkan/main.cpp @@ -49,7 +49,6 @@ static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; static ImGui_ImplVulkanH_Window g_MainWindowData; static uint32_t g_MinImageCount = 2; static bool g_SwapChainRebuild = false; -static VkImageUsageFlags g_SwapChainImageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; static void check_vk_result(VkResult err) { @@ -231,7 +230,7 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface // Create SwapChain, RenderPass, Framebuffer, etc. IM_ASSERT(g_MinImageCount >= 2); - ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount, g_SwapChainImageUsage); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount, 0); } static void CleanupVulkan() @@ -490,7 +489,7 @@ int main(int, char**) if (fb_width > 0 && fb_height > 0 && (g_SwapChainRebuild || g_MainWindowData.Width != fb_width || g_MainWindowData.Height != fb_height)) { ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount); - ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount, g_SwapChainImageUsage); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount, 0); g_MainWindowData.FrameIndex = 0; g_SwapChainRebuild = false; } diff --git a/examples/example_sdl3_vulkan/main.cpp b/examples/example_sdl3_vulkan/main.cpp index 02fd2a5b0..c2b7f793b 100644 --- a/examples/example_sdl3_vulkan/main.cpp +++ b/examples/example_sdl3_vulkan/main.cpp @@ -51,7 +51,6 @@ static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; static ImGui_ImplVulkanH_Window g_MainWindowData; static uint32_t g_MinImageCount = 2; static bool g_SwapChainRebuild = false; -static VkImageUsageFlags g_SwapChainImageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; static void check_vk_result(VkResult err) { @@ -233,7 +232,7 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface // Create SwapChain, RenderPass, Framebuffer, etc. IM_ASSERT(g_MinImageCount >= 2); - ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount, g_SwapChainImageUsage); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount, 0); } static void CleanupVulkan() @@ -492,7 +491,7 @@ int main(int, char**) if (fb_width > 0 && fb_height > 0 && (g_SwapChainRebuild || g_MainWindowData.Width != fb_width || g_MainWindowData.Height != fb_height)) { ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount); - ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, fb_height, fb_height, g_MinImageCount, g_SwapChainImageUsage); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, fb_height, fb_height, g_MinImageCount, 0); g_MainWindowData.FrameIndex = 0; g_SwapChainRebuild = false; } diff --git a/examples/example_win32_directx12/main.cpp b/examples/example_win32_directx12/main.cpp index e7b6b4979..3be25a247 100644 --- a/examples/example_win32_directx12/main.cpp +++ b/examples/example_win32_directx12/main.cpp @@ -103,8 +103,8 @@ bool CreateDeviceD3D(HWND hWnd); void CleanupDeviceD3D(); void CreateRenderTarget(); void CleanupRenderTarget(); -void WaitForLastSubmittedFrame(); -FrameContext* WaitForNextFrameResources(); +void WaitForPendingOperations(); +FrameContext* WaitForNextFrameContext(); LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // Main code @@ -270,7 +270,7 @@ int main(int, char**) // Rendering ImGui::Render(); - FrameContext* frameCtx = WaitForNextFrameResources(); + FrameContext* frameCtx = WaitForNextFrameContext(); UINT backBufferIdx = g_pSwapChain->GetCurrentBackBufferIndex(); frameCtx->CommandAllocator->Reset(); @@ -304,18 +304,17 @@ int main(int, char**) ImGui::RenderPlatformWindowsDefault(); } + g_pd3dCommandQueue->Signal(g_fence, ++g_fenceLastSignaledValue); + frameCtx->FenceValue = g_fenceLastSignaledValue; + // Present HRESULT hr = g_pSwapChain->Present(1, 0); // Present with vsync //HRESULT hr = g_pSwapChain->Present(0, g_tearingSupport ? DXGI_PRESENT_ALLOW_TEARING : 0); // Present without vsync g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED); - - UINT64 fenceValue = g_fenceLastSignaledValue + 1; - g_pd3dCommandQueue->Signal(g_fence, fenceValue); - g_fenceLastSignaledValue = fenceValue; - frameCtx->FenceValue = fenceValue; + g_frameIndex++; } - WaitForLastSubmittedFrame(); + WaitForPendingOperations(); // Cleanup ImGui_ImplDX12_Shutdown(); @@ -496,49 +495,33 @@ void CreateRenderTarget() void CleanupRenderTarget() { - WaitForLastSubmittedFrame(); + WaitForPendingOperations(); for (UINT i = 0; i < APP_NUM_BACK_BUFFERS; i++) if (g_mainRenderTargetResource[i]) { g_mainRenderTargetResource[i]->Release(); g_mainRenderTargetResource[i] = nullptr; } } -void WaitForLastSubmittedFrame() +void WaitForPendingOperations() { - FrameContext* frameCtx = &g_frameContext[g_frameIndex % APP_NUM_FRAMES_IN_FLIGHT]; + g_pd3dCommandQueue->Signal(g_fence, ++g_fenceLastSignaledValue); - UINT64 fenceValue = frameCtx->FenceValue; - if (fenceValue == 0) - return; // No fence was signaled - - frameCtx->FenceValue = 0; - if (g_fence->GetCompletedValue() >= fenceValue) - return; - - g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent); - WaitForSingleObject(g_fenceEvent, INFINITE); + g_fence->SetEventOnCompletion(g_fenceLastSignaledValue, g_fenceEvent); + ::WaitForSingleObject(g_fenceEvent, INFINITE); } -FrameContext* WaitForNextFrameResources() +FrameContext* WaitForNextFrameContext() { - UINT nextFrameIndex = g_frameIndex + 1; - g_frameIndex = nextFrameIndex; - - HANDLE waitableObjects[] = { g_hSwapChainWaitableObject, nullptr }; - DWORD numWaitableObjects = 1; - - FrameContext* frameCtx = &g_frameContext[nextFrameIndex % APP_NUM_FRAMES_IN_FLIGHT]; - UINT64 fenceValue = frameCtx->FenceValue; - if (fenceValue != 0) // means no fence was signaled + FrameContext* frame_context = &g_frameContext[g_frameIndex % APP_NUM_FRAMES_IN_FLIGHT]; + if (g_fence->GetCompletedValue() < frame_context->FenceValue) { - frameCtx->FenceValue = 0; - g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent); - waitableObjects[1] = g_fenceEvent; - numWaitableObjects = 2; + g_fence->SetEventOnCompletion(frame_context->FenceValue, g_fenceEvent); + HANDLE waitableObjects[] = { g_hSwapChainWaitableObject, g_fenceEvent }; + ::WaitForMultipleObjects(2, waitableObjects, TRUE, INFINITE); } + else + ::WaitForSingleObject(g_hSwapChainWaitableObject, INFINITE); - WaitForMultipleObjects(numWaitableObjects, waitableObjects, TRUE, INFINITE); - - return frameCtx; + return frame_context; } // Forward declare message handler from imgui_impl_win32.cpp @@ -559,7 +542,6 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) case WM_SIZE: if (g_pd3dDevice != nullptr && wParam != SIZE_MINIMIZED) { - WaitForLastSubmittedFrame(); CleanupRenderTarget(); DXGI_SWAP_CHAIN_DESC1 desc = {}; g_pSwapChain->GetDesc1(&desc); diff --git a/examples/example_win32_vulkan/main.cpp b/examples/example_win32_vulkan/main.cpp index de50e8474..c8b455a47 100644 --- a/examples/example_win32_vulkan/main.cpp +++ b/examples/example_win32_vulkan/main.cpp @@ -47,7 +47,6 @@ static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE; static ImGui_ImplVulkanH_Window g_MainWindowData; static uint32_t g_MinImageCount = 2; static bool g_SwapChainRebuild = false; -static VkImageUsageFlags g_SwapChainImageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; static void check_vk_result(VkResult err) { @@ -229,7 +228,7 @@ static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface // Create SwapChain, RenderPass, Framebuffer, etc. IM_ASSERT(g_MinImageCount >= 2); - ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount, g_SwapChainImageUsage); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount, 0); } static void CleanupVulkan() @@ -583,7 +582,7 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) if (fb_width > 0 && fb_height > 0 && (g_SwapChainRebuild || g_MainWindowData.Width != fb_width || g_MainWindowData.Height != fb_height)) { ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount); - ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, &g_MainWindowData, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount, g_SwapChainImageUsage); + ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, &g_MainWindowData, g_QueueFamily, g_Allocator, fb_width, fb_height, g_MinImageCount, 0); g_MainWindowData.FrameIndex = 0; g_SwapChainRebuild = false; } diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 0f7ce29d3..c5cafbeb4 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5531,7 +5531,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ text_col, clip_rect.AsVec4(), line_index->get_line_begin(buf_display, line_visible_n0), line_index->get_line_end(buf_display, line_visible_n1 - 1), - wrap_width, ImDrawTextFlags_WrapKeepBlanks); + wrap_width, ImDrawTextFlags_WrapKeepBlanks | ImDrawTextFlags_CpuFineClip); // Render blinking cursor if (render_cursor)