Skip to content

Commit 40b2286

Browse files
committed
(Breaking) Backends: DX12: changed ImGui_ImplDX12_Init() signature. Added ImGui_ImplDX12_InitInfo. Added support for Srv allocators.
Ref 7708
1 parent 3260ea6 commit 40b2286

File tree

4 files changed

+155
-28
lines changed

4 files changed

+155
-28
lines changed

backends/imgui_impl_dx12.cpp

+55-12
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
// CHANGELOG
2121
// (minor and older changes stripped away, please see git history for details)
22+
// 2024-11-15: DirectX12: *BREAKING CHANGE* Changed ImGui_ImplDX12_Init() signature to take a ImGui_ImplDX12_InitInfo struct. Legacy ImGui_ImplDX12_Init() signature is still supported (will obsolete).
23+
// 2024-11-15: DirectX12: *BREAKING CHANGE* User is now required to pass function pointers to allocate/free SRV Descriptors. We provide convenience legacy fields to pass a single descriptor, matching the old API, but upcoming features will want multiple.
2224
// 2024-10-23: DirectX12: Unmap() call specify written range. The range is informational and may be used by debug tools.
2325
// 2024-10-07: DirectX12: Changed default texture sampler to Clamp instead of Repeat/Wrap.
2426
// 2024-10-07: DirectX12: Expose selected render state in ImGui_ImplDX12_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
@@ -57,6 +59,7 @@
5759
struct ImGui_ImplDX12_RenderBuffers;
5860
struct ImGui_ImplDX12_Data
5961
{
62+
ImGui_ImplDX12_InitInfo InitInfo;
6063
ID3D12Device* pd3dDevice;
6164
ID3D12RootSignature* pRootSignature;
6265
ID3D12PipelineState* pPipelineState;
@@ -695,8 +698,14 @@ void ImGui_ImplDX12_InvalidateDeviceObjects()
695698
ImGuiIO& io = ImGui::GetIO();
696699
SafeRelease(bd->pRootSignature);
697700
SafeRelease(bd->pPipelineState);
701+
702+
// Free SRV descriptor used by texture
703+
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
704+
if (bd->InitInfo.SrvDescriptorFreeFn != NULL)
705+
#endif
706+
bd->InitInfo.SrvDescriptorFreeFn(&bd->InitInfo, bd->hFontSrvCpuDescHandle, bd->hFontSrvGpuDescHandle);
698707
SafeRelease(bd->pFontTextureResource);
699-
io.Fonts->SetTexID(0); // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well.
708+
io.Fonts->SetTexID(0); // We copied bd->hFontSrvGpuDescHandle to io.Fonts->TexID so let's clear that as well.
700709

701710
for (UINT i = 0; i < bd->numFramesInFlight; i++)
702711
{
@@ -706,30 +715,47 @@ void ImGui_ImplDX12_InvalidateDeviceObjects()
706715
}
707716
}
708717

