Skip to content

Commit 6f07596

Browse files
committed
Let user specifies the back buffer count by setting ImGui_ImplDX12_InitInfo::BackBuffer and add support for a single frame in flight
1 parent 95e0472 commit 6f07596

File tree

3 files changed

+47
-29
lines changed

3 files changed

+47
-29
lines changed

backends/imgui_impl_dx12.cpp

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
// CHANGELOG
2424
// (minor and older changes stripped away, please see git history for details)
2525
// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
26+
// 2025-10-25: [Docking] DirectX12: Let user specifies viewports backbuffer count by setting ImGui_ImplDX12_InitInfo::BackBuffer and add support for a single frame in flight.
2627
// 2025-10-23: [Docking] DirectX12: Fixed an issue in synchronization logic improving rendering throughput for secondary viewports. (#8961, #9025)
2728
// 2025-10-11: DirectX12: Reuse texture upload buffer and grow it only when necessary. (#9002)
2829
// 2025-09-29: DirectX12: Rework synchronization logic. (#8961)
@@ -107,6 +108,7 @@ struct ImGui_ImplDX12_Data
107108
ID3D12Fence* Fence;
108109
UINT64 FenceLastSignaledValue;
109110
HANDLE FenceEvent;
111+
UINT numBackbuffer;
110112
UINT numFramesInFlight;
111113
bool tearingSupport;
112114
bool LegacySingleDescriptorUsed;
@@ -136,13 +138,18 @@ struct ImGui_ImplDX12_RenderBuffers
136138
int VertexBufferSize;
137139
};
138140

141+
// Backbuffers used for secondary viewports created by the multi-viewports systems
142+
struct ImGui_ImplDX12_Backbuffer
143+
{
144+
ID3D12Resource* RenderTarget;
145+
D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetCpuDescriptors;
146+
};
147+
139148
// Buffers used for secondary viewports created by the multi-viewports systems
140149
struct ImGui_ImplDX12_FrameContext
141150
{
142151
UINT64 FenceValue;
143152
ID3D12CommandAllocator* CommandAllocator;
144-
ID3D12Resource* RenderTarget;
145-
D3D12_CPU_DESCRIPTOR_HANDLE RenderTargetCpuDescriptors;
146153
};
147154

148155
// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data.
@@ -156,30 +163,35 @@ struct ImGui_ImplDX12_ViewportData
156163
ID3D12DescriptorHeap* RtvDescHeap;
157164
IDXGISwapChain3* SwapChain;
158165
HANDLE SwapChainWaitableObject;
166+
UINT NumBackBuffer;
167+
ImGui_ImplDX12_Backbuffer* BackBuffer;
159168
UINT NumFramesInFlight;
160169
ImGui_ImplDX12_FrameContext* FrameCtx;
161-
162170
// Render buffers
163171
UINT FrameIndex;
164172
ImGui_ImplDX12_RenderBuffers* FrameRenderBuffers;
165173

