Switch to imgui docking branch, vendor it

This commit is contained in:
2026-02-22 18:34:37 -05:00
parent aa6065b0bd
commit 2d08a1dd54
13 changed files with 8233 additions and 310 deletions

View File

@@ -6,6 +6,8 @@
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// FIXME: The transition from removing a viewport and moving the window in an existing hosted viewport tends to flicker.
// The aim of imgui_impl_dx12.h/.cpp is to be usable in your engine without any modification.
// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
@@ -20,6 +22,8 @@
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2025-10-23: [Docking] DirectX12: Fixed an issue in synchronization logic improving rendering throughput for secondary viewports. (#8961, #9025)
// 2025-10-11: DirectX12: Reuse texture upload buffer and grow it only when necessary. (#9002)
// 2025-09-29: DirectX12: Rework synchronization logic. (#8961)
// 2025-09-29: DirectX12: Enable swapchain tearing to eliminate viewports framerate throttling. (#8965)
@@ -115,9 +119,6 @@ struct ImGui_ImplDX12_Data
UINT pTexUploadBufferSize;
void* pTexUploadBufferMapped;
ImGui_ImplDX12_RenderBuffers* pFrameResources;
UINT frameIndex;
ImGui_ImplDX12_Data() { memset((void*)this, 0, sizeof(*this)); }
};
@@ -137,11 +138,85 @@ struct ImGui_ImplDX12_RenderBuffers
int VertexBufferSize;
};
// 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;
};
// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data.
// Main viewport created by application will only use the Resources field.
// Secondary viewports created by this backend will use all the fields (including Window fields),
struct ImGui_ImplDX12_ViewportData
{
// Window
ID3D12CommandQueue* CommandQueue;
ID3D12GraphicsCommandList* CommandList;
ID3D12DescriptorHeap* RtvDescHeap;
IDXGISwapChain3* SwapChain;
HANDLE SwapChainWaitableObject;
UINT NumFramesInFlight;
ImGui_ImplDX12_FrameContext* FrameCtx;
// Render buffers
UINT FrameIndex;
ImGui_ImplDX12_RenderBuffers* FrameRenderBuffers;
ImGui_ImplDX12_ViewportData(UINT num_frames_in_flight)
{
CommandQueue = nullptr;
CommandList = nullptr;
RtvDescHeap = nullptr;
SwapChain = nullptr;
SwapChainWaitableObject = nullptr;
NumFramesInFlight = num_frames_in_flight;
FrameCtx = new ImGui_ImplDX12_FrameContext[NumFramesInFlight];
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;
// Create buffers with a default size (they will later be grown as needed)
FrameRenderBuffers[i].IndexBuffer = nullptr;
FrameRenderBuffers[i].VertexBuffer = nullptr;
FrameRenderBuffers[i].VertexBufferSize = 5000;
FrameRenderBuffers[i].IndexBufferSize = 10000;
}
}
~ImGui_ImplDX12_ViewportData()
{
IM_ASSERT(CommandQueue == nullptr && CommandList == nullptr);
IM_ASSERT(RtvDescHeap == nullptr);
IM_ASSERT(SwapChain == nullptr);
IM_ASSERT(SwapChainWaitableObject == nullptr);
for (UINT i = 0; i < NumFramesInFlight; ++i)
{
IM_ASSERT(FrameCtx[i].CommandAllocator == nullptr && FrameCtx[i].RenderTarget == nullptr);
IM_ASSERT(FrameRenderBuffers[i].IndexBuffer == nullptr && FrameRenderBuffers[i].VertexBuffer == nullptr);
}
delete[] FrameCtx; FrameCtx = nullptr;
delete[] FrameRenderBuffers; FrameRenderBuffers = nullptr;
}
};
struct VERTEX_CONSTANT_BUFFER_DX12
{
float mvp[4][4];
};
// Forward Declarations
static void ImGui_ImplDX12_InitMultiViewportSupport();
static void ImGui_ImplDX12_ShutdownMultiViewportSupport();
// FIXME-WIP: Allow user to forward declare those two, for until we come up with a backend agnostic API to do this. (#9173)
void ImGui_ImplDX12_SetupSamplerLinear(ID3D12GraphicsCommandList* command_list);
void ImGui_ImplDX12_SetupSamplerNearest(ID3D12GraphicsCommandList* command_list);
@@ -236,8 +311,9 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL
// FIXME: We are assuming that this only gets called once per frame!
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
bd->frameIndex = bd->frameIndex + 1;
ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[bd->frameIndex % bd->numFramesInFlight];
ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)draw_data->OwnerViewport->RendererUserData;
ImGui_ImplDX12_RenderBuffers* fr = &vd->FrameRenderBuffers[vd->FrameIndex % bd->numFramesInFlight];
vd->FrameIndex++;
// Create and grow vertex/index buffers if needed
if (fr->VertexBuffer == nullptr || fr->VertexBufferSize < draw_data->TotalVtxCount)
@@ -839,6 +915,13 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
return true;
}
static void ImGui_ImplDX12_DestroyRenderBuffers(ImGui_ImplDX12_RenderBuffers* render_buffers)
{
SafeRelease(render_buffers->IndexBuffer);
SafeRelease(render_buffers->VertexBuffer);
render_buffers->IndexBufferSize = render_buffers->VertexBufferSize = 0;
}
void ImGui_ImplDX12_InvalidateDeviceObjects()
{
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
@@ -871,13 +954,6 @@ void ImGui_ImplDX12_InvalidateDeviceObjects()
for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
if (tex->RefCount == 1)
ImGui_ImplDX12_DestroyTexture(tex);
for (UINT i = 0; i < bd->numFramesInFlight; i++)
{
ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[i];
SafeRelease(fr->IndexBuffer);
SafeRelease(fr->VertexBuffer);
}
}
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
@@ -926,6 +1002,15 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
io.BackendRendererName = "imgui_impl_dx12";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
ImGui_ImplDX12_InitMultiViewportSupport();
// Create a dummy ImGui_ImplDX12_ViewportData holder for the main viewport,
// Since this is created and managed by the application, we will only use the ->Resources[] fields.
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
main_viewport->RendererUserData = IM_NEW(ImGui_ImplDX12_ViewportData)(bd->numFramesInFlight);
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
if (init_info->SrvDescriptorAllocFn == nullptr)
@@ -933,18 +1018,6 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
#endif
IM_ASSERT(init_info->SrvDescriptorAllocFn != nullptr && init_info->SrvDescriptorFreeFn != nullptr);
// Create buffers with a default size (they will later be grown as needed)
bd->frameIndex = UINT_MAX;
bd->pFrameResources = new ImGui_ImplDX12_RenderBuffers[bd->numFramesInFlight];
for (int i = 0; i < (int)bd->numFramesInFlight; i++)
{
ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[i];
fr->IndexBuffer = nullptr;
fr->VertexBuffer = nullptr;
fr->IndexBufferSize = 10000;
fr->VertexBufferSize = 5000;
}
return true;
}
@@ -985,12 +1058,23 @@ void ImGui_ImplDX12_Shutdown()
ImGuiIO& io = ImGui::GetIO();
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
// Manually delete main viewport render resources in-case we haven't initialized for viewports
ImGuiViewport* main_viewport = ImGui::GetMainViewport();
if (ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)main_viewport->RendererUserData)
{
// We could just call ImGui_ImplDX12_DestroyWindow(main_viewport) as a convenience but that would be misleading since we only use data->Resources[]
for (UINT i = 0; i < bd->numFramesInFlight; i++)
ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]);
IM_DELETE(vd);
main_viewport->RendererUserData = nullptr;
}
ImGui_ImplDX12_ShutdownMultiViewportSupport();
ImGui_ImplDX12_InvalidateDeviceObjects();
delete[] bd->pFrameResources;
io.BackendRendererName = nullptr;
io.BackendRendererUserData = nullptr;
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures);
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures | ImGuiBackendFlags_RendererHasViewports);
platform_io.ClearRendererHandlers();
IM_DELETE(bd);
}
@@ -1005,6 +1089,254 @@ void ImGui_ImplDX12_NewFrame()
IM_ASSERT(0 && "ImGui_ImplDX12_CreateDeviceObjects() failed!");
}
//--------------------------------------------------------------------------------------------------------
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
// This is an _advanced_ and _optional_ feature, allowing the backend 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..
//--------------------------------------------------------------------------------------------------------
static void ImGui_ImplDX12_CreateWindow(ImGuiViewport* viewport)
{
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
ImGui_ImplDX12_ViewportData* vd = IM_NEW(ImGui_ImplDX12_ViewportData)(bd->numFramesInFlight);
viewport->RendererUserData = vd;
// PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL's WindowID).
// Some backends will leave PlatformHandleRaw == 0, in which case we assume PlatformHandle will contain the HWND.
HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle;
IM_ASSERT(hwnd != 0);
// 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));
IM_ASSERT(res == S_OK);
}
// Create command list.
res = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, vd->FrameCtx[0].CommandAllocator, nullptr, IID_PPV_ARGS(&vd->CommandList));
IM_ASSERT(res == S_OK);
vd->CommandList->Close();
// Create swap chain
// FIXME-VIEWPORT: May want to copy/inherit swap chain settings from the user/application.
DXGI_SWAP_CHAIN_DESC1 sd1;
ZeroMemory(&sd1, sizeof(sd1));
sd1.BufferCount = bd->numFramesInFlight;
sd1.Width = (UINT)viewport->Size.x;
sd1.Height = (UINT)viewport->Size.y;
sd1.Format = bd->RTVFormat;
sd1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd1.SampleDesc.Count = 1;
sd1.SampleDesc.Quality = 0;
sd1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
sd1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
sd1.Scaling = DXGI_SCALING_NONE;
sd1.Stereo = FALSE;
sd1.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
if (bd->tearingSupport)
sd1.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
IDXGISwapChain1* swap_chain = nullptr;
res = bd->pdxgiFactory->CreateSwapChainForHwnd(vd->CommandQueue, hwnd, &sd1, nullptr, nullptr, &swap_chain);
IM_ASSERT(res == S_OK);
res = bd->pdxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_WINDOW_CHANGES); // Disable e.g. Alt+Enter
IM_ASSERT(res == S_OK);
// Or swapChain.As(&mSwapChain)
IM_ASSERT(vd->SwapChain == nullptr);
swap_chain->QueryInterface(IID_PPV_ARGS(&vd->SwapChain));
swap_chain->Release();
// Create the render targets and waitable object
if (vd->SwapChain)
{
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
desc.NumDescriptors = bd->numFramesInFlight;
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
desc.NodeMask = 1;
HRESULT hr = bd->pd3dDevice->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&vd->RtvDescHeap));
IM_ASSERT(hr == S_OK);
SIZE_T rtv_descriptor_size = bd->pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = vd->RtvDescHeap->GetCPUDescriptorHandleForHeapStart();
for (UINT i = 0; i < bd->numFramesInFlight; i++)
{
vd->FrameCtx[i].RenderTargetCpuDescriptors = rtv_handle;
rtv_handle.ptr += rtv_descriptor_size;
}
ID3D12Resource* back_buffer;
for (UINT i = 0; i < bd->numFramesInFlight; i++)
{
IM_ASSERT(vd->FrameCtx[i].RenderTarget == nullptr);
vd->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer));
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++)
ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]);
}
static void ImGui_WaitForPendingOperations(ImGui_ImplDX12_ViewportData* vd)
{
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)
{
HRESULT hr = bd->Fence->SetEventOnCompletion(frame_context->FenceValue, bd->FenceEvent);
IM_ASSERT(hr == S_OK);
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)
{
// The main viewport (owned by the application) will always have RendererUserData == 0 since we didn't create the data for it.
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
if (ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData)
{
ImGui_WaitForPendingOperations(vd);
vd->CommandQueue = nullptr;
::CloseHandle(vd->SwapChainWaitableObject);
vd->SwapChainWaitableObject = nullptr;
SafeRelease(vd->CommandList);
SafeRelease(vd->SwapChain);
SafeRelease(vd->RtvDescHeap);
for (UINT i = 0; i < bd->numFramesInFlight; i++)
{
SafeRelease(vd->FrameCtx[i].RenderTarget);
SafeRelease(vd->FrameCtx[i].CommandAllocator);
ImGui_ImplDX12_DestroyRenderBuffers(&vd->FrameRenderBuffers[i]);
}
IM_DELETE(vd);
}
viewport->RendererUserData = nullptr;
}
static void ImGui_ImplDX12_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
{
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData;
ImGui_WaitForPendingOperations(vd);
for (UINT i = 0; i < bd->numFramesInFlight; i++)
SafeRelease(vd->FrameCtx[i].RenderTarget);
if (vd->SwapChain)
{
ID3D12Resource* back_buffer = nullptr;
DXGI_SWAP_CHAIN_DESC1 desc = {};
vd->SwapChain->GetDesc1(&desc);
vd->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, desc.Format, desc.Flags);
for (UINT i = 0; i < bd->numFramesInFlight; i++)
{
vd->SwapChain->GetBuffer(i, IID_PPV_ARGS(&back_buffer));
bd->pd3dDevice->CreateRenderTargetView(back_buffer, nullptr, vd->FrameCtx[i].RenderTargetCpuDescriptors);
vd->FrameCtx[i].RenderTarget = back_buffer;
}
}
}
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 = ImGui_WaitForNextFrameContext(vd);
UINT back_buffer_idx = vd->SwapChain->GetCurrentBackBufferIndex();
const ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource = vd->FrameCtx[back_buffer_idx].RenderTarget;
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
// Draw
ID3D12GraphicsCommandList* cmd_list = vd->CommandList;
frame_context->CommandAllocator->Reset();
cmd_list->Reset(frame_context->CommandAllocator, nullptr);
cmd_list->ResourceBarrier(1, &barrier);
cmd_list->OMSetRenderTargets(1, &vd->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, FALSE, nullptr);
if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))
cmd_list->ClearRenderTargetView(vd->FrameCtx[back_buffer_idx].RenderTargetCpuDescriptors, (const float*)&clear_color, 0, nullptr);
cmd_list->SetDescriptorHeaps(1, &bd->pd3dSrvDescHeap);
ImGui_ImplDX12_RenderDrawData(viewport->DrawData, cmd_list);
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
cmd_list->ResourceBarrier(1, &barrier);
cmd_list->Close();
vd->CommandQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmd_list);
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*)
{
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
ImGui_ImplDX12_ViewportData* vd = (ImGui_ImplDX12_ViewportData*)viewport->RendererUserData;
vd->SwapChain->Present(0, bd->tearingSupport ? DXGI_PRESENT_ALLOW_TEARING : 0);
}
void ImGui_ImplDX12_InitMultiViewportSupport()
{
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.Renderer_CreateWindow = ImGui_ImplDX12_CreateWindow;
platform_io.Renderer_DestroyWindow = ImGui_ImplDX12_DestroyWindow;
platform_io.Renderer_SetWindowSize = ImGui_ImplDX12_SetWindowSize;
platform_io.Renderer_RenderWindow = ImGui_ImplDX12_RenderWindow;
platform_io.Renderer_SwapBuffers = ImGui_ImplDX12_SwapBuffers;
}
void ImGui_ImplDX12_ShutdownMultiViewportSupport()
{
ImGui::DestroyPlatformWindows();
}
//-----------------------------------------------------------------------------
#endif // #ifndef IMGUI_DISABLE