709-
bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap,
710-
D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle)
718+
bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
711719
{
712720
ImGuiIO& io = ImGui::GetIO();
713721
IMGUI_CHECKVERSION();
714722
IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
715723

716724
// Setup backend capabilities flags
717725
ImGui_ImplDX12_Data* bd = IM_NEW(ImGui_ImplDX12_Data)();
726+
727+
bd->InitInfo = *init_info; // Deep copy
728+
bd->pd3dDevice = init_info->Device;
729+
bd->RTVFormat = init_info->RTVFormat;
730+
bd->numFramesInFlight = init_info->NumFramesInFlight;
731+
bd->pd3dSrvDescHeap = init_info->SrvDescriptorHeap;
732+
718733
io.BackendRendererUserData = (void*)bd;
719734
io.BackendRendererName = "imgui_impl_dx12";
720735
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
721736

722-
bd->pd3dDevice = device;
723-
bd->RTVFormat = rtv_format;
724-
bd->hFontSrvCpuDescHandle = font_srv_cpu_desc_handle;
725-
bd->hFontSrvGpuDescHandle = font_srv_gpu_desc_handle;
726-
bd->pFrameResources = new ImGui_ImplDX12_RenderBuffers[num_frames_in_flight];
727-
bd->numFramesInFlight = num_frames_in_flight;
728-
bd->pd3dSrvDescHeap = cbv_srv_heap;
729-
bd->frameIndex = UINT_MAX;
737+
// Allocate 1 SRV descriptor for the font texture
738+
if (init_info->SrvDescriptorAllocFn != NULL)
739+
{
740+
IM_ASSERT(init_info->SrvDescriptorFreeFn != NULL);
741+
init_info->SrvDescriptorAllocFn(&bd->InitInfo, &bd->hFontSrvCpuDescHandle, &bd->hFontSrvGpuDescHandle);
742+
}
743+
else
744+
{
745+
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
746+
IM_ASSERT(init_info->LegacySingleSrvCpuDescriptor.ptr != 0 && init_info->LegacySingleSrvGpuDescriptor.ptr != 0);
747+
bd->hFontSrvCpuDescHandle = init_info->LegacySingleSrvCpuDescriptor;
748+
bd->hFontSrvGpuDescHandle = init_info->LegacySingleSrvGpuDescriptor;
749+
#else
750+
IM_ASSERT(init_info->SrvDescriptorAllocFn != NULL);
751+
IM_ASSERT(init_info->SrvDescriptorFreeFn != NULL);
752+
#endif
753+
}
730754

731755
// Create buffers with a default size (they will later be grown as needed)
732-
for (int i = 0; i < num_frames_in_flight; i++)
756+
bd->frameIndex = UINT_MAX;
757+
bd->pFrameResources = new ImGui_ImplDX12_RenderBuffers[bd->numFramesInFlight];
758+
for (int i = 0; i < (int)bd->numFramesInFlight; i++)
733759
{
734760
ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[i];
735761
fr->IndexBuffer = nullptr;
@@ -741,6 +767,22 @@ bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FO
741767
return true;
742768
}
743769

770+
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
771+
// Legacy initialization API Obsoleted in 1.91.5
772+
// font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture, they must be in 'srv_descriptor_heap'
773+
bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* srv_descriptor_heap, D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle)
774+
{
775+
ImGui_ImplDX12_InitInfo init_info;
776+
init_info.Device = device;
777+
init_info.NumFramesInFlight = num_frames_in_flight;
778+
init_info.RTVFormat = rtv_format;
779+
init_info.SrvDescriptorHeap = srv_descriptor_heap;
780+
init_info.LegacySingleSrvCpuDescriptor = font_srv_cpu_desc_handle;
781+
init_info.LegacySingleSrvGpuDescriptor = font_srv_gpu_desc_handle;;
782+
return ImGui_ImplDX12_Init(&init_info);
783+
}
784+
#endif
785+
744786
void ImGui_ImplDX12_Shutdown()
745787
{
746788
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
@@ -750,6 +792,7 @@ void ImGui_ImplDX12_Shutdown()
750792
// Clean up windows and device objects
751793
ImGui_ImplDX12_InvalidateDeviceObjects();
752794
delete[] bd->pFrameResources;
795+
753796
io.BackendRendererName = nullptr;
754797
io.BackendRendererUserData = nullptr;
755798
io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;

backends/imgui_impl_dx12.h

+29-11
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,42 @@
2121
#include "imgui.h" // IMGUI_IMPL_API
2222
#ifndef IMGUI_DISABLE
2323
#include <dxgiformat.h> // DXGI_FORMAT
24+
#include <d3d12.h> // D3D12_CPU_DESCRIPTOR_HANDLE
2425

25-
struct ID3D12Device;
26-
struct ID3D12DescriptorHeap;
27-
struct ID3D12GraphicsCommandList;
28-
struct D3D12_CPU_DESCRIPTOR_HANDLE;
29-
struct D3D12_GPU_DESCRIPTOR_HANDLE;
26+
// Initialization data, for ImGui_ImplDX12_Init()
27+
struct ImGui_ImplDX12_InitInfo
28+
{
29+
ID3D12Device* Device;
30+
ID3D12CommandQueue* CommandQueue;
31+
int NumFramesInFlight;
32+
DXGI_FORMAT RTVFormat;
33+
void* UserData;
3034

31-
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
35+
// Allocating SRV descriptors for textures is up to the application, so we provide callbacks.
36+
// (current version of the backend will only allocate one descriptor, future versions will need to allocate more)
37+
ID3D12DescriptorHeap* SrvDescriptorHeap;
38+
void (*SrvDescriptorAllocFn)(ImGui_ImplDX12_InitInfo* info, D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_desc_handle);
39+
void (*SrvDescriptorFreeFn)(ImGui_ImplDX12_InitInfo* info, D3D12_CPU_DESCRIPTOR_HANDLE cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE gpu_desc_handle);
40+
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
41+
D3D12_CPU_DESCRIPTOR_HANDLE LegacySingleSrvCpuDescriptor; // To facilitate transition from single descriptor to allocator callback, you may use those.
42+
D3D12_GPU_DESCRIPTOR_HANDLE LegacySingleSrvGpuDescriptor;
43+
#endif
3244

33-
// Before calling the render function, caller must prepare the command list by resetting it and setting the appropriate
34-
// render target and descriptor heap that contains font_srv_cpu_desc_handle/font_srv_gpu_desc_handle.
35-
// font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture.
36-
IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap,
37-
D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle);
45+
ImGui_ImplDX12_InitInfo() { memset(this, 0, sizeof(*this)); }
46+
};
47+
48+
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
49+
IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* info);
3850
IMGUI_IMPL_API void ImGui_ImplDX12_Shutdown();
3951
IMGUI_IMPL_API void ImGui_ImplDX12_NewFrame();
4052
IMGUI_IMPL_API void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* graphics_command_list);
4153

