From abc6ecba08030cbdaedf149c3ca8a631ba6f09ac Mon Sep 17 00:00:00 2001 From: FakeMichau <49685661+FakeMichau@users.noreply.github.com> Date: Mon, 22 Jun 2026 16:30:04 +0200 Subject: [PATCH] Add FSR 4.1.1 support --- OptiScaler.ini | 12 ++- OptiScaler/Config.cpp | 15 ++- OptiScaler/Config.h | 4 +- OptiScaler/OptiTypes.cpp | 2 +- OptiScaler/OptiTypes.h | 8 ++ OptiScaler/State.h | 3 +- OptiScaler/dllmain.cpp | 14 +++ OptiScaler/fsr4/FSR4ModelSelection.cpp | 45 +++++++- OptiScaler/fsr4/FSR4ModelSelection.h | 3 + OptiScaler/fsr4/FSR4Upgrade.cpp | 53 +++++++++- OptiScaler/fsr4/FSR4Upgrade.h | 4 + OptiScaler/hooks/Amdxc64_Hooks.cpp | 29 +++++- OptiScaler/hooks/Amdxc64_Hooks.h | 1 + OptiScaler/hooks/Gdi32_Hooks.h | 63 ++++++++---- OptiScaler/hooks/Kernel_Hooks.cpp | 44 +++++++- OptiScaler/hooks/Kernel_Hooks.h | 9 +- OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp | 2 +- OptiScaler/menu/menu_common.cpp | 31 +++--- OptiScaler/misc/IdentifyGpu.cpp | 136 ++++++++++++++++++++----- OptiScaler/misc/IdentifyGpu.h | 5 +- OptiScaler/spoofing/Dxgi_Spoofing.cpp | 11 ++ 21 files changed, 409 insertions(+), 85 deletions(-) diff --git a/OptiScaler.ini b/OptiScaler.ini index bf50b768..434d5ad5 100644 --- a/OptiScaler.ini +++ b/OptiScaler.ini @@ -604,14 +604,16 @@ UseReactiveMaskForTransparency=auto ; 0.0 - 0.9 - Default (auto) is 0.45 DlssReactiveMaskBias=auto -; Forces Opti to try to use FSR4 in case the GPU detection says otherwise -; true or false - Default (auto) is false -Fsr4ForceCapable=auto +; Forces Opti to try to use a specific FSR 4 model +; Don't fool yourself when trying to force FP8 on actually unsupported GPUs, driver has a FSR 3 fallback +; 0 = No override | 1 = FP8 | 2 = INT8 +; From 0 to 2 - Default (auto) is 0 +Fsr4ForceModel=auto -; Select FSR4 model to use +; Select internal FSR4 preset to use ; 0 = For FSR Native AA, 1 = Ultra Quality/Quality, 2 = Balanced, 3 = Performance, 4 = DRS, 5 = Ultra Performance ; From 0 to 5 - Default (auto) is game's default -Fsr4Model=auto +Fsr4Preset=auto ; Enable FSR4 Watermark ; true or false - Default (auto) is false diff --git a/OptiScaler/Config.cpp b/OptiScaler/Config.cpp index 2c6f84cb..b8f03a33 100644 --- a/OptiScaler/Config.cpp +++ b/OptiScaler/Config.cpp @@ -262,12 +262,17 @@ bool Config::Reload(std::filesystem::path iniPath) FfxFGIndex.set_from_config(readInt("FSR", "FGIndex")); FsrUseMaskForTransparency.set_from_config(readBool("FSR", "UseReactiveMaskForTransparency")); DlssReactiveMaskBias.set_from_config(readFloat("FSR", "DlssReactiveMaskBias")); - Fsr4ForceCapable.set_from_config(readBool("FSR", "Fsr4ForceCapable")); + + if (auto v = readEnum("FSR", "Fsr4ForceModel")) + Fsr4ForceModel.set_from_config(*v); + else + Fsr4ForceModel.reset(); + Fsr4EnableWatermark.set_from_config(readBool("FSR", "Fsr4EnableWatermark")); Fsr4DoNotLoadAmdxc64.set_from_config(readBool("FSR", "Fsr4DoNotLoadAmdxc64")); - if (auto setting = readInt("FSR", "Fsr4Model"); setting.has_value() && setting >= 0 && setting <= 5) - Fsr4Model.set_from_config(setting); + if (auto setting = readInt("FSR", "Fsr4Preset"); setting.has_value() && setting >= 0 && setting <= 5) + Fsr4Preset.set_from_config(setting); FsrNonLinearColorSpace.set_from_config(readBool("FSR", "FsrNonLinearColorSpace")); FsrNonLinearPQ.set_from_config(readBool("FSR", "FsrNonLinearPQ")); @@ -1052,8 +1057,8 @@ bool Config::SaveIni() GetBoolValue(Instance()->FsrUseMaskForTransparency.value_for_config()).c_str()); ini.SetValue("FSR", "DlssReactiveMaskBias", GetFloatValue(Instance()->DlssReactiveMaskBias.value_for_config()).c_str()); - ini.SetValue("FSR", "Fsr4ForceCapable", GetBoolValue(Instance()->Fsr4ForceCapable.value_for_config()).c_str()); - ini.SetValue("FSR", "Fsr4Model", GetIntValue(Instance()->Fsr4Model.value_for_config()).c_str()); + ini.SetValue("FSR", "Fsr4ForceModel", GetIntValue(Instance()->Fsr4ForceModel.value_for_config()).c_str()); + ini.SetValue("FSR", "Fsr4Preset", GetIntValue(Instance()->Fsr4Preset.value_for_config()).c_str()); ini.SetValue("FSR", "Fsr4EnableWatermark", GetBoolValue(Instance()->Fsr4EnableWatermark.value_for_config()).c_str()); ini.SetValue("FSR", "Fsr4DoNotLoadAmdxc64", diff --git a/OptiScaler/Config.h b/OptiScaler/Config.h index 0e9a2004..7ef3f8e4 100644 --- a/OptiScaler/Config.h +++ b/OptiScaler/Config.h @@ -436,8 +436,8 @@ class Config CustomOptional FsrMinDisOccAcc { -0.333f }; // FSR4 - CustomOptional Fsr4ForceCapable { false }; - CustomOptional Fsr4Model; + CustomOptional Fsr4ForceModel { FSR4Support::None }; + CustomOptional Fsr4Preset; CustomOptional Fsr4EnableWatermark { false }; CustomOptional Fsr4DoNotLoadAmdxc64 { false }; diff --git a/OptiScaler/OptiTypes.cpp b/OptiScaler/OptiTypes.cpp index 495ba242..db77b031 100644 --- a/OptiScaler/OptiTypes.cpp +++ b/OptiScaler/OptiTypes.cpp @@ -36,7 +36,7 @@ std::string ApiUpscalerInputName(ApiUpscalerInput upscaler) std::string UpscalerDisplayName(Upscaler upscaler, API api) { - bool fsr4Capable = IdentifyGpu::getPrimaryGpu().fsr4Capable; + bool fsr4Capable = IdentifyGpu::getPrimaryGpu().fsr4Support != FSR4Support::None; switch (upscaler) { diff --git a/OptiScaler/OptiTypes.h b/OptiScaler/OptiTypes.h index 5a925301..33a0a0c9 100644 --- a/OptiScaler/OptiTypes.h +++ b/OptiScaler/OptiTypes.h @@ -91,6 +91,14 @@ enum class SharpenShader LocalContrastDepthAware }; +enum class FSR4Support : uint8_t +{ + None = 0, + FP8 = 1, + INT8 = 2, + Count +}; + std::string ApiUpscalerInputName(ApiUpscalerInput upscaler); std::string UpscalerDisplayName(Upscaler upscaler, API api = API::NotSelected); diff --git a/OptiScaler/State.h b/OptiScaler/State.h index c568891a..1a09bfe8 100644 --- a/OptiScaler/State.h +++ b/OptiScaler/State.h @@ -60,6 +60,7 @@ enum class WorkingMode : uint32_t enum class PostCode : uint32_t { SlPluginsAlreadyInMemory, + TryingFsr4Fp8OnUnsupported, _ }; @@ -241,7 +242,7 @@ class State std::vector ffxUpscalerVersionIds {}; std::vector ffxFGVersionNames {}; std::vector ffxFGVersionIds {}; - std::optional currentFsr4Model {}; + std::optional currentFsr4Preset {}; // Linux checks bool isRunningOnLinux = false; diff --git a/OptiScaler/dllmain.cpp b/OptiScaler/dllmain.cpp index 3502356b..66104aa8 100644 --- a/OptiScaler/dllmain.cpp +++ b/OptiScaler/dllmain.cpp @@ -1719,6 +1719,20 @@ DWORD WINAPI getGpuInfo(LPVOID hModuleVoid) if (primaryGpu.vendorId == VendorId::AMD) Amdxc64Hooks::Init(); + if (Config::Instance()->Fsr4ForceModel.value_or_default() == FSR4Support::INT8) + { + // We need spoofing hooks for FFX but want to avoid spoofing for the rest of the game + if (!Config::Instance()->DxgiSpoofing.value_or_default()) + { + std::wstring wname = string_to_wstring(primaryGpu.name); + Config::Instance()->SpoofedVendorId.set_volatile_value(primaryGpu.vendorId); + Config::Instance()->SpoofedDeviceId.set_volatile_value(primaryGpu.deviceId); + Config::Instance()->SpoofedGPUName.set_volatile_value(wname); + } + + Config::Instance()->DxgiSpoofing.set_volatile_value(true); + } + // If DX12 already loaded then grab the full GPU info right away if (hModuleVoid) IdentifyGpu::updateD3d12Capabilities(); diff --git a/OptiScaler/fsr4/FSR4ModelSelection.cpp b/OptiScaler/fsr4/FSR4ModelSelection.cpp index bf0277af..db83d569 100644 --- a/OptiScaler/fsr4/FSR4ModelSelection.cpp +++ b/OptiScaler/fsr4/FSR4ModelSelection.cpp @@ -7,6 +7,7 @@ PFN_getModelBlob FSR4ModelSelection::o_getModelBlobSDK = nullptr; PFN_getModelBlob FSR4ModelSelection::o_getModelBlobDriver = nullptr; PFN_createModel FSR4ModelSelection::o_createModelSDK = nullptr; PFN_createModel FSR4ModelSelection::o_createModelDriver = nullptr; +PFN_createModel2 FSR4ModelSelection::o_createModelDriver2 = nullptr; uint32_t getCorrectedPreset(uint32_t preset) { @@ -24,12 +25,12 @@ uint32_t getCorrectedPreset(uint32_t preset) correctedPreset = 1; } - if (Config::Instance()->Fsr4Model.has_value()) + if (Config::Instance()->Fsr4Preset.has_value()) { - correctedPreset = Config::Instance()->Fsr4Model.value(); + correctedPreset = Config::Instance()->Fsr4Preset.value(); } - State::Instance().currentFsr4Model = correctedPreset; + State::Instance().currentFsr4Preset = correctedPreset; return correctedPreset; } @@ -78,6 +79,17 @@ uint64_t FSR4ModelSelection::hkcreateModelDriver(void* context, uint32_t preset) return result; } +uint64_t FSR4ModelSelection::hkcreateModelDriver2(void* context, uint32_t preset, void** model) +{ + LOG_FUNC(); + + preset = getCorrectedPreset(preset); + + auto result = o_createModelDriver2(context, preset, model); + + return result; +} + void FSR4ModelSelection::Hook(HMODULE module, FSR4Source source) { if (module == nullptr) @@ -238,7 +250,32 @@ void FSR4ModelSelection::Hook(HMODULE module, FSR4Source source) } } - if (!o_createModelDriver && !o_getModelBlobDriver && source == FSR4Source::DriverDll) + if (!o_createModelDriver && !o_createModelDriver2 && source == FSR4Source::DriverDll) + { + // From amdxcffx64 2.3.0 + const char* pattern = "48 8B C4 48 89 58 ? 55 56 57 41 54 41 55 41 56 41 57 48 8D A8 ? ? ? ? 48 81 EC ? ? ? ? " + "0F 29 70 ? 0F 29 78 ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? 4D 8B E8 8B FA 48 8B"; + o_createModelDriver2 = (PFN_createModel2) scanner::GetAddress(module, pattern); + + if (o_createModelDriver2) + { + LOG_DEBUG("Hooking model selection, o_createModelDriver2: {:X}", (uintptr_t) o_createModelDriver2); + + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + + DetourAttach(&(PVOID&) o_createModelDriver2, hkcreateModelDriver2); + + auto detourResult = DetourTransactionCommit(); + if (detourResult != NO_ERROR) + { + LOG_ERROR("Failed to attach detour: {:X}", detourResult); + o_createModelDriver2 = nullptr; + } + } + } + + if (!o_createModelDriver && !o_getModelBlobDriver && !o_createModelDriver2 && source == FSR4Source::DriverDll) LOG_ERROR("Couldn't hook model selection from the driver dll"); else if (!o_createModelSDK && !o_getModelBlobSDK && source == FSR4Source::SDK) LOG_ERROR("Couldn't hook model selection from the SDK dll"); diff --git a/OptiScaler/fsr4/FSR4ModelSelection.h b/OptiScaler/fsr4/FSR4ModelSelection.h index 7dc8e6ac..070dddd2 100644 --- a/OptiScaler/fsr4/FSR4ModelSelection.h +++ b/OptiScaler/fsr4/FSR4ModelSelection.h @@ -4,6 +4,7 @@ typedef uint64_t (*PFN_getModelBlob)(uint32_t preset, uint64_t unknown, uint64_t* source, uint64_t* size); typedef uint64_t (*PFN_createModel)(void* context, uint32_t preset); +typedef uint64_t (*PFN_createModel2)(void* context, uint32_t preset, void** model); enum class FSR4Source { @@ -21,6 +22,8 @@ class FSR4ModelSelection static uint64_t hkcreateModelDriver(void* context, uint32_t preset); static PFN_createModel o_createModelSDK; static PFN_createModel o_createModelDriver; + static uint64_t hkcreateModelDriver2(void* context, uint32_t preset, void** model); + static PFN_createModel2 o_createModelDriver2; public: static void Hook(HMODULE module, FSR4Source source); diff --git a/OptiScaler/fsr4/FSR4Upgrade.cpp b/OptiScaler/fsr4/FSR4Upgrade.cpp index 5e33b55a..5c40a033 100644 --- a/OptiScaler/fsr4/FSR4Upgrade.cpp +++ b/OptiScaler/fsr4/FSR4Upgrade.cpp @@ -2,6 +2,7 @@ #include "FSR4Upgrade.h" #include +#include struct ffxProviderInterface { @@ -39,6 +40,21 @@ HRESULT STDMETHODCALLTYPE AmdExtFfxApi::UpdateFfxApiProvider(void* pData, uint32 if (effectType >= FFXStructType::Unknown) effect = "???"; + static std::optional sdkSupportsInt8; + static std::optional amdxcffx64SupportsInt8; + + if (effectType == FFXStructType::Upscaling && !sdkSupportsInt8.has_value()) + { + wchar_t sdkDllPath[MAX_PATH] = { 0 }; + GetModuleFileNameW(callerModule, sdkDllPath, MAX_PATH); + + Util::version_t sdkVersion; + Util::GetFileVersion(sdkDllPath, nullptr, &sdkVersion); + + sdkSupportsInt8 = sdkVersion >= Util::version_t(4, 1, 1, 0); + IdentifyGpu::updateInt8Support(sdkSupportsInt8, amdxcffx64SupportsInt8); + } + if (o_UpdateFfxApiProvider == nullptr) { FSR4Upgrade::moduleAmdxcffx64 = nullptr; @@ -77,10 +93,23 @@ HRESULT STDMETHODCALLTYPE AmdExtFfxApi::UpdateFfxApiProvider(void* pData, uint32 if (FSR4Upgrade::moduleAmdxcffx64) { FSR4ModelSelection::Hook(FSR4Upgrade::moduleAmdxcffx64, FSR4Source::DriverDll); + + wchar_t driverDllPath[MAX_PATH] = { 0 }; + GetModuleFileNameW(FSR4Upgrade::moduleAmdxcffx64, driverDllPath, MAX_PATH); + + Util::version_t amdxcffx64Version; + Util::GetFileVersion(driverDllPath, &amdxcffx64Version); + + amdxcffx64SupportsInt8 = amdxcffx64Version >= Util::version_t(2, 3, 0, 0); + IdentifyGpu::updateInt8Support(sdkSupportsInt8, amdxcffx64SupportsInt8); } else { LOG_WARN("Failed to load amdxcffx64.dll"); + + amdxcffx64SupportsInt8 = false; + IdentifyGpu::updateInt8Support(sdkSupportsInt8, amdxcffx64SupportsInt8); + return E_NOINTERFACE; } @@ -96,6 +125,12 @@ HRESULT STDMETHODCALLTYPE AmdExtFfxApi::UpdateFfxApiProvider(void* pData, uint32 } } + // Prevents the use of FP8 FG on unsupported cards + // As a consequence, can't use MLFG on RDNA4 when forcing INT8 + // IdentifyGpu would need to have a field for pre-spoofed fsr4 support + if (effectType == FFXStructType::FG && fsr4Support != FSR4Support::FP8) + return E_NOINTERFACE; + // Result 0x80004002 (E_NOINTERFACE) basically means that amdxcffx64 doesn't have a provider for that effect if ((effectType == FFXStructType::FG || effectType == FFXStructType::Upscaling || effectType == FFXStructType::SwapchainDX12) && @@ -133,16 +168,25 @@ HRESULT STDMETHODCALLTYPE AmdExtFfxApi::UpdateFfxApiProvider(void* pData, uint32 HRESULT STDMETHODCALLTYPE AmdExtD3DShaderIntrinsics::GetInfo(void* ShaderIntrinsicsInfo) { + if (!State::Instance().isRunningOnLinux && Amdxc64Hooks::o_amdExtD3DShaderIntrinsics) + return Amdxc64Hooks::o_amdExtD3DShaderIntrinsics->GetInfo(ShaderIntrinsicsInfo); + LOG_FUNC(); return S_OK; } HRESULT STDMETHODCALLTYPE AmdExtD3DShaderIntrinsics::CheckSupport(AmdExtD3DShaderIntrinsicsSupport intrinsic) { + if (!State::Instance().isRunningOnLinux && Amdxc64Hooks::o_amdExtD3DShaderIntrinsics) + return Amdxc64Hooks::o_amdExtD3DShaderIntrinsics->CheckSupport(intrinsic); + LOG_TRACE(": {}", magic_enum::enum_name(intrinsic)); return S_OK; } HRESULT STDMETHODCALLTYPE AmdExtD3DShaderIntrinsics::Enable() { + if (!State::Instance().isRunningOnLinux && Amdxc64Hooks::o_amdExtD3DShaderIntrinsics) + return Amdxc64Hooks::o_amdExtD3DShaderIntrinsics->Enable(); + LOG_FUNC(); return S_OK; } @@ -156,7 +200,11 @@ HRESULT STDMETHODCALLTYPE AmdExtD3DDevice8::GetWaveMatrixProperties(uint64_t* co waveMatrixProperties->nSize = 16; waveMatrixProperties->kSize = 16; - waveMatrixProperties->aType = fp8; + if (fsr4Support == FSR4Support::FP8) + waveMatrixProperties->aType = fp8; + else + waveMatrixProperties->aType = float16; // Just anything to fail the checks + waveMatrixProperties->bType = fp8; waveMatrixProperties->cType = float32; @@ -164,5 +212,8 @@ HRESULT STDMETHODCALLTYPE AmdExtD3DDevice8::GetWaveMatrixProperties(uint64_t* co waveMatrixProperties->saturatingAccumulation = false; + // TODO: fill out the rest when improving AmdExtD3DDevice8 support + *count = 1; + return S_OK; } diff --git a/OptiScaler/fsr4/FSR4Upgrade.h b/OptiScaler/fsr4/FSR4Upgrade.h index e3c49e5a..4ec6a71d 100644 --- a/OptiScaler/fsr4/FSR4Upgrade.h +++ b/OptiScaler/fsr4/FSR4Upgrade.h @@ -4,6 +4,8 @@ struct AmdExtFfxApi : public IAmdExtFfxApi { + FSR4Support fsr4Support {}; + PFN_UpdateFfxApiProvider o_UpdateFfxApiProvider = nullptr; PFN_UpdateFfxApiProviderEx o_UpdateFfxApiProviderEx = nullptr; @@ -34,6 +36,8 @@ struct AmdExtD3DShaderIntrinsics : public IAmdExtD3DShaderIntrinsics struct AmdExtD3DDevice8 : public IAmdExtD3DDevice8 { + FSR4Support fsr4Support {}; + STUB(1) STUB(2) STUB(3) diff --git a/OptiScaler/hooks/Amdxc64_Hooks.cpp b/OptiScaler/hooks/Amdxc64_Hooks.cpp index 6738e108..c8deb7f4 100644 --- a/OptiScaler/hooks/Amdxc64_Hooks.cpp +++ b/OptiScaler/hooks/Amdxc64_Hooks.cpp @@ -12,6 +12,10 @@ struct AmdExtD3DFactory : public IAmdExtD3DFactory { + bool linux = false; + FSR4Support fsr4Support {}; + bool fsr4ForcedSupport = false; + HRESULT STDMETHODCALLTYPE CreateInterface(IUnknown* pOuter, REFIID riid, void** ppvObject) override { if (riid == __uuidof(IAmdExtD3DShaderIntrinsics)) @@ -21,14 +25,21 @@ struct AmdExtD3DFactory : public IAmdExtD3DFactory *ppvObject = Amdxc64Hooks::amdExtD3DShaderIntrinsics; + Amdxc64Hooks::o_amdExtD3DFactory->CreateInterface(pOuter, riid, + (void**) &Amdxc64Hooks::o_amdExtD3DShaderIntrinsics); + LOG_INFO("Custom IAmdExtD3DShaderIntrinsics queried, returning custom AmdExtD3DShaderIntrinsics"); return S_OK; } - else if (riid == __uuidof(IAmdExtD3DDevice8)) + // TODO: Implementation too incomplete to always enable it + else if (riid == __uuidof(IAmdExtD3DDevice8) && (linux || fsr4ForcedSupport)) { if (Amdxc64Hooks::amdExtD3DDevice8 == nullptr) + { Amdxc64Hooks::amdExtD3DDevice8 = new AmdExtD3DDevice8(); + Amdxc64Hooks::amdExtD3DDevice8->fsr4Support = fsr4Support; + } *ppvObject = Amdxc64Hooks::amdExtD3DDevice8; @@ -93,6 +104,9 @@ void Amdxc64Hooks::Init() if (moduleAmdxc64 != nullptr) { + // Pin the dll so that our hooks stay valid, mainly for Linux + GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN, L"amdxc64.dll", &moduleAmdxc64); + LOG_INFO("amdxc64.dll loaded"); o_AmdExtD3DCreateInterface = (PFN_AmdExtD3DCreateInterface) KernelBaseProxy::GetProcAddress_()( moduleAmdxc64, "AmdExtD3DCreateInterface"); @@ -120,14 +134,20 @@ void Amdxc64Hooks::Init() HRESULT STDMETHODCALLTYPE Amdxc64Hooks::hkAmdExtD3DCreateInterface(IUnknown* pOuter, REFIID riid, void** ppvObject) { - const bool runFsr4Upgrade = IdentifyGpu::getPrimaryGpu().fsr4Capable; + const auto primaryGpu = IdentifyGpu::getPrimaryGpu(); + const bool runFsr4Upgrade = primaryGpu.fsr4Support != FSR4Support::None; // Proton bleeding edge ships amdxc64 that is missing some required functions - if (runFsr4Upgrade && riid == __uuidof(IAmdExtD3DFactory) && State::Instance().isRunningOnLinux) + if (runFsr4Upgrade && riid == __uuidof(IAmdExtD3DFactory)) { // Required for the custom AmdExtFfxApi, lack of it triggers visual glitches if (amdExtD3DFactory == nullptr) + { amdExtD3DFactory = new AmdExtD3DFactory(); + amdExtD3DFactory->linux = State::Instance().isRunningOnLinux; + amdExtD3DFactory->fsr4Support = primaryGpu.fsr4Support; + amdExtD3DFactory->fsr4ForcedSupport = primaryGpu.fsr4ForcedSupport; + } *ppvObject = amdExtD3DFactory; @@ -142,7 +162,10 @@ HRESULT STDMETHODCALLTYPE Amdxc64Hooks::hkAmdExtD3DCreateInterface(IUnknown* pOu else if (runFsr4Upgrade && riid == __uuidof(IAmdExtFfxApi)) { if (amdExtFfxApi == nullptr) + { amdExtFfxApi = new AmdExtFfxApi(); + amdExtFfxApi->fsr4Support = primaryGpu.fsr4Support; + } // Return custom one *ppvObject = amdExtFfxApi; diff --git a/OptiScaler/hooks/Amdxc64_Hooks.h b/OptiScaler/hooks/Amdxc64_Hooks.h index 3e059bf2..7cf82a66 100644 --- a/OptiScaler/hooks/Amdxc64_Hooks.h +++ b/OptiScaler/hooks/Amdxc64_Hooks.h @@ -135,6 +135,7 @@ class Amdxc64Hooks inline static AmdExtD3DDevice8* amdExtD3DDevice8 = nullptr; inline static AmdExtD3DShaderIntrinsics* amdExtD3DShaderIntrinsics = nullptr; + inline static AmdExtD3DShaderIntrinsics* o_amdExtD3DShaderIntrinsics = nullptr; inline static AmdExtD3DFactory* amdExtD3DFactory = nullptr; inline static AmdExtD3DFactory* o_amdExtD3DFactory = nullptr; inline static AmdExtFfxApi* amdExtFfxApi = nullptr; diff --git a/OptiScaler/hooks/Gdi32_Hooks.h b/OptiScaler/hooks/Gdi32_Hooks.h index 36b0d0d6..0a320844 100644 --- a/OptiScaler/hooks/Gdi32_Hooks.h +++ b/OptiScaler/hooks/Gdi32_Hooks.h @@ -19,7 +19,7 @@ static NTSTATUS hkD3DKMTQueryAdapterInfo(const D3DKMT_QUERYADAPTERINFO* data) auto result = o_D3DKMTQueryAdapterInfo(data); // LOG_INFO("Adapter into type: {}", (uint32_t)data->Type); - if (data->Type == KMTQAITYPE_WDDM_2_7_CAPS) + if (data->Type == KMTQAITYPE_WDDM_2_7_CAPS && Config::Instance()->SpoofHAGS.value_or_default()) { LOG_INFO("Spoofing HAGS 2.7"); @@ -36,7 +36,7 @@ static NTSTATUS hkD3DKMTQueryAdapterInfo(const D3DKMT_QUERYADAPTERINFO* data) return 0; } - else if (data->Type == KMTQAITYPE_WDDM_2_9_CAPS) + else if (data->Type == KMTQAITYPE_WDDM_2_9_CAPS && Config::Instance()->SpoofHAGS.value_or_default()) { LOG_INFO("Spoofing HAGS 2.9"); @@ -51,6 +51,38 @@ static NTSTATUS hkD3DKMTQueryAdapterInfo(const D3DKMT_QUERYADAPTERINFO* data) d3dkmt_wddm_2_9_caps->HwSchEnabled = 1; return 0; } + else if (data->Type == KMTQAITYPE_UMDRIVERPRIVATE && data->PrivateDriverDataSize == 608 && + Util::WhoIsTheCaller(_ReturnAddress()).starts_with("amd")) + { + LOG_DEBUG("Likely FSR 4 GPU Check"); + + auto primaryGpu = IdentifyGpu::getPrimaryGpu(); + + auto amd_gpu_info = static_cast(data->pPrivateDriverData); + + if (primaryGpu.fsr4ForcedSupport || State::Instance().isRunningOnLinux) + { + // Values as per https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/amd/addrlib/src/amdgpu_asic_addr.h + // Seem to be asic family and asic revision + if (primaryGpu.fsr4Support == FSR4Support::INT8) + { + amd_gpu_info[12] = 145; // gfx11 + amd_gpu_info[13] = 1; + + return 1; + } + else if (primaryGpu.fsr4Support == FSR4Support::FP8) + { + // Based on 9070xt + amd_gpu_info[12] = 152; // gfx12 + amd_gpu_info[13] = 81; + + return 1; + } + } + + return result; + } return result; } @@ -60,24 +92,21 @@ static void hookGdi32() { LOG_FUNC(); - if (Config::Instance()->SpoofHAGS.value_or_default()) + o_D3DKMTQueryAdapterInfo = + reinterpret_cast(DetourFindFunction("gdi32.dll", "D3DKMTQueryAdapterInfo")); + + if (o_D3DKMTQueryAdapterInfo != nullptr) { - o_D3DKMTQueryAdapterInfo = - reinterpret_cast(DetourFindFunction("gdi32.dll", "D3DKMTQueryAdapterInfo")); + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); - if (o_D3DKMTQueryAdapterInfo != nullptr) + DetourAttach(&(PVOID&) o_D3DKMTQueryAdapterInfo, hkD3DKMTQueryAdapterInfo); + + auto detourResult = DetourTransactionCommit(); + if (detourResult != NO_ERROR) { - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); - - DetourAttach(&(PVOID&) o_D3DKMTQueryAdapterInfo, hkD3DKMTQueryAdapterInfo); - - auto detourResult = DetourTransactionCommit(); - if (detourResult != NO_ERROR) - { - LOG_ERROR("DetourTransactionCommit error: {:X}", detourResult); - o_D3DKMTQueryAdapterInfo = nullptr; - } + LOG_ERROR("DetourTransactionCommit error: {:X}", detourResult); + o_D3DKMTQueryAdapterInfo = nullptr; } } } diff --git a/OptiScaler/hooks/Kernel_Hooks.cpp b/OptiScaler/hooks/Kernel_Hooks.cpp index 0f22a228..7fc58450 100644 --- a/OptiScaler/hooks/Kernel_Hooks.cpp +++ b/OptiScaler/hooks/Kernel_Hooks.cpp @@ -108,11 +108,17 @@ FARPROC WINAPI KernelHooks::hk_K32_GetProcAddress(HMODULE hModule, LPCSTR lpProc // 2nd check is amdxcffx64.dll trying to queue amdxc64 but amdxc64 not being loaded. // Also skip the internal call of amdxc64 if (lpProcName != nullptr && (hModule == amdxc64Mark || hModule == nullptr) && - lstrcmpA(lpProcName, "AmdExtD3DCreateInterface") == 0 && IdentifyGpu::getPrimaryGpu().fsr4Capable && + lstrcmpA(lpProcName, "AmdExtD3DCreateInterface") == 0 && + IdentifyGpu::getPrimaryGpu().fsr4Support != FSR4Support::None && Util::GetCallerModule(_ReturnAddress()) != KernelBaseProxy::GetModuleHandleW_()(L"amdxc64.dll")) { + LOG_TRACE("Giving hkAmdExtD3DCreateInterface"); return (FARPROC) &Amdxc64Hooks::hkAmdExtD3DCreateInterface; } + else if (hModule == amdxc64Mark) + { + return o_K32_GetProcAddress(KernelBaseProxy::GetModuleHandleW_()(L"amdxc64.dll"), lpProcName); + } return o_K32_GetProcAddress(hModule, lpProcName); } @@ -135,12 +141,16 @@ HMODULE WINAPI KernelHooks::hk_K32_GetModuleHandleA(LPCSTR lpModuleName) // Therefore it should be safe for us to return a custom implementation when it's not loaded // This can get removed if Proton starts to ship amdxc64 + // For system with amdxc64 loaded - we should've caught the load and hooked it + // For systems without - we provide that app with a fake handle and track the usage that way + auto original = o_K32_GetModuleHandleA(lpModuleName); auto primaryGpu = IdentifyGpu::getPrimaryGpu(); - if (original == nullptr && primaryGpu.fsr4Capable) + + if (original == nullptr && primaryGpu.fsr4Support != FSR4Support::None) { - LOG_INFO("amdxc64.dll is not loaded, giving a fake HMODULE"); + LOG_INFO("giving a fake HMODULE for amdxc64.dll"); return amdxc64Mark; } @@ -151,6 +161,34 @@ HMODULE WINAPI KernelHooks::hk_K32_GetModuleHandleA(LPCSTR lpModuleName) return o_K32_GetModuleHandleA(lpModuleName); } +VALIDATE_HOOK(hk_K32_GetModuleHandleW, Kernel32Proxy::PFN_GetModuleHandleW) +HMODULE WINAPI KernelHooks::hk_K32_GetModuleHandleW(LPCWSTR lpModuleName) +{ + if (lpModuleName != NULL) + { + if (wcscmp(lpModuleName, L"amdxc64.dll") == 0) + { + LOG_TRACE("amdxc64.dll call"); + + // See comments in hk_K32_GetModuleHandleA + + auto original = o_K32_GetModuleHandleW(lpModuleName); + + auto primaryGpu = IdentifyGpu::getPrimaryGpu(); + + if (original == nullptr && primaryGpu.fsr4Support != FSR4Support::None) + { + LOG_INFO("giving a fake HMODULE for amdxc64.dll"); + return amdxc64Mark; + } + + return original; + } + } + + return o_K32_GetModuleHandleW(lpModuleName); +} + VALIDATE_HOOK(hk_K32_GetModuleHandleExA, Kernel32Proxy::PFN_GetModuleHandleExA) BOOL WINAPI KernelHooks::hk_K32_GetModuleHandleExA(DWORD dwFlags, LPCSTR lpModuleName, HMODULE* phModule) { diff --git a/OptiScaler/hooks/Kernel_Hooks.h b/OptiScaler/hooks/Kernel_Hooks.h index d20a091b..80be5f28 100644 --- a/OptiScaler/hooks/Kernel_Hooks.h +++ b/OptiScaler/hooks/Kernel_Hooks.h @@ -28,6 +28,7 @@ class KernelHooks inline static Kernel32Proxy::PFN_LoadLibraryExW o_K32_LoadLibraryExW = nullptr; inline static Kernel32Proxy::PFN_GetProcAddress o_K32_GetProcAddress = nullptr; inline static Kernel32Proxy::PFN_GetModuleHandleA o_K32_GetModuleHandleA = nullptr; + inline static Kernel32Proxy::PFN_GetModuleHandleW o_K32_GetModuleHandleW = nullptr; inline static Kernel32Proxy::PFN_GetModuleHandleExA o_K32_GetModuleHandleExA = nullptr; inline static Kernel32Proxy::PFN_GetModuleHandleExW o_K32_GetModuleHandleExW = nullptr; inline static Kernel32Proxy::PFN_GetFileAttributesW o_K32_GetFileAttributesW = nullptr; @@ -42,10 +43,9 @@ class KernelHooks inline static KernelBaseProxy::PFN_LoadLibraryExW o_KB_LoadLibraryExW = nullptr; inline static KernelBaseProxy::PFN_GetProcAddress o_KB_GetProcAddress = nullptr; - static constexpr HMODULE amdxc64Mark = HMODULE(0xFFFFFFFF13372137); - static FARPROC WINAPI hk_K32_GetProcAddress(HMODULE hModule, LPCSTR lpProcName); static HMODULE WINAPI hk_K32_GetModuleHandleA(LPCSTR lpModuleName); + static HMODULE WINAPI hk_K32_GetModuleHandleW(LPCWSTR lpModuleName); static BOOL WINAPI hk_K32_GetModuleHandleExA(DWORD dwFlags, LPCSTR lpModuleName, HMODULE* phModule); static BOOL WINAPI hk_K32_GetModuleHandleExW(DWORD dwFlags, LPCWSTR lpModuleName, HMODULE* phModule); static FARPROC WINAPI hk_KB_GetProcAddress(HMODULE hModule, LPCSTR lpProcName); @@ -67,6 +67,8 @@ class KernelHooks static inline std::mutex hookMutexBase; public: + static constexpr HMODULE amdxc64Mark = HMODULE(0xFFFFFFFF13372137); + static void Hook() { std::lock_guard lock(hookMutex32); @@ -79,6 +81,9 @@ class KernelHooks if (o_K32_GetModuleHandleA == nullptr) o_K32_GetModuleHandleA = Kernel32Proxy::Hook_GetModuleHandleA(hk_K32_GetModuleHandleA); + if (o_K32_GetModuleHandleW == nullptr) + o_K32_GetModuleHandleW = Kernel32Proxy::Hook_GetModuleHandleW(hk_K32_GetModuleHandleW); + #ifdef LOW_LATENCY_INPUTS if (o_K32_GetModuleHandleExA == nullptr) o_K32_GetModuleHandleExA = Kernel32Proxy::Hook_GetModuleHandleExA(hk_K32_GetModuleHandleExA); diff --git a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp index 904b0005..07c19a6c 100644 --- a/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp +++ b/OptiScaler/inputs/NVNGX_DLSS_Dx12.cpp @@ -622,7 +622,7 @@ static Upscaler GetUpscalerBackend() if (NVNGXProxy::IsDx12Inited() && primaryGpu.dlssCapable) upscaler = Upscaler::DLSS; - if (primaryGpu.fsr4Capable) + if (primaryGpu.fsr4Support != FSR4Support::None) upscaler = Upscaler::FFX; if (Config::Instance()->Dx12Upscaler.has_value()) diff --git a/OptiScaler/menu/menu_common.cpp b/OptiScaler/menu/menu_common.cpp index a68c3e30..b00c4d39 100644 --- a/OptiScaler/menu/menu_common.cpp +++ b/OptiScaler/menu/menu_common.cpp @@ -1412,6 +1412,14 @@ void MenuCommon::UpdateVersionAndStartupNotifications(RenderMenuContext& ctx) ImGui::InsertNotification(notification); } + if (state.postCodes & PostCode::TryingFsr4Fp8OnUnsupported) + { + ImGuiToast notification { ImGuiToastType::Warning, 10000 }; + notification.setTitle("Silly goose detected"); + notification.setContent("FSR 4 FP8 only works on AMD"); + ImGui::InsertNotification(notification); + } + state.postDone = true; } @@ -2565,8 +2573,8 @@ void MenuCommon::RenderActiveUpscalerSettings(RenderMenuContext& ctx) // Conversion from 0 -> 6 into nullopt + 0 -> 5 is required uint32_t configModes = 0; - if (config->Fsr4Model.has_value()) - configModes = config->Fsr4Model.value_or(0) + 1; + if (config->Fsr4Preset.has_value()) + configModes = config->Fsr4Preset.value_or(0) + 1; if (configModes < 0 || configModes >= models.size()) configModes = 0; @@ -2575,24 +2583,23 @@ void MenuCommon::RenderActiveUpscalerSettings(RenderMenuContext& ctx) if (ImGui::BeginTable("nonLinear", 2, ImGuiTableFlags_SizingStretchProp)) { - ImGui::TableNextColumn(); - if (ImGui::BeginCombo("Models", selectedModel)) + if (ImGui::BeginCombo("Presets", selectedModel)) { for (int n = 0; n < models.size(); n++) { uint32_t selection = 0; - if (config->Fsr4Model.has_value()) - selection = config->Fsr4Model.value_or(0) + 1; + if (config->Fsr4Preset.has_value()) + selection = config->Fsr4Preset.value_or(0) + 1; if (ImGui::Selectable(models[n], selection == n)) { if (n < 1) - config->Fsr4Model.reset(); + config->Fsr4Preset.reset(); else - config->Fsr4Model = n - 1; + config->Fsr4Preset = n - 1; state.newBackend = currentBackend; MARK_ALL_BACKENDS_CHANGED(); @@ -2615,8 +2622,8 @@ void MenuCommon::RenderActiveUpscalerSettings(RenderMenuContext& ctx) ImGui::TableNextColumn(); - if (state.currentFsr4Model.has_value()) - ImGui::Text("Current model: %d", state.currentFsr4Model.value()); + if (state.currentFsr4Preset.has_value()) + ImGui::Text("Current preset: %d", state.currentFsr4Preset.value()); else ImGui::Text("Failed to hook"); @@ -4330,8 +4337,8 @@ void MenuCommon::RenderFrameGenerationRuntimeSettings(RenderMenuContext& ctx) // Hide to reduce confusion, config is still read bool isUnrealEngine = State::Instance().NVNGX_Engine == NVSDK_NGX_ENGINE_TYPE_UNREAL || State::Instance().gameQuirks & GameQuirk::ForceUnrealEngine; - if (!primaryGpu.dlssCapable && !primaryGpu.fsr4Capable && !primaryGpu.usesVkd3dProton && - !isUnrealEngine) + if (!primaryGpu.dlssCapable && primaryGpu.fsr4Support == FSR4Support::None && + !primaryGpu.usesVkd3dProton && !isUnrealEngine) { if (bool makeDepthCopy = config->MakeDepthCopy.value_or_default(); ImGui::Checkbox("Fix broken visuals", &makeDepthCopy)) diff --git a/OptiScaler/misc/IdentifyGpu.cpp b/OptiScaler/misc/IdentifyGpu.cpp index db07dff5..cf1da150 100644 --- a/OptiScaler/misc/IdentifyGpu.cpp +++ b/OptiScaler/misc/IdentifyGpu.cpp @@ -1,11 +1,12 @@ #include "pch.h" #include "IdentifyGpu.h" +#include +#include + #include #include #include "nvapi/NvApiTypes.h" -#include - #include using Microsoft::WRL::ComPtr; @@ -180,8 +181,6 @@ std::vector IdentifyGpu::checkGpuInfo() { queryNvapi(gpuInfo); } - - SAFE_RELEASE(gpuInfo.d3d12device); } return localCachedInfo; @@ -376,14 +375,18 @@ void IdentifyGpu::updateD3d12Capabilities(D3d12Proxy::PFN_D3D12CreateDevice o_D3 { LUID luid; bool usesVkd3dProton = false; - bool fsr4Capable = false; + FSR4Support fsr4Support = FSR4Support::None; + bool fsr4ForcedSupport = false; }; std::vector results; for (auto& gpuInfo : cache) { - if (gpuInfo.vendorId != VendorId::AMD && !gpuInfo.usesDxvk) + if (gpuInfo.vendorId != VendorId::AMD && !gpuInfo.usesDxvk && + Config::Instance()->Fsr4ForceModel.value_or_default() == FSR4Support::None) + { continue; + } ComPtr factory; if (FAILED(DxgiProxy::CreateDxgiFactory_()(__uuidof(factory), (IDXGIFactory**) factory.GetAddressOf()))) @@ -401,7 +404,7 @@ void IdentifyGpu::updateD3d12Capabilities(D3d12Proxy::PFN_D3D12CreateDevice o_D3 // D3D12 device is needed to be able to query amdxc and check for vkd3d-proton ScopedCreatingD3DDevice scopedCreating {}; ScopedSkipVulkanHooks skipVulkanHooks {}; - ID3D12Device* localDevice = nullptr; + ComPtr localDevice; auto createResult = pD3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&localDevice)); if (SUCCEEDED(createResult) && localDevice) @@ -413,14 +416,17 @@ void IdentifyGpu::updateD3d12Capabilities(D3d12Proxy::PFN_D3D12CreateDevice o_D3 if (localDevice && SUCCEEDED(localDevice->QueryInterface(IID_PPV_ARGS(&vkd3dInterop)))) res.usesVkd3dProton = true; + // Kinda questionable, may need to reconsider + if (Config::Instance()->Fsr4ForceModel.value_or_default() != FSR4Support::None) + { + res.fsr4Support = Config::Instance()->Fsr4ForceModel.value_or_default(); + res.fsr4ForcedSupport = true; + } + if (gpuInfo.vendorId == VendorId::AMD) { - // Kinda questionable, may need to reconsider - if (Config::Instance()->Fsr4ForceCapable.value_or_default()) - res.fsr4Capable = true; - // Query vkd3d-proton for extensions it's using to look for the required one for FSR 4 - if (!res.fsr4Capable && res.usesVkd3dProton) + if (res.fsr4Support == FSR4Support::None && res.usesVkd3dProton) { UINT extensionCount = 0; @@ -436,7 +442,7 @@ void IdentifyGpu::updateD3d12Capabilities(D3d12Proxy::PFN_D3D12CreateDevice o_D3 // Only RDNA4+ if (!strcmp("VK_EXT_shader_float8", exts[i])) { - res.fsr4Capable = true; + res.fsr4Support = FSR4Support::FP8; break; } } @@ -447,14 +453,14 @@ void IdentifyGpu::updateD3d12Capabilities(D3d12Proxy::PFN_D3D12CreateDevice o_D3 // Pre-RDNA4 GPUs on Linux can support FSR 4 but require a special envvar // check for the envvar and assume everything else is also setup for FSR 4 to work on those // cards - if (!res.fsr4Capable) + if (res.fsr4Support == FSR4Support::None) { const char* envvar = getenv("DXIL_SPIRV_CONFIG"); if (envvar && strstr(envvar, "wmma_rdna3_workaround")) - res.fsr4Capable = true; + res.fsr4Support = FSR4Support::FP8; } - if (!res.fsr4Capable) + if (res.fsr4Support == FSR4Support::None) { auto moduleAmdxc64 = KernelBaseProxy::GetModuleHandleW_()(L"amdxc64.dll"); @@ -471,29 +477,50 @@ void IdentifyGpu::updateD3d12Capabilities(D3d12Proxy::PFN_D3D12CreateDevice o_D3 // Query amdxc for a specific intrinsics support, FSR 4 checks more but hopefully this one // is enough amdxc on Windows hates vkd3d-proton's device, on Linux it's fine - if (!res.fsr4Capable && localDevice && - (State::Instance().isRunningOnLinux || !res.usesVkd3dProton) && AmdExtD3DCreateInterface) + if (localDevice && (State::Instance().isRunningOnLinux || !res.usesVkd3dProton) && + AmdExtD3DCreateInterface) { - if (SUCCEEDED(AmdExtD3DCreateInterface(localDevice, IID_PPV_ARGS(&amdExtD3DFactory)))) + if (SUCCEEDED(AmdExtD3DCreateInterface(localDevice.Get(), IID_PPV_ARGS(&amdExtD3DFactory)))) { ComPtr amdExtD3DShaderIntrinsics = nullptr; - if (amdExtD3DFactory && SUCCEEDED(amdExtD3DFactory->CreateInterface( - localDevice, IID_PPV_ARGS(&amdExtD3DShaderIntrinsics)))) + if (amdExtD3DFactory && + SUCCEEDED(amdExtD3DFactory->CreateInterface( + localDevice.Get(), IID_PPV_ARGS(&amdExtD3DShaderIntrinsics)))) { HRESULT float8support = amdExtD3DShaderIntrinsics->CheckSupport( AmdExtD3DShaderIntrinsicsSupport_Float8Conversion); - res.fsr4Capable = float8support == S_OK; + + if (float8support == S_OK) + res.fsr4Support = FSR4Support::FP8; } } } } + // Check for native INT8 support + if (res.fsr4Support == FSR4Support::None) + { + device_info::AdapterId adapterId(gpuInfo.vendorId, gpuInfo.deviceId, gpuInfo.revisionId); + auto cardInfo = device_info::GetCardInfo(adapterId); + + if (cardInfo.has_value()) + { + if (cardInfo.value().generation == device_info::HwGeneration::kGfx11) + res.fsr4Support = FSR4Support::INT8; + + // if (cardInfo.value().generation == device_info::HwGeneration::kGfx11_5) + //{ + // res.fsr4Support = FSR4Support::INT8; + // res.fsr4ForcedSupport = true; + // } + } + } + // TODO: could now try to ask amdxcffx for FSR 4 and see if it returns it // but our FSR 4 upgrade code call this function so it gets complicated } - localDevice->Release(); results.push_back(res); } } @@ -508,7 +535,8 @@ void IdentifyGpu::updateD3d12Capabilities(D3d12Proxy::PFN_D3D12CreateDevice o_D3 if (IsEqualLUID(gpuInfo.luid, res.luid)) { gpuInfo.usesVkd3dProton = res.usesVkd3dProton; - gpuInfo.fsr4Capable = res.fsr4Capable; + gpuInfo.fsr4Support = res.fsr4Support; + gpuInfo.fsr4ForcedSupport = res.fsr4ForcedSupport; break; } } @@ -522,12 +550,68 @@ void IdentifyGpu::updateD3d12Capabilities(D3d12Proxy::PFN_D3D12CreateDevice o_D3 for (auto& gpu : detectedGpus) { + std::string fsr4Support = "unknown"; + if (gpu.fsr4Support == FSR4Support::None) + fsr4Support = "false"; + else if (gpu.fsr4Support == FSR4Support::FP8) + fsr4Support = "fp8"; + else if (gpu.fsr4Support == FSR4Support::INT8) + fsr4Support = "int8"; + + if (gpu.fsr4ForcedSupport) + fsr4Support += " (forced)"; + gpus += std::format("{}{}\n", indent, gpu.name); gpus += std::format("{} vendorId: {:X}, deviceId: {:X}, VRAM: {}MB\n", indent, (uint32_t) gpu.vendorId, gpu.deviceId, gpu.dedicatedVramInBytes / (1024 * 1024)); gpus += std::format("{} dxvk: {}, vkd3d-proton: {}\n", indent, gpu.usesDxvk, gpu.usesVkd3dProton); - gpus += std::format("{} Upscaler support - fsr4: {}, dlss: {}\n", indent, gpu.fsr4Capable, gpu.dlssCapable); + gpus += std::format("{} Upscaler support - fsr4: {}, dlss: {}\n", indent, fsr4Support, gpu.dlssCapable); } spdlog::info(gpus); -} \ No newline at end of file + + auto primaryGpu = !detectedGpus.empty() ? detectedGpus.front() : GpuInformation {}; + + if (primaryGpu.vendorId != VendorId::AMD && primaryGpu.fsr4ForcedSupport && + primaryGpu.fsr4Support == FSR4Support::FP8) + { + State::Instance().postCodes |= PostCode::TryingFsr4Fp8OnUnsupported; + + std::scoped_lock lock(mutex); + for (auto& gpuInfo : cache) + { + gpuInfo.fsr4Support = FSR4Support::None; + gpuInfo.fsr4ForcedSupport = false; + } + } + + // This means that the user didn't want to force support but we decided that we want to force it + // We need spoofing on but if it's off then this will be too late for most cases to enable it + // But if we only force it for AMD GPUs then that's fine + if (Config::Instance()->Fsr4ForceModel.value_or_default() == FSR4Support::None && primaryGpu.fsr4ForcedSupport) + { + //// We need spoofing hooks for FFX but want to avoid spoofing for the rest of the game + // if (!Config::Instance()->DxgiSpoofing.value_or_default()) + //{ + // Config::Instance()->SpoofedVendorId.set_volatile_value(primaryGpu.vendorId); + // Config::Instance()->SpoofedDeviceId.set_volatile_value(primaryGpu.deviceId); + // } + + // Config::Instance()->DxgiSpoofing.set_volatile_value(true); + } +} + +void IdentifyGpu::updateInt8Support(std::optional& sdkSupportsInt8, std::optional& amdxcffx64SupportsInt8) +{ + if (amdxcffx64SupportsInt8 && !amdxcffx64SupportsInt8.value() && sdkSupportsInt8 && !sdkSupportsInt8.value()) + { + LOG_DEBUG("Neither driver nor the sdk supports FSR 4 Int 8, disabling"); + + std::scoped_lock lock(mutex); + for (auto& gpuInfo : cache) + { + gpuInfo.fsr4Support = FSR4Support::None; + gpuInfo.fsr4ForcedSupport = false; + } + } +} diff --git a/OptiScaler/misc/IdentifyGpu.h b/OptiScaler/misc/IdentifyGpu.h index 934bb47d..76283308 100644 --- a/OptiScaler/misc/IdentifyGpu.h +++ b/OptiScaler/misc/IdentifyGpu.h @@ -61,8 +61,8 @@ struct GpuInformation std::filesystem::path driverStore {}; // AMD - bool fsr4Capable = false; - ID3D12Device* d3d12device = nullptr; + bool fsr4ForcedSupport = false; + FSR4Support fsr4Support {}; device_info::HwGeneration amdHwGeneration = device_info::HwGeneration::kUndefinedGeneration; // Nvidia @@ -93,4 +93,5 @@ class IdentifyGpu static std::vector getAllGpus(); static GpuInformation getPrimaryGpu(); static void updateD3d12Capabilities(D3d12Proxy::PFN_D3D12CreateDevice o_D3D12CreateDevice = nullptr); + static void updateInt8Support(std::optional& sdkSupportsInt8, std::optional& amdxcffx64SupportsInt8); }; diff --git a/OptiScaler/spoofing/Dxgi_Spoofing.cpp b/OptiScaler/spoofing/Dxgi_Spoofing.cpp index 2e52ce6e..26f89a60 100644 --- a/OptiScaler/spoofing/Dxgi_Spoofing.cpp +++ b/OptiScaler/spoofing/Dxgi_Spoofing.cpp @@ -187,6 +187,17 @@ HRESULT DxgiSpoofing::hkGetDesc1(IDXGIAdapter1* This, DXGI_ADAPTER_DESC1* pDesc) LOG_DEBUG("spoofing"); #endif } + + if (Util::WhoIsTheCaller(_ReturnAddress()).starts_with("amdxcffx64")) + { + const auto primaryGpu = IdentifyGpu::getPrimaryGpu(); + + if (primaryGpu.fsr4ForcedSupport && primaryGpu.fsr4Support == FSR4Support::INT8) + { + LOG_TRACE("Spoofing vendor AMD for amdxcffx64"); + pDesc->VendorId = VendorId::AMD; + } + } } AttachToAdapter(This);