Add dxvk and vkd3d-proton checks

This commit is contained in:
FakeMichau
2026-03-01 02:39:25 +01:00
parent 5758105fc2
commit be8b0530f2
13 changed files with 114 additions and 139 deletions
-1
View File
@@ -194,7 +194,6 @@ class State
// Linux checks
bool isRunningOnLinux = false;
bool isRunningOnDXVK = false;
// Other checks
bool isRunningOnNvidia = false;
+7 -37
View File
@@ -587,45 +587,17 @@ static void CheckWorkingMode()
// dxgi.dll
if (lCaseFilename == "dxgi.dll")
{
do
{
auto pluginFilePath = pluginPath / L"dxgi.dll";
originalModule = NtdllProxy::LoadLibraryExW_Ldr(pluginFilePath.wstring().c_str(), NULL, 0);
// Init will try to load the specifc path itself
// It's to allow dxgi usage before this point
DxgiProxy::Init(nullptr, _passThruMode);
if (originalModule != nullptr)
{
if (!_passThruMode)
LOG_INFO("OptiScaler working as dxgi.dll, original dll loaded from plugin folder");
break;
}
originalModule = NtdllProxy::LoadLibraryExW_Ldr(L"dxgi-original.dll", NULL, 0);
if (originalModule != nullptr)
{
if (!_passThruMode)
LOG_INFO("OptiScaler working as dxgi.dll, dxgi-original.dll loaded");
break;
}
auto sysFilePath = sysPath / L"dxgi.dll";
originalModule = NtdllProxy::LoadLibraryExW_Ldr(sysFilePath.wstring().c_str(), NULL, 0);
if (originalModule != nullptr && !_passThruMode)
LOG_INFO("OptiScaler working as dxgi.dll, system dll loaded");
} while (false);
if (originalModule != nullptr)
if (DxgiProxy::Module() != nullptr)
{
dllNames.push_back("dxgi.dll");
dllNames.push_back("dxgi");
dllNamesW.push_back(L"dxgi.dll");
dllNamesW.push_back(L"dxgi");
DxgiProxy::Init(originalModule);
dxgi.LoadOriginalLibrary(originalModule);
State::Instance().isDxgiMode = true;
@@ -839,7 +811,7 @@ static void CheckWorkingMode()
}
// Vulkan
if (State::Instance().isRunningOnDXVK || State::Instance().isRunningOnLinux ||
if (IdentifyGpu::getPrimaryGpu().usesDxvk || State::Instance().isRunningOnLinux ||
(State::Instance().gameQuirks & GameQuirk::LoadVulkanManually))
{
vulkanModule = NtdllProxy::LoadLibraryExW_Ldr(L"vulkan-1.dll", NULL, 0);
@@ -1770,7 +1742,8 @@ DWORD WINAPI InitThread(LPVOID hModuleVoid)
// Check for Wine
spdlog::info("");
State::Instance().isRunningOnLinux = IsRunningOnWine();
State::Instance().isRunningOnDXVK = State::Instance().isRunningOnLinux;
auto primaryGpu = IdentifyGpu::getPrimaryGpu();
// Check if real DLSS available
if (Config::Instance()->DLSSEnabled.value_or_default())
@@ -1827,10 +1800,7 @@ DWORD WINAPI InitThread(LPVOID hModuleVoid)
spdlog::info("");
CheckWorkingMode();
auto d = IdentifyGpu::getGpuInfo();
// OptiFG & Overlay Checks
// TODO: Either FGInput == FGInput::Upscaler or FGOutput == FGOutput::FSRFG
if ((Config::Instance()->FGInput.value_or_default() == FGInput::Upscaler) &&
!Config::Instance()->DisableOverlays.has_value())
Config::Instance()->DisableOverlays.set_volatile_value(true);
+1
View File
@@ -27,6 +27,7 @@ void CheckForGPU();
std::vector<std::filesystem::path> GetDriverStore();
HMODULE GetFSR4Module();
// Based on the PDBs provided with FFX 2.1.0
enum AmdExtWaveMatrixPropertiesType : int32_t
{
float16 = 0x0,
-34
View File
@@ -20,28 +20,6 @@
#include <magic_enum.hpp>
#endif
void DxgiFactoryHooks::CheckAdapter(IUnknown* unkAdapter)
{
if (State::Instance().isRunningOnDXVK)
return;
// DXVK VkInterface GUID
const GUID guid = { 0x907bf281, 0xea3c, 0x43b4, { 0xa8, 0xe4, 0x9f, 0x23, 0x11, 0x07, 0xb4, 0xff } };
IDXGIAdapter* adapter = nullptr;
bool adapterOk = unkAdapter->QueryInterface(IID_PPV_ARGS(&adapter)) == S_OK;
void* dxvkAdapter = nullptr;
if (adapterOk && adapter->QueryInterface(guid, &dxvkAdapter) == S_OK)
{
State::Instance().isRunningOnDXVK = dxvkAdapter != nullptr;
((IDXGIAdapter*) dxvkAdapter)->Release();
}
if (adapterOk)
adapter->Release();
}
void DxgiFactoryHooks::HookToFactory(IDXGIFactory* pFactory)
{
if (pFactory == nullptr)
@@ -920,10 +898,7 @@ HRESULT DxgiFactoryHooks::EnumAdapters(IDXGIFactory* realFactory, UINT Adapter,
}
if (result == S_OK)
{
CheckAdapter(*ppAdapter);
DxgiSpoofing::AttachToAdapter(*ppAdapter);
}
#if _DEBUG
LOG_TRACE("result: {:X}, Adapter: {}, pAdapter: {:X}", (UINT) result, Adapter, (uintptr_t) *ppAdapter);
@@ -997,10 +972,7 @@ HRESULT DxgiFactoryHooks::EnumAdapters1(IDXGIFactory1* realFactory, UINT Adapter
}
if (result == S_OK)
{
CheckAdapter(*ppAdapter);
DxgiSpoofing::AttachToAdapter(*ppAdapter);
}
#if _DEBUG
LOG_TRACE("result: {:X}, Adapter: {}, pAdapter: {:X}", (UINT) result, Adapter, (uintptr_t) *ppAdapter);
@@ -1019,10 +991,7 @@ HRESULT DxgiFactoryHooks::EnumAdapterByLuid(IDXGIFactory4* realFactory, LUID Ada
}
if (result == S_OK)
{
CheckAdapter((IUnknown*) *ppvAdapter);
DxgiSpoofing::AttachToAdapter((IUnknown*) *ppvAdapter);
}
#if _DEBUG
LOG_TRACE("result: {:X}, pAdapter: {:X}", (UINT) result, (uintptr_t) *ppvAdapter);
@@ -1042,10 +1011,7 @@ HRESULT DxgiFactoryHooks::EnumAdapterByGpuPreference(IDXGIFactory6* realFactory,
}
if (result == S_OK)
{
CheckAdapter((IUnknown*) *ppvAdapter);
DxgiSpoofing::AttachToAdapter((IUnknown*) *ppvAdapter);
}
#if _DEBUG
LOG_TRACE("result: {:X}, Adapter: {}, pAdapter: {:X}", (UINT) result, Adapter, (uintptr_t) *ppvAdapter);
-2
View File
@@ -53,8 +53,6 @@ class DxgiFactoryHooks
inline static bool _skipFGSwapChainCreation = false;
inline static bool _skipHighPerfCheck = false;
static void CheckAdapter(IUnknown* unkAdapter);
class ScopedSkipFGSCCreation
{
private:
@@ -18,28 +18,6 @@
#include <magic_enum.hpp>
#endif
void DxgiFactoryWrappedCalls::CheckAdapter(IUnknown* unkAdapter)
{
if (State::Instance().isRunningOnDXVK)
return;
// DXVK VkInterface GUID
const GUID guid = { 0x907bf281, 0xea3c, 0x43b4, { 0xa8, 0xe4, 0x9f, 0x23, 0x11, 0x07, 0xb4, 0xff } };
IDXGIAdapter* adapter = nullptr;
bool adapterOk = unkAdapter->QueryInterface(IID_PPV_ARGS(&adapter)) == S_OK;
void* dxvkAdapter = nullptr;
if (adapterOk && adapter->QueryInterface(guid, &dxvkAdapter) == S_OK)
{
State::Instance().isRunningOnDXVK = dxvkAdapter != nullptr;
((IDXGIAdapter*) dxvkAdapter)->Release();
}
if (adapterOk)
adapter->Release();
}
HRESULT DxgiFactoryWrappedCalls::CreateSwapChain(IDXGIFactory* realFactory, WrappedIDXGIFactory7* wrappedFactory,
IUnknown* pDevice, const DXGI_SWAP_CHAIN_DESC* pDesc,
IDXGISwapChain** ppSwapChain)
@@ -810,10 +788,7 @@ HRESULT DxgiFactoryWrappedCalls::EnumAdapters(IDXGIFactory* realFactory, UINT Ad
}
if (result == S_OK)
{
CheckAdapter(*ppAdapter);
DxgiSpoofing::AttachToAdapter(*ppAdapter);
}
#if _DEBUG
LOG_TRACE("result: {:X}, Adapter: {}, pAdapter: {:X}", (UINT) result, Adapter, (uintptr_t) *ppAdapter);
@@ -886,10 +861,7 @@ HRESULT DxgiFactoryWrappedCalls::EnumAdapters1(IDXGIFactory1* realFactory, UINT
}
if (result == S_OK)
{
CheckAdapter(*ppAdapter);
DxgiSpoofing::AttachToAdapter(*ppAdapter);
}
#if _DEBUG
LOG_TRACE("result: {:X}, Adapter: {}, pAdapter: {:X}", (UINT) result, Adapter, (uintptr_t) *ppAdapter);
@@ -909,10 +881,7 @@ HRESULT DxgiFactoryWrappedCalls::EnumAdapterByLuid(IDXGIFactory4* realFactory, L
}
if (result == S_OK)
{
CheckAdapter((IUnknown*) *ppvAdapter);
DxgiSpoofing::AttachToAdapter((IUnknown*) *ppvAdapter);
}
#if _DEBUG
LOG_TRACE("result: {:X}, pAdapter: {:X}", (UINT) result, (uintptr_t) *ppvAdapter);
@@ -933,10 +902,7 @@ HRESULT DxgiFactoryWrappedCalls::EnumAdapterByGpuPreference(IDXGIFactory6* realF
}
if (result == S_OK)
{
CheckAdapter((IUnknown*) *ppvAdapter);
DxgiSpoofing::AttachToAdapter((IUnknown*) *ppvAdapter);
}
#if _DEBUG
LOG_TRACE("result: {:X}, Adapter: {}, pAdapter: {:X}", (UINT) result, Adapter, (uintptr_t) *ppvAdapter);
@@ -29,8 +29,6 @@ class DxgiFactoryWrappedCalls
inline static bool _skipFGSwapChainCreation = false;
inline static bool _skipHighPerfCheck = false;
static void CheckAdapter(IUnknown* unkAdapter);
class ScopedSkipFGSCCreation
{
private:
+4 -1
View File
@@ -18,6 +18,7 @@
#include <vulkan/vulkan.hpp>
#include <detours/detours.h>
#include <misc/IdentifyGpu.h>
// for menu rendering
static VkDevice _device = VK_NULL_HANDLE;
@@ -183,7 +184,9 @@ static VkResult hkvkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPres
// get upscaler time
UpscalerTimeVk::ReadUpscalingTime(_device);
if (!State::Instance().isRunningOnDXVK)
// ??? TODO: if we are hooking dxvk's vulkan calls then this present call could be either coming from dxvk or from a
// native vk game
if (!IdentifyGpu::getPrimaryGpu().usesDxvk)
State::Instance().swapchainApi = Vulkan;
// Tick feature to let it know if it's frozen
+9 -4
View File
@@ -23,6 +23,7 @@
#include <array>
#include <chrono>
#include <misc/IdentifyGpu.h>
#define MARK_ALL_BACKENDS_CHANGED() \
for (auto& singleChangeBackend : State::Instance().changeBackend) \
@@ -1872,7 +1873,11 @@ bool MenuCommon::RenderMenu()
ImGuiWindowFlags_NoNav))
{
std::string api;
if (state.isRunningOnDXVK || state.isRunningOnLinux)
if (IdentifyGpu::getPrimaryGpu().usesDxvk && state.api == DX11)
{
api = "DXVK";
}
else if (IdentifyGpu::getPrimaryGpu().usesVkd3dProton && state.api == DX12)
{
api = "VKD3D";
}
@@ -2390,7 +2395,7 @@ bool MenuCommon::RenderMenu()
else if (state.DeviceAdapterNames.contains(state.currentD3D12Device))
ImGui::Text(state.DeviceAdapterNames[state.currentD3D12Device].c_str());
ImGui::Text("D3D11 %s| %s %d.%d.%d", state.isRunningOnDXVK ? "(DXVK) " : "",
ImGui::Text("D3D11 %s| %s %d.%d.%d", IdentifyGpu::getPrimaryGpu().usesDxvk ? "(DXVK) " : "",
currentFeature->Name().c_str(), currentFeature->Version().major,
currentFeature->Version().minor, currentFeature->Version().patch);
ImGui::SameLine(0.0f, 6.0f);
@@ -2409,7 +2414,7 @@ bool MenuCommon::RenderMenu()
if (state.DeviceAdapterNames.contains(state.currentD3D12Device))
ImGui::Text(state.DeviceAdapterNames[state.currentD3D12Device].c_str());
ImGui::Text("D3D12 %s| %s %d.%d.%d", state.isRunningOnDXVK ? "(DXVK) " : "",
ImGui::Text("D3D12 %s| %s %d.%d.%d", IdentifyGpu::getPrimaryGpu().usesDxvk ? "(DXVK) " : "",
currentFeature->Name().c_str(), currentFeature->Version().major,
currentFeature->Version().minor, currentFeature->Version().patch);
ImGui::SameLine(0.0f, 6.0f);
@@ -2428,7 +2433,7 @@ bool MenuCommon::RenderMenu()
if (state.DeviceAdapterNames.contains(state.currentVkDevice))
ImGui::Text(state.DeviceAdapterNames[state.currentVkDevice].c_str());
ImGui::Text("Vulkan %s| %s %d.%d.%d", state.isRunningOnDXVK ? "(DXVK) " : "",
ImGui::Text("Vulkan %s| %s %d.%d.%d", IdentifyGpu::getPrimaryGpu().usesDxvk ? "(DXVK) " : "",
currentFeature->Name().c_str(), currentFeature->Version().major,
currentFeature->Version().minor, currentFeature->Version().patch);
ImGui::SameLine(0.0f, 6.0f);
+30 -19
View File
@@ -10,7 +10,7 @@ std::optional<std::vector<GpuInformation>> IdentifyGpu::cachedInfo;
using Microsoft::WRL::ComPtr;
// Maybe take into account some other GPU ordering, DxgiFactoryWrappedCalls::EnumAdapters?
// TODO: Maybe take into account some other GPU ordering, DxgiFactoryWrappedCalls::EnumAdapters?
constexpr int vendorPriority(VendorId::Value v)
{
switch (v)
@@ -75,7 +75,13 @@ void IdentifyGpu::checkGpuInfo()
std::wstring szName(adapterDesc.Description);
gpuInfo.name = wstring_to_string(szName);
if (gpuInfo.vendorId == VendorId::AMD)
// TODO: check if an invalid guid still return S_OK but nullptr
ComPtr<IDXGIVkInteropDevice> dxgiInterop;
if (SUCCEEDED(adapter->QueryInterface(IID_PPV_ARGS(&dxgiInterop))))
gpuInfo.usesDxvk = true;
// Needed to be able to query amdxc and check for vkd3d-proton
if (gpuInfo.vendorId == VendorId::AMD || gpuInfo.usesDxvk)
{
auto result =
D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&gpuInfo.d3d12device));
@@ -96,6 +102,10 @@ void IdentifyGpu::checkGpuInfo()
for (auto& gpuInfo : localCachedInfo)
{
ComPtr<ID3D12DXVKInteropDevice> vkd3dInterop;
if (gpuInfo.usesDxvk && SUCCEEDED(gpuInfo.d3d12device->QueryInterface(IID_PPV_ARGS(&vkd3dInterop))))
gpuInfo.usesVkd3dProton = true;
if (gpuInfo.vendorId == VendorId::AMD && gpuInfo.d3d12device)
{
auto moduleAmdxc64 = KernelBaseProxy::GetModuleHandleW_()(L"amdxc64.dll");
@@ -119,32 +129,27 @@ void IdentifyGpu::checkGpuInfo()
{
HRESULT float8support =
amdExtD3DShaderIntrinsics->CheckSupport(AmdExtD3DShaderIntrinsicsSupport_Float8Conversion);
gpuInfo.fsr4capable = float8support == S_OK;
gpuInfo.fsr4Capable = float8support == S_OK;
}
}
if (!gpuInfo.fsr4capable /*&& State::Instance().isRunningOnLinux*/)
if (!gpuInfo.fsr4Capable && gpuInfo.usesVkd3dProton)
{
ComPtr<ID3D12DXVKInteropDevice> interop;
UINT extensionCount = 0;
if (SUCCEEDED(gpuInfo.d3d12device->QueryInterface(IID_PPV_ARGS(&interop))))
if (SUCCEEDED(vkd3dInterop->GetDeviceExtensions(&extensionCount, nullptr)) && extensionCount > 0)
{
UINT extensionCount = 0;
std::vector<const char*> exts(extensionCount);
if (SUCCEEDED(interop->GetDeviceExtensions(&extensionCount, nullptr)) && extensionCount > 0)
if (SUCCEEDED(vkd3dInterop->GetDeviceExtensions(&extensionCount, exts.data())))
{
std::vector<const char*> exts(extensionCount);
if (SUCCEEDED(interop->GetDeviceExtensions(&extensionCount, exts.data())))
for (UINT i = 0; i < extensionCount; i++)
{
for (UINT i = 0; i < extensionCount; i++)
// Only RDNA4+
if (!strcmp("VK_EXT_shader_float8", exts[i]))
{
// Only RDNA4+
if (!strcmp("VK_EXT_shader_float8", exts[i]))
{
gpuInfo.fsr4capable = true;
break;
}
gpuInfo.fsr4Capable = true;
break;
}
}
}
@@ -236,10 +241,16 @@ void IdentifyGpu::checkGpuInfo()
}
}
std::vector<GpuInformation> IdentifyGpu::getGpuInfo()
std::vector<GpuInformation> IdentifyGpu::getAllGpus()
{
if (!cachedInfo.has_value())
checkGpuInfo();
return cachedInfo.value();
}
GpuInformation IdentifyGpu::getPrimaryGpu()
{
auto allGpus = getAllGpus();
return allGpus.size() > 0 ? allGpus[0] : GpuInformation {};
}
+19 -3
View File
@@ -1,6 +1,7 @@
#pragma once
#include <nvapi.h>
// vkd3d-proton
MIDL_INTERFACE("39da4e09-bd1c-4198-9fae-86bbe3be41fd")
ID3D12DXVKInteropDevice : public IUnknown
{
@@ -29,14 +30,24 @@ ID3D12DXVKInteropDevice : public IUnknown
virtual HRESULT STDMETHODCALLTYPE UnlockCommandQueue(ID3D12CommandQueue * pCommandQueue) = 0;
};
// dxvk
MIDL_INTERFACE("e2ef5fa5-dc21-4af7-90c4-f67ef6a09323")
IDXGIVkInteropDevice : public IUnknown
{
virtual void STDMETHODCALLTYPE GetVulkanHandles(VkInstance * pInstance, VkPhysicalDevice * pPhysDev,
VkDevice * pDevice) = 0;
};
struct GpuInformation
{
LUID luid {}; // Unique id to be able to reference the exact GPU, VkPhysicalDeviceIDProperties
std::string name {};
VendorId::Value vendorId = VendorId::Invalid;
size_t dedicatedVramInBytes = 0;
bool usesDxvk = false;
bool usesVkd3dProton = false;
bool fsr4capable = false;
bool fsr4Capable = false;
ID3D12Device* d3d12device = nullptr;
bool dlssCapable = false;
@@ -47,11 +58,15 @@ struct GpuInformation
// vulkan caps
// - Check Nvidia Arch, watch out for fakenvapi
// - Check vram amount
// - Check if dxgi uses dxvk, some dxvk specific call?
//
// - Check hags support? watch out for linux
// - Check if dxgi uses dxvk, some dxvk specific call?
// - Check if vkd3d-proton is being used? Could be helpful to display in menu
// - Check vulkan driver? radv vs amdvlk etc
// - Opti in many spots assumes a single GPU and that all handles are coming from that gpu,
// might need to always check if LUID of the held device matches the one provided by this class
// before trying to use any info from here. Could also create a method to query GpuInformation based on LUID
// - Look at removing state.DeviceAdapterNames
class IdentifyGpu
{
@@ -61,5 +76,6 @@ class IdentifyGpu
public:
// Sorted by priority, the first one should be treated as the primary one
static std::vector<GpuInformation> getGpuInfo();
static std::vector<GpuInformation> getAllGpus();
static GpuInformation getPrimaryGpu();
};
+42 -1
View File
@@ -20,7 +20,7 @@ class DxgiProxy
typedef HRESULT (*PFN_DeclareAdepterRemovalSupport)();
typedef HRESULT (*PFN_GetDebugInterface)(UINT Flags, REFIID riid, void** ppDebug);
static void Init(HMODULE module = nullptr)
static void Init(HMODULE module = nullptr, bool passThruMode = false)
{
if (_dll != nullptr)
return;
@@ -29,6 +29,47 @@ class DxgiProxy
{
_dll = GetModuleHandle(L"dxgi.dll");
if (_dll == dllModule)
{
wchar_t sysFolder[MAX_PATH];
GetSystemDirectory(sysFolder, MAX_PATH);
std::filesystem::path sysPath(sysFolder);
std::filesystem::path pluginPath(Config::Instance()->PluginPath.value_or_default());
do
{
auto pluginFilePath = pluginPath / L"dxgi.dll";
originalModule = NtdllProxy::LoadLibraryExW_Ldr(pluginFilePath.wstring().c_str(), NULL, 0);
if (originalModule != nullptr)
{
if (!passThruMode)
LOG_INFO("OptiScaler working as dxgi.dll, original dll loaded from plugin folder");
break;
}
originalModule = NtdllProxy::LoadLibraryExW_Ldr(L"dxgi-original.dll", NULL, 0);
if (originalModule != nullptr)
{
if (!passThruMode)
LOG_INFO("OptiScaler working as dxgi.dll, dxgi-original.dll loaded");
break;
}
auto sysFilePath = sysPath / L"dxgi.dll";
originalModule = NtdllProxy::LoadLibraryExW_Ldr(sysFilePath.wstring().c_str(), NULL, 0);
if (originalModule != nullptr && !passThruMode)
LOG_INFO("OptiScaler working as dxgi.dll, system dll loaded");
_dll = originalModule;
} while (false);
}
if (_dll == nullptr)
_dll = NtdllProxy::LoadLibraryExW_Ldr(L"dxgi.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
}
+2 -1
View File
@@ -16,6 +16,7 @@
#include <d3d11.h>
#include <d3d12.h>
#include <misc/IdentifyGpu.h>
#pragma intrinsic(_ReturnAddress)
@@ -214,7 +215,7 @@ static HRESULT LocalPresent(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT
}
// DXVK check, it's here because of upscaler time calculations
if (State::Instance().isRunningOnDXVK)
if (IdentifyGpu::getPrimaryGpu().usesDxvk)
{
if (pPresentParameters == nullptr)
presentResult = pSwapChain->Present(SyncInterval, Flags);