Add AMD FSR 4 upscaling through the libraries of AMD's FSR SDK 2.4, as an Upscaling choice beside FSR 3.1.

runtime/vk_ffx4.cpp loads amd_fidelityfx_loader_vk.dll from beside the executable and asks it which upscaler
versions the device can run. Where a 4.x version is listed (Radeon RX 6000, RX 7000 and RX 9000 cards with AMD's
driver 26.10.1 or newer) the menu's Upscaling row gains AMD FSR 4 Native AA, Quality, Balanced, Performance and
Ultra Performance, and fsr4 = 1 runs it in FSR 3.1's place with the same inputs. Its contexts are made with that
version asked for by its id and the provider is asked afterwards which version it runs, so no other upscaler can
be shown under the name. When a context or a dispatch fails, FSR 3.1 makes the frame, and after three failures
FSR 4 is off for the run. Each upscaler starts its history anew on the frame after a change from one to the other.
The setting is off by default and the Linux build compiles stubs.

On an RX 7900 XT (driver 26.10.1, 3440x1440, HDR) the library runs FSR 4.1.2: a context takes 54 ms to make and
217 MiB of video memory. Standing in Hyrule Field: Quality 47.8 fps where FSR 3.1 gives 51.5, Native AA 41.4.
Screenshots of 601 consecutive frames standing, 801 during a camera sweep, 701 each in Kakariko and the
graveyard, and 1,301 across changes between FSR 3.1 and FSR 4, their modes and off show no flicker that FSR 3.1
does not have, and it runs together with FSR 3.1 frame generation.

The SDK's API headers are in runtime/third_party/ffx_sdk, unmodified, with the licence. tools/make_kit.py and
tools/make_launcher.py take AMD's two signed libraries with --ffx4, and tools/make_game.py copies them beside the
game when the kit has both.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
epicmeme546
2026-11-11 03:21:51 -05:00
parent 3fe6965df9
commit 4d5454d380
19 changed files with 1444 additions and 25 deletions

View File

@@ -30,8 +30,8 @@ Report problems as issues, following [CONTRIBUTING.md](CONTRIBUTING.md).
- Game resolutions from 1080p to above the game's 1440p: 1800p, 4K, 5K or the window's own size, sharp on high-DPI
displays
- Upscaling and anti-aliasing: NVIDIA DLSS (DLAA, Quality, Balanced, Performance, Ultra Performance; RTX GPUs) or
AMD FSR 3.1 (Native AA and the same modes; any GPU), which render the 3D scene at the mode's share and keep the HUD
sharp; or AMD FSR 1 or bilinear scaling to the window, with a sharpness setting; black bars or stretch
AMD FSR 3.1 (Native AA and the same modes; any GPU) or AMD FSR 4 (the same modes; Radeon RX 6000, RX 7000 and
RX 9000 GPUs), which render the 3D scene at the mode's share and keep the HUD sharp; or AMD FSR 1 or bilinear scaling to the window, with a sharpness setting; black bars or stretch
- Frame generation: NVIDIA DLSS Frame Generation (RTX 40 and 50 GPUs, paced with Reflex) at 2x, or 3x and 4x on RTX 50
GPUs (one, two or three generated frames for every frame the game draws), or AMD FSR 3.1 (any GPU), a generated
frame between every two the game draws, with the HUD kept out of it, for about twice the frame rate on a fast

View File

@@ -106,6 +106,7 @@ folder's `licenses/` and in each component's own folder.
| 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) |
| AMD FSR SDK API headers (FSR 4 upscaling) and its signed Vulkan libraries | https://github.com/GPUOpen-LibrariesAndSDKs/FidelityFX-SDK @ `v2.4.0` (`Kits/FidelityFX/api/include/`, `Kits/FidelityFX/upscalers/include/ffx_upscale.h`, `Kits/FidelityFX/signedbin/amd_fidelityfx_loader_vk.dll`, `Kits/FidelityFX/signedbin/amd_fidelityfx_upscaler_vk.dll`), Copyright (C) Advanced Micro Devices, Inc. | MIT | `runtime/third_party/ffx_sdk/` (unmodified, with `LICENSE.txt`), used by `runtime/vk_ffx4.cpp`; the libraries ship in the build kit's `bin/` and beside the built game (never in this repository) |
## Distributed in the launcher release

View File

@@ -112,7 +112,8 @@ cannot give back what the game needs after its start: about 10 GB in Kokiri Fore
AMD FSR 3.1 loads AMD's `amd_fidelityfx_vk.dll`, which only the Windows build can use: on Linux the menu offers neither
FSR 3.1 upscaling nor FSR 3.1 frame generation, and `taa = fsr3` or `framegen = fsr3` in `nx2navi.ini` leave the
game's own scaling and swapchain as they are (the log says so).
game's own scaling and swapchain as they are (the log says so). AMD FSR 4 is in the Windows build alone for the same
reason, and `fsr4 = 1` does nothing on Linux.
NVIDIA DLSS (RTX cards) needs the driver's own `libnvidia-ngx.so.1` on the library path (the proprietary driver's
utilities package, `nvidia-utils` on Arch) and NVIDIA's DLSS library `libnvidia-ngx-dlss.so.<version>` beside
@@ -151,7 +152,8 @@ beside the game.
- **Screen** in the menu has Texture filtering (anisotropic, up to 16x, at once), Texture detail (Reduced and Low
leave out the largest versions of the big surface textures so that the game fits the video memory of 6 and 8 GB
cards; Automatic picks by the card), Upscaling (DLSS on NVIDIA RTX cards,
FSR 3.1 on any card where `amd_fidelityfx_vk.dll` loads), Frame generation (NVIDIA DLSS on RTX 40 and 50 cards, with
FSR 3.1 on any card where `amd_fidelityfx_vk.dll` loads, FSR 4 on the Radeon cards AMD's FSR 4 libraries list it
for), Frame generation (NVIDIA DLSS on RTX 40 and 50 cards, with
a DLSS multiplier of 2x, or 3x and 4x on RTX 50 cards; FSR 3.1 on any card where it loads; it needs DLSS or FSR 3.1
upscaling on, and the frame limit then counts the frames on screen; with no limit chosen it is the display's refresh
rate), HDR (the game's picture tone mapped to your display, with its own grading rows, or Native, the game's own HDR; turn HDR on in Windows first) and the
@@ -162,7 +164,8 @@ beside the game.
the keys `nx2navi.ini` binds when the build runs: after rebinding keys, run
`py -I tools\make_game.py --rebuild --out "C:\Games\Ocarina"` to draw them again (it skips everything else).
- The build copies NVIDIA's DLSS library `nvngx_dlss.dll` (with `nvngx_dlss-NOTICE.txt`, which names its licence)
and FSR 3.1's `amd_fidelityfx_vk.dll` beside `ocarina.exe` from the kit's `bin\`. To use another DLSS version, put
and FSR 3.1's `amd_fidelityfx_vk.dll` beside `ocarina.exe` from the kit's `bin\`, and FSR 4's
`amd_fidelityfx_loader_vk.dll` and `amd_fidelityfx_upscaler_vk.dll` when the kit has both. To use another DLSS version, put
your own `nvngx_dlss.dll` there: later builds keep it. On Linux the library is `libnvidia-ngx-dlss.so.<version>`
(see "Linux").
- A kit with `bin\dlssg\` (made with `tools/make_kit.py --dlssg`) also brings DLSS Frame Generation on GeForce RTX 40

View File

@@ -357,6 +357,8 @@ game folder is the default `<launcher>\game`. The dump is never touched.
```
`--ffx` puts AMD's signed `amd_fidelityfx_vk.dll` (FidelityFX SDK 1.1.4) in the release for FSR 3.1.
`--ffx4 <folder>` puts AMD's signed `amd_fidelityfx_loader_vk.dll` and `amd_fidelityfx_upscaler_vk.dll` (FSR SDK
2.4.0, the files of its `Kits/FidelityFX/signedbin/`) in the release for FSR 4; without it the menu offers no FSR 4.
NVIDIA's `nvngx_dlss.dll` goes in by itself: the script downloads the version `tools/dlss_pin.py` pins from
NVIDIA's DLSS SDK repository (cached in `toolchain/launcher-dl/`), checks its size and sha256, and puts it in
`bin/` with `nvngx_dlss-NOTICE.txt`; `make_game.py` copies both beside the game. `--dlss FILE` takes a copy on disk

View File

