Files
OptiScaler/OptiScaler/menu/menu_overlay_dx.cpp
T
2025-10-05 23:38:35 +03:00

645 lines
19 KiB
C++

#include "menu_overlay_base.h"
#include "menu_overlay_dx.h"
#include <Util.h>
#include <Logger.h>
#include <Config.h>
#include <imgui/imgui_impl_dx11.h>
#include <imgui/imgui_impl_dx12.h>
#include <imgui/imgui_impl_win32.h>
// menu
static int const NUM_BACK_BUFFERS = 8;
static int const SRV_HEAP_SIZE = 64;
static bool _dx11Device = false;
static bool _dx12Device = false;
// for dx11
static ID3D11Device* g_pd3dDevice = nullptr;
static ID3D11DeviceContext* g_pd3dDeviceContext = nullptr;
static ID3D11RenderTargetView* g_pd3dRenderTarget = nullptr;
// for dx12
static ID3D12Device* g_pd3dDeviceParam = nullptr;
static ID3D12DescriptorHeap* g_pd3dRtvDescHeap = nullptr;
static ID3D12DescriptorHeap* g_pd3dSrvDescHeap = nullptr;
static DescriptorHeapAllocator g_pd3dSrvDescHeapAlloc;
static ID3D12CommandQueue* g_pd3dCommandQueue = nullptr;
static ID3D12GraphicsCommandList* g_pd3dCommandList = nullptr;
static ID3D12CommandAllocator* g_commandAllocators[NUM_BACK_BUFFERS] = {};
static ID3D12Resource* g_mainRenderTargetResource[NUM_BACK_BUFFERS] = {};
static D3D12_CPU_DESCRIPTOR_HANDLE g_mainRenderTargetDescriptor[NUM_BACK_BUFFERS] = {};
// current command queue for dx12 swapchain
static IUnknown* currentSCCommandQueue = nullptr;
// status
static bool _isInited = false;
static bool _d3d12Captured = false;
// for showing
static bool _showRenderImGuiDebugOnce = true;
// mutexes
static std::mutex _dx11CleanMutex;
static std::mutex _dx12CleanMutex;
static IID streamlineRiid {};
static bool CheckForRealObject(std::string functionName, IUnknown* pObject, IUnknown** ppRealObject)
{
if (streamlineRiid.Data1 == 0)
{
auto iidResult = IIDFromString(L"{ADEC44E2-61F0-45C3-AD9F-1B37379284FF}", &streamlineRiid);
if (iidResult != S_OK)
return false;
}
auto qResult = pObject->QueryInterface(streamlineRiid, (void**) ppRealObject);
if (qResult == S_OK && *ppRealObject != nullptr)
{
LOG_INFO("{} Streamline proxy found!", functionName);
(*ppRealObject)->Release();
return true;
}
return false;
}
static int GetCorrectDXGIFormat(int eCurrentFormat)
{
switch (eCurrentFormat)
{
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
return DXGI_FORMAT_R8G8B8A8_UNORM;
}
return eCurrentFormat;
}
static void CreateRenderTargetDx12(ID3D12Device* device, IDXGISwapChain* pSwapChain)
{
LOG_FUNC();
DXGI_SWAP_CHAIN_DESC sd;
HRESULT hr = pSwapChain->GetDesc(&sd);
if (hr != S_OK)
{
LOG_ERROR("pSwapChain->GetDesc: {0:X}", (unsigned long) hr);
return;
}
for (UINT i = 0; i < sd.BufferCount; ++i)
{
ID3D12Resource* pBackBuffer = nullptr;
auto result = pSwapChain->GetBuffer(i, IID_PPV_ARGS(&pBackBuffer));
if (result != S_OK)
{
LOG_ERROR("pSwapChain->GetBuffer: {:X}", (unsigned long) result);
return;
}
if (pBackBuffer)
{
D3D12_RENDER_TARGET_VIEW_DESC desc = {};
desc.Format = static_cast<DXGI_FORMAT>(GetCorrectDXGIFormat(sd.BufferDesc.Format));
desc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
device->CreateRenderTargetView(pBackBuffer, &desc, g_mainRenderTargetDescriptor[i]);
g_mainRenderTargetResource[i] = pBackBuffer;
pBackBuffer->Release();
}
}
LOG_INFO("done!");
}
static void CleanupRenderTargetDx12(bool clearQueue)
{
if (!_isInited || !_dx12Device)
return;
LOG_TRACE("clearQueue: {}", clearQueue);
for (UINT i = 0; i < NUM_BACK_BUFFERS; ++i)
{
if (g_mainRenderTargetResource[i])
g_mainRenderTargetResource[i] = nullptr;
}
if (clearQueue)
{
if (MenuOverlayBase::IsInited() && g_pd3dDeviceParam != nullptr && g_pd3dSrvDescHeap != nullptr &&
ImGui::GetIO().BackendRendererUserData)
{
// std::this_thread::sleep_for(std::chrono::milliseconds(500));
ImGui_ImplDX12_Shutdown(false);
}
if (g_pd3dRtvDescHeap != nullptr)
{
g_pd3dRtvDescHeap->Release();
g_pd3dRtvDescHeap = nullptr;
}
if (g_pd3dSrvDescHeap != nullptr)
{
g_pd3dSrvDescHeap->Release();
g_pd3dSrvDescHeap = nullptr;
}
for (UINT i = 0; i < NUM_BACK_BUFFERS; ++i)
{
if (g_commandAllocators[i] != nullptr)
{
g_commandAllocators[i]->Release();
g_commandAllocators[i] = nullptr;
}
}
if (g_pd3dCommandList != nullptr)
{
g_pd3dCommandList->Release();
g_pd3dCommandList = nullptr;
}
if (g_pd3dCommandQueue != nullptr)
g_pd3dCommandQueue = nullptr;
g_pd3dSrvDescHeapAlloc.Destroy();
// if (g_pd3dDeviceParam != nullptr)
//{
// g_pd3dDeviceParam->Release();
// g_pd3dDeviceParam = nullptr;
// }
_dx12Device = false;
_isInited = false;
}
}
static void CreateRenderTargetDx11(IDXGISwapChain* pSwapChain)
{
ID3D11Texture2D* pBackBuffer = NULL;
pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
if (pBackBuffer)
{
DXGI_SWAP_CHAIN_DESC sd;
pSwapChain->GetDesc(&sd);
D3D11_RENDER_TARGET_VIEW_DESC desc = {};
desc.Format = static_cast<DXGI_FORMAT>(GetCorrectDXGIFormat(sd.BufferDesc.Format));
desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
g_pd3dDevice->CreateRenderTargetView(pBackBuffer, &desc, &g_pd3dRenderTarget);
pBackBuffer->Release();
}
}
static void CleanupRenderTargetDx11(bool shutDown)
{
if (!_isInited || !_dx11Device)
return;
if (!shutDown)
LOG_FUNC();
if (g_pd3dRenderTarget != nullptr)
{
g_pd3dRenderTarget->Release();
g_pd3dRenderTarget = nullptr;
}
if (g_pd3dDevice != nullptr)
g_pd3dDevice = nullptr;
_dx11Device = false;
_isInited = false;
}
static void RenderImGui_DX11(IDXGISwapChain* pSwapChain)
{
bool drawMenu = false;
do
{
if (!MenuOverlayBase::IsInited())
break;
// Draw only when menu activated
// if (!MenuOverlayBase::IsVisible())
// break;
if (!_dx11Device || g_pd3dDevice == nullptr)
break;
drawMenu = true;
} while (false);
if (!drawMenu)
{
MenuOverlayBase::HideMenu();
return;
}
LOG_FUNC();
ImGuiIO& io = ImGui::GetIO();
(void) io;
if (io.BackendRendererUserData == nullptr)
{
if (pSwapChain->GetDevice(IID_PPV_ARGS(&g_pd3dDevice)) == S_OK)
{
g_pd3dDevice->GetImmediateContext(&g_pd3dDeviceContext);
ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);
}
}
if (_isInited)
{
if (!g_pd3dRenderTarget)
CreateRenderTargetDx11(pSwapChain);
if (ImGui::GetCurrentContext() && g_pd3dRenderTarget)
{
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
if (MenuOverlayBase::RenderMenu())
{
ImGui::Render();
g_pd3dDeviceContext->OMSetRenderTargets(1, &g_pd3dRenderTarget, NULL);
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
}
}
}
}
static void RenderImGui_DX12(IDXGISwapChain* pSwapChainPlain)
{
bool drawMenu = false;
IDXGISwapChain3* pSwapChain = nullptr;
do
{
if (pSwapChainPlain->QueryInterface(IID_PPV_ARGS(&pSwapChain)) != S_OK || pSwapChain == nullptr)
return;
if (!MenuOverlayBase::IsInited())
break;
// Draw only when menu activated
// if (!MenuOverlayBase::IsVisible())
// break;
if (!_dx12Device || currentSCCommandQueue == nullptr || g_pd3dDeviceParam == nullptr)
break;
drawMenu = true;
} while (false);
if (!drawMenu)
{
MenuOverlayBase::HideMenu();
auto releaseResult = pSwapChain->Release();
return;
}
LOG_FUNC();
// Get device from swapchain
ID3D12Device* device = g_pd3dDeviceParam;
ImGuiIO& io = ImGui::GetIO();
(void) io;
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures;
// Generate ImGui resources
if (!io.BackendRendererUserData && currentSCCommandQueue != nullptr)
{
LOG_DEBUG("ImGui::GetIO().BackendRendererUserData == nullptr");
HRESULT result;
{
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
desc.NumDescriptors = NUM_BACK_BUFFERS;
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
desc.NodeMask = 1;
State::Instance().skipHeapCapture = true;
result = device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&g_pd3dRtvDescHeap));
if (result != S_OK)
{
LOG_ERROR("CreateDescriptorHeap(g_pd3dRtvDescHeap): {0:X}", (unsigned long) result);
MenuOverlayBase::HideMenu();
CleanupRenderTargetDx12(true);
pSwapChain->Release();
State::Instance().skipHeapCapture = false;
return;
}
SIZE_T rtvDescriptorSize = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = g_pd3dRtvDescHeap->GetCPUDescriptorHandleForHeapStart();
for (UINT i = 0; i < NUM_BACK_BUFFERS; ++i)
{
g_mainRenderTargetDescriptor[i] = rtvHandle;
rtvHandle.ptr += rtvDescriptorSize;
}
}
{
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
desc.NumDescriptors = SRV_HEAP_SIZE;
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
result = device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&g_pd3dSrvDescHeap));
State::Instance().skipHeapCapture = false;
if (result != S_OK)
{
LOG_ERROR("CreateDescriptorHeap(g_pd3dSrvDescHeap): {0:X}", (unsigned long) result);
MenuOverlayBase::HideMenu();
CleanupRenderTargetDx12(true);
pSwapChain->Release();
return;
}
g_pd3dSrvDescHeapAlloc.Create(device, g_pd3dSrvDescHeap);
}
for (UINT i = 0; i < NUM_BACK_BUFFERS; ++i)
{
result =
device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&g_commandAllocators[i]));
if (result != S_OK)
{
LOG_ERROR("CreateCommandAllocator[{0}]: {1:X}", i, (unsigned long) result);
MenuOverlayBase::HideMenu();
CleanupRenderTargetDx12(true);
pSwapChain->Release();
return;
}
}
result = device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, g_commandAllocators[0], NULL,
IID_PPV_ARGS(&g_pd3dCommandList));
if (result != S_OK)
{
LOG_ERROR("CreateCommandList: {0:X}", (unsigned long) result);
MenuOverlayBase::HideMenu();
CleanupRenderTargetDx12(true);
pSwapChain->Release();
return;
}
result = g_pd3dCommandList->Close();
if (result != S_OK)
{
LOG_ERROR("g_pd3dCommandList->Close: {0:X}", (unsigned long) result);
MenuOverlayBase::HideMenu();
CleanupRenderTargetDx12(false);
pSwapChain->Release();
return;
}
DXGI_SWAP_CHAIN_DESC scDesc;
pSwapChain->GetDesc(&scDesc);
ImGui_ImplDX12_InitInfo initInfo {};
initInfo.Device = device;
initInfo.CommandQueue = (ID3D12CommandQueue*) currentSCCommandQueue;
initInfo.NumFramesInFlight = NUM_BACK_BUFFERS;
initInfo.RTVFormat = scDesc.BufferDesc.Format;
initInfo.DSVFormat = DXGI_FORMAT_UNKNOWN;
initInfo.SrvDescriptorHeap = g_pd3dSrvDescHeap;
initInfo.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); };
initInfo.SrvDescriptorFreeFn =
[](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle)
{ return g_pd3dSrvDescHeapAlloc.Free(cpu_handle, gpu_handle); };
ImGui_ImplDX12_Init(&initInfo);
pSwapChain->Release();
return;
}
if (_isInited && currentSCCommandQueue != nullptr)
{
// Generate render targets
if (!g_mainRenderTargetResource[0])
{
CreateRenderTargetDx12(device, pSwapChain);
pSwapChain->Release();
return;
}
// If everything is ready render the frame
if (ImGui::GetCurrentContext() && g_mainRenderTargetResource[0])
{
_showRenderImGuiDebugOnce = true;
ImGui_ImplDX12_NewFrame();
if (MenuOverlayBase::RenderMenu())
{
ImGui::Render();
UINT backBufferIdx = pSwapChain->GetCurrentBackBufferIndex();
ID3D12CommandAllocator* commandAllocator = g_commandAllocators[backBufferIdx];
auto result = commandAllocator->Reset();
if (result != S_OK)
{
LOG_ERROR("commandAllocator->Reset: {0:X}", (unsigned long) result);
CleanupRenderTargetDx12(false);
pSwapChain->Release();
return;
}
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource = g_mainRenderTargetResource[backBufferIdx];
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
result = g_pd3dCommandList->Reset(commandAllocator, nullptr);
if (result != S_OK)
{
LOG_ERROR("g_pd3dCommandList->Reset: {0:X}", (unsigned long) result);
pSwapChain->Release();
return;
}
g_pd3dCommandList->ResourceBarrier(1, &barrier);
g_pd3dCommandList->OMSetRenderTargets(1, &g_mainRenderTargetDescriptor[backBufferIdx], FALSE, NULL);
g_pd3dCommandList->SetDescriptorHeaps(1, &g_pd3dSrvDescHeap);
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), g_pd3dCommandList);
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
g_pd3dCommandList->ResourceBarrier(1, &barrier);
result = g_pd3dCommandList->Close();
if (result != S_OK)
{
LOG_ERROR("g_pd3dCommandList->Close: {0:X}", (unsigned long) result);
CleanupRenderTargetDx12(true);
pSwapChain->Release();
return;
}
ID3D12CommandList* ppCommandLists[] = { g_pd3dCommandList };
((ID3D12CommandQueue*) currentSCCommandQueue)->ExecuteCommandLists(1, ppCommandLists);
}
}
else
{
if (_showRenderImGuiDebugOnce)
LOG_INFO("!(ImGui::GetCurrentContext() && currentSCCommandQueue && g_mainRenderTargetResource[0])");
MenuOverlayBase::HideMenu();
_showRenderImGuiDebugOnce = false;
}
}
pSwapChain->Release();
}
ID3D12GraphicsCommandList* MenuOverlayDx::MenuCommandList() { return g_pd3dCommandList; }
void MenuOverlayDx::CleanupRenderTarget(bool clearQueue, HWND hWnd)
{
LOG_FUNC();
auto fg = State::Instance().currentFG;
if (fg != nullptr && fg->IsActive())
{
State::Instance().FGchanged = true;
fg->UpdateTarget();
fg->Deactivate();
}
if (_dx11Device)
CleanupRenderTargetDx11(false);
else
CleanupRenderTargetDx12(clearQueue);
}
void MenuOverlayDx::Present(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags,
const DXGI_PRESENT_PARAMETERS* pPresentParameters, IUnknown* pDevice, HWND hWnd, bool isUWP)
{
LOG_DEBUG("");
ID3D12CommandQueue* cq = nullptr;
ID3D11Device* device = nullptr;
ID3D12Device* device12 = nullptr;
// try to obtain directx objects and find the path
if (pDevice->QueryInterface(IID_PPV_ARGS(&device)) == S_OK)
{
if (!_dx11Device)
LOG_DEBUG("D3D11Device captured");
_dx11Device = true;
}
else if (pDevice->QueryInterface(IID_PPV_ARGS(&cq)) == S_OK)
{
if (!_dx12Device)
LOG_DEBUG("D3D12CommandQueue captured");
if (!CheckForRealObject(__FUNCTION__, pDevice, &currentSCCommandQueue))
currentSCCommandQueue = pDevice;
if (((ID3D12CommandQueue*) currentSCCommandQueue)->GetDevice(IID_PPV_ARGS(&device12)) == S_OK)
{
if (!_dx12Device)
LOG_DEBUG("D3D12Device captured");
_dx12Device = true;
}
}
// Process window handle changed, update base
if (MenuOverlayBase::Handle() != hWnd)
{
LOG_DEBUG("Handle changed");
if (MenuOverlayBase::IsInited())
MenuOverlayBase::Shutdown();
MenuOverlayBase::Init(hWnd, isUWP);
_isInited = false;
}
// Init
if (!_isInited)
{
if (_dx11Device)
{
CleanupRenderTargetDx11(false);
g_pd3dDevice = device;
CreateRenderTargetDx11(pSwapChain);
MenuOverlayBase::Dx11Ready();
_isInited = true;
}
else if (_dx12Device && (g_pd3dDeviceParam != nullptr || device12 != nullptr))
{
if (g_pd3dDeviceParam != nullptr && device12 == nullptr)
device12 = g_pd3dDeviceParam;
CleanupRenderTargetDx12(true);
g_pd3dCommandQueue = cq;
g_pd3dDeviceParam = device12;
MenuOverlayBase::Dx12Ready();
_isInited = true;
}
}
State::Instance().skipHeapCapture = true;
// Render menu
if (_dx11Device)
RenderImGui_DX11(pSwapChain);
else if (_dx12Device)
RenderImGui_DX12(pSwapChain);
State::Instance().skipHeapCapture = false;
// release used objects
if (cq != nullptr)
cq->Release();
if (device != nullptr)
device->Release();
if (device12 != nullptr)
device12->Release();
}