1
0
Fork 0
mirror of https://github.com/ocornut/imgui.git synced 2026-01-11 00:04:24 +00:00

Enable swapchain tearing

This commit is contained in:
Rémy Tassoux 2025-10-23 15:09:39 +02:00
parent e989b8263f
commit f65982c9c1
2 changed files with 79 additions and 47 deletions

View file

@ -51,6 +51,7 @@
// DirectX
#include <stdio.h>
#include <d3d11.h>
#include <dxgi1_5.h>
#include <d3dcompiler.h>
#ifdef _MSC_VER
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
@ -73,7 +74,8 @@ struct ImGui_ImplDX11_Data
{
ID3D11Device* pd3dDevice;
ID3D11DeviceContext* pd3dDeviceContext;
IDXGIFactory* pFactory;
IDXGIFactory5* pFactory;
bool tearingSupport;
ID3D11Buffer* pVB;
ID3D11Buffer* pIB;
ID3D11VertexShader* pVertexShader;
@ -86,7 +88,7 @@ struct ImGui_ImplDX11_Data
ID3D11DepthStencilState* pDepthStencilState;
int VertexBufferSize;
int IndexBufferSize;
ImVector<DXGI_SWAP_CHAIN_DESC> SwapChainDescsForViewports;
ImVector<DXGI_SWAP_CHAIN_DESC1> SwapChainDescsForViewports;
ImGui_ImplDX11_Data() { memset((void*)this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; }
};
@ -433,6 +435,10 @@ bool ImGui_ImplDX11_CreateDeviceObjects()
return false;
ImGui_ImplDX11_InvalidateDeviceObjects();
BOOL allow_tearing = FALSE;
bd->pFactory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing, sizeof(allow_tearing));
bd->tearingSupport = (allow_tearing == TRUE);
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
// If you would like to use this DX11 sample code but remove this dependency you can:
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
@ -636,7 +642,7 @@ bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co
// Get factory from device
IDXGIDevice* pDXGIDevice = nullptr;
IDXGIAdapter* pDXGIAdapter = nullptr;
IDXGIFactory* pFactory = nullptr;
IDXGIFactory5* pFactory = nullptr;
if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK)
if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
@ -645,6 +651,7 @@ bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co
bd->pd3dDevice = device;
bd->pd3dDeviceContext = device_context;
bd->pFactory = pFactory;
bd->tearingSupport = false;
}
if (pDXGIDevice) pDXGIDevice->Release();
if (pDXGIAdapter) pDXGIAdapter->Release();
@ -695,7 +702,7 @@ void ImGui_ImplDX11_NewFrame()
// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data.
struct ImGui_ImplDX11_ViewportData
{
IDXGISwapChain* SwapChain;
IDXGISwapChain1* SwapChain;
ID3D11RenderTargetView* RTView;
ImGui_ImplDX11_ViewportData() { SwapChain = nullptr; RTView = nullptr; }
@ -704,12 +711,12 @@ struct ImGui_ImplDX11_ViewportData
// Multi-Viewports: configure templates used when creating swapchains for secondary viewports. Will try them in order.
// This is intentionally not declared in the .h file yet, so you will need to copy this declaration:
void ImGui_ImplDX11_SetSwapChainDescs(const DXGI_SWAP_CHAIN_DESC* desc_templates, int desc_templates_count);
void ImGui_ImplDX11_SetSwapChainDescs(const DXGI_SWAP_CHAIN_DESC* desc_templates, int desc_templates_count)
void ImGui_ImplDX11_SetSwapChainDescs(const DXGI_SWAP_CHAIN_DESC1* desc_templates, int desc_templates_count);
void ImGui_ImplDX11_SetSwapChainDescs(const DXGI_SWAP_CHAIN_DESC1* desc_templates, int desc_templates_count)
{
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
bd->SwapChainDescsForViewports.resize(desc_templates_count);
memcpy(bd->SwapChainDescsForViewports.Data, desc_templates, sizeof(DXGI_SWAP_CHAIN_DESC));
memcpy(bd->SwapChainDescsForViewports.Data, desc_templates, sizeof(DXGI_SWAP_CHAIN_DESC1));
}
static void ImGui_ImplDX11_CreateWindow(ImGuiViewport* viewport)
@ -726,14 +733,17 @@ static void ImGui_ImplDX11_CreateWindow(ImGuiViewport* viewport)
// Create swap chain
HRESULT hr = DXGI_ERROR_UNSUPPORTED;
for (const DXGI_SWAP_CHAIN_DESC& sd_template : bd->SwapChainDescsForViewports)
for (const DXGI_SWAP_CHAIN_DESC1& sd_template : bd->SwapChainDescsForViewports)
{
IM_ASSERT(sd_template.BufferDesc.Width == 0 && sd_template.BufferDesc.Height == 0 && sd_template.OutputWindow == nullptr);
DXGI_SWAP_CHAIN_DESC sd = sd_template;
sd.BufferDesc.Width = (UINT)viewport->Size.x;
sd.BufferDesc.Height = (UINT)viewport->Size.y;
sd.OutputWindow = hwnd;
hr = bd->pFactory->CreateSwapChain(bd->pd3dDevice, &sd, &vd->SwapChain);
IM_ASSERT(sd_template.Width == 0 && sd_template.Height == 0);
DXGI_SWAP_CHAIN_DESC1 sd = sd_template;
sd.Width = (UINT)viewport->Size.x;
sd.Height = (UINT)viewport->Size.y;
if (bd->tearingSupport)
sd.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
hr = bd->pFactory->CreateSwapChainForHwnd(bd->pd3dDevice, hwnd, &sd, nullptr, nullptr, &vd->SwapChain);
if (SUCCEEDED(hr))
break;
}
@ -778,7 +788,9 @@ static void ImGui_ImplDX11_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
if (vd->SwapChain)
{
ID3D11Texture2D* pBackBuffer = nullptr;
vd->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0);
DXGI_SWAP_CHAIN_DESC1 desc = {};
vd->SwapChain->GetDesc1(&desc);
vd->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, desc.Flags);
vd->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
if (pBackBuffer == nullptr) { fprintf(stderr, "ImGui_ImplDX11_SetWindowSize() failed creating buffers.\n"); return; }
bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &vd->RTView);
@ -799,9 +811,10 @@ static void ImGui_ImplDX11_RenderWindow(ImGuiViewport* viewport, void*)
static void ImGui_ImplDX11_SwapBuffers(ImGuiViewport* viewport, void*)
{
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
ImGui_ImplDX11_ViewportData* vd = (ImGui_ImplDX11_ViewportData*)viewport->RendererUserData;
if (vd->SwapChain)
vd->SwapChain->Present(0, 0); // Present without vsync
vd->SwapChain->Present(0, bd->tearingSupport ? DXGI_PRESENT_ALLOW_TEARING : 0); // Present without vsync
}
static void ImGui_ImplDX11_InitMultiViewportSupport()
@ -814,14 +827,13 @@ static void ImGui_ImplDX11_InitMultiViewportSupport()
platform_io.Renderer_SwapBuffers = ImGui_ImplDX11_SwapBuffers;
// Default swapchain format
DXGI_SWAP_CHAIN_DESC sd;
DXGI_SWAP_CHAIN_DESC1 sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferCount = 2;
sd.Windowed = TRUE;
sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
sd.Flags = 0;
ImGui_ImplDX11_SetSwapChainDescs(&sd, 1);