166-
ImGui_ImplDX12_ViewportData(UINT num_frames_in_flight)
174+
ImGui_ImplDX12_ViewportData(UINT num_backbuffer, UINT num_frames_in_flight)
167175
{
168176
CommandQueue = nullptr;
169177
CommandList = nullptr;
170178
RtvDescHeap = nullptr;
171179
SwapChain = nullptr;
172180
SwapChainWaitableObject = nullptr;
181+
NumBackBuffer = num_backbuffer;
182+
BackBuffer = new ImGui_ImplDX12_Backbuffer[num_backbuffer];
173183
NumFramesInFlight = num_frames_in_flight;
174184
FrameCtx = new ImGui_ImplDX12_FrameContext[NumFramesInFlight];
175185
FrameIndex = 0;
176186
FrameRenderBuffers = new ImGui_ImplDX12_RenderBuffers[NumFramesInFlight];
177187

188+
for (UINT i = 0; i < NumBackBuffer; ++i)
189+
BackBuffer[i].RenderTarget = nullptr;
190+
178191
for (UINT i = 0; i < NumFramesInFlight; ++i)
179192
{
180193
FrameCtx[i].FenceValue = 0;
181194
FrameCtx[i].CommandAllocator = nullptr;
182-
FrameCtx[i].RenderTarget = nullptr;
183195

184196
// Create buffers with a default size (they will later be grown as needed)
185197
FrameRenderBuffers[i].IndexBuffer = nullptr;
@@ -195,12 +207,16 @@ struct ImGui_ImplDX12_ViewportData
195207
IM_ASSERT(SwapChain == nullptr);
196208
IM_ASSERT(SwapChainWaitableObject == nullptr);
197209

210+
for (UINT i = 0; i < NumBackBuffer; ++i)
211+
IM_ASSERT(BackBuffer[i].RenderTarget == nullptr);
212+
198213
for (UINT i = 0; i < NumFramesInFlight; ++i)
199214
{
200-
IM_ASSERT(FrameCtx[i].CommandAllocator == nullptr && FrameCtx[i].RenderTarget == nullptr);
215+
IM_ASSERT(FrameCtx[i].CommandAllocator == nullptr);
201216
IM_ASSERT(FrameRenderBuffers[i].IndexBuffer == nullptr && FrameRenderBuffers[i].VertexBuffer == nullptr);
202217
}
203218

219+
delete[] BackBuffer; BackBuffer = nullptr;
204220
delete[] FrameCtx; FrameCtx = nullptr;
205221
delete[] FrameRenderBuffers; FrameRenderBuffers = nullptr;
206222
}
@@ -955,6 +971,7 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
955971
bd->pCommandQueue = init_info->CommandQueue;
956972
bd->RTVFormat = init_info->RTVFormat;
957973
bd->DSVFormat = init_info->DSVFormat;
974+
bd->numBackbuffer = init_info->NumBackBuffer >= 2 ? init_info->NumBackBuffer : 2;
958975
bd->numFramesInFlight = init_info->NumFramesInFlight;
959976
bd->pd3dSrvDescHeap = init_info->SrvDescriptorHeap;
960977
bd->tearingSupport = false;
@@ -971,7 +988,7 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
971988
// Create a dummy ImGui_ImplDX12_ViewportData holder for the main viewport,
972989
// Since this is created and managed by the application, we will only use the ->Resources[] fields.
973990
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
974-
main_viewport->RendererUserData = IM_NEW(ImGui_ImplDX12_ViewportData)(bd->numFramesInFlight);
991+
main_viewport->RendererUserData = IM_NEW(ImGui_ImplDX12_ViewportData)(0, bd->numFramesInFlight);
975992

976993
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
977994
if (init_info->SrvDescriptorAllocFn == nullptr)
@@ -1059,7 +1076,7 @@ void ImGui_ImplDX12_NewFrame()
10591076
static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport)
10601077
{
10611078
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
1062-
ImGui_ImplDX12_ViewportData* vd = IM_NEW(ImGui_ImplDX12_ViewportData)(bd->numFramesInFlight);
1079+
ImGui_ImplDX12_ViewportData* vd = IM_NEW(ImGui_ImplDX12_ViewportData)(bd->numBackbuffer, bd->numFramesInFlight);
10631080
viewport->RendererUserData = vd;
10641081

10651082
// PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL's WindowID).
@@ -1088,7 +1105,7 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport)
10881105
// FIXME-VIEWPORT: May want to copy/inherit swap chain settings from the user/application.
10891106
DXGI_SWAP_CHAIN_DESC1 sd1;
10901107
ZeroMemory(&sd1, sizeof(sd1));
1091-
sd1.BufferCount = bd->numFramesInFlight;
1108+
sd1.BufferCount = bd->numBackbuffer;
10921109
sd1.Width = (UINT)viewport->Size.x;
10931110
sd1.Height = (UINT)viewport->Size.y;
10941111
sd1.Format = bd->RTVFormat;
@@ -1120,7 +1137,7 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport)
11201137
{
11211138
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
11221139
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
1123-
desc.NumDescriptors = bd->numFramesInFlight;
1140+
desc.NumDescriptors = bd->numBackbuffer;
11241141
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
11251142
desc.NodeMask = 1;
11261143

@@ -1129,22 +1146,19 @@ static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport)
11291146

11301147
SIZE_T rtv_descriptor_size = bd->pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
11311148
D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = vd->RtvDescHeap->GetCPUDescriptorHandleForHeapStart();
1132-
for (UINT i = 0; i < bd->numFramesInFlight; i++)
1149+
ID3D12Resource* back_buffer;
1150+
for (UINT i = 0; i < bd->numBackbuffer; i++)
11331151
{
1134-
vd->FrameCtx[i].RenderTargetCpuDescriptors = rtv_handle;
1152+
vd->BackBuffer[i].RenderTargetCpuDescriptors = rtv_handle;
11351153
rtv_handle.ptr += rtv_descriptor_size;
1136-
}
11371154

1138-
ID3D12Resource* back_buffer;
1139-
for (UINT i = 0; i < bd->numFramesInFlight; i++)
1140-
{
1141-
IM_ASSERT(vd->FrameCtx[i].RenderTarget == nullptr);
1155+
IM_ASSERT(vd->BackBuffer[i].RenderTarget == nullptr);
11421156
vd->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer));
1143-
bd->pd3dDevice->CreateRenderTargetView(back_buffer, nullptr, vd->FrameCtx[i].RenderTargetCpuDescriptors);
1144-
vd->FrameCtx[i].RenderTarget = back_buffer;
1157+
bd->pd3dDevice->CreateRenderTargetView(back_buffer, nullptr, vd->BackBuffer[i].RenderTargetCpuDescriptors);
1158+
vd->BackBuffer[i].RenderTarget = back_buffer;
11451159
}
11461160

