FidelityFX API 简介
FidelityFX API 是一个简单的 API,专为 ABI 接口小且向前兼容而设计。它以动态链接库 (DLL) 的形式提供,包含 5 个函数,声明在 ffx_api.h 中。
ffxCreateContextffxDestroyContextffxDispatchffxQueryffxConfigure
参数以结构体链表的形式提供,每个结构体都有一个包含结构体类型和指向下一个结构体的指针的头部。
使用 FidelityFX API 的应用程序必须使用提供的签名 DLL 之一。这可以通过 `LoadLibrary` 和 `GetProcAddress` 在运行时加载(推荐),或通过 .lib 文件在应用程序启动时通过动态链接器加载。
特定于后端的(目前仅限 DirectX 12)功能仅在使用相应的 DLL 时支持。
为了方便 C++ 应用程序使用,提供了用于正确初始化结构体类型和链接头文件的辅助函数。只需使用每个头文件的 `.hpp` 版本,并将 `ffx` 前缀替换为 `ffx::` 命名空间。请注意,包装 API 函数的辅助函数仅在链接使用 .lib 文件时才有效。使用运行时加载它们将导致链接器错误。
DLL 结构
从 AMD FidelityFX™ SDK 2.0.0 开始,之前组合在 `amd_fidelityfx_dx12.dll` 中的效果被拆分为基于效果类型的多个 DLL。
- `amd_fidelityfx_loader_dx12.dll`:一个小型加载器 DLL,不包含任何效果代码。此 DLL 仅管理效果类型 DLL 的加载,以便将效果提供给调用应用程序。它在接口和行为上与 `amd_fidelityfx_dx12.dll` 兼容。
- `amd_fidelityfx_framegeneration_dx12.dll`:此 DLL 包含与 FidelityFX 帧生成相关的提供程序,即 帧插值效果和 帧插值交换链。
- `amd_fidelityfx_upscaler_dx12.dll`:此 DLL 包含最新 FidelityFX Super Resolution 技术(FSR4)以及旧技术(FSR2 和 FSR3.1 升级器)的提供程序。
拆分的好处是应用程序可以仅包含受支持的 AMD FidelityFX™ 效果类型。
先前加载 `amd_fidelityfx_dx12.dll`(或链接到 `amd_fidelityfx_dx12.lib`)的应用程序需要改为加载 `amd_fidelityfx_loader_dx12.dll`(或链接到 `amd_fidelityfx_loader_dx12.lib`)。
描述符结构
效果的功能通过描述符结构公开。每个结构体都有一个头部
typedef struct ffxApiHeader{ ffxStructType_t type; ///< The structure type. Must always be set to the corresponding value for any structure (found nearby with a similar name). struct ffxApiHeader* pNext; ///< Pointer to next structure, used for optional parameters and extensions. Can be null.} ffxApiHeader;每个描述符都有一个关联的结构体类型,通常在头文件中的结构体声明正上方定义。头部中的 `type` 字段必须设置为该结构体类型。否则将导致未定义行为并可能导致崩溃。C++ 头文件(扩展名为 `.hpp`)提供了自动初始化的辅助函数。
`pNext` 字段用于在链表(或“链”)中指定其他参数和扩展。某些调用需要链接特定的其他结构体。
创建上下文
通过调用 `ffxCreateContext` 来创建上下文,其声明如下:
ffxReturnCode_t ffxCreateContext(ffxContext* context, ffxCreateContextDescHeader* desc, const ffxAllocationCallbacks* memCb);在调用之前,`context` 应初始化为 `null`。请注意,第二个参数是指向结构体头部的指针,而不是结构体本身。第三个参数用于自定义分配器,可以为 `null`,请参阅 内存分配部分。
示例调用
struct ffxCreateBackendDX12Desc createBackend = /*...*/;
struct ffxCreateContextDescUpscale createUpscale = { 0 };createUpscale.header.type = FFX_API_CREATE_CONTEXT_DESC_TYPE_UPSCALE;
// fill struct ...
createUpscale.header.pNext = &createBackend.header;
ffxContext upscaleContext = NULL;ffxReturnCode_t retCode = ffxContextCreate(&upscaleContext, &createUpscale.header, NULL);// handle errors请注意,在使用 C++ 包装器时,第三个参数会排在第二位,以支持可变参数描述符。
// equivalent of above, chain of createUpscale and createBackend will be linked automatically.ffxReturnCode_t retCode = ffx::ContextCreate(upscaleContext, nullptr, createUpscale, createBackend);上下文销毁
要销毁上下文,请调用 `ffxDestroyContext`
ffxReturnCode_t ffxDestroyContext(ffxContext* context, const ffxAllocationCallbacks* memCb);调用后,`context` 将为 `null`。内存回调必须与上下文创建期间使用的分配回调兼容,请参阅 内存分配部分。
查询
要从效果中查询信息或资源,请使用 `ffxQuery`
ffxReturnCode_t ffxQuery(ffxContext* context, ffxQueryDescHeader* desc);输出值将通过写入查询描述符中传递的指针来返回。
某些查询描述符类型需要传递由 `ffxCreateContext` 创建的有效上下文。
从 SDK 示例的默认版本开始,创建上下文后的效果查询版本示例
struct ffxQueryGetProviderVersion getVersion = {0};getVersion.header.type = FFX_API_QUERY_DESC_TYPE_GET_PROVIDER_VERSION;
ffxReturnCode_t retCode_t = ffxQuery(&m_UpscalingContext, &getVersion.header);使用 `NULL` 上下文查询描述符类型时,需要提供更多输入信息,如设备、标志和分辨率。
从 SDK 示例中,创建上下文前查询升级器 GPU 内存使用量的示例。
struct FfxApiEffectMemoryUsage gpuMemoryUsageUpscaler = {0};struct ffxQueryDescUpscaleGetGPUMemoryUsageV2 upscalerGetGPUMemoryUsageV2 = {0};upscalerGetGPUMemoryUsageV2.header.type = FFX_API_QUERY_DESC_TYPE_UPSCALE_GPU_MEMORY_USAGE_V2;upscalerGetGPUMemoryUsageV2.device = GetDevice()->GetImpl()->DX12Device();upscalerGetGPUMemoryUsageV2.maxRenderSize = { resInfo.RenderWidth, resInfo.RenderHeight };upscalerGetGPUMemoryUsageV2.maxUpscaleSize = { resInfo.UpscaleWidth, resInfo.UpscaleHeight };upscalerGetGPUMemoryUsageV2.flags = FFX_UPSCALE_ENABLE_AUTO_EXPOSURE | FFX_UPSCALE_ENABLE_HIGH_DYNAMIC_RANGE;upscalerGetGPUMemoryUsageV2.gpuMemoryUsageUpscaler = &gpuMemoryUsageUpscaler;
ffxReturnCode_t retCode_t = ffxQuery(nullptr, &upscalerGetGPUMemoryUsageV2.header);
CAUDRON_LOG_INFO(L"Default Upscaler Query GPUMemoryUsageV2 totalUsageInBytes %f ", gpuMemoryUsageUpscaler.totalUsageInBytes / 1048576.f);如果使用 C++ 辅助函数,当上下文为 `NULL` 时,需要省略上下文参数。
//this ffx:Query call is equivalent to above ffxQuery callffx::ReturnCode retCode = ffx::Query(upscalerGetGPUMemoryUsageV2);要查询效果的特定版本,请在主结构体头部的 `pNext` 字段中附加一个类型为 `ffxOverrideVersion` 的结构体。
继续上面的示例,查询特定版本升级器的 GPU 内存使用量
struct ffxOverrideVersion versionOverride = {0};versionOverride.header.type = FFX_API_DESC_TYPE_OVERRIDE_VERSION;versionOverride.versionId = m_FsrVersionIds[m_FsrVersionIndex];upscalerGetGPUMemoryUsageV2.header.pNext = &versionOverride.header;
ffxReturnCode_t retCode_t = ffxQuery(nullptr, &upscalerGetGPUMemoryUsageV2.header);配置
要配置效果,请使用 `ffxConfigure`
ffxReturnCode_t ffxConfigure(ffxContext* context, const ffxConfigureDescHeader* desc);上下文必须是 `ffxCreateContext` 创建的有效上下文,除非特定配置描述符的文档另有说明。
所有效果都有一个键值配置结构,用于简单的选项,例如:
struct ffxConfigureDescUpscaleKeyValue{ ffxConfigureDescHeader header; uint64_t key; ///< Configuration key, member of the FfxApiConfigureUpscaleKey enumeration. uint64_t u64; ///< Integer value or enum value to set. void* ptr; ///< Pointer to set or pointer to value to set.};可用的键以及要传递的值的约束在相关技术的文档中指定。
调度
要调度渲染命令或其他功能,请使用 `ffxDispatch`
ffxReturnCode_t ffxDispatch(ffxContext* context, const ffxDispatchDescHeader* desc);上下文必须是 `ffxCreateContext` 创建的有效上下文。
GPU 渲染调度会将命令编码到作为输入的命令列表/缓冲区中。
CPU 调度通常会同步且立即执行其功能。
资源结构
纹理和缓冲区等资源通过 `FfxApiResource` 结构体传递给 FidelityFX API。
对于 C++ 应用程序,后端头文件定义了用于从原生资源句柄构建此结构的辅助函数。
版本选择
FidelityFX API 支持在创建上下文时覆盖每个效果的版本。这是一项可选功能。如果使用了版本覆盖,则 **必须** 保持一致。
- 仅使用 `ffxQuery` 在 `ffxQueryDescGetVersions` 中返回的版本 ID,并使用适当的创建结构体类型。
- 请勿硬编码版本 ID
- 如果使用 `NULL` 参数调用 `ffxQuery`(无上下文),请使用与 `ffxCreateContext` 相同的版本覆盖。
- 版本选择应留给默认行为(无覆盖)或用户(显示在选项 UI 中)。
- 版本查询报告的版本可能因参数而异。不要单独查询版本名称和 ID,因为它们的顺序可能会改变。
版本查询示例
struct ffxQueryDescGetVersions versionQuery = {0};versionQuery.createDescType = FFX_API_CREATE_CONTEXT_DESC_TYPE_UPSCALE;versionQuery.device = GetDX12Device(); // only for DirectX 12 applicationsuint64_t versionCount = 0;versionQuery.outputCount = &versionCount;// get number of versions for allocationffxQuery(nullptr, &versionQuery.header);
std::vector<const char*> versionNames;std::vector<uint64_t> versionIds;m_FsrVersionIds.resize(versionCount);versionNames.resize(versionCount);versionQuery.versionIds = versionIds.data();versionQuery.versionNames = versionNames.data();// fill version ids and names arrays.ffxQuery(nullptr, &versionQuery.header);要覆盖创建上下文时的版本,请在主结构体头部的 `pNext` 字段中附加一个类型为 `ffxOverrideVersion` 的结构体。
使用 C++ 辅助函数,从 SDK 示例中创建带有版本覆盖的上下文示例
ffx::ReturnCode retCode;// lifetime of this must last until after CreateContext callffx::CreateContextDescOverrideVersion versionOverride{};if (m_FsrVersionIndex < m_FsrVersionIds.size() && m_overrideVersion){ versionOverride.versionId = m_FsrVersionIds[m_FsrVersionIndex]; retCode = ffx::CreateContext(m_UpscalingContext, nullptr, createFsr, backendDesc, versionOverride);}您可以使用 `ffxQuery` 和 `ffxQueryGetProviderVersion` 结构体查询任何已创建上下文的版本。
错误处理
所有对 FidelityFX API 的调用都返回 `ffxReturnCode_t` 类型的值。如果使用 C++ 包装器,则此类型被替换为 `ffx::ReturnCode`。
成功操作将返回 `FFX_API_RETURN_OK`。所有其他返回码均为错误。将来的版本可能会添加头文件中尚不存在的新返回码。
应用程序应能够优雅地处理错误,即使是不可恢复的错误。
内存分配
要控制内存分配,请将 `ffxAllocationCallbacks` 的指针传递给 `ffxCreateContext` 和 `ffxDestroyContext`。
如果传递空指针,则将使用全局 `malloc` 和 `free`。
对于自定义分配器,需要两个函数:
// Memory allocation function. Must return a valid pointer to at least size bytes of memory aligned to hold any type.// May return null to indicate failure. Standard library malloc fulfills this requirement.typedef void* (*ffxAlloc)(void* pUserData, uint64_t size);
// Memory deallocation function. May be called with null pointer as second argument.typedef void (*ffxDealloc)(void* pUserData, void* pMem);
typedef struct ffxAllocationCallbacks{ void* pUserData; ffxAlloc alloc; ffxDealloc dealloc;} ffxAllocationCallbacks;`pUserData` 会原样传递给回调,不做修改或验证。FidelityFX API 代码不会尝试解引用它,也不会存储它。
如果为上下文创建使用了自定义分配器,则必须使用兼容的结构体进行销毁。这意味着在上下文创建期间使用回调和用户数据分配的任何指针都必须能够使用传递给 `ffxDestroyContext` 的回调和用户数据进行释放。