Merge branch 'master' into docking

# Conflicts:
#	backends/imgui_impl_dx12.cpp
This commit is contained in:
ocornut
2025-09-29 16:16:16 +02:00
5 changed files with 71 additions and 73 deletions

View File

@@ -23,6 +23,8 @@
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2025-09-29: DirectX12: Rework synchronization logic. (#8961)
// 2025-09-29: DirectX12: Reuse a command list and allocator for texture uploads instead of recreating them each time.
// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown.
// 2025-06-19: Fixed build on MinGW. (#8702, #4594)
// 2025-06-11: DirectX12: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas.
@@ -98,8 +100,14 @@ struct ImGui_ImplDX12_Data
DXGI_FORMAT RTVFormat;
DXGI_FORMAT DSVFormat;
ID3D12DescriptorHeap* pd3dSrvDescHeap;
ID3D12Fence* Fence;
UINT64 FenceLastSignaledValue;
HANDLE FenceEvent;
UINT numFramesInFlight;
ID3D12CommandAllocator* pTexCmdAllocator;
ID3D12GraphicsCommandList* pTexCmdList;
ImGui_ImplDX12_RenderBuffers* pFrameResources;
UINT frameIndex;
@@ -529,29 +537,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;
@@ -606,20 +600,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);
}
@@ -857,6 +847,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;
}
@@ -878,6 +882,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)

View File

@@ -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 <GLFW/glfw3.h>
#ifdef _WIN32
#undef APIENTRY
@@ -128,8 +133,8 @@
#define GLFW_EXPOSE_NATIVE_COCOA
#endif
#include <GLFW/glfw3native.h>
#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 <X11/Xatom.h>
#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.

View File

@@ -66,6 +66,8 @@ 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:

View File

@@ -102,8 +102,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
@@ -269,7 +269,7 @@ int main(int, char**)
// Rendering
ImGui::Render();
FrameContext* frameCtx = WaitForNextFrameResources();
FrameContext* frameCtx = WaitForNextFrameContext();
UINT backBufferIdx = g_pSwapChain->GetCurrentBackBufferIndex();
frameCtx->CommandAllocator->Reset();
@@ -295,6 +295,8 @@ int main(int, char**)
g_pd3dCommandList->Close();
g_pd3dCommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&g_pd3dCommandList);
g_pd3dCommandQueue->Signal(g_fence, ++g_fenceLastSignaledValue);
frameCtx->FenceValue = g_fenceLastSignaledValue;
// Update and Render additional Platform Windows
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
@@ -307,14 +309,10 @@ int main(int, char**)
HRESULT hr = g_pSwapChain->Present(1, 0); // Present with vsync
//HRESULT hr = g_pSwapChain->Present(0, 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();
@@ -484,49 +482,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
@@ -547,7 +529,6 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
case WM_SIZE:
if (g_pd3dDevice != nullptr && wParam != SIZE_MINIMIZED)
{
WaitForLastSubmittedFrame();
CleanupRenderTarget();
HRESULT result = g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT);
assert(SUCCEEDED(result) && "Failed to resize swapchain.");

View File

@@ -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)