1147-
hr = vd->SwapChain->SetMaximumFrameLatency(bd->numFramesInFlight);
1161+
hr = vd->SwapChain->SetMaximumFrameLatency(bd->numBackbuffer);
11481162
IM_ASSERT(hr == S_OK);
11491163
vd->SwapChainWaitableObject = vd->SwapChain->GetFrameLatencyWaitableObject();
11501164
}
@@ -1197,9 +1211,11 @@ static void ImGui_ImplDX12_DestroyWindow(ImGuiViewport* viewport)
11971211
SafeRelease(vd->SwapChain);
11981212
SafeRelease(vd->RtvDescHeap);
11991213

1214+
for (UINT i = 0; i < bd->numBackbuffer; i++)
1215+
SafeRelease(vd->BackBuffer[i].RenderTarget);
1216+
12001217
for (UINT i = 0; i < bd->numFramesInFlight; i++)
12011218
{
1202-
SafeRelease(vd->FrameCtx[i].RenderTarget);
12031219
SafeRelease(vd->FrameCtx[i].CommandAllocator);
12041220
ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]);
12051221
}
@@ -1215,20 +1231,20 @@ static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
12151231

12161232
ImGui_WaitForPendingOperations(vd);
12171233

1218-
for (UINT i = 0; i < bd->numFramesInFlight; i++)
1219-
SafeRelease(vd->FrameCtx[i].RenderTarget);
1234+
for (UINT i = 0; i < bd->numBackbuffer; i++)
1235+
SafeRelease(vd->BackBuffer[i].RenderTarget);
12201236

12211237
if (vd->SwapChain)
12221238
{
12231239
ID3D12Resource* back_buffer = nullptr;
12241240
DXGI_SWAP_CHAIN_DESC1 desc = {};
12251241
vd->SwapChain->GetDesc1(&desc);
12261242
vd->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, desc.Format, desc.Flags);
1227-
for (UINT i = 0; i < bd->numFramesInFlight; i++)
1243+
for (UINT i = 0; i < bd->numBackbuffer; i++)
12281244
{
12291245
vd->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer));
1230-
bd->pd3dDevice->CreateRenderTargetView(back_buffer, nullptr, vd->FrameCtx[i].RenderTargetCpuDescriptors);
1231-
vd->FrameCtx[i].RenderTarget = back_buffer;
1246+
bd->pd3dDevice->CreateRenderTargetView(back_buffer, nullptr, vd->BackBuffer[i].RenderTargetCpuDescriptors);
1247+
vd->BackBuffer[i].RenderTarget = back_buffer;
12321248
}
12331249
}
12341250
}
@@ -1245,7 +1261,7 @@ static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*)
12451261
D3D12_RESOURCE_BARRIER barrier = {};
12461262
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
12471263
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
1248-
barrier.Transition.pResource = vd->FrameCtx[back_buffer_idx].RenderTarget;
1264+
barrier.Transition.pResource = vd->BackBuffer[back_buffer_idx].RenderTarget;
12491265
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
12501266
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
12511267
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
@@ -1256,9 +1272,9 @@ static void ImGui_ImplDX12_RenderWindow(ImGuiViewport* viewport, void*)
12561272
frame_context->CommandAllocator->Reset();
12571273
cmd_list->Reset(frame_context->CommandAllocator, nullptr);
12581274
cmd_list->ResourceBarrier(1, &barrier);
1259-
cmd_list->OMSetRenderTargets(1, &vd->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, FALSE, nullptr);
1275+
cmd_list->OMSetRenderTargets(1, &vd->BackBuffer[back_buffer_idx].RenderTargetCpuDescriptors, FALSE, nullptr);
12601276
if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))
1261-
cmd_list->ClearRenderTargetView(vd->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, (const float*)&clear_color, 0, nullptr);
1277+
cmd_list->ClearRenderTargetView(vd->BackBuffer[back_buffer_idx].RenderTargetCpuDescriptors, (const float*)&clear_color, 0, nullptr);
12621278
cmd_list->SetDescriptorHeaps(1, &bd->pd3dSrvDescHeap);
12631279

12641280
ImGui_ImplDX12_RenderDrawData(viewport->DrawData, cmd_list);

backends/imgui_impl_dx12.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ struct ImGui_ImplDX12_InitInfo
3030
{
3131
ID3D12Device* Device;
3232
ID3D12CommandQueue* CommandQueue; // Command queue used for queuing texture uploads.
33+
int NumBackBuffer;
3334
int NumFramesInFlight;
3435
DXGI_FORMAT RTVFormat; // RenderTarget format.
3536
DXGI_FORMAT DSVFormat; // DepthStencilView format.

examples/example_win32_directx12/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ int main(int, char**)
166166
ImGui_ImplDX12_InitInfo init_info = {};
167167
init_info.Device = g_pd3dDevice;
168168
init_info.CommandQueue = g_pd3dCommandQueue;
169+
init_info.NumBackBuffer = APP_NUM_BACK_BUFFERS;
169170
init_info.NumFramesInFlight = APP_NUM_FRAMES_IN_FLIGHT;
170171
init_info.RTVFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
171172
init_info.DSVFormat = DXGI_FORMAT_UNKNOWN;

0 commit comments

Comments
 (0)