View file

@ -10,12 +10,14 @@
#include "imgui_impl_win32.h"
#include "imgui_impl_dx11.h"
#include <d3d11.h>
#include <dxgi1_5.h>
#include <tchar.h>
// Data
static ID3D11Device* g_pd3dDevice = nullptr;
static ID3D11DeviceContext* g_pd3dDeviceContext = nullptr;
static IDXGISwapChain* g_pSwapChain = nullptr;
static IDXGISwapChain1* g_pSwapChain = nullptr;
static bool g_SwapChainTearingSupport = false;
static bool g_SwapChainOccluded = false;
static UINT g_ResizeWidth = 0, g_ResizeHeight = 0;
static ID3D11RenderTargetView* g_mainRenderTargetView = nullptr;
@ -136,7 +138,9 @@ int main(int, char**)
if (g_ResizeWidth != 0 && g_ResizeHeight != 0)
{
CleanupRenderTarget();
g_pSwapChain->ResizeBuffers(0, g_ResizeWidth, g_ResizeHeight, DXGI_FORMAT_UNKNOWN, 0);
DXGI_SWAP_CHAIN_DESC1 desc = {};
g_pSwapChain->GetDesc1(&desc);
g_pSwapChain->ResizeBuffers(0, g_ResizeWidth, g_ResizeHeight, DXGI_FORMAT_UNKNOWN, desc.Flags);
g_ResizeWidth = g_ResizeHeight = 0;
CreateRenderTarget();
}
@ -199,7 +203,7 @@ int main(int, char**)
// Present
HRESULT hr = g_pSwapChain->Present(1, 0); // Present with vsync
//HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync
//HRESULT hr = g_pSwapChain->Present(0, g_SwapChainTearingSupport ? DXGI_PRESENT_ALLOW_TEARING : 0); // Present without vsync
g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED);
}
@ -218,42 +222,58 @@ int main(int, char**)
// Helper functions
bool CreateDeviceD3D(HWND hWnd)
{
// Setup swap chain
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferCount = 2;
sd.BufferDesc.Width = 0;
sd.BufferDesc.Height = 0;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = hWnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE;
sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
UINT createDeviceFlags = 0;
//createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
D3D_FEATURE_LEVEL featureLevel;
const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, };
HRESULT res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
HRESULT res = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
if (res == DXGI_ERROR_UNSUPPORTED) // Try high-performance WARP software driver if hardware is not available.
res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
res = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
if (res != S_OK)
return false;
IDXGIDevice* pDXGIDevice = nullptr;
IDXGIAdapter* pDXGIAdapter = nullptr;
IDXGIFactory5* pIDXGIFactory = nullptr;
if (g_pd3dDevice->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK)
if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
res = pDXGIAdapter->GetParent(IID_PPV_ARGS(&pIDXGIFactory));
if (pDXGIDevice) pDXGIDevice->Release();
if (pDXGIAdapter) pDXGIAdapter->Release();
if (pIDXGIFactory == nullptr || res != S_OK)
return false;
// Setup swap chain
DXGI_SWAP_CHAIN_DESC1 sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferCount = 2;
sd.Width = 0;
sd.Height = 0;
sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
BOOL allow_tearing = FALSE;
pIDXGIFactory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing, sizeof(allow_tearing));
g_SwapChainTearingSupport = (allow_tearing == TRUE);
if (g_SwapChainTearingSupport)
sd.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
res = pIDXGIFactory->CreateSwapChainForHwnd(g_pd3dDevice, hWnd, &sd, nullptr, nullptr, &g_pSwapChain);
if (res != S_OK)
return false;
// Disable DXGI's default Alt+Enter fullscreen behavior.
// - You are free to leave this enabled, but it will not work properly with multiple viewports.
// - This must be done for all windows associated to the device. Our DX11 backend does this automatically for secondary viewports that it creates.
IDXGIFactory* pSwapChainFactory;
if (SUCCEEDED(g_pSwapChain->GetParent(IID_PPV_ARGS(&pSwapChainFactory))))
{
pSwapChainFactory->MakeWindowAssociation(hWnd, DXGI_MWA_NO_ALT_ENTER);
pSwapChainFactory->Release();
}
pIDXGIFactory->MakeWindowAssociation(hWnd, DXGI_MWA_NO_ALT_ENTER);
pIDXGIFactory->Release();
CreateRenderTarget();
return true;