Add AMD FSR 3.1 upscaling, and run DLSS's smaller modes on a 16:9 picture too.

The Upscaling choice gains FSR 3.1 Native AA, Quality, Balanced, Performance and Ultra Performance. FSR 3.1 runs
through AMD's signed amd_fidelityfx_vk.dll (loaded at run time; the build kit ships it) in DLSS's place, on the same
scene light, depth and camera motion vectors and at the same render scales. Its Vulkan back end needs
VK_KHR_get_memory_requirements2, which the device now enables when FSR 3.1 is chosen.

A half-size effect pass with three targets and a half-size depth was taken for a G-buffer, which switched every later
frame to the deferred frame shape: there the output pass of a smaller scene was never found, so DLSS Quality,
Balanced, Performance and Ultra Performance did nothing on a 16:9 picture. Such a pass smaller than the scene is now
left alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
chrg6
2026-11-07 12:17:56 -05:00
parent fc6d84aa63
commit ca5e5eb5a4
18 changed files with 1430 additions and 20 deletions

View File

@@ -106,3 +106,4 @@ folder's `licenses/` and in each component's own folder.
| AppImage type-2 runtime (static; includes libfuse and squashfuse), the AppImage's first part; appimagetool packs the folder and is not distributed | https://github.com/AppImage/type2-runtime | MIT; libfuse LGPL-2.1; squashfuse BSD-2-Clause | the AppImage header |
| MacNativeGames shader variant list | as in the installer release row above | MIT | `data/shader_list.tsv` |
| Weeping of the Empire menu cursor: eui's focus box read through ScreenMgr, BoxCursorMgr and the screens' node lists, AnimButton's hit test, the stale-hit-pane fallback to the parts panes, the three writes of moveBoxCursor, the structure offsets (1.0.0; the ScreenMgr instance placed in 1.4.3 by us) | https://github.com/daniel-madrid-07/weeping-of-the-empire @ `df0586cdc5702ca19bbebfa59697e0b6980f6237` (`engine/runtime/nx2navi_menu_cursor.cpp`), Copyright (c) 2026 daniel-madrid-07 | MIT | `runtime/menu_cursor.c` |
| AMD FidelityFX API headers (FSR 3.1 upscaling and frame generation) and its signed Vulkan library | https://github.com/GPUOpen-LibrariesAndSDKs/FidelityFX-SDK @ `v1.1.4` (`ffx-api/include/ffx_api/`, `PrebuiltSignedDLL/amd_fidelityfx_vk.dll`), Copyright (C) 2024 Advanced Micro Devices, Inc. | MIT | `runtime/third_party/ffx_api/` (unmodified, with `LICENSE.txt`), used by `runtime/vk_ffx.cpp`; the library ships in the build kit's `bin/` and beside the built game (never in this repository) |

View File