54+
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
55+
// Legacy initialization API Obsoleted in 1.91.5
56+
// font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture, they must be in 'srv_descriptor_heap'
57+
IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* srv_descriptor_heap, D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle);
58+
#endif
59+
4260
// Use if you want to reset your rendering device without losing Dear ImGui state.
4361
IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects();
4462
IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateDeviceObjects();

docs/CHANGELOG.txt

+10
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,20 @@ HOW TO UPDATE?
4141

4242
Breaking changes:
4343

44+
- Backends: DX12: Changed ImGui_ImplDX12_Init() signature to take a
45+
ImGui_ImplDX12_InitInfo struct.
46+
- Using the new API, application is now required to pass function pointers
47+
to allocate/free SRV Descriptors.
48+
- We provide convenience legacy fields to pass a single descriptor,
49+
matching the old API, but upcoming features will want multiple.
50+
- Legacy ImGui_ImplDX12_Init() signature is still supported (will obsolete).
51+
4452
Other changes:
4553

4654
- Error Handling: fixed cases where recoverable error handling would crash when
4755
processing errors outside of the NewFrame()..EndFrame() scope. (#1651)
56+
- Examples: Win32+DX12: Using a basic free-list allocator to manage multiple
57+
SRV descriptors.
4858

4959

5060
-----------------------------------------------------------------------

examples/example_win32_directx12/main.cpp

+61-5
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,67 @@
2525
// Config for example app
2626
static const int APP_NUM_FRAMES_IN_FLIGHT = 3;
2727
static const int APP_NUM_BACK_BUFFERS = 3;
28+
static const int APP_SRV_HEAP_SIZE = 64;
2829

2930
struct FrameContext
3031
{
3132
ID3D12CommandAllocator* CommandAllocator;
3233
UINT64 FenceValue;
3334
};
3435

36+
// Simple free list based allocator
37+
struct ExampleDescriptorHeapAllocator
38+
{
39+
ID3D12DescriptorHeap* Heap = nullptr;
40+
D3D12_DESCRIPTOR_HEAP_TYPE HeapType = D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES;
41+
D3D12_CPU_DESCRIPTOR_HANDLE HeapStartCpu;
42+
D3D12_GPU_DESCRIPTOR_HANDLE HeapStartGpu;
43+
UINT HeapHandleIncrement;
44+
ImVector<int> FreeIndices;
45+
46+
void Create(ID3D12Device* device, ID3D12DescriptorHeap* heap)
47+
{
48+
IM_ASSERT(Heap == nullptr && FreeIndices.empty());
49+
Heap = heap;
50+
D3D12_DESCRIPTOR_HEAP_DESC desc = heap->GetDesc();
51+
HeapType = desc.Type;
52+
HeapStartCpu = Heap->GetCPUDescriptorHandleForHeapStart();
53+
HeapStartGpu = Heap->GetGPUDescriptorHandleForHeapStart();
54+
HeapHandleIncrement = device->GetDescriptorHandleIncrementSize(HeapType);
55+
FreeIndices.reserve((int)desc.NumDescriptors);
56+
for (int n = desc.NumDescriptors; n > 0; n--)
57+
FreeIndices.push_back(n);
58+
}
59+
void Destroy()
60+
{
61+
Heap = NULL;
62+
FreeIndices.clear();
63+
}
64+
void Alloc(D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_desc_handle)
65+
{
66+
IM_ASSERT(FreeIndices.Size > 0);
67+
int idx = FreeIndices.back();
68+
FreeIndices.pop_back();
69+
out_cpu_desc_handle->ptr = HeapStartCpu.ptr + (idx * HeapHandleIncrement);
70+
out_gpu_desc_handle->ptr = HeapStartGpu.ptr + (idx * HeapHandleIncrement);
71+
}
72+
void Free(D3D12_CPU_DESCRIPTOR_HANDLE out_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE out_gpu_desc_handle)
73+
{
74+
int cpu_idx = (int)((out_cpu_desc_handle.ptr - HeapStartCpu.ptr) / HeapHandleIncrement);
75+
int gpu_idx = (int)((out_gpu_desc_handle.ptr - HeapStartGpu.ptr) / HeapHandleIncrement);
76+
IM_ASSERT(cpu_idx == gpu_idx);
77+
FreeIndices.push_back(cpu_idx);
78+
}
79+
};
80+
3581
// Data
3682
static FrameContext g_frameContext[APP_NUM_FRAMES_IN_FLIGHT] = {};
3783
static UINT g_frameIndex = 0;
3884

3985
static ID3D12Device* g_pd3dDevice = nullptr;
4086
static ID3D12DescriptorHeap* g_pd3dRtvDescHeap = nullptr;
4187
static ID3D12DescriptorHeap* g_pd3dSrvDescHeap = nullptr;
88+
static ExampleDescriptorHeapAllocator g_pd3dSrvDescHeapAlloc;
4289
static ID3D12CommandQueue* g_pd3dCommandQueue = nullptr;
4390
static ID3D12GraphicsCommandList* g_pd3dCommandList = nullptr;
4491
static ID3D12Fence* g_fence = nullptr;
@@ -93,10 +140,18 @@ int main(int, char**)
93140

94141
// Setup Platform/Renderer backends
95142
ImGui_ImplWin32_Init(hwnd);
96-
ImGui_ImplDX12_Init(g_pd3dDevice, NUM_FRAMES_IN_FLIGHT,
97-
DXGI_FORMAT_R8G8B8A8_UNORM, g_pd3dSrvDescHeap,
98-
g_pd3dSrvDescHeap->GetCPUDescriptorHandleForHeapStart(),
99-
g_pd3dSrvDescHeap->GetGPUDescriptorHandleForHeapStart());
143+
144+
ImGui_ImplDX12_InitInfo init_info = {};
145+
init_info.Device = g_pd3dDevice;
146+
init_info.CommandQueue = g_pd3dCommandQueue;
147+
init_info.NumFramesInFlight = APP_NUM_FRAMES_IN_FLIGHT;
148+
init_info.RTVFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
149+
// Allocating SRV descriptors (for textures) is up to the application, so we provide callbacks.
150+
// (current version of the backend will only allocate one descriptor, future versions will need to allocate more)
151+
init_info.SrvDescriptorHeap = g_pd3dSrvDescHeap;
152+
init_info.SrvDescriptorAllocFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_handle) { return g_pd3dSrvDescHeapAlloc.Alloc(out_cpu_handle, out_gpu_handle); };
153+
init_info.SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle) { return g_pd3dSrvDescHeapAlloc.Free(cpu_handle, gpu_handle); };
154+
ImGui_ImplDX12_Init(&init_info);
100155

101156
// Load Fonts
102157
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
@@ -310,10 +365,11 @@ bool CreateDeviceD3D(HWND hWnd)
310365
{
311366
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
312367
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
313-
desc.NumDescriptors = 1;
368+
desc.NumDescriptors = APP_SRV_HEAP_SIZE;
314369
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
315370
if (g_pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&g_pd3dSrvDescHeap)) != S_OK)
316371
return false;
372+
g_pd3dSrvDescHeapAlloc.Create(g_pd3dDevice, g_pd3dSrvDescHeap);
317373
}
318374

319375
{

0 commit comments

Comments
 (0)