@@ -18,6 +18,16 @@ Draft notes for the release after 0.7.1. The version number is not decided yet.
## New
- **AMD FSR 4 upscaling** on Radeon RX 6000, RX 7000 and RX 9000 cards (Windows, AMD's driver 26.10.1 or newer):
AMD's machine-learned upscaler, in the menu under Screen, Upscaling, as *AMD FSR 4 Native AA*, *Quality*,
*Balanced*, *Performance* and *Ultra Performance* (`fsr4 = 1` in `nx2navi.ini`). It keeps thin things whole that
FSR 3.1 breaks up or smears when the camera moves: grass and leaves, Navi's wings, the edges of Link's hat. It is
off until you choose it, because it costs more than FSR 3.1: standing in Hyrule Field on an RX 7900 XT at
3440x1440, 47 fps in Quality where FSR 3.1 gives 51, and 41 fps in Native AA; it takes about 220 MB of video
memory. The setup brings AMD's two libraries for it (FSR SDK 2.4, `amd_fidelityfx_loader_vk.dll` and
`amd_fidelityfx_upscaler_vk.dll`), which ask the driver whether the card can run FSR 4: the rows appear only
where it can, and the log names the version (`ffx4:`). It works together with FSR 3.1 frame generation, and you
can switch between FSR 3.1 and FSR 4 while playing.
- **Add to Steam** in Customize Launcher (Windows): adds the game to your Steam library as *The Legend of Zelda
Ocarina of Time*, with Navi as its icon and the title screen's artwork and logo. Starting it from Steam starts the
game. Steam restarts once while it is added.

View File

@@ -393,6 +393,17 @@
# to match). They apply only with dlaa = 1 and the DLSS model next to the executable; otherwise the scene follows
# render_scale. Changes while the game runs take effect at once.
# dlss = dlaa
# AMD FSR in DLSS's place, on any GPU (Windows): with taa = fsr3 and dlaa = 1 the scene is scaled by AMD FSR 3.1 in the
# mode dlss names (dlaa is FSR's Native AA). The menu's Upscaling row sets all of these. Changes while the game runs
# take effect at once.
# taa = dlss
# AMD FSR 4 in FSR 3.1's place (taa = fsr3, dlaa = 1): the machine-learned upscaler, a sharper and steadier picture
# that costs more GPU time (on an RX 7900 XT at 3440x1440, Quality: 47 fps where FSR 3.1 gives 51) and about 220 MB of
# video memory. It runs where AMD's amd_fidelityfx_loader_vk.dll and amd_fidelityfx_upscaler_vk.dll are next to the
# executable (the setup puts them there) and that library lists FSR 4 for the GPU: Radeon RX 6000, RX 7000 and RX 9000
# cards with AMD's driver 26.10.1 or newer. Elsewhere, or when it fails, FSR 3.1 runs and the log says why. Changes
# while the game runs take effect at once.
# fsr4 = 0
# The DLSS model: default (NVIDIA's advice per mode: K for dlaa, quality and balanced, M for performance, L for
# ultra_performance), k (K in every mode) or j (the other transformer model: slightly less ghosting behind moving
# characters, a little more flicker). Any other model by its letter, a to m: on slower RTX cards the older model e is

View File

