mirror of
https://github.com/optiscaler/OptiScaler.git
synced 2026-11-15 18:31:28 +00:00
555 lines
18 KiB
C++
555 lines
18 KiB
C++
#include "pch.h"
|
|
#include "FSR4Upgrade.h"
|
|
|
|
#include <proxies/Dxgi_Proxy.h>
|
|
#include <detours/detours.h>
|
|
#include <scanner/scanner.h>
|
|
#include <ffx_framegeneration.h>
|
|
#include <ffx_upscale.h>
|
|
#include "FSR4ModelSelection.h"
|
|
#include "proxies/FfxApi_Proxy.h"
|
|
#include <magic_enum.hpp>
|
|
|
|
// A mess to be able to import both
|
|
#define FFX_API_CONFIGURE_FG_SWAPCHAIN_KEY_WAITCALLBACK FFX_API_CONFIGURE_FG_SWAPCHAIN_KEY_WAITCALLBACK_DX12
|
|
#define FFX_API_CONFIGURE_FG_SWAPCHAIN_KEY_FRAMEPACINGTUNING FFX_API_CONFIGURE_FG_SWAPCHAIN_KEY_FRAMEPACINGTUNING_DX12
|
|
|
|
#include <dx12/ffx_api_dx12.h>
|
|
|
|
#undef FFX_API_CONFIGURE_FG_SWAPCHAIN_KEY_WAITCALLBACK
|
|
#undef FFX_API_CONFIGURE_FG_SWAPCHAIN_KEY_FRAMEPACINGTUNING
|
|
|
|
#define FFX_API_CONFIGURE_FG_SWAPCHAIN_KEY_WAITCALLBACK FFX_API_CONFIGURE_FG_SWAPCHAIN_KEY_WAITCALLBACK_VK
|
|
#define FFX_API_CONFIGURE_FG_SWAPCHAIN_KEY_FRAMEPACINGTUNING FFX_API_CONFIGURE_FG_SWAPCHAIN_KEY_FRAMEPACINGTUNING_VK
|
|
|
|
#include <vk/ffx_api_vk.h>
|
|
|
|
#undef FFX_API_CONFIGURE_FG_SWAPCHAIN_KEY_WAITCALLBACK
|
|
#undef FFX_API_CONFIGURE_FG_SWAPCHAIN_KEY_FRAMEPACINGTUNING
|
|
|
|
#pragma intrinsic(_ReturnAddress)
|
|
|
|
static HMODULE moduleAmdxc64 = nullptr;
|
|
static HMODULE moduleAmdxcffx64 = nullptr;
|
|
|
|
static AmdExtD3DDevice8* amdExtD3DDevice8 = nullptr;
|
|
static AmdExtD3DShaderIntrinsics* amdExtD3DShaderIntrinsics = nullptr;
|
|
static AmdExtD3DFactory* amdExtD3DFactory = nullptr;
|
|
static AmdExtD3DFactory* o_amdExtD3DFactory = nullptr;
|
|
static AmdExtFfxApi* amdExtFfxApi = nullptr;
|
|
|
|
static PFN_AmdExtD3DCreateInterface o_AmdExtD3DCreateInterface = nullptr;
|
|
|
|
typedef decltype(&D3DKMTQueryAdapterInfo) PFN_D3DKMTQueryAdapterInfo;
|
|
typedef decltype(&D3DKMTEnumAdapters) PFN_D3DKMTEnumAdapters;
|
|
typedef decltype(&D3DKMTCloseAdapter) PFN_D3DKMTCloseAdapter;
|
|
|
|
std::vector<std::filesystem::path> GetDriverStore()
|
|
{
|
|
std::vector<std::filesystem::path> result;
|
|
|
|
// Load D3DKMT functions dynamically
|
|
bool libraryLoaded = false;
|
|
HMODULE hGdi32 = KernelBaseProxy::GetModuleHandleW_()(L"Gdi32.dll");
|
|
|
|
if (hGdi32 == nullptr)
|
|
{
|
|
hGdi32 = NtdllProxy::LoadLibraryExW_Ldr(L"Gdi32.dll", NULL, 0);
|
|
libraryLoaded = hGdi32 != nullptr;
|
|
}
|
|
|
|
if (hGdi32 == nullptr)
|
|
{
|
|
LOG_ERROR("Failed to load Gdi32.dll");
|
|
return result;
|
|
}
|
|
|
|
do
|
|
{
|
|
auto o_D3DKMTEnumAdapters =
|
|
(PFN_D3DKMTEnumAdapters) KernelBaseProxy::GetProcAddress_()(hGdi32, "D3DKMTEnumAdapters");
|
|
auto o_D3DKMTQueryAdapterInfo =
|
|
(PFN_D3DKMTQueryAdapterInfo) KernelBaseProxy::GetProcAddress_()(hGdi32, "D3DKMTQueryAdapterInfo");
|
|
auto o_D3DKMTCloseAdapter =
|
|
(PFN_D3DKMTCloseAdapter) KernelBaseProxy::GetProcAddress_()(hGdi32, "D3DKMTCloseAdapter");
|
|
|
|
if (o_D3DKMTEnumAdapters == nullptr || o_D3DKMTQueryAdapterInfo == nullptr || o_D3DKMTCloseAdapter == nullptr)
|
|
{
|
|
LOG_ERROR("Failed to resolve D3DKMT functions");
|
|
break;
|
|
}
|
|
|
|
D3DKMT_UMDFILENAMEINFO umdFileInfo = {};
|
|
D3DKMT_QUERYADAPTERINFO queryAdapterInfo = {};
|
|
|
|
queryAdapterInfo.Type = KMTQAITYPE_UMDRIVERNAME;
|
|
queryAdapterInfo.pPrivateDriverData = &umdFileInfo;
|
|
queryAdapterInfo.PrivateDriverDataSize = sizeof(umdFileInfo);
|
|
|
|
D3DKMT_ENUMADAPTERS enumAdapters = {};
|
|
|
|
// Query the number of adapters first
|
|
if (o_D3DKMTEnumAdapters(&enumAdapters) != 0)
|
|
{
|
|
LOG_ERROR("Failed to enumerate adapters.");
|
|
break;
|
|
}
|
|
|
|
// If there are any adapters, the first one should be in the list
|
|
if (enumAdapters.NumAdapters > 0)
|
|
{
|
|
for (size_t i = 0; i < enumAdapters.NumAdapters; i++)
|
|
{
|
|
D3DKMT_ADAPTERINFO adapter = enumAdapters.Adapters[i];
|
|
queryAdapterInfo.hAdapter = adapter.hAdapter;
|
|
|
|
auto hr = o_D3DKMTQueryAdapterInfo(&queryAdapterInfo);
|
|
|
|
if (hr != 0)
|
|
LOG_WARN("Failed to query adapter info {:X}", hr);
|
|
else
|
|
result.push_back(std::filesystem::path(umdFileInfo.UmdFileName).parent_path());
|
|
|
|
D3DKMT_CLOSEADAPTER closeAdapter = {};
|
|
closeAdapter.hAdapter = adapter.hAdapter;
|
|
auto closeResult = o_D3DKMTCloseAdapter(&closeAdapter);
|
|
if (closeResult != 0)
|
|
LOG_ERROR("D3DKMTCloseAdapter error: {:X}", closeResult);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_ERROR("No adapters found.");
|
|
break;
|
|
}
|
|
|
|
} while (false);
|
|
|
|
if (libraryLoaded)
|
|
NtdllProxy::FreeLibrary_Ldr(hGdi32);
|
|
|
|
return result;
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
void CheckForGPU()
|
|
{
|
|
// CheckForGPU already ran before, no need to run it again
|
|
if (State::Instance().isRunningOnRDNA4.has_value())
|
|
return;
|
|
|
|
// Call init for any case
|
|
DxgiProxy::Init();
|
|
|
|
IDXGIFactory* factory = nullptr;
|
|
HRESULT result = DxgiProxy::CreateDxgiFactory_()(__uuidof(factory), &factory);
|
|
|
|
if (result != S_OK || factory == nullptr)
|
|
return;
|
|
|
|
UINT adapterIndex = 0;
|
|
DXGI_ADAPTER_DESC adapterDesc {};
|
|
IDXGIAdapter* adapter;
|
|
|
|
while (factory->EnumAdapters(adapterIndex, &adapter) == S_OK)
|
|
{
|
|
if (adapter == nullptr)
|
|
{
|
|
adapterIndex++;
|
|
continue;
|
|
}
|
|
|
|
{
|
|
ScopedSkipSpoofing skipSpoofing {};
|
|
result = adapter->GetDesc(&adapterDesc);
|
|
}
|
|
|
|
if (result == S_OK && adapterDesc.VendorId != VendorId::Microsoft)
|
|
{
|
|
if (!State::Instance().isRunningOnRDNA4.has_value() || !State::Instance().isRunningOnRDNA4.value())
|
|
State::Instance().isRunningOnRDNA4 = false;
|
|
|
|
std::wstring szName(adapterDesc.Description);
|
|
std::string descStr = std::format("Adapter: {}, VRAM: {} MB", wstring_to_string(szName),
|
|
adapterDesc.DedicatedVideoMemory / (1024.0 * 1024.0));
|
|
LOG_INFO("{}", descStr);
|
|
|
|
// If GPU is AMD
|
|
if (adapterDesc.VendorId == VendorId::AMD)
|
|
{
|
|
// If GPU Name contains 90XX or GFX12 (Linux) always set it to true
|
|
if (szName.find(L" 90") != std::wstring::npos || szName.find(L" GFX12") != std::wstring::npos)
|
|
{
|
|
LOG_DEBUG("RDNA4 GPU detected");
|
|
State::Instance().isRunningOnRDNA4 = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_DEBUG("Can't get description of adapter: {}", adapterIndex);
|
|
}
|
|
|
|
adapter->Release();
|
|
adapter = nullptr;
|
|
adapterIndex++;
|
|
}
|
|
|
|
factory->Release();
|
|
factory = nullptr;
|
|
|
|
// If not set at Config enable/disable Fsr4Update according to GPU detection
|
|
if (!Config::Instance()->Fsr4Update.has_value())
|
|
Config::Instance()->Fsr4Update.set_volatile_value(State::Instance().isRunningOnRDNA4.value());
|
|
|
|
LOG_INFO("RNDA4: {}, Fsr4Update: {}", State::Instance().isRunningOnRDNA4.value(),
|
|
Config::Instance()->Fsr4Update.value_or_default());
|
|
}
|
|
|
|
struct ffxProviderInterface
|
|
{
|
|
uint64_t versionId;
|
|
const char* versionName;
|
|
PVOID canProvide;
|
|
PVOID createContext;
|
|
PVOID destroyContext;
|
|
PVOID configure;
|
|
PVOID query;
|
|
PVOID dispatch;
|
|
};
|
|
|
|
struct ExternalProviderData
|
|
{
|
|
uint32_t structVersion = 2;
|
|
uint64_t descType;
|
|
ffxProviderInterface provider;
|
|
};
|
|
|
|
struct AmdExtFfxApi : public IAmdExtFfxApi
|
|
{
|
|
PFN_UpdateFfxApiProvider o_UpdateFfxApiProvider = nullptr;
|
|
PFN_UpdateFfxApiProviderEx o_UpdateFfxApiProviderEx = nullptr;
|
|
|
|
HRESULT STDMETHODCALLTYPE UpdateFfxApiProvider(void* pData, uint32_t dataSizeInBytes) override
|
|
{
|
|
auto returnAddress = _ReturnAddress();
|
|
auto callerModule = Util::GetCallerModule(returnAddress);
|
|
|
|
if (callerModule == exeModule)
|
|
{
|
|
LOG_WARN("Called from game module, returning E_NOINTERFACE");
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
auto effectType = FfxApiProxy::GetType(reinterpret_cast<ExternalProviderData*>(pData)->descType);
|
|
|
|
auto effect = magic_enum::enum_name(effectType);
|
|
if (effectType >= FFXStructType::Unknown)
|
|
effect = "???";
|
|
|
|
if (o_UpdateFfxApiProvider == nullptr)
|
|
{
|
|
moduleAmdxcffx64 = NtdllProxy::LoadLibraryExW_Ldr(L"amdxcffx64.dll", NULL, 0);
|
|
|
|
if (moduleAmdxcffx64 == nullptr)
|
|
{
|
|
auto storePath = GetDriverStore();
|
|
|
|
for (size_t i = 0; i < storePath.size(); i++)
|
|
{
|
|
if (moduleAmdxcffx64 == nullptr)
|
|
{
|
|
auto dllPath = storePath[i] / L"amdxcffx64.dll";
|
|
LOG_DEBUG("Trying to load: {}", wstring_to_string(dllPath.c_str()));
|
|
moduleAmdxcffx64 = NtdllProxy::LoadLibraryExW_Ldr(dllPath.c_str(), NULL, 0);
|
|
|
|
if (moduleAmdxcffx64 != nullptr)
|
|
{
|
|
LOG_INFO(L"amdxcffx64 loaded from {}", dllPath.wstring());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_INFO("amdxcffx64 loaded from game folder");
|
|
}
|
|
|
|
if (moduleAmdxcffx64)
|
|
{
|
|
FSR4ModelSelection::Hook(moduleAmdxcffx64, FSR4Source::DriverDll);
|
|
}
|
|
else
|
|
{
|
|
LOG_WARN("Failed to load amdxcffx64.dll");
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
o_UpdateFfxApiProvider =
|
|
(PFN_UpdateFfxApiProvider) KernelBaseProxy::GetProcAddress_()(moduleAmdxcffx64, "UpdateFfxApiProvider");
|
|
o_UpdateFfxApiProviderEx = (PFN_UpdateFfxApiProviderEx) KernelBaseProxy::GetProcAddress_()(
|
|
moduleAmdxcffx64, "UpdateFfxApiProviderEx");
|
|
|
|
if (o_UpdateFfxApiProvider == nullptr)
|
|
{
|
|
LOG_ERROR("Failed to get UpdateFfxApiProvider");
|
|
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) &&
|
|
o_UpdateFfxApiProviderEx != nullptr)
|
|
{
|
|
State::DisableChecks(1);
|
|
|
|
magicData data = { { 0, 1, 1, 0 }, nullptr };
|
|
auto result = o_UpdateFfxApiProviderEx(pData, dataSizeInBytes, &data);
|
|
|
|
auto level = SUCCEEDED(result) ? spdlog::level::info : spdlog::level::err;
|
|
spdlog::log(level, "UpdateFfxApiProviderEx for: {}, result: {:#X}", effect, (UINT) result);
|
|
|
|
State::EnableChecks(1);
|
|
return result;
|
|
}
|
|
|
|
else if (o_UpdateFfxApiProvider != nullptr)
|
|
{
|
|
State::DisableChecks(1);
|
|
|
|
auto result = o_UpdateFfxApiProvider(pData, dataSizeInBytes);
|
|
|
|
auto level = SUCCEEDED(result) ? spdlog::level::info : spdlog::level::err;
|
|
spdlog::log(level, "UpdateFfxApiProvider for: {}, result: {:#X}", effect, (UINT) result);
|
|
|
|
State::EnableChecks(1);
|
|
return result;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObject) override { return E_NOTIMPL; }
|
|
|
|
ULONG __stdcall AddRef(void) override { return 0; }
|
|
|
|
ULONG __stdcall Release(void) override { return 0; }
|
|
};
|
|
|
|
#define STUB(number) \
|
|
HRESULT STDMETHODCALLTYPE unknown##number() \
|
|
{ \
|
|
LOG_FUNC(); \
|
|
return S_OK; \
|
|
}
|
|
|
|
struct AmdExtD3DShaderIntrinsics : public IAmdExtD3DShaderIntrinsics
|
|
{
|
|
HRESULT STDMETHODCALLTYPE GetInfo(void* ShaderIntrinsicsInfo)
|
|
{
|
|
LOG_FUNC();
|
|
return S_OK;
|
|
}
|
|
HRESULT STDMETHODCALLTYPE CheckSupport(AmdExtD3DShaderIntrinsicsSupport intrinsic)
|
|
{
|
|
LOG_TRACE(": {}", magic_enum::enum_name(intrinsic));
|
|
return S_OK;
|
|
}
|
|
HRESULT STDMETHODCALLTYPE Enable()
|
|
{
|
|
LOG_FUNC();
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObject) override { return E_NOTIMPL; }
|
|
ULONG __stdcall AddRef(void) override { return 0; }
|
|
ULONG __stdcall Release(void) override { return 0; }
|
|
};
|
|
|
|
struct AmdExtD3DDevice8 : public IAmdExtD3DDevice8
|
|
{
|
|
STUB(1)
|
|
STUB(2)
|
|
STUB(3)
|
|
STUB(4)
|
|
STUB(5)
|
|
STUB(6)
|
|
STUB(7)
|
|
STUB(8)
|
|
STUB(9)
|
|
STUB(10)
|
|
STUB(11)
|
|
STUB(12)
|
|
STUB(13)
|
|
HRESULT STDMETHODCALLTYPE GetWaveMatrixProperties(uint64_t* count, AmdExtWaveMatrixProperties* waveMatrixProperties)
|
|
{
|
|
LOG_TRACE(": {}", *count);
|
|
|
|
waveMatrixProperties->mSize = 16;
|
|
waveMatrixProperties->nSize = 16;
|
|
waveMatrixProperties->kSize = 16;
|
|
|
|
waveMatrixProperties->aType = fp8;
|
|
waveMatrixProperties->bType = fp8;
|
|
|
|
waveMatrixProperties->cType = float32;
|
|
waveMatrixProperties->resultType = float32;
|
|
|
|
waveMatrixProperties->saturatingAccumulation = false;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObject) override { return E_NOTIMPL; }
|
|
ULONG __stdcall AddRef(void) override { return 0; }
|
|
ULONG __stdcall Release(void) override { return 0; }
|
|
};
|
|
|
|
struct AmdExtD3DFactory : public IAmdExtD3DFactory
|
|
{
|
|
HRESULT STDMETHODCALLTYPE CreateInterface(IUnknown* pOuter, REFIID riid, void** ppvObject) override
|
|
{
|
|
if (riid == __uuidof(IAmdExtD3DShaderIntrinsics))
|
|
{
|
|
if (amdExtD3DShaderIntrinsics == nullptr)
|
|
amdExtD3DShaderIntrinsics = new AmdExtD3DShaderIntrinsics();
|
|
|
|
*ppvObject = amdExtD3DShaderIntrinsics;
|
|
|
|
LOG_INFO("Custom IAmdExtD3DShaderIntrinsics queried, returning custom AmdExtD3DShaderIntrinsics");
|
|
|
|
return S_OK;
|
|
}
|
|
else if (riid == __uuidof(IAmdExtD3DDevice8))
|
|
{
|
|
if (amdExtD3DDevice8 == nullptr)
|
|
amdExtD3DDevice8 = new AmdExtD3DDevice8();
|
|
|
|
*ppvObject = amdExtD3DDevice8;
|
|
|
|
LOG_INFO("Custom IAmdExtD3DDevice8 queried, returning custom AmdExtD3DDevice8");
|
|
|
|
return S_OK;
|
|
}
|
|
else if (o_amdExtD3DFactory)
|
|
{
|
|
return o_amdExtD3DFactory->CreateInterface(pOuter, riid, ppvObject);
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObject) override
|
|
{
|
|
if (o_amdExtD3DFactory)
|
|
return o_amdExtD3DFactory->QueryInterface(riid, ppvObject);
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
ULONG __stdcall AddRef(void) override
|
|
{
|
|
if (o_amdExtD3DFactory)
|
|
return o_amdExtD3DFactory->AddRef();
|
|
|
|
return 0;
|
|
}
|
|
ULONG __stdcall Release(void) override
|
|
{
|
|
if (o_amdExtD3DFactory)
|
|
{
|
|
auto result = o_amdExtD3DFactory->Release();
|
|
|
|
if (result == 0)
|
|
o_amdExtD3DFactory = nullptr;
|
|
|
|
return result;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
void InitFSR4Update()
|
|
{
|
|
if (Config::Instance()->Fsr4Update.has_value() && !Config::Instance()->Fsr4Update.value())
|
|
return;
|
|
|
|
if (o_AmdExtD3DCreateInterface != nullptr)
|
|
return;
|
|
|
|
LOG_DEBUG("");
|
|
|
|
// For FSR4 Upgrade
|
|
moduleAmdxc64 = KernelBaseProxy::GetModuleHandleW_()(L"amdxc64.dll");
|
|
if (moduleAmdxc64 == nullptr)
|
|
moduleAmdxc64 = NtdllProxy::LoadLibraryExW_Ldr(L"amdxc64.dll", NULL, 0);
|
|
|
|
if (moduleAmdxc64 != nullptr)
|
|
{
|
|
LOG_INFO("amdxc64.dll loaded");
|
|
o_AmdExtD3DCreateInterface = (PFN_AmdExtD3DCreateInterface) KernelBaseProxy::GetProcAddress_()(
|
|
moduleAmdxc64, "AmdExtD3DCreateInterface");
|
|
|
|
if (o_AmdExtD3DCreateInterface != nullptr)
|
|
{
|
|
LOG_DEBUG("Hooking AmdExtD3DCreateInterface");
|
|
DetourTransactionBegin();
|
|
DetourUpdateThread(GetCurrentThread());
|
|
DetourAttach(&(PVOID&) o_AmdExtD3DCreateInterface, hkAmdExtD3DCreateInterface);
|
|
DetourTransactionCommit();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_INFO("Failed to load amdxc64.dll");
|
|
}
|
|
}
|
|
|
|
HMODULE GetFSR4Module() { return moduleAmdxcffx64; }
|
|
|
|
HRESULT STDMETHODCALLTYPE hkAmdExtD3DCreateInterface(IUnknown* pOuter, REFIID riid, void** ppvObject)
|
|
{
|
|
CheckForGPU();
|
|
|
|
if (!Config::Instance()->Fsr4Update.value_or_default() && o_AmdExtD3DCreateInterface != nullptr)
|
|
return o_AmdExtD3DCreateInterface(pOuter, riid, ppvObject);
|
|
|
|
// Proton bleeding edge ships amdxc64 that is missing some required functions
|
|
else if (riid == __uuidof(IAmdExtD3DFactory) && State::Instance().isRunningOnLinux)
|
|
{
|
|
// Required for the custom AmdExtFfxApi, lack of it triggers visual glitches
|
|
if (amdExtD3DFactory == nullptr)
|
|
amdExtD3DFactory = new AmdExtD3DFactory();
|
|
|
|
*ppvObject = amdExtD3DFactory;
|
|
|
|
LOG_INFO("IAmdExtD3DFactory queried, returning custom AmdExtD3DFactory");
|
|
|
|
if (o_AmdExtD3DCreateInterface != nullptr && o_amdExtD3DFactory == nullptr)
|
|
o_AmdExtD3DCreateInterface(pOuter, riid, (void**) &o_amdExtD3DFactory);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
else if (riid == __uuidof(IAmdExtFfxApi))
|
|
{
|
|
if (amdExtFfxApi == nullptr)
|
|
amdExtFfxApi = new AmdExtFfxApi();
|
|
|
|
// Return custom one
|
|
*ppvObject = amdExtFfxApi;
|
|
|
|
LOG_INFO("IAmdExtFfxApi queried, returning custom AmdExtFfxApi");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
else if (o_AmdExtD3DCreateInterface != nullptr)
|
|
return o_AmdExtD3DCreateInterface(pOuter, riid, ppvObject);
|
|
|
|
return E_NOINTERFACE;
|
|
}
|