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.

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

Copied!

FfxFsr3Context  fsr3Context;

FSR 3.1 / FFX API C

Copied!

ffxContext upscalingContext, framegenContext;

FFX API C++

Copied!

ffx::Context upscalingContext, framegenContext;

Context creation (upscaling only)

FSR 3.0

Copied!

FfxFsr3ContextDescription  fsr3ContextDesc = {0};
// create backend interface ...
// fill fsr3ContextDesc ...
fsr3ContextDesc.flags  |= FFX_FSR3_ENABLE_UPSCALING_ONLY;
ffxFsr3ContextCreate(&fsr3Context, &fsr3ContextDesc);

FSR 3.1 / FFX API C

Copied!

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++

Copied!

ffx::CreateContextDescFsrUpscale fsrContextDesc{};
// fill fsrContextDesc ...
// backend interface desc ...
ffx::CreateContext(&upscalingContext, nullptr, fsrContextDesc, backendDesc);

DX12 backend creation

FSR 3.0

Copied!

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

Copied!

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++

Copied!

ffx::CreateContextDescFsrUpscale fsrContextDesc{};
// fill fsrContextDesc ...
// backend interface desc (dx12)
ffx::CreateBackendDX12Desc backendDesc{};
backendDesc.device = dx12Device;
ffx::CreateContext(&upscalingContext, nullptr, fsrContextDesc, backendDesc);

Context creation (upscale + framegen)

FSR 3.0

Copied!

FfxFsr3ContextDescription  fsr3ContextDesc = {0};
// create backend interface (x3) ...
// fill fsr3ContextDesc ...
ffxFsr3ContextCreate(&fsr3Context, &fsr3ContextDesc);

FSR 3.1 / FFX API C

Copied!

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++

Copied!

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

Copied!

float upscaleRatio = ffxFsr3GetUpscaleRatioFromQualityMode(FFX_FSR3_QUALITY_MODE_BALANCED);

uint32_t renderWidth, renderHeight;
ffxFsr3GetRenderResolutionFromQualityMode(&renderWidth, &renderHeight, displayWidth, displayHeight, FFX_FSR3_QUALITY_MODE_BALANCED);

FSR 3.1 / FFX API C

Copied!

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++

Copied!

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

Copied!

int32_t jitterCount = ffxFsr3GetJitterPhaseCount(renderWidth, displayWidth);
<!-- -->
float jitterX, jitterY;
ffxFsr3GetJitterOffset(&jitterX, &jitterY, jitterIndex, jitterCount);

FSR 3.1 / FFX API C

Copied!

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++

Copied!

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

Copied!

FfxFrameGenerationConfig  config = {0};
// fill config ...
ffxFsr3ConfigureFrameGeneration(&fsr3Context, &config);

FSR 3.1 / FFX API C

Copied!

struct ffxConfigureDescFrameGeneration config = {0};
config.header.type = FFX_API_CONFIGURE_DESC_TYPE_FRAMEGENERATION;
// fill config ...
ffxConfigure(&fsrContext, &config);

FFX API C++

Copied!

ffx::ConfigureDescFrameGeneration config{};
// fill config ...
ffxConfigure(fsrContext, config);

Present callback

FSR 3.0

Copied!

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

Copied!

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++

Copied!

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

Copied!

// (context created with UPSCALING_ONLY flag)
FfxFsr3DispatchUpscaleDescription  params = {0};
// fill params ...
ffxFsr3ContextDispatchUpscale(&fsr3Context, &params);

FSR 3.1 / FFX API C

Copied!

struct ffxDispatchDescFsrUpscale params = {0};
params.header.type = FFX_API_DISPATCH_DESC_TYPE_FSR_UPSCALE;
// fill params ...
ffxDispatch(&fsrContext, &params);

FFX API C++

Copied!

ffx::DispatchDescFsrUpscale params{};
// fill params ...
ffx::Dispatch (fsrContext, params);

Dispatch upscaling and fg prepare

FSR 3.0

Copied!

// dispatch upscaling and prepare resources for frame generation
FfxFsr3DispatchUpscaleDescription  params = {0};
// fill params ...
ffxFsr3ContextDispatchUpscale(&fsr3Context, &params);

FSR 3.1 / FFX API C

Copied!

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++

Copied!

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

Copied!

FfxFrameGenerationDispatchDescription  fgDesc = {0};
ffxGetFrameinterpolationCommandlistDX12 (ffxSwapChain, fgDesc.commandList );
fgDesc.outputs [0]            = ffxGetFrameinterpolationTextureDX12 (ffxSwapChain);
// other parameters ...
ffxFsr3DispatchFrameGeneration(&fgDesc);

FSR 3.1 / FFX API C

Copied!

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++

Copied!

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);

Destroy context and backend

FSR 3.0

Copied!

ffxFsr3ContextDestroy(&fsr3Context);
// for each backend interface:
free(backendInterface.scratchBuffer);

FSR 3.1 / FFX API C

Copied!

ffxDestroyContext(&fsrContext, NULL);

FFX API C++

Copied!

ffx::DestroyContext(fsrContext, nullptr);