@@ -62,7 +62,7 @@ static const char *const known[] = {
"SVC_SLOW_MS",
"SHOTS_FULL", "SPEED_PROBE", "SPIN", "SPIN_HANDOFF_US", "SPLIT_DRAWS", "SPLIT_IDLE", "SPLIT_IN_PASS", "SPLIT_US", "SPV_MARK_TEST", "SPV_OVERRIDE", "STICK_DEADZONE", "STICK_SENS",
"SWKBD_ANSWER",
"SET_AT", "SWKBD_TEST", "TAA", "TEST_PAD", "TEST_PAD_AT", "TEST_PREPARE_SHADERS", "TEX_ALIAS", "TEX_DISUSE", "TEX_GEN_UNDO", "TEX_MEMO", "TEX_VRAM_PRESSURE", "TEX_RELEASED_MB", "TEX_REUSE", "TEX_COMMIT_FREE_MB", "TEX_PROC_COMMIT_MB", "TEX_EVICT", "TEX_COMMIT_EVICT", "MIRROR_EVICT", "VRAM_BUDGET_MB", "TIMER_1MS", "TRACE", "TXD_TRANSPOSE", "UBO_SNAPSHOT", "EVO_TRACE", "EVENT_AT_SYNC", "UPSCALER", "VK_DYN_A2C", "VK_DYN_VERTEX", "VK_NAN_PRESERVE", "VK_NOOPT", "VK_NO_FLOAT64",
"SET_AT", "SWKBD_TEST", "TAA", "FSR4", "TEST_PAD", "TEST_PAD_AT", "TEST_PREPARE_SHADERS", "TEX_ALIAS", "TEX_DISUSE", "TEX_GEN_UNDO", "TEX_MEMO", "TEX_VRAM_PRESSURE", "TEX_RELEASED_MB", "TEX_REUSE", "TEX_COMMIT_FREE_MB", "TEX_PROC_COMMIT_MB", "TEX_EVICT", "TEX_COMMIT_EVICT", "MIRROR_EVICT", "VRAM_BUDGET_MB", "TIMER_1MS", "TRACE", "TXD_TRANSPOSE", "UBO_SNAPSHOT", "EVO_TRACE", "EVENT_AT_SYNC", "UPSCALER", "VK_DYN_A2C", "VK_DYN_VERTEX", "VK_NAN_PRESERVE", "VK_NOOPT", "VK_NO_FLOAT64",
"VK_PRESENT", "VK_RECONVERGE", "VK_NAN_BITS", "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", "WB_NO_REUP", "WB_MERGE_MAX", "WB_PROBE", "WB_SPLIT", "WB_LAZY", "WB_LAZY_TOUCH", "WINDOW_SIZE", "XLATE", "XLATE_ID", "XLATE_KEEP", "XLATE_PROCS",
"ZSTD_CHECK", "ZSTD_NATIVE",
@@ -88,7 +88,7 @@ static const struct { const char *key, *section; } sections[] = {
{"HDR_HIGHLIGHTS", "display"}, {"HDR_SHADOWS", "display"}, {"HDR_CONTRAST", "display"}, {"HDR_SATURATION", "display"},
{"HDR_HIGHLIGHT_SAT", "display"}, {"HDR_BLOWOUT", "display"}, {"HDR_FLARE", "display"}, {"HDR_HUE", "display"},
{"HDR_GRADE", "display"}, {"SHADOW_RES", "display"},
{"DLAA", "display"}, {"DLSS", "display"}, {"DLSS_PRESET", "display"}, {"DLSS_IDLE_FREE", "display"}, {"DLSS_CHAR_MASK", "display"}, {"FXAA", "display"},
{"DLAA", "display"}, {"FSR4", "display"}, {"DLSS", "display"}, {"DLSS_PRESET", "display"}, {"DLSS_IDLE_FREE", "display"}, {"DLSS_CHAR_MASK", "display"}, {"FXAA", "display"},
{"FRAMEGEN", "display"}, {"FRAMEGEN_MULTIPLIER", "display"}, {"FG_MULT", "display"},
{"DEVICE_PROFILE", "display"}, {"ASSUME_FOCUS", "display"},
{"DISCORD_RPC", "discord"}, {"DISCORD_APP_ID", "discord"}, {"DISCORD_IMAGE", "discord"},
@@ -152,6 +152,7 @@ static const CfgMeta metas[] = {
{"UPSCALER", "fsr", "fsr|bilinear", LIVE}, {"SHARPNESS", "80", "0..100", LIVE},
{"DLAA", "0", "0|1", LIVE}, {"DLSS", "dlaa", "dlaa|quality|balanced|performance|ultra_performance", LIVE},
{"DLSS_PRESET", "default", "default|k|j", LIVE}, {"DLSS_IDLE_FREE", "1", "0|1|2", RESTART}, {"TAA", "dlss", "dlss|fsr3", LIVE}, // vk_dlaa.c fsr3
{"FSR4", "0", "0|1", LIVE}, // vk_ffx.cpp vk_ffx_fsr4
{"FRAMEGEN", "off", "off|fsr3|dlss", LIVE}, // vk_fg.cpp, vk_dlssg.c
{"FRAMEGEN_MULTIPLIER", "2", "2|3|4", LIVE}, // vk_dlssg.c: frames on screen per rendered frame with DLSS
// the same where DLSS's frame generation runs through NGX itself (gfx_present.c ng_multiplier): 2x to 6x or dynamic

11
runtime/third_party/ffx_sdk/LICENSE.txt vendored Normal file
View File

@@ -0,0 +1,11 @@
AMD FSR SDK 2.4.0 (https://github.com/GPUOpen-LibrariesAndSDKs/FidelityFX-SDK, tag v2.4.0), the files of
Kits/FidelityFX/api/include and Kits/FidelityFX/upscalers/include kept here at the same relative paths, unmodified.
docs/license.md of that tag lists them among the files under this licence:
Copyright (C) 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.

View File

@@ -0,0 +1,213 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2026 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.
FFX_API_RETURN_PROVIDER_NO_SUPPORT_NEW_DESCTYPE = 7, ///< The structure type given is new and not supported in the old provider. This is likely fixed with driver upgrade or effect DLL upgrade.
};
typedef void* ffxContext;
typedef uint32_t ffxReturnCode_t;
#define FFX_API_EFFECT_MASK 0x00ff0000u
#define FFX_API_BACKEND_MASK 0xff000000u
#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_GLOBALDEBUG 7u
struct ffxConfigureDescGlobalDebug
{
ffxConfigureDescHeader header;
uint64_t effectId;
ffxApiMessage fpMessage; ///< A pointer to a function that can receive messages from the runtime. May be null.
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. For VK, pointer to VkDeviceContext.
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);
// FFX_API_EFFECT_IDs
#define FFX_API_EFFECT_ID_UPSCALE 0x00010000u
#define FFX_API_EFFECT_ID_FRAMEGENERATION 0x00020000u
#define FFX_API_EFFECT_ID_FRAMEGENERATIONSWAPCHAIN 0x00030000u
// Need to keep this ID around for the deprecated VK frame gen swapchain
#define FFX_API_EFFECT_ID_FRAMEGENERATIONSWAPCHAIN_VK 0x00040000u
#define FFX_API_EFFECT_ID_DENOISER 0x00050000u
#define FFX_API_EFFECT_ID_RADIANCECACHE 0x00060000u
#define FFX_API_EFFECT_COUNT 6u
#define FFX_API_MAKE_EFFECT_SUB_ID(effectId, subversion) ((effectId & FFX_API_EFFECT_MASK) | (subversion & ~FFX_API_EFFECT_MASK))
// FFX_APID_BACKEND_IDs
#define FFX_API_BACKEND_ID_DX12 0x00000000u
#define FFX_API_BACKEND_ID_VK 0x01000000u // For new effects going forward, please use this backend ID for vulkan specifics
#define FFX_API_BACKEND_ID_XBOX 0x02000000u
#define FFX_API_MAKE_BACKEND_SUB_ID(backendId, subversion) ((backendId & FFX_API_BACKEND_MASK) | (subversion & ~FFX_API_BACKEND_MASK))
// Combiner for BACKEND-specific EFFECT sub-Ids
#define FFX_API_MAKE_BACKEND_EFFECT_SUB_ID(backendId, effectId, subversion) ((subversion & ~FFX_API_EFFECT_MASK) | (effectId & FFX_API_EFFECT_MASK) | (backendId & FFX_API_BACKEND_MASK) | (subversion & ~(FFX_API_BACKEND_MASK | FFX_API_EFFECT_MASK)))
// Pragma macros for controlling warnings so that deprecations take affect externally but can be suppressed internally.
// This is so we can maintain the API/ABI until we are ready to make the breaking change.
// These are sadly compiler specific.
#if defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__) && !defined(__INTEL_COMPILER)
#define FFX_PRAGMA_WARNING_PUSH warning( push )
#define FFX_PRAGMA_WARNING_POP warning( pop )
#define FFX_PRAGMA_WARNING_DISABLE_DEPRECATIONS warning( disable: 4996 )
#define FFX_PRAGMA_WARNING_WARN_DEPRECATIONS warning( default: 4996 )
#elif defined(__clang__) || defined(__GNUC__)
#define FFX_PRAGMA_WARNING_PUSH GCC diagnostic push
#define FFX_PRAGMA_WARNING_POP GCC diagnostic pop
#define FFX_PRAGMA_WARNING_DISABLE_DEPRECATIONS GCC diagnostic ignored "-Wdeprecated"
#define FFX_PRAGMA_WARNING_WARN_DEPRECATIONS GCC diagnostic warning "-Wdeprecated"
#else
#define FFX_PRAGMA_WARNING_PUSH
#define FFX_PRAGMA_WARNING_POP
#define FFX_PRAGMA_WARNING_DISABLE_DEPRECATIONS
#endif
#define FFX_DEPRECATION(message) [[deprecated(message)]]
#define FFX_API_CONFIGURE_DESC_TYPE_GLOBALDEBUG1 0x0000001u
struct FFX_DEPRECATION("Use ffxConfigureDescGlobalDebug instead of ffxConfigureDescGlobalDebug1.") ffxConfigureDescGlobalDebug1
{
ffxConfigureDescHeader header;
ffxApiMessage fpMessage;
uint32_t debugLevel;
};
#if defined(__cplusplus)
}
#endif // #if defined(__cplusplus)

View File

@@ -0,0 +1,257 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2026 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 FfxApiSurfaceFormat
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
FFX_API_SURFACE_FORMAT_R32G32_UINT, ///< 32 bit per channel, 2 channel uint format
FFX_API_SURFACE_FORMAT_R8_SNORM, ///< 8 bit per channel, 1 channel signed normalized 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.
FFX_API_RESOURCE_USAGE_DCC_RENDERTARGET = (1<<15), ///< Indicates a resource that should specify optimal render target memory access flags (for console use)
};
/// 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
FFX_API_RESOURCE_STATE_DEPTH_ATTACHMENT = (1 << 9), ///< Indicates a resource is in the state to be used as depth attachment
};
/// 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 3-dimensional set of floating point coordinates.
struct FfxApiFloatCoords3D
{
float x; ///< The x coordinate of a 3-dimensional point.
float y; ///< The y coordinate of a 3-dimensional point.
float z; ///< The z coordinate of a 3-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.
};
#define FFX_RESOURCE_NAME_SIZE 64
struct FfxApiResource
{
void* resource;
struct FfxApiResourceDescription description;
uint32_t state;
};
//struct definition matches FfxApiEffectMemoryUsage
typedef struct FfxApiEffectMemoryUsage
{
uint64_t totalUsageInBytes;
uint64_t aliasableUsageInBytes;
} FfxApiEffectMemoryUsage;
/// A structure describing a constant buffer allocation.
///
/// @ingroup SDKTypes
typedef struct FfxApiConstantBufferAllocation
{
struct FfxApiResource resource; ///< The resource representing the constant buffer resource.
uint64_t handle; ///< The binding handle for the constant buffer
} FfxApiConstantBufferAllocation;
/// A function definition for a constant buffer allocation callback
/// Used to provide a constant buffer allocator to the calling backend
///
/// @param [in] data The constant buffer data.
/// @param [in] dataSize The size of the constant buffer data.
///
/// @ingroup SDKTypes
typedef FfxApiConstantBufferAllocation(*FfxApiConstantBufferAllocator)(void* data, const uint64_t dataSize);
/// A 4-component floating point vector
typedef struct FfxApiFloat4
{
float x;
float y;
float z;
float w;
} FfxApiFloat4;
/// A 4x4 matrix in row-major layout with row-vector convention.
/// This is the canonical format expected by FFX APIs.
///
/// Memory layout: rows[row_index][column_index]
/// Mathematical convention: v * M (row vector multiplied by matrix)
typedef struct FfxApiMatrix4x4
{
FfxApiFloat4 rows[4]; ///< Four rows of the matrix. Access as rows[row].x, rows[row].y etc.
} FfxApiMatrix4x4;
/// An inclusive floating-point interval [min, max].
/// Valid instances must satisfy min <= max.
typedef struct FfxApiFloatBounds
{
float min; ///< Lower bound of the interval.
float max; ///< Upper bound of the interval.
} FfxApiFloatBounds;

View File