@@ -57,7 +57,7 @@ static const char *const known[] = {
"SHADER_MEMO", "SHADOW_COMPARE", "SHADOW_RANGE", "SHADOW_RES", "SHADOW_SCALE", "SHARED_FONTS", "SHARPNESS", "SHOTS", "SHOTS_DIR",
"SHOTS_FULL", "SPEED_PROBE", "SPIN", "SPIN_HANDOFF_US", "SPLIT_DRAWS", "SPLIT_US", "STICK_DEADZONE", "STICK_SENS",
"SWKBD_ANSWER",
"SWKBD_TEST", "TEST_PAD", "TEST_PAD_AT", "TEST_PREPARE_SHADERS", "TEX_ALIAS", "TEX_MEMO", "TEX_VRAM_PRESSURE", "TIMER_1MS", "TRACE", "TXD_TRANSPOSE", "UBO_SNAPSHOT", "UPSCALER", "VK_DYN_A2C", "VK_DYN_VERTEX", "VK_NOOPT", "VK_NO_FLOAT64",
"SWKBD_TEST", "TAA", "TEST_PAD", "TEST_PAD_AT", "TEST_PREPARE_SHADERS", "TEX_ALIAS", "TEX_MEMO", "TEX_VRAM_PRESSURE", "TIMER_1MS", "TRACE", "TXD_TRANSPOSE", "UBO_SNAPSHOT", "UPSCALER", "VK_DYN_A2C", "VK_DYN_VERTEX", "VK_NOOPT", "VK_NO_FLOAT64",
"VK_PRESENT", "VK_RECORDER", "VK_SSBO_ALIGN", "VK_SUBGROUP", "VK_SUBGROUP_PIN", "VK_VALIDATION", "VOLUME", "VRAM_HEADROOM", "VS_OUTS",
"WALK_KEY", "WALK_SCALE", "WB_EXACT", "WB_FENCE_LIMIT", "WB_MERGE", "WINDOW_SIZE", "XLATE", "XLATE_ID", "XLATE_KEEP", "XLATE_PROCS",
"ZSTD_CHECK", "ZSTD_NATIVE",
@@ -118,7 +118,7 @@ static const CfgMeta metas[] = {
{"OUTPUT_RES", "1920x1080", NULL, RESTART}, {"RENDER_SCALE", "game", "game|50|67|75|100|125|150|200", RESTART},
{"UPSCALER", "fsr", "fsr|bilinear", LIVE}, {"SHARPNESS", "80", "0..100", LIVE},
{"DLAA", "0", "0|1", RESTART}, {"DLSS", "dlaa", "dlaa|quality|balanced|performance|ultra_performance", LIVE},
{"DLSS_PRESET", "default", "default|k|j", LIVE},
{"DLSS_PRESET", "default", "default|k|j", LIVE}, {"TAA", "dlss", "dlss|fsr3", RESTART}, // vk_dlaa.c fsr3
{"DLSS_CHAR_MASK", "65", "0|35|65|100", LIVE}, {"FXAA", "1", "0|1", LIVE},
{"GAME_ASPECT", "16:9", "16:9|window|21:9|32:9", RESTART}, // widescreen.c
{"GAME_RES", "1440", "1440|1800|2160|2880|window", RESTART}, // widescreen.c: the picture's height

21
runtime/third_party/ffx_api/LICENSE.txt vendored Normal file
View File

@@ -0,0 +1,21 @@
This file is part of the FidelityFX SDK.
Copyright (C) 2024 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

158
runtime/third_party/ffx_api/ffx_api.h vendored Normal file
View File

@@ -0,0 +1,158 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#if defined(__cplusplus)
extern "C" {
#endif // #if defined(__cplusplus)
#define FFX_API_ENTRY __declspec(dllexport)
#include <stdint.h>
enum FfxApiReturnCodes
{
FFX_API_RETURN_OK = 0, ///< The oparation was successful.
FFX_API_RETURN_ERROR = 1, ///< An error occurred that is not further specified.
FFX_API_RETURN_ERROR_UNKNOWN_DESCTYPE = 2, ///< The structure type given was not recognized for the function or context with which it was used. This is likely a programming error.
FFX_API_RETURN_ERROR_RUNTIME_ERROR = 3, ///< The underlying runtime (e.g. D3D12, Vulkan) or effect returned an error code.
FFX_API_RETURN_NO_PROVIDER = 4, ///< No provider was found for the given structure type. This is likely a programming error.
FFX_API_RETURN_ERROR_MEMORY = 5, ///< A memory allocation failed.
FFX_API_RETURN_ERROR_PARAMETER = 6, ///< A parameter was invalid, e.g. a null pointer, empty resource or out-of-bounds enum value.
};
typedef void* ffxContext;
typedef uint32_t ffxReturnCode_t;
#define FFX_API_EFFECT_MASK 0xffff0000u
#define FFX_API_EFFECT_ID_GENERAL 0x00000000u
// Base Descriptor types
typedef uint64_t ffxStructType_t;
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;
typedef ffxApiHeader ffxCreateContextDescHeader;
typedef ffxApiHeader ffxConfigureDescHeader;
typedef ffxApiHeader ffxQueryDescHeader;
typedef ffxApiHeader ffxDispatchDescHeader;
// Extensions for global debug
#define FFX_API_CONFIGURE_GLOBALDEBUG_LEVEL_SILENCE 0x0000000u
#define FFX_API_CONFIGURE_GLOBALDEBUG_LEVEL_ERRORS 0x0000001u
#define FFX_API_CONFIGURE_GLOBALDEBUG_LEVEL_WARNINGS 0x0000002u
#define FFX_API_CONFIGURE_GLOBALDEBUG_LEVEL_VERBOSE 0xfffffffu
enum FfxApiMsgType
{
FFX_API_MESSAGE_TYPE_ERROR = 0,
FFX_API_MESSAGE_TYPE_WARNING = 1,
FFX_API_MESSAGE_TYPE_COUNT
};
typedef void (*ffxApiMessage)(uint32_t type, const wchar_t* message);
#define FFX_API_CONFIGURE_DESC_TYPE_GLOBALDEBUG1 0x0000001u
struct ffxConfigureDescGlobalDebug1
{
ffxConfigureDescHeader header;
ffxApiMessage fpMessage;
uint32_t debugLevel;
};
#define FFX_API_QUERY_DESC_TYPE_GET_VERSIONS 4u
struct ffxQueryDescGetVersions
{
ffxQueryDescHeader header;
uint64_t createDescType; ///< Create description for the effect whose versions should be enumerated.
void* device; ///< For DX12: pointer to ID3D12Device.
uint64_t *outputCount; ///< Input capacity of id and name arrays. Output number of returned versions. If initially zero, output is number of available versions.
uint64_t *versionIds; ///< Output array of version ids to be used as version overrides. If null, only names and count are returned.
const char** versionNames; ///< Output array of version names for display. If null, only ids and count are returned. If both this and versionIds are null, only count is returned.
};
#define FFX_API_DESC_TYPE_OVERRIDE_VERSION 5u
struct ffxOverrideVersion
{
ffxApiHeader header;
uint64_t versionId; ///< Id of version to use. Must be a value returned from a query in ffxQueryDescGetVersions.versionIds array.
};
#define FFX_API_QUERY_DESC_TYPE_GET_PROVIDER_VERSION 6u
struct ffxQueryGetProviderVersion
{
ffxQueryDescHeader header;
uint64_t versionId; ///< Id of provider being used for queried context. 0 if invalid.
const char* versionName; ///< Version name for display. If nullptr, the query was invalid.
};
// 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;
// Creates a FFX object context.
// Depending on the desc structures provided to this function, the context will be created with the desired version and attributes.
// Non-zero return indicates error code.
// Pointers passed in desc must remain live until ffxDestroyContext is called on the context.
// MemCb may be null; the system allocator (malloc/free) will be used in this case.
FFX_API_ENTRY ffxReturnCode_t ffxCreateContext(ffxContext* context, ffxCreateContextDescHeader* desc, const ffxAllocationCallbacks* memCb);
typedef ffxReturnCode_t (*PfnFfxCreateContext)(ffxContext* context, ffxCreateContextDescHeader* desc, const ffxAllocationCallbacks* memCb);
// Destroys an FFX object context.
// Non-zero return indicates error code.
// MemCb must be compatible with the callbacks passed into ffxCreateContext.
FFX_API_ENTRY ffxReturnCode_t ffxDestroyContext(ffxContext* context, const ffxAllocationCallbacks* memCb);
typedef ffxReturnCode_t (*PfnFfxDestroyContext)(ffxContext* context, const ffxAllocationCallbacks* memCb);
// Configures the provided FFX object context.
// If context is null, configure operates on any global state.
// Non-zero return indicates error code.
FFX_API_ENTRY ffxReturnCode_t ffxConfigure(ffxContext* context, const ffxConfigureDescHeader* desc);
typedef ffxReturnCode_t (*PfnFfxConfigure)(ffxContext* context, const ffxConfigureDescHeader* desc);
// Queries the provided FFX object context.
// If context is null, query operates on any global state.
// Non-zero return indicates error code.
FFX_API_ENTRY ffxReturnCode_t ffxQuery(ffxContext* context, ffxQueryDescHeader* desc);
typedef ffxReturnCode_t (*PfnFfxQuery)(ffxContext* context, ffxQueryDescHeader* desc);
// Dispatches work on the given FFX object context defined by the dispatch descriptor.
// Non-zero return indicates error code.
FFX_API_ENTRY ffxReturnCode_t ffxDispatch(ffxContext* context, const ffxDispatchDescHeader* desc);
typedef ffxReturnCode_t (*PfnFfxDispatch)(ffxContext* context, const ffxDispatchDescHeader* desc);
#if defined(__cplusplus)
}
#endif // #if defined(__cplusplus)

View File

@@ -0,0 +1,243 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include <stdint.h>
#include <stdbool.h>
/// An enumeration of surface formats. Needs to match enum FfxSurfaceFormat
enum FfxApiSurfaceFormat
{
FFX_API_SURFACE_FORMAT_UNKNOWN, ///< Unknown format
FFX_API_SURFACE_FORMAT_R32G32B32A32_TYPELESS, ///< 32 bit per channel, 4 channel typeless format
FFX_API_SURFACE_FORMAT_R32G32B32A32_UINT, ///< 32 bit per channel, 4 channel uint format
FFX_API_SURFACE_FORMAT_R32G32B32A32_FLOAT, ///< 32 bit per channel, 4 channel float format
FFX_API_SURFACE_FORMAT_R16G16B16A16_FLOAT, ///< 16 bit per channel, 4 channel float format
FFX_API_SURFACE_FORMAT_R32G32B32_FLOAT, ///< 32 bit per channel, 3 channel float format
FFX_API_SURFACE_FORMAT_R32G32_FLOAT, ///< 32 bit per channel, 2 channel float format
FFX_API_SURFACE_FORMAT_R8_UINT, ///< 8 bit per channel, 1 channel float format
FFX_API_SURFACE_FORMAT_R32_UINT, ///< 32 bit per channel, 1 channel float format
FFX_API_SURFACE_FORMAT_R8G8B8A8_TYPELESS, ///< 8 bit per channel, 4 channel typeless format
FFX_API_SURFACE_FORMAT_R8G8B8A8_UNORM, ///< 8 bit per channel, 4 channel unsigned normalized format
FFX_API_SURFACE_FORMAT_R8G8B8A8_SNORM, ///< 8 bit per channel, 4 channel signed normalized format
FFX_API_SURFACE_FORMAT_R8G8B8A8_SRGB, ///< 8 bit per channel, 4 channel srgb normalized
FFX_API_SURFACE_FORMAT_B8G8R8A8_TYPELESS, ///< 8 bit per channel, 4 channel typeless format
FFX_API_SURFACE_FORMAT_B8G8R8A8_UNORM, ///< 8 bit per channel, 4 channel unsigned normalized format
FFX_API_SURFACE_FORMAT_B8G8R8A8_SRGB, ///< 8 bit per channel, 4 channel srgb normalized
FFX_API_SURFACE_FORMAT_R11G11B10_FLOAT, ///< 32 bit 3 channel float format
FFX_API_SURFACE_FORMAT_R10G10B10A2_UNORM, ///< 10 bit per 3 channel, 2 bit for 1 channel normalized format
FFX_API_SURFACE_FORMAT_R16G16_FLOAT, ///< 16 bit per channel, 2 channel float format
FFX_API_SURFACE_FORMAT_R16G16_UINT, ///< 16 bit per channel, 2 channel unsigned int format
FFX_API_SURFACE_FORMAT_R16G16_SINT, ///< 16 bit per channel, 2 channel signed int format
FFX_API_SURFACE_FORMAT_R16_FLOAT, ///< 16 bit per channel, 1 channel float format
FFX_API_SURFACE_FORMAT_R16_UINT, ///< 16 bit per channel, 1 channel unsigned int format
FFX_API_SURFACE_FORMAT_R16_UNORM, ///< 16 bit per channel, 1 channel unsigned normalized format
FFX_API_SURFACE_FORMAT_R16_SNORM, ///< 16 bit per channel, 1 channel signed normalized format
FFX_API_SURFACE_FORMAT_R8_UNORM, ///< 8 bit per channel, 1 channel unsigned normalized format
FFX_API_SURFACE_FORMAT_R8G8_UNORM, ///< 8 bit per channel, 2 channel unsigned normalized format
FFX_API_SURFACE_FORMAT_R8G8_UINT, ///< 8 bit per channel, 2 channel unsigned integer format
FFX_API_SURFACE_FORMAT_R32_FLOAT, ///< 32 bit per channel, 1 channel float format
FFX_API_SURFACE_FORMAT_R9G9B9E5_SHAREDEXP, ///< 9 bit per channel, 5 bit exponent format
FFX_API_SURFACE_FORMAT_R16G16B16A16_TYPELESS, ///< 16 bit per channel, 4 channel typeless format
FFX_API_SURFACE_FORMAT_R32G32_TYPELESS, ///< 32 bit per channel, 2 channel typeless format
FFX_API_SURFACE_FORMAT_R10G10B10A2_TYPELESS, ///< 10 bit per 3 channel, 2 bit for 1 channel typeless format
FFX_API_SURFACE_FORMAT_R16G16_TYPELESS, ///< 16 bit per channel, 2 channel typless format
FFX_API_SURFACE_FORMAT_R16_TYPELESS, ///< 16 bit per channel, 1 channel typeless format
FFX_API_SURFACE_FORMAT_R8_TYPELESS, ///< 8 bit per channel, 1 channel typeless format
FFX_API_SURFACE_FORMAT_R8G8_TYPELESS, ///< 8 bit per channel, 2 channel typeless format
FFX_API_SURFACE_FORMAT_R32_TYPELESS, ///< 32 bit per channel, 1 channel typeless format
};
/// An enumeration of resource usage.
enum FfxApiResourceUsage
{
FFX_API_RESOURCE_USAGE_READ_ONLY = 0, ///< No usage flags indicate a resource is read only.
FFX_API_RESOURCE_USAGE_RENDERTARGET = (1<<0), ///< Indicates a resource will be used as render target.
FFX_API_RESOURCE_USAGE_UAV = (1<<1), ///< Indicates a resource will be used as UAV.
FFX_API_RESOURCE_USAGE_DEPTHTARGET = (1<<2), ///< Indicates a resource will be used as depth target.
FFX_API_RESOURCE_USAGE_INDIRECT = (1<<3), ///< Indicates a resource will be used as indirect argument buffer
FFX_API_RESOURCE_USAGE_ARRAYVIEW = (1<<4), ///< Indicates a resource that will generate array views. Works on 2D and cubemap textures
FFX_API_RESOURCE_USAGE_STENCILTARGET = (1<<5), ///< Indicates a resource will be used as stencil target.
};
/// An enumeration of resource states.
enum FfxApiResourceState
{
FFX_API_RESOURCE_STATE_COMMON = (1 << 0),
FFX_API_RESOURCE_STATE_UNORDERED_ACCESS = (1 << 1), ///< Indicates a resource is in the state to be used as UAV.
FFX_API_RESOURCE_STATE_COMPUTE_READ = (1 << 2), ///< Indicates a resource is in the state to be read by compute shaders.
FFX_API_RESOURCE_STATE_PIXEL_READ = (1 << 3), ///< Indicates a resource is in the state to be read by pixel shaders.
FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ = (FFX_API_RESOURCE_STATE_PIXEL_READ | FFX_API_RESOURCE_STATE_COMPUTE_READ), ///< Indicates a resource is in the state to be read by pixel or compute shaders.
FFX_API_RESOURCE_STATE_COPY_SRC = (1 << 4), ///< Indicates a resource is in the state to be used as source in a copy command.
FFX_API_RESOURCE_STATE_COPY_DEST = (1 << 5), ///< Indicates a resource is in the state to be used as destination in a copy command.
FFX_API_RESOURCE_STATE_GENERIC_READ = (FFX_API_RESOURCE_STATE_COPY_SRC | FFX_API_RESOURCE_STATE_COMPUTE_READ), ///< Indicates a resource is in generic (slow) read state.
FFX_API_RESOURCE_STATE_INDIRECT_ARGUMENT = (1 << 6), ///< Indicates a resource is in the state to be used as an indirect command argument
FFX_API_RESOURCE_STATE_PRESENT = (1 << 7), ///< Indicates a resource is in the state to be used to present to the swap chain
FFX_API_RESOURCE_STATE_RENDER_TARGET = (1 << 8), ///< Indicates a resource is in the state to be used as render target
};
/// An enumeration of surface dimensions.
enum FfxApiResourceDimension
{
FFX_API_RESOURCE_DIMENSION_TEXTURE_1D, ///< A resource with a single dimension.
FFX_API_RESOURCE_DIMENSION_TEXTURE_2D, ///< A resource with two dimensions.
};
/// An enumeration of resource flags.
enum FfxApiResourceFlags
{
FFX_API_RESOURCE_FLAGS_NONE = 0, ///< No flags.
FFX_API_RESOURCE_FLAGS_ALIASABLE = (1 << 0), ///< A bit indicating a resource does not need to persist across frames.
FFX_API_RESOURCE_FLAGS_UNDEFINED = (1 << 1), ///< Special case flag used internally when importing resources that require additional setup
};
// An enumeration for different resource types
enum FfxApiResourceType
{
FFX_API_RESOURCE_TYPE_BUFFER, ///< The resource is a buffer.
FFX_API_RESOURCE_TYPE_TEXTURE1D, ///< The resource is a 1-dimensional texture.
FFX_API_RESOURCE_TYPE_TEXTURE2D, ///< The resource is a 2-dimensional texture.
FFX_API_RESOURCE_TYPE_TEXTURE_CUBE, ///< The resource is a cube map.
FFX_API_RESOURCE_TYPE_TEXTURE3D, ///< The resource is a 3-dimensional texture.
};
enum FfxApiBackbufferTransferFunction
{
FFX_API_BACKBUFFER_TRANSFER_FUNCTION_SRGB,
FFX_API_BACKBUFFER_TRANSFER_FUNCTION_PQ,
FFX_API_BACKBUFFER_TRANSFER_FUNCTION_SCRGB
};
/// A structure encapsulating a 2-dimensional point, using 32bit unsigned integers.
struct FfxApiDimensions2D
{
uint32_t width; ///< The width of a 2-dimensional range.
uint32_t height; ///< The height of a 2-dimensional range.
};
/// A structure encapsulating a 2-dimensional set of floating point coordinates.
struct FfxApiFloatCoords2D
{
float x; ///< The x coordinate of a 2-dimensional point.
float y; ///< The y coordinate of a 2-dimensional point.
};
/// A structure encapsulating a 2-dimensional rect.
struct FfxApiRect2D
{
int32_t left;
int32_t top;
int32_t width;
int32_t height;
};
/// A structure describing a resource.
///
/// @ingroup SDKTypes
struct FfxApiResourceDescription
{
uint32_t type; ///< The type of the resource.
uint32_t format; ///< The surface format.
union {
uint32_t width; ///< The width of the texture resource.
uint32_t size; ///< The size of the buffer resource.
};
union {
uint32_t height; ///< The height of the texture resource.
uint32_t stride; ///< The stride of the buffer resource.
};
union {
uint32_t depth; ///< The depth of the texture resource.
uint32_t alignment; ///< The alignment of the buffer resource.
};
uint32_t mipCount; ///< Number of mips (or 0 for full mipchain).
uint32_t flags; ///< A set of resource flags.
uint32_t usage; ///< Resource usage flags.
};
struct FfxApiResource
{
void* resource;
struct FfxApiResourceDescription description;
uint32_t state;
};
//struct definition matches FfxEffectMemoryUsage
typedef struct FfxApiEffectMemoryUsage
{
uint64_t totalUsageInBytes;
uint64_t aliasableUsageInBytes;
} FfxApiEffectMemoryUsage;
/*
Tuning varianceFactor and safetyMarginInMs Tips:
Calculation of frame pacing algorithm's next target timestamp:
target frametime delta = average Frametime - (variance * varianceFactor) - safetyMarginInMs
Default Tuning uses safetyMarginInMs==0.1ms and varianceFactor==0.1.
Say Tuning set A uses safetyMarginInMs==0.75ms, and varianceFactor==0.1.
Say Tuning Set B uses safetyMarginInMs==0.01ms and varianceFactor==0.3.
Example #1 - Actual Game Cutscene. Game's framerate after FG ON during camera pan from normal (19ms avg frametime) to complex (37ms avg frametime over 1 sec) and back to normal scene complexity (19ms avg frametime).
After the panning is done,
- Default Tuning now gets stuck at targeting ~33ms after panning to a complex scene. GPU utilization significantly lower in this case.
- Tuning Set B and Tuning Set A are able to recover close to ~19ms because of these 2 tuning result in lower "target frametime delta" than Default Tuning.
However, larger varianceFactor or safetyMarginInMs results in higher variance. As seen in Example #2 bellow.
Example #2 - FFX_API_FSR sample. Set app fps cap to 33.33ms. Use OCAT to capture 10s at default camera position.
FSR 3.1.0 FG msbetweenpresents ping-pong between 16.552 (5th-percentile) and 16.832 (95th-percentile). Variance is 0.01116.
Tuning set A FG msbetweenpresents ping-pong between 15.901 (5th-percentile) and 17.500 (95th-percentile). Variance is 0.057674.
Tuning Set B FG msbetweenpresents ping-pong between 16.589 (5th-percentile) and 16.971 (95th-percentile). Variance is 0.014452.
| FG output Frames timestamp | n | n+1 | n+2 | n+3 | frame delta n+1 to n+2 | frame delta n+2 to n+3 |
| -------------------------- | - | ------ | ----- | ------ | ---------------------- | ---------------------- |
| App real frame presents | 0 | | 33.33 | | | |
| Default Tuning | 0 | 16.552 | 33.33 | 49.882 | 16.778 | 16.552 |
| Tuning Set A | 0 | 15.901 | 33.33 | 49.231 | 17.429 | 15.901 |
| Tuning Set B | 0 | 16.589 | 33.33 | 49.919 | 16.741 | 16.589 |
Analysis of table data in words:
Ignoring the cost of FI,
"Tuning set A"'s "target frametime delta" of 15.901 results in larger frame to frame delta (or in other words larger variance) vs Default tuning.
"Tuning set B"'s "target frametime delta" of 16.589 results in a bit larger frame to frame delta (or in other words a bit larger variance) vs Default tuning.
TLDR:
If your game when using FG, frame rate is running at unexpectly low frame rate, after gradual transition from rendering complex to easy scene complexity, you could try setting "Tuning Set B" to recover lost FPS at cost of a bit higher variance.
*/
//struct definition matches FfxSwapchainFramePacingTuning
typedef struct FfxApiSwapchainFramePacingTuning
{
float safetyMarginInMs; // in Millisecond. Default is 0.1ms
float varianceFactor; // valid range [0.0,1.0]. Default is 0.1
bool allowHybridSpin; //Allows pacing spinlock to sleep. Default is false.
uint32_t hybridSpinTime; //How long to spin if allowHybridSpin is true. Measured in timer resolution units. Not recommended to go below 2. Will result in frequent overshoots. Default is 2.
bool allowWaitForSingleObjectOnFence; //Allows WaitForSingleObject instead of spinning for fence value. Default is false.
} FfxApiSwapchainFramePacingTuning;

View File

@@ -0,0 +1,190 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "ffx_api.h"
#include "ffx_api_types.h"
#define FFX_API_EFFECT_ID_FRAMEGENERATION 0x00020000u
#if defined(__cplusplus)
extern "C" {
#endif
enum FfxApiCreateContextFramegenerationFlags
{
FFX_FRAMEGENERATION_ENABLE_ASYNC_WORKLOAD_SUPPORT = (1<<0),
FFX_FRAMEGENERATION_ENABLE_DISPLAY_RESOLUTION_MOTION_VECTORS = (1<<1), ///< A bit indicating if the motion vectors are rendered at display resolution.
FFX_FRAMEGENERATION_ENABLE_MOTION_VECTORS_JITTER_CANCELLATION = (1<<2), ///< A bit indicating that the motion vectors have the jittering pattern applied to them.
FFX_FRAMEGENERATION_ENABLE_DEPTH_INVERTED = (1<<3), ///< A bit indicating that the input depth buffer data provided is inverted [1..0].
FFX_FRAMEGENERATION_ENABLE_DEPTH_INFINITE = (1<<4), ///< A bit indicating that the input depth buffer data provided is using an infinite far plane.
FFX_FRAMEGENERATION_ENABLE_HIGH_DYNAMIC_RANGE = (1<<5), ///< A bit indicating if the input color data provided to all inputs is using a high-dynamic range.
FFX_FRAMEGENERATION_ENABLE_DEBUG_CHECKING = (1<<6), ///< A bit indicating that the runtime should check some API values and report issues.
};
enum FfxApiDispatchFramegenerationFlags
{
FFX_FRAMEGENERATION_FLAG_DRAW_DEBUG_TEAR_LINES = (1 << 0), ///< A bit indicating that the debug tear lines will be drawn to the generated output.
FFX_FRAMEGENERATION_FLAG_DRAW_DEBUG_RESET_INDICATORS = (1 << 1), ///< A bit indicating that the debug reset indicators will be drawn to the generated output.
FFX_FRAMEGENERATION_FLAG_DRAW_DEBUG_VIEW = (1 << 2), ///< A bit indicating that the generated output resource will contain debug views with relevant information.
FFX_FRAMEGENERATION_FLAG_NO_SWAPCHAIN_CONTEXT_NOTIFY = (1 << 3), ///< A bit indicating that the context should only run frame interpolation and not modify the swapchain.
FFX_FRAMEGENERATION_FLAG_DRAW_DEBUG_PACING_LINES = (1 << 4), ///< A bit indicating that the debug pacing lines will be drawn to the generated output.
FFX_FRAMEGENERATION_FLAG_RESERVED_1 = (1 << 5),
FFX_FRAMEGENERATION_FLAG_RESERVED_2 = (1 << 6),
};
enum FfxApiUiCompositionFlags
{
FFX_FRAMEGENERATION_UI_COMPOSITION_FLAG_USE_PREMUL_ALPHA = (1 << 0), ///< A bit indicating that we use premultiplied alpha for UI composition.
FFX_FRAMEGENERATION_UI_COMPOSITION_FLAG_ENABLE_INTERNAL_UI_DOUBLE_BUFFERING = (1 << 1), ///< A bit indicating that the swapchain should doublebuffer the UI resource.
};
#define FFX_API_CREATE_CONTEXT_DESC_TYPE_FRAMEGENERATION 0x00020001u
struct ffxCreateContextDescFrameGeneration
{
ffxCreateContextDescHeader header;
uint32_t flags; ///< A combination of zero or more values from FfxApiCreateContextFramegenerationFlags.
struct FfxApiDimensions2D displaySize; ///< The resolution at which both rendered and generated frames will be displayed.
struct FfxApiDimensions2D maxRenderSize; ///< The maximum rendering resolution.
uint32_t backBufferFormat; ///< The surface format for the backbuffer. One of the values from FfxApiSurfaceFormat.
};
#define FFX_API_CALLBACK_DESC_TYPE_FRAMEGENERATION_PRESENT 0x00020005u
typedef struct ffxCallbackDescFrameGenerationPresent
{
ffxDispatchDescHeader header;
void* device; ///< The device passed in (from a backend description) during context creation.
void* commandList; ///< A command list that will be executed before presentation.
struct FfxApiResource currentBackBuffer; ///< Backbuffer image either rendered or generated.
struct FfxApiResource currentUI; ///< UI image for composition if passed. Otherwise empty.
struct FfxApiResource outputSwapChainBuffer; ///< Output image that will be presented.
bool isGeneratedFrame; ///< true if this frame is generated, false if rendered.
uint64_t frameID; ///< Identifier used to select internal resources when async support is enabled. Must increment by exactly one (1) for each frame. Any non-exactly-one difference will reset the frame generation logic.
} ffxCallbackDescFrameGenerationPresent;
#define FFX_API_DISPATCH_DESC_TYPE_FRAMEGENERATION 0x00020003u
typedef struct ffxDispatchDescFrameGeneration
{
ffxDispatchDescHeader header;
void* commandList; ///< The command list on which to register render commands.
struct FfxApiResource presentColor; ///< The current presentation color, this will be used as source data.
struct FfxApiResource outputs[4]; ///< Destination targets (1 for each frame in numGeneratedFrames).
uint32_t numGeneratedFrames; ///< The number of frames to generate from the passed in color target.
bool reset; ///< A boolean value which when set to true, indicates the camera has moved discontinuously.
uint32_t backbufferTransferFunction; ///< The transfer function use to convert frame generation source color data to linear RGB. One of the values from FfxApiBackbufferTransferFunction.
float minMaxLuminance[2]; ///< Min and max luminance values, used when converting HDR colors to linear RGB.
struct FfxApiRect2D generationRect; ///< The area of the backbuffer that should be used for generation in case only a part of the screen is used e.g. due to movie bars.
uint64_t frameID; ///< Identifier used to select internal resources when async support is enabled. Must increment by exactly one (1) for each frame. Any non-exactly-one difference will reset the frame generation logic.
} ffxDispatchDescFrameGeneration;
typedef ffxReturnCode_t(*FfxApiPresentCallbackFunc)(ffxCallbackDescFrameGenerationPresent* params, void* pUserCtx);
typedef ffxReturnCode_t(*FfxApiFrameGenerationDispatchFunc)(ffxDispatchDescFrameGeneration* params, void* pUserCtx);
#define FFX_API_CONFIGURE_DESC_TYPE_FRAMEGENERATION 0x00020002u
struct ffxConfigureDescFrameGeneration
{
ffxConfigureDescHeader header;
void* swapChain; ///< The swapchain to use with frame generation.
FfxApiPresentCallbackFunc presentCallback; ///< A UI composition callback to call when finalizing the frame image.
void* presentCallbackUserContext; ///< A pointer to be passed to the UI composition callback.
FfxApiFrameGenerationDispatchFunc frameGenerationCallback; ///< The frame generation callback to use to generate a frame.
void* frameGenerationCallbackUserContext; ///< A pointer to be passed to the frame generation callback.
bool frameGenerationEnabled; ///< Sets the state of frame generation. Set to false to disable frame generation.
bool allowAsyncWorkloads; ///< Sets the state of async workloads. Set to true to enable generation work on async compute.
struct FfxApiResource HUDLessColor; ///< The hudless back buffer image to use for UI extraction from backbuffer resource. May be empty.
uint32_t flags; ///< Zero or combination of flags from FfxApiDispatchFrameGenerationFlags.
bool onlyPresentGenerated; ///< Set to true to only present generated frames.
struct FfxApiRect2D generationRect; ///< The area of the backbuffer that should be used for generation in case only a part of the screen is used e.g. due to movie bars
uint64_t frameID; ///< Identifier used to select internal resources when async support is enabled. Must increment by exactly one (1) for each frame. Any non-exactly-one difference will reset the frame generation logic.
};
#define FFX_API_DISPATCH_DESC_TYPE_FRAMEGENERATION_PREPARE 0x00020004u
struct ffxDispatchDescFrameGenerationPrepare
{
ffxDispatchDescHeader header;
uint64_t frameID; ///< Identifier used to select internal resources when async support is enabled. Must increment by exactly one (1) for each frame. Any non-exactly-one difference will reset the frame generation logic.
uint32_t flags; ///< Zero or combination of values from FfxApiDispatchFrameGenerationFlags.
void* commandList; ///< A command list to record frame generation commands into.
struct FfxApiDimensions2D renderSize; ///< The dimensions used to render game content, dilatedDepth, dilatedMotionVectors are expected to be of ths size.
struct FfxApiFloatCoords2D jitterOffset; ///< The subpixel jitter offset applied to the camera.
struct FfxApiFloatCoords2D motionVectorScale; ///< The scale factor to apply to motion vectors.
float frameTimeDelta; ///< Time elapsed in milliseconds since the last frame.
bool unused_reset; ///< A (currently unused) boolean value which when set to true, indicates FrameGeneration will be called in reset mode
float cameraNear; ///< The distance to the near plane of the camera.
float cameraFar; ///< The distance to the far plane of the camera. This is used only used in case of non infinite depth.
float cameraFovAngleVertical; ///< The camera angle field of view in the vertical direction (expressed in radians).
float viewSpaceToMetersFactor; ///< The scale factor to convert view space units to meters
struct FfxApiResource depth; ///< The depth buffer data
struct FfxApiResource motionVectors; ///< The motion vector data
};
#define FFX_API_CONFIGURE_DESC_TYPE_FRAMEGENERATION_KEYVALUE 0x00020006u
struct ffxConfigureDescFrameGenerationKeyValue
{
ffxConfigureDescHeader header;
uint64_t key; ///< Configuration key, member of the FfxApiConfigureFrameGenerationKey enumeration.
uint64_t u64; ///< Integer value or enum value to set.
void* ptr; ///< Pointer to set or pointer to value to set.
};
enum FfxApiConfigureFrameGenerationKey
{
// No values.
};
#define FFX_API_QUERY_DESC_TYPE_FRAMEGENERATION_GPU_MEMORY_USAGE 0x00020007u
struct ffxQueryDescFrameGenerationGetGPUMemoryUsage
{
ffxQueryDescHeader header;
struct FfxApiEffectMemoryUsage* gpuMemoryUsageFrameGeneration;
};
#define FFX_API_CONFIGURE_DESC_TYPE_FRAMEGENERATION_REGISTERDISTORTIONRESOURCE 0x00020008u
struct ffxConfigureDescFrameGenerationRegisterDistortionFieldResource
{
ffxConfigureDescHeader header;
struct FfxApiResource distortionField; ///< A resource containing distortion offset data. Needs to be 2-component (ie. RG). Read by FG shaders via Sample. Resource's xy components encodes [UV coordinate of pixel after lens distortion effect- UV coordinate of pixel before lens distortion].
};
#define FFX_API_CREATE_CONTEXT_DESC_TYPE_FRAMEGENERATION_HUDLESS 0x00020009u
//Pass this optional linked struct at FG context creation to enable app to use different hudlessBackBufferformat (IE.RGBA8_UNORM) from backBufferFormat (IE. BGRA8_UNORM)
struct ffxCreateContextDescFrameGenerationHudless
{
ffxCreateContextDescHeader header;
uint32_t hudlessBackBufferFormat; ///< The surface format for the hudless back buffer. One of the values from FfxApiSurfaceFormat.
};
#define FFX_API_DISPATCH_DESC_TYPE_FRAMEGENERATION_PREPARE_CAMERAINFO 0x0002000au
//Link this struct after ffxDispatchDescFrameGenerationPrepare. This is a required input to FSR3.1.4 and onwards for best quality.
struct ffxDispatchDescFrameGenerationPrepareCameraInfo
{
ffxConfigureDescHeader header;
float cameraPosition[3]; ///< The camera position in world space
float cameraUp[3]; ///< The camera up normalized vector in world space.
float cameraRight[3]; ///< The camera right normalized vector in world space.
float cameraForward[3]; ///< The camera forward normalized vector in world space.
};
#if defined(__cplusplus)
} // extern "C"
#endif

View File

@@ -0,0 +1,188 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "ffx_api.h"
#include "ffx_api_types.h"
#ifdef __cplusplus
extern "C" {
#endif
enum FfxApiUpscaleQualityMode
{
FFX_UPSCALE_QUALITY_MODE_NATIVEAA = 0, ///< Perform upscaling with a per-dimension upscaling ratio of 1.0x.
FFX_UPSCALE_QUALITY_MODE_QUALITY = 1, ///< Perform upscaling with a per-dimension upscaling ratio of 1.5x.
FFX_UPSCALE_QUALITY_MODE_BALANCED = 2, ///< Perform upscaling with a per-dimension upscaling ratio of 1.7x.
FFX_UPSCALE_QUALITY_MODE_PERFORMANCE = 3, ///< Perform upscaling with a per-dimension upscaling ratio of 2.0x.
FFX_UPSCALE_QUALITY_MODE_ULTRA_PERFORMANCE = 4 ///< Perform upscaling with a per-dimension upscaling ratio of 3.0x.
};
enum FfxApiCreateContextUpscaleFlags
{
FFX_UPSCALE_ENABLE_HIGH_DYNAMIC_RANGE = (1<<0), ///< A bit indicating if the input color data provided is using a high-dynamic range.
FFX_UPSCALE_ENABLE_DISPLAY_RESOLUTION_MOTION_VECTORS = (1<<1), ///< A bit indicating if the motion vectors are rendered at display resolution.
FFX_UPSCALE_ENABLE_MOTION_VECTORS_JITTER_CANCELLATION = (1<<2), ///< A bit indicating that the motion vectors have the jittering pattern applied to them.
FFX_UPSCALE_ENABLE_DEPTH_INVERTED = (1<<3), ///< A bit indicating that the input depth buffer data provided is inverted [1..0].
FFX_UPSCALE_ENABLE_DEPTH_INFINITE = (1<<4), ///< A bit indicating that the input depth buffer data provided is using an infinite far plane.
FFX_UPSCALE_ENABLE_AUTO_EXPOSURE = (1<<5), ///< A bit indicating if automatic exposure should be applied to input color data.
FFX_UPSCALE_ENABLE_DYNAMIC_RESOLUTION = (1<<6), ///< A bit indicating that the application uses dynamic resolution scaling.
FFX_UPSCALE_ENABLE_DEBUG_CHECKING = (1<<7), ///< A bit indicating that the runtime should check some API values and report issues.
FFX_UPSCALE_ENABLE_NON_LINEAR_COLORSPACE = (1<<8), ///< A bit indicating that the color resource contains perceptual (gamma corrected) colors
};
enum FfxApiDispatchFsrUpscaleFlags
{
FFX_UPSCALE_FLAG_DRAW_DEBUG_VIEW = (1 << 0), ///< A bit indicating that the output resource will contain debug views with relevant information.
FFX_UPSCALE_FLAG_NON_LINEAR_COLOR_SRGB = (1 << 1), ///< A bit indicating that the input color resource contains perceptual sRGB colors
FFX_UPSCALE_FLAG_NON_LINEAR_COLOR_PQ = (1 << 2), ///< A bit indicating that the input color resource contains perceptual PQ colors
};
enum FfxApiDispatchUpscaleAutoreactiveFlags
{
FFX_UPSCALE_AUTOREACTIVEFLAGS_APPLY_TONEMAP = (1<<0),
FFX_UPSCALE_AUTOREACTIVEFLAGS_APPLY_INVERSETONEMAP = (1<<1),
FFX_UPSCALE_AUTOREACTIVEFLAGS_APPLY_THRESHOLD = (1<<2),
FFX_UPSCALE_AUTOREACTIVEFLAGS_USE_COMPONENTS_MAX = (1<<3),
};
#define FFX_API_EFFECT_ID_UPSCALE 0x00010000u
#define FFX_API_CREATE_CONTEXT_DESC_TYPE_UPSCALE 0x00010000u
struct ffxCreateContextDescUpscale
{
ffxCreateContextDescHeader header;
uint32_t flags; ///< Zero or a combination of values from FfxApiCreateContextFsrFlags.
struct FfxApiDimensions2D maxRenderSize; ///< The maximum size that rendering will be performed at.
struct FfxApiDimensions2D maxUpscaleSize; ///< The size of the presentation resolution targeted by the upscaling process.
ffxApiMessage fpMessage; ///< A pointer to a function that can receive messages from the runtime. May be null.
};
#define FFX_API_DISPATCH_DESC_TYPE_UPSCALE 0x00010001u
struct ffxDispatchDescUpscale
{
ffxDispatchDescHeader header;
void* commandList; ///< Command list to record upscaling rendering commands into.
struct FfxApiResource color; ///< Color buffer for the current frame (at render resolution).
struct FfxApiResource depth; ///< 32bit depth values for the current frame (at render resolution).
struct FfxApiResource motionVectors; ///< 2-dimensional motion vectors (at render resolution if <c><i>FFX_FSR_ENABLE_DISPLAY_RESOLUTION_MOTION_VECTORS</i></c> is not set).
struct FfxApiResource exposure; ///< Optional resource containing a 1x1 exposure value.
struct FfxApiResource reactive; ///< Optional resource containing alpha value of reactive objects in the scene.
struct FfxApiResource transparencyAndComposition; ///< Optional resource containing alpha value of special objects in the scene.
struct FfxApiResource output; ///< Output color buffer for the current frame (at presentation resolution).
struct FfxApiFloatCoords2D jitterOffset; ///< The subpixel jitter offset applied to the camera.
struct FfxApiFloatCoords2D motionVectorScale; ///< The scale factor to apply to motion vectors.
struct FfxApiDimensions2D renderSize; ///< The resolution that was used for rendering the input resources.
struct FfxApiDimensions2D upscaleSize; ///< The resolution that the upscaler will upscale to (optional, assumed maxUpscaleSize otherwise).
bool enableSharpening; ///< Enable an additional sharpening pass.
float sharpness; ///< The sharpness value between 0 and 1, where 0 is no additional sharpness and 1 is maximum additional sharpness.
float frameTimeDelta; ///< The time elapsed since the last frame (expressed in milliseconds).
float preExposure; ///< The pre exposure value (must be > 0.0f)
bool reset; ///< A boolean value which when set to true, indicates the camera has moved discontinuously.
float cameraNear; ///< The distance to the near plane of the camera.
float cameraFar; ///< The distance to the far plane of the camera.
float cameraFovAngleVertical; ///< The camera angle field of view in the vertical direction (expressed in radians).
float viewSpaceToMetersFactor; ///< The scale factor to convert view space units to meters
uint32_t flags; ///< Zero or a combination of values from FfxApiDispatchFsrUpscaleFlags.
};
#define FFX_API_QUERY_DESC_TYPE_UPSCALE_GETUPSCALERATIOFROMQUALITYMODE 0x00010002u
struct ffxQueryDescUpscaleGetUpscaleRatioFromQualityMode
{
ffxQueryDescHeader header;
uint32_t qualityMode; ///< The desired quality mode for FSR upscaling.
float* pOutUpscaleRatio; ///< A pointer to a <c>float</c> which will hold the upscaling the per-dimension upscaling ratio.
};
#define FFX_API_QUERY_DESC_TYPE_UPSCALE_GETRENDERRESOLUTIONFROMQUALITYMODE 0x00010003u
struct ffxQueryDescUpscaleGetRenderResolutionFromQualityMode
{
ffxQueryDescHeader header;
uint32_t displayWidth; ///< The target display resolution width.
uint32_t displayHeight; ///< The target display resolution height.
uint32_t qualityMode; ///< The desired quality mode for FSR upscaling.
uint32_t* pOutRenderWidth; ///< A pointer to a <c>uint32_t</c> which will hold the calculated render resolution width.
uint32_t* pOutRenderHeight; ///< A pointer to a <c>uint32_t</c> which will hold the calculated render resolution height.
};
#define FFX_API_QUERY_DESC_TYPE_UPSCALE_GETJITTERPHASECOUNT 0x00010004u
struct ffxQueryDescUpscaleGetJitterPhaseCount
{
ffxQueryDescHeader header;
uint32_t renderWidth; ///< The render resolution width.
uint32_t displayWidth; ///< The output resolution width.
int32_t* pOutPhaseCount; ///< A pointer to a <c>int32_t</c> which will hold the jitter phase count for the scaling factor between <c><i>renderWidth</i></c> and <c><i>displayWidth</i></c>.
};
#define FFX_API_QUERY_DESC_TYPE_UPSCALE_GETJITTEROFFSET 0x00010005u
struct ffxQueryDescUpscaleGetJitterOffset
{
ffxQueryDescHeader header;
int32_t index; ///< The index within the jitter sequence.
int32_t phaseCount; ///< The length of jitter phase. See <c><i>ffxQueryDescFsrGetJitterPhaseCount</i></c>.
float* pOutX; ///< A pointer to a <c>float</c> which will contain the subpixel jitter offset for the x dimension.
float* pOutY; ///< A pointer to a <c>float</c> which will contain the subpixel jitter offset for the y dimension.
};
#define FFX_API_DISPATCH_DESC_TYPE_UPSCALE_GENERATEREACTIVEMASK 0x00010006u
struct ffxDispatchDescUpscaleGenerateReactiveMask
{
ffxDispatchDescHeader header;
void* commandList; ///< The <c><i>FfxCommandList</i></c> to record FSRUPSCALE rendering commands into.
struct FfxApiResource colorOpaqueOnly; ///< A <c><i>FfxResource</i></c> containing the opaque only color buffer for the current frame (at render resolution).
struct FfxApiResource colorPreUpscale; ///< A <c><i>FfxResource</i></c> containing the opaque+translucent color buffer for the current frame (at render resolution).
struct FfxApiResource outReactive; ///< A <c><i>FfxResource</i></c> containing the surface to generate the reactive mask into.
struct FfxApiDimensions2D renderSize; ///< The resolution that was used for rendering the input resources.
float scale; ///< A value to scale the output
float cutoffThreshold; ///< A threshold value to generate a binary reactive mask
float binaryValue; ///< A value to set for the binary reactive mask
uint32_t flags; ///< Flags to determine how to generate the reactive mask
};
#define FFX_API_CONFIGURE_DESC_TYPE_UPSCALE_KEYVALUE 0x00010007u
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.
};
enum FfxApiConfigureUpscaleKey
{
FFX_API_CONFIGURE_UPSCALE_KEY_FVELOCITYFACTOR = 0, //Override constant buffer fVelocityFactor. The float value is casted from void * ptr. Value of 0.0f can improve temporal stability of bright pixels. Default value is 1.0f. Value is clamped to [0.0f, 1.0f].
FFX_API_CONFIGURE_UPSCALE_KEY_FREACTIVENESSSCALE = 1, //Override constant buffer fReactivenessScale. The float value is casted from void * ptr. Meant for development purpose to test if writing a larger value to reactive mask, reduces ghosting. Default value is 1.0f. Value is clamped to [0.0f, +infinity].
FFX_API_CONFIGURE_UPSCALE_KEY_FSHADINGCHANGESCALE = 2, //Override fShadingChangeScale. Increasing this scales fsr3.1 computed shading change value at read to have higher reactiveness. Default value is 1.0f. Value is clamped to [0.0f, +infinity].
FFX_API_CONFIGURE_UPSCALE_KEY_FACCUMULATIONADDEDPERFRAME = 3, // Override constant buffer fAccumulationAddedPerFrame. Corresponds to amount of accumulation added per frame at pixel coordinate where disocclusion occured or when reactive mask value is > 0.0f. Decreasing this and drawing the ghosting object (IE no mv) to reactive mask with value close to 1.0f can decrease temporal ghosting. Decreasing this value could result in more thin feature pixels flickering. Default value is 0.333. Value is clamped to [0.0f, 1.0f].
FFX_API_CONFIGURE_UPSCALE_KEY_FMINDISOCCLUSIONACCUMULATION = 4, //Override constant buffer fMinDisocclusionAccumulation. Increasing this value may reduce white pixel temporal flickering around swaying thin objects that are disoccluding one another often. Too high value may increase ghosting. A sufficiently negative value means for pixel coordinate at frame N that is disoccluded, add fAccumulationAddedPerFrame starting at frame N+2. Default value is -0.333. Value is clamped to [-1.0f, 1.0f].
};
#define FFX_API_QUERY_DESC_TYPE_UPSCALE_GPU_MEMORY_USAGE 0x00010008u
struct ffxQueryDescUpscaleGetGPUMemoryUsage
{
ffxQueryDescHeader header;
struct FfxApiEffectMemoryUsage* gpuMemoryUsageUpscaler;
};
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,360 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "../ffx_api.h"
#include "../ffx_api_types.h"
#include <vulkan/vulkan.h>
/// FFX specific callback type when submitting a command buffer to a queue.
typedef VkResult (*PFN_vkQueueSubmitFFXAPI)(uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence);
/// Helper stucture
struct VkQueueInfoFFXAPI
{
VkQueue queue; ///< the vulkan queue
uint32_t familyIndex; ///< the queue family index, that will be used to perform queue family ownership transfer
PFN_vkQueueSubmitFFXAPI submitFunc; ///< an optional submit function in case there might be some concurrent submissions
};
#define FFX_API_CREATE_CONTEXT_DESC_TYPE_BACKEND_VK 0x0000003u
struct ffxCreateBackendVKDesc
{
ffxCreateContextDescHeader header;
VkDevice vkDevice; ///< the logical device used by the program.
VkPhysicalDevice vkPhysicalDevice; ///< the physical device used by the program.
PFN_vkGetDeviceProcAddr vkDeviceProcAddr; ///< function pointer to get device procedure addresses
};
#define FFX_API_EFFECT_ID_FGSC_VK 0x00040000u
#define FFX_API_CREATE_CONTEXT_DESC_TYPE_FGSWAPCHAIN_VK 0x40001u
struct ffxCreateContextDescFrameGenerationSwapChainVK
{
ffxCreateContextDescHeader header;
VkPhysicalDevice physicalDevice; ///< the physicak device used by the program.
VkDevice device; ///< the logical device used by the program.
VkSwapchainKHR* swapchain; ///< the current swapchain to be replaced. Will be destroyed when the context is created. This can be VK_NULL_HANDLE. Will contain the new swapchain on return.
VkAllocationCallbacks* allocator; ///< optional allocation callbacks.
VkSwapchainCreateInfoKHR createInfo; ///< the description of the desired swapchain. If its VkSwapchainCreateInfoKHR::oldSwapchain field isn't VK_NULL_HANDLE, it should be the same as the ffxCreateContextDescFrameGenerationSwapChainVK::swapchain field above.
VkQueueInfoFFXAPI gameQueue; ///< the main graphics queue, where Present is called.
VkQueueInfoFFXAPI asyncComputeQueue; ///< A queue with Compute capability.
VkQueueInfoFFXAPI presentQueue; ///< A queue with Transfer and Present capabilities.
VkQueueInfoFFXAPI imageAcquireQueue; ///< A queue with no capability required.
};
#define FFX_API_CONFIGURE_DESC_TYPE_FGSWAPCHAIN_REGISTERUIRESOURCE_VK 0x40002u
struct ffxConfigureDescFrameGenerationSwapChainRegisterUiResourceVK
{
ffxConfigureDescHeader header;
struct FfxApiResource uiResource; ///< Resource containing user interface for composition. May be empty.
uint32_t flags; ///< Zero or combination of values from FfxApiUiCompositionFlags.
};
#define FFX_API_QUERY_DESC_TYPE_FGSWAPCHAIN_INTERPOLATIONCOMMANDLIST_VK 0x40003u
struct ffxQueryDescFrameGenerationSwapChainInterpolationCommandListVK
{
ffxQueryDescHeader header;
void** pOutCommandList; ///< Output command nuffer (VkCommandBuffer) to be used for frame generation dispatch.
};
#define FFX_API_QUERY_DESC_TYPE_FGSWAPCHAIN_INTERPOLATIONTEXTURE_VK 0x40004u
struct ffxQueryDescFrameGenerationSwapChainInterpolationTextureVK
{
ffxQueryDescHeader header;
struct FfxApiResource* pOutTexture; ///< Output resource in which the frame interpolation result should be placed.
};
#define FFX_API_DISPATCH_DESC_TYPE_FGSWAPCHAIN_WAIT_FOR_PRESENTS_VK 0x40007u
struct ffxDispatchDescFrameGenerationSwapChainWaitForPresentsVK
{
ffxDispatchDescHeader header;
};
#define FFX_API_CONFIGURE_DESC_TYPE_FRAMEGENERATIONSWAPCHAIN_KEYVALUE_VK 0x40008u
struct ffxConfigureDescFrameGenerationSwapChainKeyValueVK
{
ffxConfigureDescHeader header;
uint64_t key; ///< Configuration key, member of the FfxApiConfigureFrameGenerationSwapChainKeyVK enumeration.
uint64_t u64; ///< Integer value or enum value to set.
void* ptr; ///< Pointer to set or pointer to value to set.
};
//enum value matches enum FfxFrameInterpolationSwapchainConfigureKey
enum FfxApiConfigureFrameGenerationSwapChainKeyVK
{
FFX_API_CONFIGURE_FG_SWAPCHAIN_KEY_WAITCALLBACK = 0, ///< Sets FfxWaitCallbackFunc
FFX_API_CONFIGURE_FG_SWAPCHAIN_KEY_FRAMEPACINGTUNING = 2, ///< Sets FfxApiSwapchainFramePacingTuning casted from ptr
};
#define FFX_API_QUERY_DESC_TYPE_FRAMEGENERATIONSWAPCHAIN_GPU_MEMORY_USAGE_VK 0x00040009u
struct ffxQueryFrameGenerationSwapChainGetGPUMemoryUsageVK
{
ffxQueryDescHeader header;
struct FfxApiEffectMemoryUsage* gpuMemoryUsageFrameGenerationSwapchain;
};
/// Function to get the number of presents. This is useful when using frame interpolation
typedef uint64_t (*PFN_getLastPresentCountFFXAPI)(VkSwapchainKHR);
/// FFX API specific functions to create and destroy a swapchain
typedef VkResult(*PFN_vkCreateSwapchainFFXAPI)(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain, void* pContext);
typedef void(*PFN_vkDestroySwapchainFFXAPI)(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks* pAllocator, void* pContext);
#define FFX_API_QUERY_DESC_TYPE_FGSWAPCHAIN_FUNCTIONS_VK 0x40005u
struct ffxQueryDescSwapchainReplacementFunctionsVK
{
ffxQueryDescHeader header;
PFN_vkCreateSwapchainFFXAPI pOutCreateSwapchainFFXAPI; ///< Replacement of vkCreateSwapchainKHR. Can be called when swapchain is recreated but swapchain context isn't (for example when toggling vsync).
PFN_vkDestroySwapchainFFXAPI pOutDestroySwapchainFFXAPI; ///< Replacement of vkDestroySwapchainKHR. Can be called when swapchain is destroyed but swapchain context isn't.
PFN_vkGetSwapchainImagesKHR pOutGetSwapchainImagesKHR; ///< Replacement of vkGetSwapchainImagesKHR.
PFN_vkAcquireNextImageKHR pOutAcquireNextImageKHR; ///< Replacement of vkAcquireNextImageKHR.
PFN_vkQueuePresentKHR pOutQueuePresentKHR; ///< Replacement of vkQueuePresentKHR.
PFN_vkSetHdrMetadataEXT pOutSetHdrMetadataEXT; ///< Replacement of vkSetHdrMetadataEXT.
PFN_getLastPresentCountFFXAPI pOutGetLastPresentCountFFXAPI; ///< Additional function to get the number of times present has been called since the swapchain creation.
};
#define FFX_API_CREATE_CONTEXT_DESC_TYPE_FGSWAPCHAIN_MODE_VK 0x40010u
struct ffxCreateContextDescFrameGenerationSwapChainModeVK
{
ffxCreateContextDescHeader header;
bool composeOnPresentQueue; ///< flags indicating that composition will happen on the present queue
};
static inline uint32_t ffxApiGetSurfaceFormatVK(VkFormat fmt)
{
switch (fmt)
{
case VK_FORMAT_R32G32B32A32_SFLOAT:
return FFX_API_SURFACE_FORMAT_R32G32B32A32_FLOAT;
case VK_FORMAT_R32G32B32_SFLOAT:
return FFX_API_SURFACE_FORMAT_R32G32B32_FLOAT;
case VK_FORMAT_R32G32B32A32_UINT:
return FFX_API_SURFACE_FORMAT_R32G32B32A32_UINT;
case VK_FORMAT_R16G16B16A16_SFLOAT:
return FFX_API_SURFACE_FORMAT_R16G16B16A16_FLOAT;
case VK_FORMAT_R32G32_SFLOAT:
return FFX_API_SURFACE_FORMAT_R32G32_FLOAT;
case VK_FORMAT_R32_UINT:
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_X8_D24_UNORM_PACK32:
return FFX_API_SURFACE_FORMAT_R32_UINT;
case VK_FORMAT_R8G8B8A8_UNORM:
return FFX_API_SURFACE_FORMAT_R8G8B8A8_UNORM;
case VK_FORMAT_R8G8B8A8_SNORM:
return FFX_API_SURFACE_FORMAT_R8G8B8A8_SNORM;
case VK_FORMAT_R8G8B8A8_SRGB:
return FFX_API_SURFACE_FORMAT_R8G8B8A8_SRGB;
case VK_FORMAT_B8G8R8A8_UNORM:
return FFX_API_SURFACE_FORMAT_B8G8R8A8_UNORM;
case VK_FORMAT_B8G8R8A8_SRGB:
return FFX_API_SURFACE_FORMAT_B8G8R8A8_SRGB;
case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
return FFX_API_SURFACE_FORMAT_R11G11B10_FLOAT;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
return FFX_API_SURFACE_FORMAT_R10G10B10A2_UNORM;
case VK_FORMAT_R16G16_SFLOAT:
return FFX_API_SURFACE_FORMAT_R16G16_FLOAT;
case VK_FORMAT_R16G16_UINT:
return FFX_API_SURFACE_FORMAT_R16G16_UINT;
case VK_FORMAT_R16G16_SINT:
return FFX_API_SURFACE_FORMAT_R16G16_SINT;
case VK_FORMAT_R16_SFLOAT:
return FFX_API_SURFACE_FORMAT_R16_FLOAT;
case VK_FORMAT_R16_UINT:
return FFX_API_SURFACE_FORMAT_R16_UINT;
case VK_FORMAT_R16_UNORM:
case VK_FORMAT_D16_UNORM:
case VK_FORMAT_D16_UNORM_S8_UINT:
return FFX_API_SURFACE_FORMAT_R16_UNORM;
case VK_FORMAT_R16_SNORM:
return FFX_API_SURFACE_FORMAT_R16_SNORM;
case VK_FORMAT_R8_UNORM:
return FFX_API_SURFACE_FORMAT_R8_UNORM;
case VK_FORMAT_R8_UINT:
case VK_FORMAT_S8_UINT:
return FFX_API_SURFACE_FORMAT_R8_UINT;
case VK_FORMAT_R8G8_UNORM:
return FFX_API_SURFACE_FORMAT_R8G8_UNORM;
case VK_FORMAT_R8G8_UINT:
return FFX_API_SURFACE_FORMAT_R8G8_UINT;
case VK_FORMAT_R32_SFLOAT:
case VK_FORMAT_D32_SFLOAT:
case VK_FORMAT_D32_SFLOAT_S8_UINT:
return FFX_API_SURFACE_FORMAT_R32_FLOAT;
case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32:
return FFX_API_SURFACE_FORMAT_R9G9B9E5_SHAREDEXP;
case VK_FORMAT_UNDEFINED:
return FFX_API_SURFACE_FORMAT_UNKNOWN;
default:
// NOTE: we do not support typeless formats here
//FFX_ASSERT_MESSAGE(false, "Format not yet supported");
return FFX_API_SURFACE_FORMAT_UNKNOWN;
}
}
static inline uint32_t ffxApiGetSurfaceFormatToGamma(uint32_t fmt)
{
switch (fmt)
{
case (FFX_API_SURFACE_FORMAT_R8G8B8A8_UNORM):
return FFX_API_SURFACE_FORMAT_R8G8B8A8_SRGB;
case (FFX_API_SURFACE_FORMAT_B8G8R8A8_UNORM):
return FFX_API_SURFACE_FORMAT_B8G8R8A8_SRGB;
default:
return fmt;
}
}
static inline bool ffxApiIsDepthFormat(VkFormat fmt)
{
switch (fmt)
{
case VK_FORMAT_D16_UNORM:
case VK_FORMAT_X8_D24_UNORM_PACK32:
case VK_FORMAT_D32_SFLOAT:
case VK_FORMAT_D16_UNORM_S8_UINT:
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_D32_SFLOAT_S8_UINT:
return true;
default:
return false;
}
}
static inline bool ffxApiIsStencilFormat(VkFormat fmt)
{
switch (fmt)
{
case VK_FORMAT_S8_UINT:
case VK_FORMAT_D16_UNORM_S8_UINT:
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_D32_SFLOAT_S8_UINT:
return true;
default:
return false;
}
}
static inline FfxApiResourceDescription ffxApiGetBufferResourceDescriptionVK(const VkBuffer buffer,
const VkBufferCreateInfo createInfo,
uint32_t additionalUsages)
{
FfxApiResourceDescription resourceDescription = {};
// This is valid
if (buffer == VK_NULL_HANDLE)
return resourceDescription;
resourceDescription.flags = FFX_API_RESOURCE_FLAGS_NONE;
resourceDescription.usage = additionalUsages;
resourceDescription.size = (uint32_t)createInfo.size;
resourceDescription.stride = 0;
resourceDescription.format = FFX_API_SURFACE_FORMAT_UNKNOWN;
if ((createInfo.usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0)
resourceDescription.usage |= FFX_API_RESOURCE_USAGE_UAV;
if ((createInfo.usage & VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT) != 0)
resourceDescription.usage |= FFX_API_RESOURCE_USAGE_INDIRECT;
// What should we initialize this to?? No case for this yet
resourceDescription.depth = 0;
resourceDescription.mipCount = 0;
// Set the type
resourceDescription.type = FFX_API_RESOURCE_TYPE_BUFFER;
return resourceDescription;
}
static inline FfxApiResourceDescription ffxApiGetImageResourceDescriptionVK(const VkImage image, const VkImageCreateInfo createInfo, uint32_t additionalUsages)
{
FfxApiResourceDescription resourceDescription = {};
// This is valid
if (image == VK_NULL_HANDLE)
return resourceDescription;
// Set flags properly for resource registration
resourceDescription.flags = FFX_API_RESOURCE_FLAGS_NONE;
resourceDescription.usage = FFX_API_RESOURCE_USAGE_READ_ONLY;
// Check for depth stencil use
if (ffxApiIsDepthFormat(createInfo.format))
resourceDescription.usage |= FFX_API_RESOURCE_USAGE_DEPTHTARGET;
if (ffxApiIsStencilFormat(createInfo.format))
resourceDescription.usage |= FFX_API_RESOURCE_USAGE_STENCILTARGET;
// Unordered access use
if ((createInfo.usage & VK_IMAGE_USAGE_STORAGE_BIT) != 0)
resourceDescription.usage |= FFX_API_RESOURCE_USAGE_UAV;
// Resource-specific supplemental use flags
resourceDescription.usage |= additionalUsages;
resourceDescription.width = createInfo.extent.width;
resourceDescription.height = createInfo.extent.height;
resourceDescription.mipCount = createInfo.mipLevels;
resourceDescription.format = ffxApiGetSurfaceFormatVK(createInfo.format);
// if the mutable flag is present, assume that the real format is sRGB
if ((createInfo.flags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT) != 0)
resourceDescription.format = ffxApiGetSurfaceFormatToGamma(resourceDescription.format);
switch (createInfo.imageType)
{
case VK_IMAGE_TYPE_1D:
resourceDescription.type = FFX_API_RESOURCE_TYPE_TEXTURE1D;
break;
case VK_IMAGE_TYPE_2D:
resourceDescription.depth = createInfo.arrayLayers;
if ((additionalUsages & FFX_API_RESOURCE_USAGE_ARRAYVIEW) != 0)
resourceDescription.type = FFX_API_RESOURCE_TYPE_TEXTURE2D;
else if ((createInfo.flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) != 0)
resourceDescription.type = FFX_API_RESOURCE_TYPE_TEXTURE_CUBE;
else
resourceDescription.type = FFX_API_RESOURCE_TYPE_TEXTURE2D;
break;
case VK_IMAGE_TYPE_3D:
resourceDescription.depth = createInfo.extent.depth;
resourceDescription.type = FFX_API_RESOURCE_TYPE_TEXTURE3D;
break;
default:
//FFX_ASSERT_MESSAGE(false, "FFXInterface: VK: Unsupported texture dimension requested. Please implement.");
break;
}
return resourceDescription;
}
static inline FfxApiResource ffxApiGetResourceVK(void* vkResource, FfxApiResourceDescription ffxResDescription, uint32_t state)
{
FfxApiResource resource = {};
resource.resource = vkResource;
resource.state = state;
resource.description = ffxResDescription;
return resource;
}

View File

@@ -731,24 +731,30 @@ static void row_upscaling(void) {
static const char *const modes[] = {"dlaa", "quality", "balanced", "performance", "ultra_performance"};
static const UiTextId mode_names[] = {UI_MENU_DLSS_DLAA, UI_MENU_DLSS_QUALITY, UI_MENU_DLSS_BALANCED,
UI_MENU_DLSS_PERFORMANCE, UI_MENU_DLSS_ULTRA};
static char dl[5][96];
const char *names[7] = {menu_text(UI_MENU_UPSCALER_BILINEAR), menu_text(UI_MENU_UPSCALER_FSR)};
// FSR 3.1's modes take the same render scales as DLSS's (Native AA is FSR's name for DLAA's full size)
static const UiTextId fsr_names[] = {UI_MENU_FSR3_NATIVE, UI_MENU_DLSS_QUALITY, UI_MENU_DLSS_BALANCED,
UI_MENU_DLSS_PERFORMANCE, UI_MENU_DLSS_ULTRA};
static char dl[10][96];
const char *names[12] = {menu_text(UI_MENU_UPSCALER_BILINEAR), menu_text(UI_MENU_UPSCALER_FSR)};
for (int i = 0; i < 5; i++) {
snprintf(dl[i], sizeof dl[i], i ? "DLSS %s" : "NVIDIA %s", menu_text(mode_names[i]));
names[2 + i] = dl[i];
snprintf(dl[5 + i], sizeof dl[5 + i], "AMD FSR 3.1 %s", menu_text(fsr_names[i]));
names[2 + i] = dl[i], names[7 + i] = dl[5 + i];
}
int cur = strcmp(cfg_value("NX2NAVI_UPSCALER"), "fsr") ? 0 : 1;
if (cfg_bool("NX2NAVI_DLAA")) {
cur = 2;
int base = strcmp(cfg_value("NX2NAVI_TAA"), "fsr3") ? 2 : 7;
cur = base;
for (int i = 0; i < 5; i++)
if (!strcmp(cfg_value("NX2NAVI_DLSS"), modes[i])) cur = 2 + i;
if (!strcmp(cfg_value("NX2NAVI_DLSS"), modes[i])) cur = base + i;
}
int nv = opt_choice_index("NX2NAVI_UPSCALER", UI_MENU_UPSCALING, names, 7, cur, 1, UI_MENU_UPSCALING_DESC,
int nv = opt_choice_index("NX2NAVI_UPSCALER", UI_MENU_UPSCALING, names, 12, cur, 1, UI_MENU_UPSCALING_DESC,
cur >= 2 ? menu_text(UI_MENU_UPSCALING_DLSS_NOTE) : nullptr);
if (nv < 0) return;
opt_apply("NX2NAVI_UPSCALER", nv == 1 ? "fsr" : "bilinear");
opt_apply("NX2NAVI_DLAA", nv >= 2 ? "1" : "0");
if (nv >= 2) opt_apply("NX2NAVI_DLSS", modes[nv - 2]);
if (nv >= 2) opt_apply("NX2NAVI_DLSS", modes[(nv - 2) % 5]);
if (nv >= 2) opt_apply("NX2NAVI_TAA", nv >= 7 ? "fsr3" : "dlss");
}
// The picture's height (widescreen.c picture_height): the game's 1440 or more.

View File

@@ -165,7 +165,7 @@ static const char *const text_en[UI_TEXT_COUNT] = {
[UI_MENU_DLSS_DESC] = "DLAA draws the 3D scene at full resolution. Quality, Balanced, Performance and Ultra "
"Performance draw it at 67, 58, 50 and 33 % and let DLSS scale it up, which saves GPU time; "
"they replace the 3D render scale. Takes effect at once.",
[UI_MENU_DLSS_DLAA] = "DLAA (native)", [UI_MENU_DLSS_QUALITY] = "Quality", [UI_MENU_DLSS_BALANCED] = "Balanced",
[UI_MENU_DLSS_DLAA] = "DLAA (native)", [UI_MENU_FSR3_NATIVE] = "Native AA", [UI_MENU_DLSS_QUALITY] = "Quality", [UI_MENU_DLSS_BALANCED] = "Balanced",
[UI_MENU_DLSS_PERFORMANCE] = "Performance", [UI_MENU_DLSS_ULTRA] = "Ultra Performance",
[UI_MENU_FXAA] = "Game anti-aliasing (FXAA)",
[UI_MENU_FXAA_DESC] = "The game's own FXAA edge smoothing. It blurs fine detail a little; with DLAA on it is not needed.",
@@ -508,9 +508,11 @@ static const char *const text_en[UI_TEXT_COUNT] = {
"NVIDIA DLSS (RTX GPUs; " DLSS_FILE " next to the game's .exe) renders the 3D scene at "
"its mode's share of the picture and scales it up with NVIDIA's newest model for the mode: "
"DLAA 100 %, Quality 67 %, Balanced 58 %, Performance 50 %, Ultra Performance 33 %. The "
"HUD is not touched. Turning DLSS on or off takes effect after a restart; the mode changes "
"at once.",
[UI_MENU_UPSCALING_DLSS_NOTE] = "DLSS on or off: after restart",
"HUD is not touched. AMD FSR 3.1 (any GPU; amd_fidelityfx_vk.dll next to the game's .exe) "
"works the same way, with Native AA at 100 % and the same shares for its other modes. "
"Turning DLSS or FSR 3.1 on or off, or changing between them, takes effect after a "
"restart; the mode changes at once.",
[UI_MENU_UPSCALING_DLSS_NOTE] = "DLSS or FSR 3.1 on or off: after restart",
[UI_MENU_GAME_RES] = "Resolution",
[UI_MENU_GAME_RES_DESC] = "The height of the game's picture: 1440p is the game's own; higher draws a sharper picture "
"and its scene in proportion, at a matching cost on the graphics card. Native: the "

View File

@@ -56,7 +56,7 @@ typedef enum {
UI_MENU_CONTACT_SHADOWS, UI_MENU_CONTACT_SHADOWS_DESC,
UI_MENU_PCSS, UI_MENU_PCSS_DESC,
UI_MENU_DLAA, UI_MENU_DLAA_DESC, UI_MENU_DLSS, UI_MENU_DLSS_DESC, UI_MENU_DLSS_DLAA, UI_MENU_DLSS_QUALITY,
UI_MENU_DLSS_BALANCED, UI_MENU_DLSS_PERFORMANCE, UI_MENU_DLSS_ULTRA, UI_MENU_DLSS_PRESET, UI_MENU_DLSS_PRESET_DESC,
UI_MENU_DLSS_BALANCED, UI_MENU_DLSS_PERFORMANCE, UI_MENU_DLSS_ULTRA, UI_MENU_FSR3_NATIVE, UI_MENU_DLSS_PRESET, UI_MENU_DLSS_PRESET_DESC,
UI_MENU_DLSS_PRESET_DEFAULT, UI_MENU_DLSS_PRESET_K, UI_MENU_DLSS_PRESET_J, UI_MENU_DLSS_CHARS, UI_MENU_DLSS_CHARS_DESC,
UI_MENU_DLSS_CHARS_OFF, UI_MENU_DLSS_CHARS_LIGHT, UI_MENU_DLSS_CHARS_MEDIUM, UI_MENU_DLSS_CHARS_FULL, UI_MENU_FXAA, UI_MENU_FXAA_DESC,

View File

@@ -263,7 +263,11 @@ static int create_device(void) {
CHAIN(fbl, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_FEATURES_EXT);
CHAIN(lr, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_LOCAL_READ_FEATURES_KHR);
}
vk_dlaa_device_exts(ep, ne, ext, &n_ext, 40); // NGX's; the eight slots after it stay for the seven below
vk_dlaa_device_exts(ep, ne, ext, &n_ext, 40); // NGX's; the eight slots after it stay for the eight below
// FSR 3.1 (vk_ffx.cpp): its Vulkan back end looks vkGetBufferMemoryRequirements2KHR up by that name, which resolves
// only with the extension enabled (the core function has no suffix); without it the back end called address 0
if (vk_dlaa_fsr3() && has_ext(ep, ne, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME))
ext[n_ext++] = VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME;
if (has_ext(ep, ne, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME)) { g_vk.has_memory_budget = 1; ext[n_ext++] = VK_EXT_MEMORY_BUDGET_EXTENSION_NAME; }
// GPU timestamps on the CPU clock (vk_sync.c): the perf line splits GPU idle time into waiting for a submission or not.
if (has_ext(ep, ne, VK_KHR_CALIBRATED_TIMESTAMPS_EXTENSION_NAME)) { g_vk.has_calibrated = 2; ext[n_ext++] = VK_KHR_CALIBRATED_TIMESTAMPS_EXTENSION_NAME; }

View File

@@ -25,6 +25,7 @@
#include "vk.h"
#include "vk_dlaa.h"
#include "vk_exec.h"
#include "vk_ffx.h"
#include "vk_rec.h"
#include "dlaa_mv_spv.h"
@@ -111,6 +112,16 @@ int vk_dlaa_wanted(void) {
return wanted;
}
// The temporal upscaler behind dlaa (NX2NAVI_TAA, read once: like dlaa it applies after a restart): NVIDIA DLSS through
// NGX, or AMD FSR 3.1 through amd_fidelityfx_vk.dll (vk_ffx.cpp), which takes the same inputs and needs no device
// extensions. The DLSS mode setting (NX2NAVI_DLSS) is the render scale for both.
static int fsr3(void) {
static int on = -1;
if (on < 0) on = !strcmp(cfg_value("NX2NAVI_TAA"), "fsr3");
return on;
}
int vk_dlaa_fsr3(void) { return vk_dlaa_wanted() && fsr3(); }
static int core_load(void) {
if (N.tried) return N.loaded;
N.tried = 1;
@@ -166,7 +177,7 @@ static int listed(const char *const *list, uint32_t n, const char *s) {
void vk_dlaa_instance_exts(const char **ext, uint32_t *n, uint32_t max) {
unsigned ni = 0, nd = 0;
const char **ie = NULL, **de = NULL;
if (!vk_dlaa_wanted() || !core_load() || !NGX_OK(N.req_ext(&ni, &ie, &nd, &de))) return;
if (!vk_dlaa_wanted() || fsr3() || !core_load() || !NGX_OK(N.req_ext(&ni, &ie, &nd, &de))) return;
for (unsigned i = 0; i < ni; i++)
if (!listed(ext, *n, ie[i]) && *n < max) ext[(*n)++] = ie[i];
}
@@ -175,7 +186,7 @@ void vk_dlaa_device_exts(const VkExtensionProperties *have, uint32_t nhave, cons
uint32_t max) {
unsigned ni = 0, nd = 0;
const char **ie = NULL, **de = NULL;
if (!vk_dlaa_wanted() || !core_load() || !NGX_OK(N.req_ext(&ni, &ie, &nd, &de))) return;
if (!vk_dlaa_wanted() || fsr3() || !core_load() || !NGX_OK(N.req_ext(&ni, &ie, &nd, &de))) return;
for (unsigned i = 0; i < nd; i++) {
int ok = 0;
for (uint32_t k = 0; k < nhave && !ok; k++) ok = !strcmp(have[k].extensionName, de[i]);
@@ -225,6 +236,11 @@ static int utf8_to_wide(const char *in, wchar_t *out, size_t cap) {
#endif
void vk_dlaa_init(void) {
if (vk_dlaa_wanted() && fsr3()) {
N.available = vk_ffx_load();
rt_log("dlaa: the temporal upscaler is AMD FSR 3.1%s", N.available ? "" : ", which is missing: the game's own scaling");
return;
}
if (!vk_dlaa_wanted() || !core_load()) return;
#ifdef _WIN32
wchar_t wdir[MAX_PATH];
@@ -378,7 +394,7 @@ static void free_size(struct Size *e) {
if (e->chars_view[i]) vkDestroyImageView(c->device, e->chars_view[i], NULL);
if (e->chars[i]) vmaDestroyImage(c->vma, e->chars[i], e->chars_mem[i]);
}
if (e->feat) N.release(e->feat);
if (e->feat && N.release) N.release(e->feat);
memset(e, 0, sizeof *e);
}
@@ -1027,7 +1043,7 @@ static void evaluate(VkExec *ex, const VkExecTarget *dst) {
struct Size *e = size_for(cb, w, h, ow, oh);
if (!e) return dl_fail("work image creation", 0);
if (e != D.last) D.reset = 1, D.why[0]++, e->chars_ok = 0; // another size's history is from another moment
if (!D.params && !NGX_OK(N.alloc_params(&D.params))) return dl_fail("parameter allocation", 0);
if (!fsr3() && !D.params && !NGX_OK(N.alloc_params(&D.params))) return dl_fail("parameter allocation", 0);
NgxParam *p = D.params;
VkImageView zv = vk_exec_target_view(z->img, z->format, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_DEPTH_BIT, z->level,
z->base_layer, 1, 0);
@@ -1050,6 +1066,21 @@ static void evaluate(VkExec *ex, const VkExecTarget *dst) {
uint64_t c0 = timed ? kern_now_ns() : 0;
rec_drain(); // NGX records into the command buffer itself: the recorder thread must have made every call so far
if (timed) T.cpu_drain += kern_now_ns() - c0;
if (fsr3()) { // FSR 3.1 (vk_ffx.cpp) in DLSS's place: the same images, then the same blit below
if (t->level || t->base_layer || z->level || z->base_layer) return dl_fail("FSR 3.1 on a target that is not level 0, layer 0", 0);
if (D.last_color != t->image) D.reset = 1, D.why[2]++, D.last_color = t->image;
uint64_t now = plat_now_ns();
double dt = D.last_eval_ns ? (now - D.last_eval_ns) / 1e6 : 0;
D.last_eval_ns = now;
VkFfxImage fc = {t->image, t->format, w, h, 0}, fz = {z->image, z->format, w, h, 0},
fm = {e->mv, VK_FORMAT_R16G16_SFLOAT, w, h, 1}, fo = {e->out, VK_FORMAT_R16G16B16A16_SFLOAT, ow, oh, 1};
if (!vk_ffx_upscale(cb, &fc, &fz, &fm, &fo, jx, jy, D.reset, dt > 0 && dt < 100 ? (float)dt : 16.67f, 0))
return dl_fail("FSR 3.1", 0);
static int told;
if (!told++) rt_log("dlaa: FSR 3.1 at %ux%u -> %ux%u (scene format %d, depth format %d)", w, h, ow, oh,
(int)t->format, (int)z->format);
goto evaluated;
}
if (!e->feat) {
p_setu(p, "CreationNodeMask", 1);
p_setu(p, "VisibilityNodeMask", 1);
@@ -1115,6 +1146,7 @@ static void evaluate(VkExec *ex, const VkExecTarget *dst) {
NgxResult r = N.evaluate(cb, e->feat, p, NULL);
if (timed) T.cpu_ngx += kern_now_ns() - c0;
if (!NGX_OK(r)) return dl_fail("DLSS evaluation", r);
evaluated:
if (timed) times_mark(cb, T_DL1); // through the recorder, after NGX's own commands
D.reset = 0;
D.last = e, e->used = D.evals + 1;
@@ -1177,7 +1209,13 @@ void vk_dlaa_pass(VkExec *ex) {
// The G-buffer: several colour targets with the D32F scene depth. Only the light target drawn after it arms the
// frame; at night the game draws into the light target alone with the scene depth before the G-buffer too, and
// DLAA then ran at a small pass ahead of the whole scene, which reached the screen jittered.
if (ex->pass_ncolor >= 3 && scene) {
// A pass of that shape smaller than the light target already armed is not one: Ocarina draws a half-size effect
// into three targets with a half-size D32F depth after its scene, and taking that for a G-buffer switched every
// later frame to the deferred shape, where DLSS's and FSR 3.1's smaller modes never found their output pass.
if (ex->pass_ncolor >= 3 && scene && !(D.armed && (z->width < D.color.width || z->height < D.color.height))) {
if (!deferred)
rt_log("dlaa: a G-buffer pass (%d colour targets, depth %ux%u, target 0 format %d): the deferred frame shape",
ex->pass_ncolor, z->width, z->height, c->image ? (int)c->format : 0);
D.gbuf = 1, D.armed = 0, deferred = 1;
// target 0 is the object-class buffer (RGBA8, the scene size): the characters' bias mask reads its green. A
// smaller pass of the same shape (640x360, seen while loading) does not replace the scene's in the same frame.
@@ -1195,6 +1233,10 @@ void vk_dlaa_pass(VkExec *ex) {
(!D.armed || (c->width >= D.color.width && c->height >= D.color.height)) && c->samples <= 1 && !c->is3d) {
int first = !D.armed;
D.color = *c, D.depth = *z, D.armed = 1;
static int told;
if (first && !told++)
rt_log("dlaa: first scene armed: light target %ux%u format %d, depth %ux%u", c->width, c->height, (int)c->format,
z->width, z->height);
if (first) mask_snapshot(ex); // the lit scene, before the transparents (reactive mask)
return;
}

View File

@@ -7,6 +7,7 @@
#define DLAA_SCENE_MIN_W 320u
int vk_dlaa_wanted(void); // the setting is on (the device may still lack DLSS)
int vk_dlaa_fsr3(void); // ... and its temporal upscaler is AMD FSR 3.1 (NX2NAVI_TAA = fsr3) instead of DLSS
// vk_device.c: NGX's instance and device extensions join the lists (room for max names); vk_dlaa_init after the
// device exists brings NGX up and checks that DLSS is available.
void vk_dlaa_instance_exts(const char **ext, uint32_t *n, uint32_t max);

171
runtime/vk_ffx.cpp Normal file
View File

@@ -0,0 +1,171 @@
// AMD FidelityFX through its API library (amd_fidelityfx_vk.dll next to the executable, loaded at run time): FSR 3.1
// temporal upscaling here, in the place of DLSS in vk_dlaa.c, and frame generation (vk_ffx_fg_*, below). The API's
// headers are in third_party/ffx_api (MIT); they are C++ in places, hence this file. Nothing of it runs unless the
// upscaler or frame generation asks for FSR 3.1.
//
// FSR 3.1 takes the same inputs vk_dlaa.c makes for DLSS: the scene's light target (linear HDR), its D32F depth
// (OpenGL-style, an infinite far plane, not inverted) and the camera motion vectors in render pixels from the current
// frame to the previous one, the convention both share, so the vectors go in unscaled.
#ifdef _WIN32
#include <windows.h>
#endif
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include "third_party/ffx_api/ffx_api.h"
#include "third_party/ffx_api/ffx_upscale.h"
#include "third_party/ffx_api/vk/ffx_api_vk.h"
extern "C" {
#include "cfg.h"
#include "host.h"
#include "vk.h"
#include "vk_ffx.h"
}
namespace {
struct Api {
void *lib = nullptr;
PfnFfxCreateContext create = nullptr;
PfnFfxDestroyContext destroy = nullptr;
PfnFfxConfigure configure = nullptr;
PfnFfxQuery query = nullptr;
PfnFfxDispatch dispatch = nullptr;
int tried = 0, ok = 0;
} A;
void message(uint32_t type, const wchar_t *msg) {
char s[512];
size_t n = 0;
for (; msg && msg[n] && n < sizeof s - 1; n++) s[n] = msg[n] < 128 ? (char)msg[n] : '?';
s[n] = 0;
static int told;
if (told++ < 40) rt_log("ffx: %s %s", type == FFX_API_MESSAGE_TYPE_ERROR ? "error:" : "warning:", s);
}
// One upscaling context per render and output size (the menu switches the mode live, so a few may exist); never
// destroyed before shutdown, so none is released while the GPU may still run its work.
struct Up {
ffxContext ctx = nullptr;
uint32_t w = 0, h = 0, ow = 0, oh = 0;
} U[6];
FfxApiResource image(const VkFfxImage *i, uint32_t state, uint32_t usage) {
VkImageCreateInfo ci = {};
ci.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
ci.imageType = VK_IMAGE_TYPE_2D;
ci.format = i->format;
ci.extent = {i->width, i->height, 1};
ci.mipLevels = 1;
ci.arrayLayers = 1;
ci.usage = i->storage ? VK_IMAGE_USAGE_STORAGE_BIT : 0;
return ffxApiGetResourceVK((void *)i->image, ffxApiGetImageResourceDescriptionVK(i->image, ci, usage), state);
}
Up *context_for(uint32_t w, uint32_t h, uint32_t ow, uint32_t oh) {
for (Up &u : U)
if (u.ctx && u.w == w && u.h == h && u.ow == ow && u.oh == oh) return &u;
Up *slot = nullptr;
for (Up &u : U)
if (!u.ctx) { slot = &u; break; }
if (!slot) return nullptr;
const VkCtx *c = vk_ctx();
ffxCreateBackendVKDesc be = {};
be.header.type = FFX_API_CREATE_CONTEXT_DESC_TYPE_BACKEND_VK;
be.vkDevice = c->device;
be.vkPhysicalDevice = c->phys;
be.vkDeviceProcAddr = vkGetDeviceProcAddr;
ffxCreateContextDescUpscale d = {};
d.header.type = FFX_API_CREATE_CONTEXT_DESC_TYPE_UPSCALE;
d.header.pNext = &be.header;
d.flags = FFX_UPSCALE_ENABLE_HIGH_DYNAMIC_RANGE | FFX_UPSCALE_ENABLE_DEPTH_INFINITE | FFX_UPSCALE_ENABLE_AUTO_EXPOSURE;
d.maxRenderSize = {w, h};
d.maxUpscaleSize = {ow, oh};
d.fpMessage = message;
ffxReturnCode_t r = A.create(&slot->ctx, &d.header, nullptr);
if (r != FFX_API_RETURN_OK) {
rt_log("ffx: FSR 3.1 context creation at %ux%u -> %ux%u failed (%u)", w, h, ow, oh, (unsigned)r);
slot->ctx = nullptr;
return nullptr;
}
slot->w = w, slot->h = h, slot->ow = ow, slot->oh = oh;
ffxQueryGetProviderVersion v = {};
v.header.type = FFX_API_QUERY_DESC_TYPE_GET_PROVIDER_VERSION;
A.query(&slot->ctx, &v.header);
rt_log("ffx: FSR 3.1 upscaling at %ux%u -> %ux%u (%s)", w, h, ow, oh, v.versionName ? v.versionName : "provider unknown");
return slot;
}
} // namespace
extern "C" int vk_ffx_load(void) {
if (A.tried) return A.ok;
A.tried = 1;
#ifdef _WIN32
char path[MAX_PATH];
DWORD n = GetModuleFileNameA(NULL, path, MAX_PATH);
while (n && path[n - 1] != '\\') n--;
snprintf(path + n, sizeof path - n, "amd_fidelityfx_vk.dll");
HMODULE m = LoadLibraryA(path);
if (!m) {
rt_log("ffx: %s not found: FSR 3.1 off", path);
return 0;
}
A.lib = m;
A.create = (PfnFfxCreateContext)(void *)GetProcAddress(m, "ffxCreateContext");
A.destroy = (PfnFfxDestroyContext)(void *)GetProcAddress(m, "ffxDestroyContext");
A.configure = (PfnFfxConfigure)(void *)GetProcAddress(m, "ffxConfigure");
A.query = (PfnFfxQuery)(void *)GetProcAddress(m, "ffxQuery");
A.dispatch = (PfnFfxDispatch)(void *)GetProcAddress(m, "ffxDispatch");
#endif
A.ok = A.create && A.destroy && A.configure && A.query && A.dispatch;
rt_log("ffx: amd_fidelityfx_vk.dll %s", A.ok ? "loaded" : "lacks the API entry points: FSR 3.1 off");
return A.ok;
}
extern "C" int vk_ffx_upscale(VkCommandBuffer cb, const VkFfxImage *color, const VkFfxImage *depth, const VkFfxImage *mv,
const VkFfxImage *out, float jx, float jy, int reset, float dt_ms, float fov_y) {
if (!vk_ffx_load()) return 0;
Up *u = context_for(color->width, color->height, out->width, out->height);
if (!u) return 0;
ffxDispatchDescUpscale d = {};
d.header.type = FFX_API_DISPATCH_DESC_TYPE_UPSCALE;
d.commandList = cb;
// Every image is in GENERAL, which the backend reads as its unordered-access state, and is left there
d.color = image(color, FFX_API_RESOURCE_STATE_UNORDERED_ACCESS, 0);
d.depth = image(depth, FFX_API_RESOURCE_STATE_UNORDERED_ACCESS, 0);
d.motionVectors = image(mv, FFX_API_RESOURCE_STATE_UNORDERED_ACCESS, 0);
d.output = image(out, FFX_API_RESOURCE_STATE_UNORDERED_ACCESS, FFX_API_RESOURCE_USAGE_UAV);
d.jitterOffset = {jx, jy};
d.motionVectorScale = {1.0f, 1.0f};
d.renderSize = {color->width, color->height};
d.upscaleSize = {out->width, out->height};
// The sharpness slider (0-100) drives FSR's own RCAS here, as it does for FSR 1
int sharp = cfg_int("NX2NAVI_SHARPNESS");
d.enableSharpening = sharp > 0;
d.sharpness = sharp > 100 ? 1.0f : sharp / 100.0f;
d.frameTimeDelta = dt_ms;
d.preExposure = 1.0f;
d.reset = reset != 0;
d.cameraNear = 0.1f; // Ocarina's projection (vk_dlaa.c ocam_vp): near 0.1, far at infinity
d.cameraFar = 1e8f;
d.cameraFovAngleVertical = fov_y > 0 ? fov_y : 1.0f;
d.viewSpaceToMetersFactor = 0.01f; // the game's units are centimetres
ffxReturnCode_t r = A.dispatch(&u->ctx, &d.header);
if (r != FFX_API_RETURN_OK) {
static int told;
if (told++ < 8) rt_log("ffx: FSR 3.1 dispatch failed (%u)", (unsigned)r);
return 0;
}
return 1;
}
extern "C" void vk_ffx_release(void) {
if (!A.ok) return;
for (Up &u : U)
if (u.ctx) A.destroy(&u.ctx, nullptr), u.ctx = nullptr;
}

18
runtime/vk_ffx.h Normal file
View File

@@ -0,0 +1,18 @@
// AMD FidelityFX (vk_ffx.cpp): FSR 3.1 temporal upscaling through amd_fidelityfx_vk.dll, loaded at run time.
#pragma once
#include <vulkan/vulkan.h>
typedef struct {
VkImage image; // level 0, layer 0, in VK_IMAGE_LAYOUT_GENERAL
VkFormat format;
uint32_t width, height;
int storage; // created with VK_IMAGE_USAGE_STORAGE_BIT (FSR writes it)
} VkFfxImage;
int vk_ffx_load(void); // 1 when the library is there with its entry points (loaded once)
// Records FSR 3.1 into cb: color (the scene light, render size), depth (D32F), mv (R16G16F, render pixels, current to
// previous), out (the output size, RGBA16F, storage). jx, jy: the jitter the scene took, render pixels. fov_y in
// radians (0: unknown). 0 on failure (the caller falls back as it does when DLSS fails).
int vk_ffx_upscale(VkCommandBuffer cb, const VkFfxImage *color, const VkFfxImage *depth, const VkFfxImage *mv,
const VkFfxImage *out, float jx, float jy, int reset, float dt_ms, float fov_y);
void vk_ffx_release(void); // before the device goes away

View File

@@ -393,6 +393,8 @@ def step_compile(out, cache, msys, jobs):
shutil.copy2(gen / 'romfs-index.bin', out / 'romfs-index.bin')
for name in ('nx2navi_xlate.exe', 'nx2navi_xlate.id'):
shutil.copy2(KIT / 'bin' / name, out / name)
if (KIT / 'bin' / 'amd_fidelityfx_vk.dll').is_file(): # FSR 3.1 (runtime/vk_ffx.cpp)
shutil.copy2(KIT / 'bin' / 'amd_fidelityfx_vk.dll', out / 'amd_fidelityfx_vk.dll')
say(f' ocarina.exe {exe.stat().st_size / 1e6:.0f} MB; beside it: {", ".join(copy_dlls(exe, msys / "bin", out))}')
(out / 'profile').mkdir(exist_ok=True)

View File

@@ -33,6 +33,7 @@ def copy_tree(src, dst):
def main():
ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
ap.add_argument('--xlate', type=Path, required=True, help='folder with nx2navi_xlate.exe and nx2navi_xlate.id')
ap.add_argument('--ffx', type=Path, help='AMD's signed amd_fidelityfx_vk.dll (FidelityFX SDK 1.1.4, MIT) for FSR 3.1')
ap.add_argument('--out', type=Path, default=ROOT / 'build' / 'kit')
ap.add_argument('--zip', action='store_true')
a = ap.parse_args()
@@ -52,6 +53,8 @@ def main():
(kit / 'bin').mkdir()
for n in ('nx2navi_xlate.exe', 'nx2navi_xlate.id'):
shutil.copy2(a.xlate / n, kit / 'bin' / n)
if a.ffx: # FSR 3.1 loads it from beside the game (runtime/vk_ffx.cpp); its licence is runtime/third_party/ffx_api
shutil.copy2(a.ffx, kit / 'bin' / 'amd_fidelityfx_vk.dll')
sys.path.insert(0, str(ROOT / 'tools'))
import check_content
bad = [f'{p.relative_to(kit)}: {m}' for p in sorted(kit.rglob('*')) if p.is_file()