Advance native Windows RR integration, kernel tooling and local packaging
Implement native NGX feature-13 RR through a LUID-matched D3D12/Vulkan shared-memory and timeline-fence bridge. Add owned parameter snapshots, GPU resource lifetime handling, inherited/enhanced barriers, optional guides, sampled input formats and depth-plane support with stencil preservation. Separate RR initialization from the SR CUDA runtime and expose explicit backend/model selection through player configuration. Add synthetic bridge, snapshot and NGX lifecycle harnesses covering camera/history/reset, parameter mutation, delayed submission, early release, capability gating and unsupported-input rejection. Add exact gfx11 D/E model generation, verified caches, versioned local deployment with configuration backups, and isolated portable model tools with pinned Python, NumPy and glslang dependencies. Extend Windows K benchmarking and private native-cache selection. Fix LLVM 22 emitter/helper compatibility and preserve exact-output validation for local output-kernel optimization. Add native FSR RR provider diagnostics, local development package refresh tooling, installer/checker updates, FidelityFX headers and Windows parity documentation. Keep NVIDIA binaries, derived models and private optimized kernels out of distributable packages. Native RR evidence remains synthetic; game validation, FSR RR dispatch, installer integration and Linux performance parity remain incomplete.
This commit is contained in:
1 parent
3345fffcd3
commit
247fe1092d
60 files changed
+7168
-66
No files matched your search
@@ -0,0 +1,279 @@
|
||||
// AMD FSR Ray Regeneration (FidelityFX denoiser 1.2) runtime access, shared by the NGX shim's
|
||||
// FSR Ray Reconstruction backend and tools/fsr_rr_probe.cpp.
|
||||
//
|
||||
// The denoiser is AMD's signed amd_fidelityfx_denoiser_dx12.dll, loaded from an explicit path
|
||||
// (never from a search path, never patched) and driven through its five FFX API exports. It
|
||||
// records its compute work into the caller's D3D12 command list; nothing here involves CUDA,
|
||||
// ZLUDA or NVIDIA's denoiser. Struct layouts come from AMD's MIT-licensed headers in
|
||||
// third_party/fidelityfx (SDK 2.3.0 / FSR Ray Regeneration 1.2.0).
|
||||
//
|
||||
// Each stage reports separately (see FsrRrStage): a missing DLL, a missing export, a provider
|
||||
// query that returns no denoiser for this device, a failed context creation (where shader
|
||||
// pipelines are built) and a failed dispatch are different failures and are never folded together.
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <d3d12.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../third_party/fidelityfx/api/include/ffx_api.h"
|
||||
#include "../third_party/fidelityfx/api/include/ffx_api_types.h"
|
||||
#include "../third_party/fidelityfx/denoisers/include/ffx_denoiser.h"
|
||||
|
||||
// ffx_api_dx12.h's backend descriptor, declared here because that header's inline helpers call
|
||||
// ID3D12Resource::GetDesc() by value, which does not compile with WIDL_EXPLICIT_AGGREGATE_RETURNS.
|
||||
#ifndef FFX_API_CREATE_CONTEXT_DESC_TYPE_BACKEND_DX12
|
||||
#define FFX_API_CREATE_CONTEXT_DESC_TYPE_BACKEND_DX12 FFX_API_MAKE_BACKEND_SUB_ID(FFX_API_BACKEND_ID_DX12, 0x02)
|
||||
struct ffxCreateBackendDX12Desc
|
||||
{
|
||||
ffxCreateContextDescHeader header;
|
||||
ID3D12Device* device;
|
||||
};
|
||||
#endif
|
||||
static_assert(sizeof(ffxCreateBackendDX12Desc) == 24, "ffx_api_dx12.h layout");
|
||||
// The 1.2 descriptors as the signed runtime reads them (x64, natural alignment, same as MSVC).
|
||||
static_assert(sizeof(FfxApiResource) == 48 && sizeof(FfxApiDenoiserSignal) == 104, "ffx_api_types.h layout");
|
||||
static_assert(sizeof(ffxCreateContextDescDenoiser) == 40, "ffx_denoiser.h 1.2 create layout");
|
||||
static_assert(sizeof(ffxDispatchDescDenoiser) == 448 && offsetof(ffxDispatchDescDenoiser, view) == 296 &&
|
||||
offsetof(ffxDispatchDescDenoiser, renderSize) == 432,
|
||||
"ffx_denoiser.h 1.2 dispatch layout");
|
||||
|
||||
enum class FsrRrStage
|
||||
{
|
||||
NotLoaded, // nothing tried yet
|
||||
DllMissing, // no file at the configured path
|
||||
DllLoadFailed, // LoadLibrary failed (dependencies, architecture)
|
||||
ExportsMissing, // not an FFX API DLL
|
||||
NoProvider, // the runtime returned no denoiser version for this device
|
||||
Ready, // a provider was enumerated for this device; contexts may still fail
|
||||
};
|
||||
|
||||
inline const char* fsr_rr_stage_name(FsrRrStage stage)
|
||||
{
|
||||
switch (stage)
|
||||
{
|
||||
case FsrRrStage::NotLoaded: return "not loaded";
|
||||
case FsrRrStage::DllMissing: return "denoiser DLL missing";
|
||||
case FsrRrStage::DllLoadFailed: return "denoiser DLL failed to load";
|
||||
case FsrRrStage::ExportsMissing: return "denoiser DLL lacks the FFX API exports";
|
||||
case FsrRrStage::NoProvider: return "no FSR Ray Regeneration provider for this device";
|
||||
case FsrRrStage::Ready: return "provider available";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
inline const char* fsr_rr_return_name(ffxReturnCode_t code)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
case FFX_API_RETURN_OK: return "OK";
|
||||
case FFX_API_RETURN_ERROR: return "ERROR";
|
||||
case FFX_API_RETURN_ERROR_UNKNOWN_DESCTYPE: return "ERROR_UNKNOWN_DESCTYPE";
|
||||
case FFX_API_RETURN_ERROR_RUNTIME_ERROR: return "ERROR_RUNTIME_ERROR";
|
||||
case FFX_API_RETURN_NO_PROVIDER: return "NO_PROVIDER";
|
||||
case FFX_API_RETURN_ERROR_MEMORY: return "ERROR_MEMORY";
|
||||
case FFX_API_RETURN_ERROR_PARAMETER: return "ERROR_PARAMETER";
|
||||
case FFX_API_RETURN_PROVIDER_NO_SUPPORT_NEW_DESCTYPE: return "PROVIDER_NO_SUPPORT_NEW_DESCTYPE";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
// The loaded runtime and the provider the device was offered. One per process: the DLL keeps
|
||||
// global state (its debug callback), so it is loaded once and never unloaded while contexts exist.
|
||||
struct FsrRrRuntime
|
||||
{
|
||||
HMODULE module = nullptr;
|
||||
std::wstring path; // as configured
|
||||
std::wstring mappedPath; // as the loader mapped it
|
||||
PfnFfxCreateContext createContext = nullptr;
|
||||
PfnFfxDestroyContext destroyContext = nullptr;
|
||||
PfnFfxConfigure configure = nullptr;
|
||||
PfnFfxQuery query = nullptr;
|
||||
PfnFfxDispatch dispatch = nullptr;
|
||||
FsrRrStage stage = FsrRrStage::NotLoaded;
|
||||
ffxReturnCode_t queryResult = FFX_API_RETURN_OK;
|
||||
std::vector<uint64_t> versionIds;
|
||||
std::vector<std::string> versionNames;
|
||||
uint64_t providerId = 0; // the provider contexts are created with (the first enumerated)
|
||||
std::string providerName;
|
||||
|
||||
// Loads `dll` with its own directory searched for dependencies. Does not query providers.
|
||||
bool load(const std::wstring& dll)
|
||||
{
|
||||
path = dll;
|
||||
if (GetFileAttributesW(dll.c_str()) == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
stage = FsrRrStage::DllMissing;
|
||||
return false;
|
||||
}
|
||||
module = LoadLibraryExW(dll.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
|
||||
if (module == nullptr)
|
||||
{
|
||||
stage = FsrRrStage::DllLoadFailed;
|
||||
return false;
|
||||
}
|
||||
wchar_t mapped[MAX_PATH] = {};
|
||||
GetModuleFileNameW(module, mapped, MAX_PATH);
|
||||
mappedPath = mapped;
|
||||
createContext = reinterpret_cast<PfnFfxCreateContext>(reinterpret_cast<void*>(GetProcAddress(module, "ffxCreateContext")));
|
||||
destroyContext = reinterpret_cast<PfnFfxDestroyContext>(reinterpret_cast<void*>(GetProcAddress(module, "ffxDestroyContext")));
|
||||
configure = reinterpret_cast<PfnFfxConfigure>(reinterpret_cast<void*>(GetProcAddress(module, "ffxConfigure")));
|
||||
query = reinterpret_cast<PfnFfxQuery>(reinterpret_cast<void*>(GetProcAddress(module, "ffxQuery")));
|
||||
dispatch = reinterpret_cast<PfnFfxDispatch>(reinterpret_cast<void*>(GetProcAddress(module, "ffxDispatch")));
|
||||
if (!createContext || !destroyContext || !configure || !query || !dispatch)
|
||||
{
|
||||
stage = FsrRrStage::ExportsMissing;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Routes the runtime's own error/warning messages to `callback` (may be called on any thread).
|
||||
ffxReturnCode_t set_message_callback(ffxApiMessage callback, uint32_t level)
|
||||
{
|
||||
ffxConfigureDescGlobalDebug desc = {};
|
||||
desc.header.type = FFX_API_CONFIGURE_DESC_TYPE_GLOBALDEBUG;
|
||||
desc.effectId = FFX_API_EFFECT_ID_DENOISER;
|
||||
desc.fpMessage = callback;
|
||||
desc.debugLevel = level;
|
||||
return configure(nullptr, &desc.header);
|
||||
}
|
||||
|
||||
// The two-call version enumeration AMD's sample uses, against the real device. Zero
|
||||
// versions is the "no provider for this device" outcome, distinct from a query error.
|
||||
bool query_providers(ID3D12Device* device)
|
||||
{
|
||||
versionIds.clear();
|
||||
versionNames.clear();
|
||||
uint64_t count = 0;
|
||||
ffxQueryDescGetVersions desc = {};
|
||||
desc.header.type = FFX_API_QUERY_DESC_TYPE_GET_VERSIONS;
|
||||
desc.createDescType = FFX_API_EFFECT_ID_DENOISER;
|
||||
desc.device = device;
|
||||
desc.outputCount = &count;
|
||||
queryResult = query(nullptr, &desc.header);
|
||||
if (queryResult != FFX_API_RETURN_OK || count == 0)
|
||||
{
|
||||
stage = FsrRrStage::NoProvider;
|
||||
return false;
|
||||
}
|
||||
versionIds.resize(count);
|
||||
std::vector<const char*> names(count, nullptr);
|
||||
desc.versionIds = versionIds.data();
|
||||
desc.versionNames = names.data();
|
||||
queryResult = query(nullptr, &desc.header);
|
||||
if (queryResult != FFX_API_RETURN_OK || count == 0)
|
||||
{
|
||||
stage = FsrRrStage::NoProvider;
|
||||
return false;
|
||||
}
|
||||
versionIds.resize(count);
|
||||
for (uint64_t index = 0; index < count; ++index)
|
||||
versionNames.emplace_back(names[index] != nullptr ? names[index] : "");
|
||||
providerId = versionIds[0];
|
||||
providerName = versionNames[0];
|
||||
stage = FsrRrStage::Ready;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// GPU memory the denoiser would allocate for this configuration (total, aliasable).
|
||||
inline ffxReturnCode_t fsr_rr_query_memory(FsrRrRuntime& runtime, ID3D12Device* device, uint32_t width,
|
||||
uint32_t height, uint32_t signals, uint32_t flags,
|
||||
FfxApiEffectMemoryUsage& usage)
|
||||
{
|
||||
ffxOverrideVersion version = {};
|
||||
version.header.type = FFX_API_DESC_TYPE_OVERRIDE_VERSION;
|
||||
version.versionId = runtime.providerId;
|
||||
ffxQueryDescDenoiserGetGPUMemoryUsage desc = {};
|
||||
desc.header.type = FFX_API_QUERY_DESC_TYPE_DENOISER_GPU_MEMORY_USAGE;
|
||||
desc.header.pNext = &version.header;
|
||||
desc.device = device;
|
||||
desc.maxRenderSize = {width, height};
|
||||
desc.signalFlags = signals;
|
||||
desc.flags = flags;
|
||||
desc.gpuMemoryUsage = &usage;
|
||||
return runtime.query(nullptr, &desc.header);
|
||||
}
|
||||
|
||||
// Creates one denoiser context on the enumerated provider. The descriptor chain only has to live
|
||||
// for the call: ffx_api.h requires pointers *inside* descriptors to outlive the context, and
|
||||
// none of these carry any.
|
||||
inline ffxReturnCode_t fsr_rr_create_context(FsrRrRuntime& runtime, ID3D12Device* device, uint32_t maxWidth,
|
||||
uint32_t maxHeight, uint32_t signals, uint32_t flags,
|
||||
ffxContext& context)
|
||||
{
|
||||
ffxCreateContextDescDenoiser desc = {};
|
||||
desc.header.type = FFX_API_CREATE_CONTEXT_DESC_TYPE_DENOISER;
|
||||
desc.version = FFX_DENOISER_VERSION;
|
||||
desc.maxRenderSize = {maxWidth, maxHeight};
|
||||
desc.signalFlags = signals;
|
||||
desc.flags = flags;
|
||||
ffxCreateBackendDX12Desc backend = {};
|
||||
backend.header.type = FFX_API_CREATE_CONTEXT_DESC_TYPE_BACKEND_DX12;
|
||||
backend.device = device;
|
||||
ffxOverrideVersion version = {};
|
||||
version.header.type = FFX_API_DESC_TYPE_OVERRIDE_VERSION;
|
||||
version.versionId = runtime.providerId;
|
||||
desc.header.pNext = &backend.header;
|
||||
backend.header.pNext = &version.header;
|
||||
context = nullptr;
|
||||
return runtime.createContext(&context, &desc.header, nullptr);
|
||||
}
|
||||
|
||||
// Asks a live context which provider actually serves it (the runtime's own account, logged as
|
||||
// the "actual backend").
|
||||
inline std::string fsr_rr_context_provider(FsrRrRuntime& runtime, ffxContext& context, uint64_t* id = nullptr)
|
||||
{
|
||||
ffxQueryGetProviderVersion desc = {};
|
||||
desc.header.type = FFX_API_QUERY_DESC_TYPE_GET_PROVIDER_VERSION;
|
||||
if (runtime.query(&context, &desc.header) != FFX_API_RETURN_OK || desc.versionName == nullptr)
|
||||
return std::string();
|
||||
if (id != nullptr)
|
||||
*id = desc.versionId;
|
||||
return desc.versionName;
|
||||
}
|
||||
|
||||
// An FfxApiResource for a 2D texture whose description the caller already knows (the shim never
|
||||
// calls GetDesc() by value; see above).
|
||||
inline FfxApiResource fsr_rr_texture(ID3D12Resource* resource, const D3D12_RESOURCE_DESC& desc, uint32_t format,
|
||||
uint32_t state)
|
||||
{
|
||||
FfxApiResource result = {};
|
||||
result.resource = resource;
|
||||
result.state = state;
|
||||
if (resource == nullptr)
|
||||
return result;
|
||||
result.description.type = FFX_API_RESOURCE_TYPE_TEXTURE2D;
|
||||
result.description.format = format;
|
||||
result.description.width = static_cast<uint32_t>(desc.Width);
|
||||
result.description.height = desc.Height;
|
||||
result.description.depth = desc.DepthOrArraySize;
|
||||
result.description.mipCount = desc.MipLevels;
|
||||
result.description.usage = FFX_API_RESOURCE_USAGE_READ_ONLY;
|
||||
if (desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)
|
||||
result.description.usage |= FFX_API_RESOURCE_USAGE_UAV;
|
||||
return result;
|
||||
}
|
||||
|
||||
// The FFX surface formats the adapter's own intermediate textures use.
|
||||
inline uint32_t fsr_rr_surface_format(DXGI_FORMAT format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case DXGI_FORMAT_R32_FLOAT: return FFX_API_SURFACE_FORMAT_R32_FLOAT;
|
||||
case DXGI_FORMAT_R16G16B16A16_FLOAT: return FFX_API_SURFACE_FORMAT_R16G16B16A16_FLOAT;
|
||||
case DXGI_FORMAT_R32G32B32A32_FLOAT: return FFX_API_SURFACE_FORMAT_R32G32B32A32_FLOAT;
|
||||
case DXGI_FORMAT_R10G10B10A2_UNORM: return FFX_API_SURFACE_FORMAT_R10G10B10A2_UNORM;
|
||||
case DXGI_FORMAT_R8G8B8A8_UNORM: return FFX_API_SURFACE_FORMAT_R8G8B8A8_UNORM;
|
||||
case DXGI_FORMAT_R16_FLOAT: return FFX_API_SURFACE_FORMAT_R16_FLOAT;
|
||||
case DXGI_FORMAT_R8_UNORM: return FFX_API_SURFACE_FORMAT_R8_UNORM;
|
||||
default: return FFX_API_SURFACE_FORMAT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user