@@ -0,0 +1,308 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2026 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
};
// We need to keep this one alive because of VK verison of FSR3.1.4
// Deprecated version of the VK backend create context descriptor, please use the non-deprecated version below.
#define FFX_API_CREATE_CONTEXT_DESC_TYPE_BACKEND_VK_DEPRECATED 0x0000003u
struct ffxCreateBackendVKDesc_DEPRECATED
{
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
};
/// Convenience structure to hold all VK-related device information.
typedef struct VkDeviceContext {
VkDevice vkDevice; ///< The Vulkan device
VkPhysicalDevice vkPhysicalDevice; ///< The Vulkan physical device
PFN_vkGetDeviceProcAddr vkDeviceProcAddr; ///< The device's function address table
} VkDeviceContext;
#define FFX_API_CREATE_CONTEXT_DESC_TYPE_BACKEND_VK FFX_API_MAKE_BACKEND_SUB_ID(FFX_API_BACKEND_ID_VK, 0x03)
struct ffxCreateBackendVKDesc
{
ffxCreateContextDescHeader header;
VkDeviceContext deviceContext; ///< the VK device information used by the program.
};
/// Resource allocation callback function. (TODO)
typedef ffxReturnCode_t(*PfnFfxResourceAllocatorFunc)(uint32_t effectId/*,
D3D12_RESOURCE_STATES initialState,
const D3D12_HEAP_PROPERTIES* pHeapProps,
const D3D12_RESOURCE_DESC* pD3DDesc,
const FfxApiResourceDescription* pFfxDesc,
const D3D12_CLEAR_VALUE* pOptimizedClear,
ID3D12Resource** ppD3DResource*/);
/// Resource destruction callback function. (TODO)
typedef ffxReturnCode_t(*PfnFfxResourceDeallocatorFunc)(uint32_t effectId/*, ID3D12Resource* pResource*/);
#define FFX_API_CREATE_CONTEXT_DESC_TYPE_BACKEND_VK_ALLOCATION_CALLBACKS FFX_API_MAKE_BACKEND_SUB_ID(FFX_API_BACKEND_ID_VK, 0x04)
struct ffxCreateBackendVKAllocationCallbacksDesc
{
ffxCreateContextDescHeader header;
PfnFfxResourceAllocatorFunc pfnFfxResourceAllocator; ///< Resource allocation function (can be null) <------- NOT IMPL YET, SEE DX12/XBOX IMPL
PfnFfxResourceDeallocatorFunc pfnFfxResourceDeallocator; ///< Resource deallocation function (can be null) <------- NOT IMPL YET, SEE DX12/XBOX IMPL
FfxApiConstantBufferAllocator pfnFfxConstantBufferAllocator; ///< Constant buffer allocation function (can be null)
};
#if defined(__cplusplus)
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_SNORM:
return FFX_API_SURFACE_FORMAT_R8_SNORM;
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;
}
#endif

View File

