Migrating from FSR 3.0 to FSR 3.1
The new API for FSR 3.1 changes how applications interact with FSR in a few ways:
-
Upscaling and frame generation are separated.
-
Custom backends are no longer supported.
-
Backends are created in tandem with the effect context and their lifetimes are tied together.
-
All calls are done using ABI-stable structs with support for future extensions.
See ../docs/getting-started/ffx-api.md “Introduction to FidelityFX API” for more detailed information about the new API.
API setup
Setup your build system to include headers in ffx-api/include and to link against one of the amd_fidelityfx_dx12 or amd_fidelityfx_vk libraries.
In source files, include ffx_api/ffx_api.h
for the C api or ffx_api/ffx_api.hpp
for C++ convenience utilities.
For upscaling, include ffx_api/ffx_fsr_upscale.h
(or .hpp
) and for frame generation, include ffx_api/ffx_fg.h
(or .hpp
).
For backend creation and the frame generation swapchain, you will also need one of ffx_api/dx12/ffx_api_dx12.h
or ffx_api/vk/ffx_api_vk.h
(or .hpp
).
Context creation
Instead of calling ffxFsr3ContextCreate
, call ffxCreateContext
.
ffxCreateContextDescFsrUpscale
takes a subset of parameters from FfxFsr3ContextDescription
, except that flags now are prefixed FFX_FSR_
instead of FFX_FSR3_
.
A backend creation description must be passed in the pNext
field of the header
in ffxCreateContextDescFsrUpscale
. This can be either a ffxCreateBackendDX12Desc
or a ffxCreateBackendVKDesc
.
The type
field on the header
of each structure must be set to the associated type id. In the C headers, these are adjacent to their associated types. When using the C++ namespaced types, structure types are initialized by the constructor.
For frame generation, a separate context has to be created, as well as one for the frame generation swapchain (backend specific).
For memory allocation, a set of callbacks can be passed. If set to NULL
, the system malloc
and free
are used.
Querying Information from FSR
The functions ffxFsr3GetUpscaleRatioFromQualityMode
, ffxFsr3GetRenderResolutionFromQualityMode
, ffxFsr3GetJitterPhaseCount
and ffxFsr3GetJitterOffset
are replaced with structs passed to the ffxQuery
function. Pointer parameters to these query functions are struct members in the new API.
Configuring FSR
ffxFsr3ConfigureFrameGeneration
is replaced by ffxConfigure
with the frame generation context. The configuration structure is nearly identical. A new optional callback context has been added. See the comparison table for details.
Dispatching FSR
All dispatches are done using the ffxDispatch
function.
Resources are passed in a new struct FfxApiResource
, which is very similar to FfxResource
, but with more platform stability. With DirectX, a helper function ffxApiGetResourceDX12
can be used to fill the resource description.
When using frame generation, a new preparation dispatch must be called every frame. This can be placed at the same location as the upscaling dispatch and takes a subset of the upscaling resources.
Comparison
The following section shows FSR 3.0 code patterns and their FSR 3.1 equivalents.
Context names
FSR 3.0
FfxFsr3Context fsr3Context;
FSR 3.1 / FFX API C
FFX API C++
Context creation (upscaling only)
FSR 3.0
FfxFsr3ContextDescription fsr3ContextDesc = {0};
// create backend interface ...
// fill fsr3ContextDesc ...
fsr3ContextDesc.flags |= FFX_FSR3_ENABLE_UPSCALING_ONLY;
ffxFsr3ContextCreate(&fsr3Context, &fsr3ContextDesc);
FSR 3.1 / FFX API C
ffxCreateContextDescFsrUpscale fsrContextDesc = {0};
fsrContextDesc.header.type = FFX_API_CREATE_CONTEXT_DESC_TYPE_FSR_UPSCALE;
// fill fsrContextDesc ...
// backend interface desc ...
fsrContextDesc.header.pNext = &backendDesc.header;
ffxCreateContext(&upscalingContext, &fsrContextDesc.header, NULL);
FFX API C++
DX12 backend creation
FSR 3.0
FfxFsr3ContextDescription fsr3ContextDesc = {0};
// create backend interface (DX12)
size_t scratchBufferSize = ffxGetScratchMemorySizeDX12 (1);
void* scratchBuffer = malloc(scrathBufferSize);
memset(scratchBuffer, 0, scrathBufferSize);
errorCode = ffxGetInterfaceDX12 (&fsr3ContextDesc.backendInterfaceUpscaling , ffxGetDeviceDX12 (dx12Device), scratchBuffer, scratchBufferSize, 1);
assert(errorCode == FFX_OK );
FSR 3.1 / FFX API C
ffxCreateContextDescFsrUpscale fsrContextDesc = {0};
// fill fsrContextDesc ...
// backend interface desc (dx12)
ffxCreateBackendDX12Desc backendDesc = {0};
backendDesc.header.type = FFX_API_CREATE_CONTEXT_DESC_TYPE_BACKEND_DX12;
backendDesc.device = dx12Device;
fsrContextDesc.header.pNext = &backendDesc.header;
// create context and backend
ffxCreateContext(&upscalingContext, &fsrContextDesc.header, NULL);
FFX API C++
Context creation (upscale + framegen)
FSR 3.0
FfxFsr3ContextDescription fsr3ContextDesc = {0};
// create backend interface (x3) ...
// fill fsr3ContextDesc ...
ffxFsr3ContextCreate(&fsr3Context, &fsr3ContextDesc);
FSR 3.1 / FFX API C
ffxCreateContextDescFsrUpscale fsrContextDesc = {0};
fsrContextDesc.header.type = FFX_API_CREATE_CONTEXT_DESC_TYPE_FSR_UPSCALE;
// fill fsrContextDesc ...
// backend interface desc ...
fsrContextDesc.header.pNext = &backendDesc.header;
ffxCreateContext(&upscalingContext, &fsrContextDesc.header, NULL);
ffxCreateContextDescFsrFrameGeneneration fgContextDesc = {0};
// fill fgContextDesc ...
// backend interface desc ...
fgContextDesc.header.pNext = &backendDesc.header;
ffxCreateContext(&frameGenContext, &fgContextDesc.header, NULL);
FFX API C++
ffx::CreateContextDescFsrUpscale fsrContextDesc{};
// fill fsrContextDesc ...
// backend interface desc ...
ffx::CreateContext(&upscalingContext, nullptr, fsrContextDesc, backendDesc);
ffx::CreateContextDescFsrFrameGen fgContextDesc{};
// fill fgContextDesc ...
// backend interface desc ...
ffx::CreateContext(&frameGenContext, nullptr, fgContextDesc, backendDesc);
Query upscale ratio / render resolution
FSR 3.0
FSR 3.1 / FFX API C
float upscaleRatio;
struct ffxQueryDescFsrGetUpscaleRatioFromQualityMode queryDesc1 = {0};
queryDesc1.header.type = FFX_API_QUERY_DESC_TYPE_FSR_GETUPSCALERATIOFROMQUALITYMODE;
queryDesc1.qualityMode = FFX_FSR_QUALITY_MODE_BALANCED;
queryDesc1.pOutUpscaleRatio = &upscaleRatio;
ffxQuery(&fsrContext, &queryDesc1.header);
<!-- -->
uint32_t renderWidth, renderHeight;
struct ffxQueryDescFsrGetRenderResolutionFromQualityMode queryDesc2 = {0};
queryDesc2.header.type = FFX_API_QUERY_DESC_TYPE_FSR_GETRENDERRESOLUTIONFROMQUALITYMODE;
queryDesc2.displayWidth = displayWidth;
queryDesc2.displayHeight = displayHeight;
queryDesc2.qualityMode = FFX_FSR_QUALITY_MODE_BALANCED;
queryDesc2.pOutRenderWidth = &renderWidth;
queryDesc2.pOutRenderHeight = &renderHeight;
ffxQuery(&fsrContext, &queryDesc2.header);
FFX API C++
float upscaleRatio;
ffx::QueryDescFsrGetUpscaleRatioFromQualityMode queryDesc1{};
queryDesc1.qualityMode = FFX_FSR_QUALITY_MODE_BALANCED;
queryDesc1.pOutUpscaleRatio = &upscaleRatio;
ffx::Query(fsrContext, queryDesc1);
<!-- -->
uint32_t renderWidth, renderHeight;
ffx::QueryDescFsrGetRenderResolutionFromQualityMode queryDesc2{};
queryDesc2.displayWidth = displayWidth;
queryDesc2.displayHeight = displayHeight;
queryDesc2.qualityMode = FFX_FSR_QUALITY_MODE_BALANCED;
queryDesc2.pOutRenderWidth = &renderWidth;
queryDesc2.pOutRenderHeight = &renderHeight;
ffx::Query(fsrContext, queryDesc2);
Query Jitter count / offset
FSR 3.0
FSR 3.1 / FFX API C
int32_t jitterCount;
struct ffxQueryDescFsrGetJitterPhaseCount queryDesc1 = {0};
queryDesc1.header.type = FFX_API_QUERY_DESC_TYPE_FSR_GETJITTERPHASECOUNT;
queryDesc1.renderWidth = renderWidth;
queryDesc1.displayWidth = displayWidth;
queryDesc1.pOutPhaseCount = &jitterCount;
ffxQuery(&fsrContext, &queryDesc1.header);
<!-- -->
float jitterX, jitterY;
struct ffxQueryDescFsrGetJitterOffset queryDesc2 = {0};
queryDesc2.header.type = FFX_API_QUERY_DESC_TYPE_FSR_GETJITTEROFFSET;
queryDesc2.index = jitterIndex;
queryDesc2.phaseCount = jitterCount;
queryDesc2.pOutX = &jitterX;
queryDesc2.pOutY = &jitterY;
ffxQuery(&fsrContext, &queryDesc2.header);
FFX API C++
int32_t jitterCount;
ffx::QueryDescFsrGetJitterPhaseCount queryDesc1{};
queryDesc1.renderWidth = renderWidth;
queryDesc1.displayWidth = displayWidth;
queryDesc1.pOutPhaseCount = &jitterCount;
ffx::Query(fsrContext, queryDesc1);
<!-- -->
float jitterX, jitterY;
ffxQueryDescFsrGetJitterOffset queryDesc2{};
queryDesc2.index = jitterIndex;
queryDesc2.phaseCount = jitterCount;
queryDesc2.pOutX = &jitterX;
queryDesc2.pOutY = &jitterY;
ffx::Query(fsrContext, queryDesc2);
Configure FrameGen
FSR 3.0
FfxFrameGenerationConfig config = {0};
// fill config ...
ffxFsr3ConfigureFrameGeneration(&fsr3Context, &config);
FSR 3.1 / FFX API C
FFX API C++
Present callback
FSR 3.0
FfxErrorCode myPresentCallback(const FfxPresentCallbackDescription * params, void* userCtx) // note: pre-3.1 does not have user context pointer
{
// pre-presentation work, e.g. UI
return FFX_OK ;
}
<!-- -->
// ...
FfxFrameGenerationConfig config;
config.presentCallback = myPresentCallback;
config.presentCallbackContext = &myEngineContext;
FSR 3.1 / FFX API C
ffxReturnCode_t myPresentCallback(struct ffxCallbackDescFrameGenerationPresent* params, void* pUserCtx)
{
// pre-presentation work, e.g. UI
return FFX_API_RETURN_OK;
}
<!-- -->
// ...
struct ffxConfigureDescFrameGeneration config;
config.presentCallback = myPresentCallback;
config.presentCallbackUserContext = &myEngineContext;
FFX API C++
extern "C" ffxReturnCode_t myPresentCallback(ffxCallbackDescFrameGenerationPresent* params, void* pUserCtx)
{
// pre-presentation work, e.g. UI
return FFX_API_RETURN_OK;
}
<!-- -->
// ...
ffx::ConfigureDescFrameGeneration config;
config.presentCallback = myPresentCallback;
config.presentCallbackUserContext = &myEngineContext;
Dispatch upscaling (no fg)
FSR 3.0
// (context created with UPSCALING_ONLY flag)
FfxFsr3DispatchUpscaleDescription params = {0};
// fill params ...
ffxFsr3ContextDispatchUpscale(&fsr3Context, ¶ms);
FSR 3.1 / FFX API C
FFX API C++
ffx::DispatchDescFsrUpscale params{};
// fill params ...
ffx::Dispatch (fsrContext, params);
Dispatch upscaling and fg prepare
FSR 3.0
// dispatch upscaling and prepare resources for frame generation
FfxFsr3DispatchUpscaleDescription params = {0};
// fill params ...
ffxFsr3ContextDispatchUpscale(&fsr3Context, ¶ms);
FSR 3.1 / FFX API C
struct ffxDispatchDescFsrUpscale upscaleParams = {0};
upscaleParams.header.type = FFX_API_DISPATCH_DESC_TYPE_FSR_UPSCALE;
struct ffxDispatchDescFrameGenerationPrepare frameGenParams = {0};
frameGenParams.header.type = FFX_API_DISPATCH_DESC_TYPE_FRAMEGENERATION_PREPARE;
// fill both structs with params ...
ffxDispatch(&fsrContext, &upscaleParams);
ffxDispatch(&fgContext, &frameGenParams);
FFX API C++
ffx::DispatchDescFsrUpscale upscaleParams{};
ffx::DispatchDescFrameGenerationPrepare frameGenParams{};
// fill both structs with params ...
ffx::Dispatch (fsrContext, upscaleParams);
ffx::Dispatch (fgContext, frameGenParams);
Dispatch frame gen (no callback mode)
FSR 3.0
FfxFrameGenerationDispatchDescription fgDesc = {0};
ffxGetFrameinterpolationCommandlistDX12 (ffxSwapChain, fgDesc.commandList );
fgDesc.outputs [0] = ffxGetFrameinterpolationTextureDX12 (ffxSwapChain);
// other parameters ...
ffxFsr3DispatchFrameGeneration(&fgDesc);
FSR 3.1 / FFX API C
struct ffxDispatchDescFrameGeneration dispatchFg = {0};
dispatchFg.header.type = FFX_API_DISPATCH_DESC_TYPE_FRAMEGENERATION;
<!-- -->
struct ffxQueryDescFrameGenerationSwapChainInterpolationCommandListDX12 queryCmdList = {0};
queryCmdList.header.type = FFX_API_QUERY_DESC_TYPE_FRAMEGENERATIONSWAPCHAIN_INTERPOLATIONCOMMANDLIST_DX12;
queryCmdList.pOutCommandList = &dispatchFg.commandList;
ffxQuery(&swapChainContext, &queryCmdList);
<!-- -->
struct ffxQueryDescFrameGenerationSwapChainInterpolationTextureDX12 queryFiTexture{};
queryFiTexture.header.type = FFX_API_QUERY_DESC_TYPE_FRAMEGENERATIONSWAPCHAIN_INTERPOLATIONTEXTURE_DX12;
queryFiTexture.pOutTexture = &dispatchFg.outputs[0];
ffxQuery(&swapChainContext, &queryFiTexture);
<!-- -->
// other parameters ...
ffxDispatch(&fgContext, &dispatchFg);
FFX API C++
ffx::DispatchDescFrameGeneration dispatchFg{};
<!-- -->
ffx::QueryDescFrameGenerationSwapChainInterpolationCommandListDX12 queryCmdList{};
queryCmdList.pOutCommandList = &dispatchFg.commandList;
ffx::Query(swapChainContext, queryCmdList);
<!-- -->
ffx::QueryDescFrameGenerationSwapChainInterpolationTextureDX12 queryFiTexture{};
queryFiTexture.pOutTexture = &dispatchFg.outputs[0];
ffx::Query(swapChainContext, queryFiTexture);
<!-- -->
// other parameters ...
ffx::Dispatch(fgContext, dispatchFg);