@@ -0,0 +1,230 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2026 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 "../../api/include/ffx_api.h"
#include "../../api/include/ffx_api_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#define FFX_UPSCALER_VERSION_MAJOR 4
#define FFX_UPSCALER_VERSION_MINOR 1
#define FFX_UPSCALER_VERSION_PATCH 2
#define FFX_UPSCALER_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch))
#define FFX_UPSCALER_VERSION FFX_UPSCALER_MAKE_VERSION(FFX_UPSCALER_VERSION_MAJOR, FFX_UPSCALER_VERSION_MINOR, FFX_UPSCALER_VERSION_PATCH)
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
FFX_UPSCALE_ENABLE_DEBUG_VISUALIZATION = (1 << 9), ///< A bit indicating if debug visualization is allowed. (memory consumption could increase)
};
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), ///< Apply tonemapping function prior to calculating reactivity.
FFX_UPSCALE_AUTOREACTIVEFLAGS_APPLY_INVERSETONEMAP = (1 << 1), ///< Apply inverse tonemapping function prior to calculating reactivity.
FFX_UPSCALE_AUTOREACTIVEFLAGS_APPLY_THRESHOLD = (1 << 2), ///< Compare to a threshold value and only record reactivity above the threshold.
FFX_UPSCALE_AUTOREACTIVEFLAGS_USE_COMPONENTS_MAX = (1 << 3), ///< When enabled use the max. component value, otherwise use the length to calculate reactivity.
};
#define FFX_API_CREATE_CONTEXT_DESC_TYPE_UPSCALE FFX_API_MAKE_EFFECT_SUB_ID(FFX_API_EFFECT_ID_UPSCALE, 0x00)
struct ffxCreateContextDescUpscale
{
ffxCreateContextDescHeader header; ///< Header descriptor, use type FFX_API_CREATE_CONTEXT_DESC_TYPE_UPSCALE.
uint32_t flags; ///< Zero or a combination of values from FfxApiCreateContextUpscaleFlags.
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 FFX_API_MAKE_EFFECT_SUB_ID(FFX_API_EFFECT_ID_UPSCALE, 0x01)
struct ffxDispatchDescUpscale
{
ffxDispatchDescHeader header; ///< Header descriptor, use type FFX_API_DISPATCH_DESC_TYPE_UPSCALE.
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 convert motion vectors to pixel space. Internally: pixelSpaceMV = motionVector * motionVectorScale. Set to renderSize if motion vectors are in UV space [0,1], or to (1.0, 1.0) if motion vectors are already in pixel space. When using display-resolution motion vectors, set to displaySize.
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 FFX_API_MAKE_EFFECT_SUB_ID(FFX_API_EFFECT_ID_UPSCALE, 0x02)
struct ffxQueryDescUpscaleGetUpscaleRatioFromQualityMode
{
ffxQueryDescHeader header; ///< Header descriptor, use type FFX_API_QUERY_DESC_TYPE_UPSCALE_GETUPSCALERATIOFROMQUALITYMODE.
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 FFX_API_MAKE_EFFECT_SUB_ID(FFX_API_EFFECT_ID_UPSCALE, 0x03)
struct ffxQueryDescUpscaleGetRenderResolutionFromQualityMode
{
ffxQueryDescHeader header; ///< Header descriptor, use type FFX_API_QUERY_DESC_TYPE_UPSCALE_GETRENDERRESOLUTIONFROMQUALITYMODE.
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 FFX_API_MAKE_EFFECT_SUB_ID(FFX_API_EFFECT_ID_UPSCALE, 0x04)
struct ffxQueryDescUpscaleGetJitterPhaseCount
{
ffxQueryDescHeader header; ///< Header descriptor, use type FFX_API_QUERY_DESC_TYPE_UPSCALE_GETJITTERPHASECOUNT.
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 FFX_API_MAKE_EFFECT_SUB_ID(FFX_API_EFFECT_ID_UPSCALE, 0x05)
struct ffxQueryDescUpscaleGetJitterOffset
{
ffxQueryDescHeader header; ///< Header descriptor, use type FFX_API_QUERY_DESC_TYPE_UPSCALE_GETJITTEROFFSET.
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 FFX_API_MAKE_EFFECT_SUB_ID(FFX_API_EFFECT_ID_UPSCALE, 0x06)
struct ffxDispatchDescUpscaleGenerateReactiveMask
{
ffxDispatchDescHeader header; ///< Header descriptor, use type FFX_API_DISPATCH_DESC_TYPE_UPSCALE_GENERATEREACTIVEMASK.
void* commandList; ///< The <c><i>FfxCommandList</i></c> to record FSRUPSCALE rendering commands into.
struct FfxApiResource colorOpaqueOnly; ///< A <c><i>FfxApiResource</i></c> containing the opaque only color buffer for the current frame (at render resolution).
struct FfxApiResource colorPreUpscale; ///< A <c><i>FfxApiResource</i></c> containing the opaque+translucent color buffer for the current frame (at render resolution).
struct FfxApiResource outReactive; ///< A <c><i>FfxApiResource</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 FFX_API_MAKE_EFFECT_SUB_ID(FFX_API_EFFECT_ID_UPSCALE, 0x07)
struct ffxConfigureDescUpscaleKeyValue
{
ffxConfigureDescHeader header; ///< Header descriptor, use type FFX_API_CONFIGURE_DESC_TYPE_UPSCALE_KEYVALUE.
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 FFX_API_MAKE_EFFECT_SUB_ID(FFX_API_EFFECT_ID_UPSCALE, 0x08)
struct ffxQueryDescUpscaleGetGPUMemoryUsage
{
ffxQueryDescHeader header; ///< Header descriptor, use type FFX_API_QUERY_DESC_TYPE_UPSCALE_GPU_MEMORY_USAGE.
struct FfxApiEffectMemoryUsage* gpuMemoryUsageUpscaler; ///< Output values by Query() call.
};
#define FFX_API_QUERY_DESC_TYPE_UPSCALE_GPU_MEMORY_USAGE_V2 FFX_API_MAKE_EFFECT_SUB_ID(FFX_API_EFFECT_ID_UPSCALE, 0x09)
struct ffxQueryDescUpscaleGetGPUMemoryUsageV2
{
ffxQueryDescHeader header; ///< Header descriptor, use type FFX_API_QUERY_DESC_TYPE_UPSCALE_GPU_MEMORY_USAGE_V2.
void* device; ///< For DX12: pointer to ID3D12Device. For VK, pointer to VkDeviceContext. App needs to fill out before Query() call.
struct FfxApiDimensions2D maxRenderSize; ///< App needs to fill out before Query() call.
struct FfxApiDimensions2D maxUpscaleSize; ///< App needs to fill out before Query() call.
uint32_t flags; ///< Zero or a combination of values from FfxApiCreateContextUpscaleFlags. App needs to fill out before Query() call.
struct FfxApiEffectMemoryUsage* gpuMemoryUsageUpscaler; ///< Output values by Query() call.
};
enum FfxApiQueryResourceIdentifiers
{
FFX_API_QUERY_RESOURCE_INPUT_COLOR = (1 << 0), ///< Color buffer for the current frame (at render resolution).
FFX_API_QUERY_RESOURCE_INPUT_DEPTH = (1 << 1), ///< 32bit depth values for the current frame (at render resolution).
FFX_API_QUERY_RESOURCE_INPUT_MV = (1 << 2), ///< 2-dimensional motion vectors (at render resolution if FFX_FSR_ENABLE_DISPLAY_RESOLUTION_MOTION_VECTORS is not set).
FFX_API_QUERY_RESOURCE_INPUT_EXPOSURE = (1 << 3), ///< A 1x1 texture containing exposure value or the FFX_UPSCALE_ENABLE_AUTO_EXPOSURE set at context creation.
FFX_API_QUERY_RESOURCE_INPUT_REACTIVEMASK = (1 << 4), ///< An R8 UNORM texture at render resolution that controls the influence of pixel history on upscaling result.
FFX_API_QUERY_RESOURCE_INPUT_TRANSPARENCYCOMPOSITION = (1 << 5), ///< An R8 UNORM texture at render resolution that controls the blending of color in the upscaling result.
};
#define FFX_API_QUERY_DESC_TYPE_UPSCALE_GET_RESOURCE_REQUIREMENTS FFX_API_MAKE_EFFECT_SUB_ID(FFX_API_EFFECT_ID_UPSCALE, 0x0a)
struct ffxQueryDescUpscaleGetResourceRequirements
{
ffxQueryDescHeader header; ///< Header descriptor, use type FFX_API_QUERY_DESC_TYPE_UPSCALE_GET_RESOURCE_REQUIREMENTS.
uint64_t required_resources; ///< resources 64b bitfield, that given current context state, are required for effect correctness.
uint64_t optional_resources; ///< resources 64b bitfield, that given current context state, will be consumed if provided, but are optional.
};
#define FFX_API_CREATE_CONTEXT_DESC_TYPE_UPSCALE_VERSION FFX_API_MAKE_EFFECT_SUB_ID(FFX_API_EFFECT_ID_UPSCALE, 0x0b)
struct ffxCreateContextDescUpscaleVersion
{
ffxCreateContextDescHeader header; ///< Header descriptor, use type FFX_API_CREATE_CONTEXT_DESC_TYPE_UPSCALE_VERSION.
uint32_t version; ///< The version of the API the application was built against. This must be set to FFX_UPSCALER_VERSION.
};
#ifdef __cplusplus
}
#endif

View File

@@ -71,6 +71,8 @@ int vk_ngxfg_wanted(void); // vk_dlaa.c: DLSS's frame generation th
int vk_ngxfg_max(void); // ... and how many frames it may generate per rendered one (0: it cannot)
int vk_ffx_available(void); // vk_ffx.cpp: FSR 3.1 upscaling can run (asked without loading the library)
int vk_ffx_framegen_available(void); // ... and FSR 3.1 frame generation
int vk_ffx4_available(void); // vk_ffx4.cpp: AMD's FSR 4 runs on this device (its libraries, card and driver)
int vk_ffx_fsr4(void); // vk_ffx.cpp: ... and the setting asks for it (NX2NAVI_FSR4)
int vk_texture_detail_auto(void); // vk_texture.c: the texture detail auto picks on this card, 0 full to 2 low
double frame_step_limit_hz(void);
int frame_limit_follows_display(void);
@@ -832,7 +834,8 @@ static void upscaling_name(char *out, size_t size) {
int mode = 0;
for (int i = 0; i < 5; i++)
if (!strcmp(cfg_value("NX2NAVI_DLSS"), modes[i])) mode = i;
if (!strcmp(cfg_value("NX2NAVI_TAA"), "fsr3")) snprintf(out, size, "AMD FSR 3.1 %s", menu_text(fsr3[mode]));
if (!strcmp(cfg_value("NX2NAVI_TAA"), "fsr3"))
snprintf(out, size, vk_ffx_fsr4() ? "AMD FSR 4 %s" : "AMD FSR 3.1 %s", menu_text(fsr3[mode]));
else snprintf(out, size, mode ? "DLSS %s" : "NVIDIA %s", menu_text(dlss[mode]));
}
@@ -847,20 +850,22 @@ static void row_upscaling(void) {
// 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};
enum { BILINEAR, FSR1, DLSS, FSR3 };
// FSR 4 (vk_ffx4.cpp) is the same setting as FSR 3.1 with NX2NAVI_FSR4 on, and takes the same modes: listed where
// it runs on this device, after FSR 3.1's
enum { BILINEAR, FSR1, DLSS, FSR3, FSR4 };
struct Entry { int kind, mode; };
static char text[10][96];
Entry list[12];
const char *names[12];
static char text[15][96];
Entry list[18];
const char *names[18];
int n = 0;
auto add = [&](int kind, int mode) {
list[n] = {kind, mode};
if (kind == BILINEAR) names[n] = menu_text(UI_MENU_UPSCALER_BILINEAR);
else if (kind == FSR1) names[n] = menu_text(UI_MENU_UPSCALER_FSR);
else {
char *t = text[(kind == FSR3 ? 5 : 0) + mode];
char *t = text[(kind == FSR4 ? 10 : kind == FSR3 ? 5 : 0) + mode];
if (kind == DLSS) snprintf(t, sizeof text[0], mode ? "DLSS %s" : "NVIDIA %s", menu_text(mode_names[mode]));
else snprintf(t, sizeof text[0], "AMD FSR 3.1 %s", menu_text(fsr_names[mode]));
else snprintf(t, sizeof text[0], kind == FSR4 ? "AMD FSR 4 %s" : "AMD FSR 3.1 %s", menu_text(fsr_names[mode]));
names[n] = t;
}
n++;
@@ -868,15 +873,16 @@ static void row_upscaling(void) {
// what is set now
Entry now = {strcmp(cfg_value("NX2NAVI_UPSCALER"), "fsr") ? BILINEAR : FSR1, 0};
if (cfg_bool("NX2NAVI_DLAA")) {
now = {strcmp(cfg_value("NX2NAVI_TAA"), "fsr3") ? DLSS : FSR3, 0};
for (int i = 0; i < 5; i++)
now = {strcmp(cfg_value("NX2NAVI_TAA"), "fsr3") ? DLSS : vk_ffx_fsr4() ? FSR4 : FSR3, 0}; // set where it does
for (int i = 0; i < 5; i++) // not run: FSR 3.1 does
if (!strcmp(cfg_value("NX2NAVI_DLSS"), modes[i])) now.mode = i;
}
const bool dlss = dlss_offered(), fsr = vk_ffx_available();
const bool dlss = dlss_offered(), fsr = vk_ffx_available(), fsr4 = fsr && vk_ffx4_available();
add(BILINEAR, 0);
add(FSR1, 0);
for (int i = 0; dlss && i < 5; i++) add(DLSS, i);
for (int i = 0; fsr && i < 5; i++) add(FSR3, i);
for (int i = 0; fsr4 && i < 5; i++) add(FSR4, i);
if (!dlss && now.kind == DLSS) add(DLSS, now.mode);
if (!fsr && now.kind == FSR3) add(FSR3, now.mode);
int cur = 0;
@@ -895,7 +901,8 @@ static void row_upscaling(void) {
opt_apply("NX2NAVI_DLAA", e.kind >= DLSS ? "1" : "0");
if (e.kind < DLSS) opt_apply("NX2NAVI_FRAMEGEN", "off"); // it needs DLSS's or FSR 3.1's motion vectors (row_framegen)
if (e.kind >= DLSS) opt_apply("NX2NAVI_DLSS", modes[e.mode]);
if (e.kind >= DLSS) opt_apply("NX2NAVI_TAA", e.kind == FSR3 ? "fsr3" : "dlss");
if (e.kind >= DLSS) opt_apply("NX2NAVI_TAA", e.kind >= FSR3 ? "fsr3" : "dlss");
if (e.kind >= FSR3) opt_apply("NX2NAVI_FSR4", e.kind == FSR4 ? "1" : "0");
}
// Under the HDR value: "after restart" when what is set waits for one (the game's own HDR, to or from it:

View File

@@ -191,9 +191,27 @@ extern "C" int vk_ffx_available(void) {
extern "C" int vk_ffx_framegen_available(void) { return vk_ffx_available(); } // the same library, its API calls
// NX2NAVI_FSR4 (default 0, live): FSR 4 where it runs (vk_ffx4.cpp), else FSR 3.1.
extern "C" int vk_ffx_fsr4(void) { return cfg_bool("NX2NAVI_FSR4") && vk_ffx4_available(); }
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;
// Each upscaler keeps a history of its own: the frame after a change from one to the other starts it anew, or it
// would blend this picture with whatever that upscaler last saw.
static int last; // 3 or 4: which one made the frame before
if (vk_ffx_fsr4()) {
if (vk_ffx4_upscale(cb, color, depth, mv, out, jx, jy, reset || last != 4, dt_ms, fov_y)) {
if (last != 4) rt_log("ffx: the upscaler is %s", vk_ffx4_name());
last = 4;
return 1;
}
// it failed and said so: FSR 3.1 makes this frame
}
if (last != 3) {
if (last) rt_log("ffx: the upscaler is AMD FSR 3.1");
reset = 1, last = 3;
}
Up *u = context_for(color->width, color->height, out->width, out->height);
if (!u) return 0;
ffxDispatchDescUpscale d = {};
@@ -243,6 +261,7 @@ extern "C" ffxReturnCode_t vk_ffx_dispatch(ffxContext *ctx, const ffxDispatchDes
}
extern "C" void vk_ffx_release(void) {
vk_ffx4_release();
if (!A.ok) return;
for (Up &u : U)
if (u.ctx) A.destroy(&u.ctx, nullptr), u.ctx = nullptr;

View File

@@ -23,7 +23,18 @@ int vk_ffx_upscale(VkCommandBuffer cb, const VkFfxImage *color, const VkFfxImage
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
#ifdef __cplusplus
// AMD FSR 4 (vk_ffx4.cpp): the ML upscaler of AMD's FSR SDK 2.4, through amd_fidelityfx_loader_vk.dll and
// amd_fidelityfx_upscaler_vk.dll beside the executable. vk_ffx_upscale runs it in FSR 3.1's place when the setting
// asks (NX2NAVI_FSR4) and it runs here, and falls back to FSR 3.1 when it fails.
int vk_ffx4_available(void); // the libraries are there and list a 4.x version for the device; asked once, with the
// device, from any thread (0 before the device exists)
const char *vk_ffx4_name(void); // "AMD FSR 4.1.2" once vk_ffx4_available found it
int vk_ffx4_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); // as vk_ffx_upscale
void vk_ffx4_release(void);
int vk_ffx_fsr4(void); // the setting asks for FSR 4 and it runs here: what vk_ffx_upscale uses, for the menu
#if defined(__cplusplus) && !defined(VK_FFX_NO_API)
// The API's calls themselves, for vk_fg.cpp (0 = FFX_API_RETURN_OK): the library loaded first (vk_ffx_load).
#include "third_party/ffx_api/ffx_api.h"
extern "C" {

318
runtime/vk_ffx4.cpp Normal file
View File

@@ -0,0 +1,318 @@
// AMD FSR 4 (FSR Upscaling 4.x, the machine-learned upscaler) through the libraries of AMD's FSR SDK 2.4:
// amd_fidelityfx_loader_vk.dll and amd_fidelityfx_upscaler_vk.dll next to the executable, loaded at run time. The SDK's
// headers are in third_party/ffx_sdk (MIT), at the paths they have in the SDK. FSR 3.1 and frame generation stay with
// vk_ffx.cpp and the library of SDK 1.1.4: the two sets of headers name the same types with other layouts, so nothing
// of the API crosses between the two files, and vk_ffx.cpp's vk_ffx_upscale calls in here first.
//
// The loader answers which upscaler versions it can run on a device (ffxQueryDescGetVersions with the device given):
// 4.1.2, 3.1.6 and 2.3.4 on a Radeon the ML upscaler supports (RX 6000, 7000 and 9000 cards and some Ryzen APUs, with
// AMD's driver 26.10.1 or newer on Windows), 3.1.6 and 2.3.4 on any other. FSR 4 runs only where a 4.x version is
// listed, and its contexts are made with that version asked for by its id, so the loader cannot hand out another.
// Where it is not listed, or a context or a dispatch fails, vk_ffx.cpp's FSR 3.1 serves the frame as before.
//
// The inputs are the ones FSR 3.1 takes (vk_ffx.cpp): the scene's light target in linear HDR, its D32F depth (not
// inverted, the far plane at infinity) and the camera motion vectors in render pixels from this frame to the previous
// one. FSR 4 needs no reactive or transparency mask.
#define VK_FFX_NO_API // vk_ffx.h: without the API declarations of the other set of headers
#ifndef _WIN32
// AMD publishes the SDK's libraries for Windows only: in any other build FSR 4 is never listed, vk_ffx.cpp's FSR 3.1
// path is all there is, and the SDK's headers are not read.
extern "C" {
#include "vk.h"
#include "vk_ffx.h"
}
extern "C" int vk_ffx4_available(void) { return 0; }
extern "C" const char *vk_ffx4_name(void) { return "AMD FSR 4"; }
extern "C" int vk_ffx4_upscale(VkCommandBuffer, const VkFfxImage *, const VkFfxImage *, const VkFfxImage *, const VkFfxImage *,
float, float, int, float, float) {
return 0;
}
extern "C" void vk_ffx4_release(void) {}
#else
#include <windows.h>
#include <atomic>
#include <mutex>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "third_party/ffx_sdk/api/include/ffx_api.h"
#include "third_party/ffx_sdk/api/include/vk/ffx_api_vk.h"
#include "third_party/ffx_sdk/upscalers/include/ffx_upscale.h"
extern "C" {
#include "cfg.h"
#include "host.h"
#include "platform/platform.h"
#include "vk.h"
#include "vk_ffx.h"
}
namespace {
struct Api {
PfnFfxCreateContext create = nullptr;
PfnFfxDestroyContext destroy = nullptr;
PfnFfxConfigure configure = nullptr;
PfnFfxQuery query = nullptr;
PfnFfxDispatch dispatch = nullptr;
} A;
std::mutex g_lock; // the probe: the menu asks from its thread, the replay from its own
std::atomic<int> g_state{-1}; // -1 not probed; 0 FSR 4 does not run here; 1 it does
uint64_t g_version; // the id of the 4.x version the loader listed for the device
char g_name[48] = "AMD FSR 4"; // "AMD FSR 4.1.2", for the menu and the log
int g_failures; // contexts or dispatches that failed; past FAIL_MAX FSR 4 is off for the run
const int FAIL_MAX = 3;
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("ffx4: %s %s", type == FFX_API_MESSAGE_TYPE_ERROR ? "error:" : "warning:", s);
}
// a library of the SDK next to the executable; 0 when the path does not fit
int beside_exe(const char *name, char *path, size_t size) {
DWORD n = GetModuleFileNameA(NULL, path, (DWORD)size);
while (n && path[n - 1] != '\\') n--;
return snprintf(path + n, size - n, "%s", name) < (int)(size - n);
}
int file_there(const char *path) {
DWORD attr = GetFileAttributesA(path);
return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY);
}
// Loads the loader and asks it for the device's upscaler versions. Under g_lock, once the device exists.
int probe(void) {
const VkCtx *c = vk_ctx();
if (!c || !c->device) return -1; // asked before the device: again later
char loader[MAX_PATH], upscaler[MAX_PATH];
if (!beside_exe("amd_fidelityfx_loader_vk.dll", loader, sizeof loader) ||
!beside_exe("amd_fidelityfx_upscaler_vk.dll", upscaler, sizeof upscaler) || !file_there(loader) ||
!file_there(upscaler))
return 0; // not shipped beside this build: nothing is said, FSR 3.1 is what the menu offers
HMODULE m = LoadLibraryA(loader);
if (!m) {
rt_log("ffx4: %s does not load (error %lu): FSR 4 off", loader, (unsigned long)GetLastError());
return 0;
}
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");
if (!A.create || !A.destroy || !A.configure || !A.query || !A.dispatch) {
rt_log("ffx4: amd_fidelityfx_loader_vk.dll lacks the API entry points: FSR 4 off");
return 0;
}
VkDeviceContext dc = {c->device, c->phys, vkGetDeviceProcAddr};
uint64_t ids[16] = {}, n = 0;
const char *names[16] = {};
ffxQueryDescGetVersions q = {};
q.header.type = FFX_API_QUERY_DESC_TYPE_GET_VERSIONS;
q.createDescType = FFX_API_CREATE_CONTEXT_DESC_TYPE_UPSCALE;
q.device = &dc;
q.outputCount = &n;
ffxReturnCode_t r = A.query(nullptr, &q.header); // the count
if (r == FFX_API_RETURN_OK && n) {
if (n > 16) n = 16;
q.versionIds = ids;
q.versionNames = names;
r = A.query(nullptr, &q.header);
}
if (r != FFX_API_RETURN_OK || !n) {
rt_log("ffx4: the FSR SDK's loader lists no upscaler for this device (%u): FSR 4 off", (unsigned)r);
return 0;
}
char list[200] = "";
size_t len = 0;
int best = -1, best_major = 0;
for (uint64_t i = 0; i < n; i++) {
const char *nm = names[i] ? names[i] : "?";
len += (size_t)snprintf(list + len, sizeof list - len, "%s%s", i ? ", " : "", nm);
if (len >= sizeof list) len = sizeof list - 1;
int major = atoi(nm); // "4.1.2", and "4.1.2 *" for a version the driver brings
if (major >= 4 && major > best_major) best = (int)i, best_major = major;
}
if (best < 0) {
rt_log("ffx4: upscaler versions for this device: %s; none is FSR 4 (it needs a Radeon RX 6000, 7000 or 9000 card "
"or a supported Ryzen APU, and AMD's driver 26.10.1 or newer): FSR 3.1 stays", list);
return 0;
}
g_version = ids[best];
snprintf(g_name, sizeof g_name, "AMD FSR %.24s", names[best]); // the name is the library's memory: copied
for (char *p = g_name; *p; p++)
if (*p == '*') *p = 0; // the driver's mark
for (size_t k = strlen(g_name); k && g_name[k - 1] == ' '; k--) g_name[k - 1] = 0;
rt_log("ffx4: upscaler versions for this device: %s; FSR 4 is %s (id %#llx)", list, g_name, (unsigned long long)g_version);
return 1;
}
// One context per render and output size, as vk_ffx.cpp keeps them for FSR 3.1; the descriptors a context was made
// from stay with it, as the API asks ("must remain live until ffxDestroyContext").
struct Up {
ffxContext ctx = nullptr;
uint32_t w = 0, h = 0, ow = 0, oh = 0;
uint64_t used = 0;
ffxCreateContextDescUpscale desc = {};
ffxCreateBackendVKDesc backend = {};
ffxOverrideVersion version = {};
ffxCreateContextDescUpscaleVersion built = {};
} U[6];
uint64_t up_tick;
const uint64_t UP_RECENT = 8; // contexts used this recently may sit in a command buffer not submitted yet
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) {
up_tick++;
for (Up &u : U)
if (u.ctx && u.w == w && u.h == h && u.ow == ow && u.oh == oh) {
u.used = up_tick;
return &u;
}
Up *slot = nullptr, *lru = nullptr;
for (Up &u : U) {
if (!u.ctx) {
slot = &u;
break;
}
if (up_tick - u.used >= UP_RECENT && (!lru || u.used < lru->used)) lru = &u;
}
const VkCtx *c = vk_ctx();
if (!slot && lru) {
vk_queue_lock(), vkDeviceWaitIdle(c->device), vk_queue_unlock(); // its work may still be in flight
A.destroy(&lru->ctx, nullptr);
*lru = Up();
slot = lru;
}
if (!slot) return nullptr;
*slot = Up();
slot->built.header.type = FFX_API_CREATE_CONTEXT_DESC_TYPE_UPSCALE_VERSION;
slot->built.version = FFX_UPSCALER_VERSION;
slot->version.header.type = FFX_API_DESC_TYPE_OVERRIDE_VERSION;
slot->version.header.pNext = &slot->built.header;
slot->version.versionId = g_version;
slot->backend.header.type = FFX_API_CREATE_CONTEXT_DESC_TYPE_BACKEND_VK;
slot->backend.header.pNext = &slot->version.header;
slot->backend.deviceContext = {c->device, c->phys, vkGetDeviceProcAddr};
slot->desc.header.type = FFX_API_CREATE_CONTEXT_DESC_TYPE_UPSCALE;
slot->desc.header.pNext = &slot->backend.header;
slot->desc.flags =
FFX_UPSCALE_ENABLE_HIGH_DYNAMIC_RANGE | FFX_UPSCALE_ENABLE_DEPTH_INFINITE | FFX_UPSCALE_ENABLE_AUTO_EXPOSURE;
slot->desc.maxRenderSize = {w, h};
slot->desc.maxUpscaleSize = {ow, oh};
slot->desc.fpMessage = message;
uint64_t t0 = plat_now_ns();
ffxReturnCode_t r = A.create(&slot->ctx, &slot->desc.header, nullptr);
if (r != FFX_API_RETURN_OK || !slot->ctx) {
rt_log("ffx4: %s context creation at %ux%u -> %ux%u failed (%u)", g_name, w, h, ow, oh, (unsigned)r);
*slot = Up();
return nullptr;
}
slot->w = w, slot->h = h, slot->ow = ow, slot->oh = oh, slot->used = up_tick;
ffxQueryGetProviderVersion v = {};
v.header.type = FFX_API_QUERY_DESC_TYPE_GET_PROVIDER_VERSION;
A.query(&slot->ctx, &v.header);
FfxApiEffectMemoryUsage mem = {};
ffxQueryDescUpscaleGetGPUMemoryUsage mq = {};
mq.header.type = FFX_API_QUERY_DESC_TYPE_UPSCALE_GPU_MEMORY_USAGE;
mq.gpuMemoryUsageUpscaler = &mem;
A.query(&slot->ctx, &mq.header);
rt_log("ffx4: FSR %s upscaling at %ux%u -> %ux%u, made in %.0f ms, %.0f MiB of video memory",
v.versionName ? v.versionName : "4", w, h, ow, oh, (double)(plat_now_ns() - t0) / 1e6,
(double)mem.totalUsageInBytes / 1048576.0);
// The provider is asked for the version it runs: anything but a 4.x here means the loader gave another upscaler
if (v.versionName && atoi(v.versionName) < 4) {
rt_log("ffx4: the context runs FSR %s, not FSR 4: FSR 4 off", v.versionName);
A.destroy(&slot->ctx, nullptr);
*slot = Up();
return nullptr;
}
return slot;
}
void failed(const char *what, unsigned code) {
if (++g_failures == FAIL_MAX) {
rt_log("ffx4: %s failed %d times (last %u): FSR 4 is off for this run, FSR 3.1 takes its place", what, FAIL_MAX, code);
g_state.store(0, std::memory_order_release);
}
}
} // namespace
extern "C" int vk_ffx4_available(void) {
int s = g_state.load(std::memory_order_acquire);
if (s >= 0) return s;
std::lock_guard<std::mutex> hold(g_lock);
s = g_state.load(std::memory_order_acquire);
if (s >= 0) return s;
s = probe();
if (s >= 0) g_state.store(s, std::memory_order_release);
return s > 0;
}
extern "C" const char *vk_ffx4_name(void) { return g_name; }
extern "C" int vk_ffx4_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_ffx4_available()) return 0;
Up *u = context_for(color->width, color->height, out->width, out->height);
if (!u) {
failed("making a context", 0);
return 0;
}
ffxDispatchDescUpscale d = {};
d.header.type = FFX_API_DISPATCH_DESC_TYPE_UPSCALE;
d.commandList = cb;
// Every image is in GENERAL, which the back end 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};
int sharp = cfg_int("NX2NAVI_SHARPNESS"); // the slider drives the upscaler's own sharpening, as for FSR 3.1
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("ffx4: %s dispatch failed (%u)", g_name, (unsigned)r);
failed("a dispatch", (unsigned)r);
return 0;
}
return 1;
}
extern "C" void vk_ffx4_release(void) {
if (!A.destroy) return;
for (Up &u : U)
if (u.ctx) A.destroy(&u.ctx, nullptr), u = Up();
}
#endif // _WIN32

View File

@@ -126,6 +126,7 @@ XLATE_NAME = 'nx2navi_xlate.exe' if WIN else 'nx2navi_xlate'
# through a runtime header, is compiled on the player's PC; every other runtime source can come prebuilt.
GENERATED_HEADERS = ('layout.h', 'patches.h', 'romfs_config.h')
RUNTIME_LIB = 'libnx2navirt.a'
FFX4_DLLS = ('amd_fidelityfx_loader_vk.dll', 'amd_fidelityfx_upscaler_vk.dll') # AMD's FSR SDK 2.4, for FSR 4
RUNTIME_PATTERNS = ('*.c', '*.cpp', 'platform/*.c', 'ui/*.cpp', 'third_party/imgui/*.cpp',
'third_party/imgui/backends/*.cpp')
INCLUDE = re.compile(r'^[ \t]*#[ \t]*include[ \t]+"([^"]+)"', re.M)
@@ -1358,6 +1359,9 @@ def step_compile(out, cache_dir, msys, jobs, romfs_job=None, update=False):
copy_if_changed(xlate / name, out / name)
if (KIT / 'bin' / 'amd_fidelityfx_vk.dll').is_file(): # FSR 3.1 (runtime/vk_ffx.cpp)
copy_if_changed(KIT / 'bin' / 'amd_fidelityfx_vk.dll', out / 'amd_fidelityfx_vk.dll')
for name in FFX4_DLLS: # FSR 4 (runtime/vk_ffx4.cpp): both or neither
if all((KIT / 'bin' / n).is_file() for n in FFX4_DLLS):
copy_if_changed(KIT / 'bin' / name, out / name)
kept = place_dlss(out, linux=not WIN) # NVIDIA DLSS (runtime/vk_dlaa.c): this platform's library from the kit
if kept:
say(f' {kept}')

View File

@@ -1,6 +1,7 @@
"""Assemble the build kit: what a player needs to build Ocarina of Time on their own PC with tools/make_game.py.
python tools/make_kit.py --xlate <dir with nx2navi_xlate.exe and .id> [--ffx amd_fidelityfx_vk.dll]
[--ffx4 FOLDER]
[--dlss nvngx_dlss.dll | --no-dlss] [--dlssg nx2navi_dlssg.dll [--streamline SDK]]
[--prompt-art FOLDER [FOLDER ...]] [--out build/kit] [--zip]
@@ -10,9 +11,9 @@ translator built from tools/xlate,
NVIDIA's DLSS library with its notice (tools/dlss_pin.py: downloaded from NVIDIA's DLSS SDK repository and checked,
or --dlss; not with --no-dlss), DLSS Frame Generation in bin/dlssg/ with --dlssg (nx2navi_dlssg.dll from
tools/build_dlssg.py beside NVIDIA Streamline's runtime, tools/streamline_pin.py: downloaded from NVIDIA's Streamline
SDK release and checked, or --streamline), AMD's FSR 3.1 library with --ffx, the button art with --prompt-art, and
README-BuildKit.md; no game code, data or anything generated from the game. check_content runs over the folder before
it is zipped.
SDK release and checked, or --streamline), AMD's FSR 3.1 library with --ffx, its two FSR 4 libraries with --ffx4, the
button art with --prompt-art, and README-BuildKit.md; no game code, data or anything generated from the game.
check_content runs over the folder before it is zipped.
--prompt-art names folders that hold XGamepad, P5Gamepad and Keyboard_Mouse, or those three folders themselves: the
pictures tools/pad_prompts.py reads go into the kit's prompt-art folder (tools/prompt_art.py art_files), and
@@ -50,6 +51,8 @@ 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('--ffx4', type=Path, help="the folder with AMD's signed amd_fidelityfx_loader_vk.dll and "
'amd_fidelityfx_upscaler_vk.dll (FSR SDK 2.4.0, MIT) for FSR 4')
ap.add_argument('--dlss', type=Path, help="NVIDIA's nvngx_dlss.dll from disk instead of downloading it from "
"NVIDIA's DLSS SDK repository")
ap.add_argument('--no-dlss', action='store_true', help='leave NVIDIA DLSS out of the kit')
@@ -92,6 +95,9 @@ def main():
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')
if a.ffx4: # FSR 4 loads them from beside the game (runtime/vk_ffx4.cpp); their licence is runtime/third_party/ffx_sdk
for n in ('amd_fidelityfx_loader_vk.dll', 'amd_fidelityfx_upscaler_vk.dll'):
shutil.copy2(a.ffx4 / n, kit / 'bin' / n)
if dlss: # DLSS: NGX loads it from beside the game (runtime/vk_dlaa.c); make_game.py copies it there
dlss_pin.place(dlss, kit / 'bin')
if sdk: # DLSS Frame Generation (runtime/vk_dlssg.c): make_game.py place_dlssg copies it beside the game

View File

@@ -1,6 +1,7 @@
"""Build a release of the NX2navi launcher (docs/launcher.md, "Releases").
python tools/make_launcher.py --xlate build/xlate [--ffx amd_fidelityfx_vk.dll] [--dlss nvngx_dlss.dll | --no-dlss]
[--ffx4 FOLDER]
[--prompt-art FOLDER [FOLDER ...]] [--out build/launcher-release]
[--feed owner/name]
[--notes notes.md] [--publish]
@@ -17,7 +18,8 @@ The release folder (--out) holds:
python/ python.org's embeddable Python 3.14.9, tkinter and Tcl/Tk 9.0 from its tcltk.msi,
the packages of tools/requirements-launcher.txt, runtime-id.txt
app/current.txt, app/<version>/ launcher/ (without native/), runtime/, recomp/, the tools make_game.py uses,
bin/nx2navi_xlate.exe and .id (and amd_fidelityfx_vk.dll with --ffx),
bin/nx2navi_xlate.exe and .id (and amd_fidelityfx_vk.dll with --ffx, AMD's two
FSR 4 libraries with --ffx4),
bin/nvngx_dlss.dll and its notice (NVIDIA DLSS at the version tools/dlss_pin.py
pins, downloaded from NVIDIA's DLSS SDK repository and checked, or --dlss; not with
--no-dlss),
@@ -177,7 +179,7 @@ def build_python(py):
# ---------------------------------------------------------------- app/<version>/
def build_app(app, ver, xlate, feed_repo, lock, ffx=None, dlss=None, art=None, dlssg=None):
def build_app(app, ver, xlate, feed_repo, lock, ffx=None, dlss=None, art=None, dlssg=None, ffx4=None):
# The runtime's own shaders are source in the repository and SPIR-V headers in a release: a player's PC compiles
# runtime/ (make_game.py) and has no shader compiler. Build the missing and the stale ones before the copy.
build_shaders.build(ROOT / 'runtime')
@@ -198,6 +200,9 @@ def build_app(app, ver, xlate, feed_repo, lock, ffx=None, dlss=None, art=None, d
shutil.copy2(xlate / n, app / 'bin' / n)
if ffx: # FSR 3.1 loads it from beside the game (runtime/vk_ffx.cpp); make_game.py copies it there
shutil.copy2(ffx, app / 'bin' / 'amd_fidelityfx_vk.dll')
if ffx4: # FSR 4 loads them from beside the game (runtime/vk_ffx4.cpp); make_game.py copies them there
for n in ('amd_fidelityfx_loader_vk.dll', 'amd_fidelityfx_upscaler_vk.dll'):
shutil.copy2(ffx4 / n, app / 'bin' / n)
if dlss: # DLSS: NGX loads it from beside the game (runtime/vk_dlaa.c); make_game.py copies it there
dlss_pin.place(dlss, app / 'bin')
if dlssg: # DLSS Frame Generation (runtime/vk_dlssg.c): make_game.py place_dlssg copies it beside the game
@@ -314,6 +319,8 @@ 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('--ffx4', type=Path, help="the folder with AMD's signed amd_fidelityfx_loader_vk.dll and "
'amd_fidelityfx_upscaler_vk.dll (FSR SDK 2.4.0, MIT) for FSR 4')
ap.add_argument('--dlss', type=Path, help=f"NVIDIA's nvngx_dlss.dll (DLSS {dlss_pin.VERSION}) from disk instead of "
"downloading it from NVIDIA's DLSS SDK repository")
ap.add_argument('--no-dlss', action='store_true', help='leave NVIDIA DLSS out of the release')
@@ -356,7 +363,7 @@ def main():
dlssg = (a.dlssg, streamline_pin.resolve(a.streamline, DL)) if a.dlssg else None
say(f'DLSS Frame Generation: {a.dlssg}, Streamline {streamline_pin.VERSION}: {dlssg[1]}' if dlssg
else 'DLSS Frame Generation left out (no --dlssg)')
build_app(folder / 'app' / ver, ver, a.xlate, a.feed, lock, a.ffx, dlss, a.prompt_art, dlssg)
build_app(folder / 'app' / ver, ver, a.xlate, a.feed, lock, a.ffx, dlss, a.prompt_art, dlssg, a.ffx4)
(folder / 'app' / 'current.txt').write_text(ver + '\n', encoding='utf-8')
build_python(folder / 'python')
cc, windres = native_compiler(lock)