Home » Blogs » Guest blog by EBB Software: Integrating FidelityFX Super Resolution 2 (FSR 2) into Scorn

Learn how EBB Software integrated our Unreal Engine plugin for FSR 2.1

EBB Software
EBB Software

Ebb Software was founded in 2013 by a group of highly motivated individuals with the sole purpose of creating a different breed of video games. Soon after the small team started developing the company’s current flagship project “Scorn” – an atmospheric first-person horror adventure game.

link to Guest blog by EBB Software: Integrating FidelityFX Super Resolution 2 (FSR 2) into Scorn

Hello everyone,

We are EBB Software, a game developer studio from Belgrade. On October 14 2022 our first title, Scorn, was released on PC and Xbox (Series X and S) platforms. In this blog post, we will be discussing the process of how and why we ended up integrating AMD FidelityFX™ Super Resolution 2.1 in our project.

Scorn was developed in Unreal Engine 4.27. Scorn takes place in a nightmarish world with different interconnected regions. Each region is a maze-like structure with various rooms and paths to discover. All the storytelling happens in-game, with no cut scenes and loading screens to distract you from the grisly reality of the living, breathing world you’re in. The environment is built out of a large number of unique and complex models and textures. On top of that, there is a considerable amount of VFX and post-process effects. With all those things considered keeping high visual fidelity at 60fps, especially at 4K resolution was very challenging.

After profiling, we found out that we were heavily GPU bottlenecked on lots of locations in the game. An optimization pass, mostly focused on LOD and instancing, helped to a certain extent but we were still heavily pixel-bound on specific locations in the game. As keeping high image quality and hitting 60fps on all platforms was our goal we decided that we will need to use some kind of upscaling method to help us achieve that. After going through some research we decided to go with AMD FidelityFX Super Resolution.

FidelityFX Super Resolution 2.1

FidelityFX Super Resolution 2.1 (FSR 2.1) is a great way to boost up the performance of any game by reducing GPU workload while maintaining the quality of the rendered frame. FSR 2.1 can achieve that by using a short frame history to determine, along with the current frame rendered at lower than display resolution, the upscaled content of the pixels. With this temporal method you can meet demands for higher frame rates at a desired level of visual quality.

Integration and setup of FSR 2.1

The process of integration was pretty straightforward. FSR 2.1 comes in the form of a plugin for Unreal Engine. On GPUOpen AMD provided an easy to follow guide that can be used for this purpose. Additionally, AMD offers detailed documentation that might be of interest to those who need more knowledge on the matter.

Regarding the integration for the Microsoft platforms, some additional steps were required that will be further discussed.

For their platforms, Microsoft distributes FSR 2.1 through a sample project which is not an Unreal Engine project. It is necessary to build the project for each platform so that shader permutation files would be generated. This is necessary because files differ depending on the GDK version used. We used the GDK version 220301. This required a change in the build script, found in the sample project so that it can find the correct path of the GDK when building shader permutations. To integrate the changes from the Microsoft sample project into the FSR 2.1 UE plugin we copied generated shader permutation files and merged Microsoft’s changes to ffx-fsr2-api.xbox

Now we needed to modify the FSR 2.1 plugin to support the Microsoft platforms and to use shader permutation files that were generated. Under SupportedTargetPlatforms, and WhitelistPlatforms of the FSR2TemporalUpscalingD3D12 module properties, we added XSX and WinGDK.

{
    "FileVersion": 3,
    "Version": 1,
    "VersionName": "2.1.2",
    "FriendlyName": "FSR 2.1",
    "Description": "FidelityFX Super Resolution 2.1",
    "Category": "Rendering",
    "CreatedBy": "AMD",
    "CreatedByURL": "https://gpuopen.com/unreal-engine/",
    "DocsURL": "",
    "MarketplaceURL": "",
    "SupportURL": "",
    "EngineVersion": "4.27.0",
    "CanContainContent": true,
    "Installed": true,
    "SupportedTargetPlatforms": [
        "Win64",
        "XSX",
        "WinGDK"
    ],
    "Modules": [
        {...
        // Nothing changed
        ...},
        {
            "Name": "FSR2TemporalUpscalingD3D12",
            "Type": "Runtime",
            "LoadingPhase": "PostConfigInit",
            "WhitelistPlatforms": [
                "Win64",
                "Win32",
                "XSX",
                "WinGDK"
            ]
        },
        {...
        // Nothing changed
        ...},
    ]
}

For Xbox we copied the shader permutation files to the FSR2\Source\ffx-fsr2-api\shaders\dx12-xbox, and for the WinGDK we copied to FSR2\Source\ffx-fsr2-api\shaders\dx12-wingdk. Since we just copied the files from the sample project, it was necessary to change the included paths in the permutations.h files to match the new folder structure. For example in dx12-wingdk\ffx_fsr2_accumulate_pass_16bit_permutations.h, new include paths would be:

shaders/dx12-wingdk/ffx_fsr2_accumulate_pass_16bit_permutations.h: ../Source/ffx_fsr2_callbacks_hlsl.h ../Source/ffx_core_gpu_common.h ../Source/ffx_core_gpu_common_half.h ../Source/ffx_fsr2_resources.h ../Source/ffx_core.h ../Source/ffx_core_portability.h ../Source/ffx_common_types.h ../Source/ffx_core_hlsl.h ../Source/ffx_fsr2_common.h ../Source/ffx_fsr2_reproject.h ../Source/ffx_fsr2_sample.h ../Source/ffx_fsr2_upsample.h ../Source/ffx_fsr2_postprocess_lock_status.h ../Source/ffx_fsr2_accumulate.h

Last change to the plugin is for every module to correctly include necessary files. For example:
FFXFSR2D3D12.h (changed lines are highlighted in gray)

#pragma once
#include "HAL/Platform.h"
#include "FFXFSR2.h"
#if PLATFORM_WINDOWS || WITH_EDITOR || PLATFORM_WINGDK
    #define FSR2_ENABLE_DX12 1

    #include "Windows/AllowWindowsPlatformTypes.h"
    THIRD_PARTY_INCLUDES_START
#ifdef _MSC_VER
    #pragma warning(push)
    #pragma warning(disable : 4190)
#endif
    #include "dx12/ffx_fsr2_dx12.h"
#ifdef _MSC_VER
    #pragma warning(pop)
#endif
    THIRD_PARTY_INCLUDES_END
    #include "Windows/HideWindowsPlatformTypes.h"
#elif PLATFORM_XSX
    #define FSR2_ENABLE_DX12 1

    #include "Windows/AllowWindowsPlatformTypes.h"
    THIRD_PARTY_INCLUDES_START
#ifdef _MSC_VER
    #pragma warning(push)
    #pragma warning(disable : 4190)
#endif
    #include "dx12-xbox/ffx_fsr2_dx12x.h"

#ifdef _MSC_VER
    #pragma warning(pop)
#endif
    THIRD_PARTY_INCLUDES_END
    #include "Windows/HideWindowsPlatformTypes.h"
#else
    #define FSR2_ENABLE_DX12 0
#endif

FSR2TemporalUpscalingD3D12.Build.cs (changed lines are highlighted in gray)

using UnrealBuildTool;
using System.IO;

public class FSR2TemporalUpscalingD3D12</strong>: ModuleRules {
    public FSR2TemporalUpscalingD3D12(ReadOnlyTargetRules Target): base(Target) {
        bAllowConfidentialPlatformDefines = true;
        PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
        ...
        // Nothing changed
        ...

        if (Target.Platform.IsInGroup(UnrealPlatformGroup.Microsoft) || Target.Platform == 
        UnrealBuildTool.UnrealTargetPlatform.XSX || Target.Platform == 
        UnrealBuildTool.UnrealTargetPlatform.WinGDK) {
            PrivateDependencyModuleNames.AddRange(new string[] {"D3D12RHI"});

            AddEngineThirdPartyPrivateStaticDependencies(Target, "DX12");

            // Required for some private headers needed for the rendering support.
            var EngineDir = Path.GetFullPath(Target.RelativeEnginePath);
            PrivateIncludePaths.AddRange(new string[] {
                Path.Combine(EngineDir, @"SourceRuntimeD3D12RHIPrivate")
            });

            if (Target.Platform.IsInGroup(UnrealPlatformGroup.Windows) || Target.Platform == 
            UnrealBuildTool.UnrealTargetPlatform.WinGDK) {
                PrivateIncludePaths.AddRange(
                    new string[] {
                        Path.Combine(EngineDir, @"SourceRuntimeD3D12RHIPrivateWindows")
                    });
            } else if (Target.Platform == UnrealBuildTool.UnrealTargetPlatform.XSX) {
            PrivateDependencyModuleNames.AddRange(
            new string[] {
                "D3D12RHI"
            });

            PrivateIncludePaths.AddRange(
            new string[] {
                Path.Combine(EngineDir, @"PlatformsXboxCommonSourceRuntimeD3D12RHIPrivate"),
                    Path.Combine(EngineDir, @"PlatformsXSXSourceRuntimeD3D12RHIPrivate")
            });
        } else {
            PrivateIncludePaths.AddRange(
            new string[] {
                Path.Combine(EngineDir, @"SourceRuntimeD3D12RHIPrivate" + Target.Platform)
            });
        }
        PrecompileForTargets = PrecompileTargetsType.Any;
        } else {
        PrecompileForTargets = PrecompileTargetsType.None;
        }
    }
}

FSR 2.1 worked very well out of the box and gave us reasonable results. With some tweaking of the following FSR 2.1 console commands very quickly we were able to get great image quality with a huge performance boost both on PC and Xbox Series X platforms.

r.FidelityFX.FSR2.ReactiveMaskTranslucencyBias=0.5
r.FidelityFX.FSR2.ReactiveMaskTranslucencyLumaBias=0.5
r.FidelityFX.FSR2.ReactiveMaskPreDOFTranslucencyScale=0.8
r.FidelityFX.FSR2.UseSSRExperimentalDenoiser=1
r.FidelityFX.FSR2.DeDither=1

On PC we exposed three main FSR 2.1 quality modes that users can choose from. These vary the amount of scaling to apply to the source image, depending on the quality/performance ratio desired:

  1. Quality mode provides an image quality equal to or superior to native rendering with significant performance gain. ( 1.5x per dimension scale factor)

  2. Balanced mode offers an ideal compromise between image quality and performance gain. (1.7x per dimension scale factor)

  3. Performance mode provides an image quality similar to native rendering with a major performance gain. ( 2.0x per dimension scale factor)

On the Xbox Series X platform, we choose to use Quality mode which provided us with great image quality and a significant performance boost. Besides that, we also reclaimed 100-500 MB in various locations on the memory side due to the 2560 x 1440 input resolution of Quality mode.

In addition to quality modes, AMD provides configurable RCAS sharpening to help emphasize pixel detail. This option is exposed as a sharpness slider allowing users to set the sharpening amount according to their preferences.

In the next section of the blog, you can see the performance and image quality results we were able to get using FSR 2.1.

Performance and results

In the tables below you can see the average gain on GPU (in ms) we were able to get over the native 4K resolution in our game. Tests are done on the recommended PC platform and Xbox Series X.

FSR 2.1 Quality modes

PC

Xbox Series X

Quality

3.4 ms

4.2 ms

Balanced

4.5 ms

/

Performance

5.3 ms

/

In the images below you can see the comparison between native 4K and FSR 2.1 Quality mode on the PC platform.


In the video below, you can see the comparison between native 4K and FSR 2.1 Quality mode on the PC platform.

Please note that this game may be unsuitable for minors under the age of 17

In the images below you can see the comparison between native 4K and FSR 2.1 Quality mode on Xbox Series X.



Conclusion

The implementation process took us less time than we initially thought it would, and this meant so much to us since we were already on a tight schedule in relation to the deadlines we had set. With FSR 2.1 implemented we were able to hit stable 60fps on all of the platforms even at 4K resolution. Since it delivers a significant performance boost with little or no image quality cost it delivered a huge benefit for our project.

This technology is definitely something we would recommend to anyone.

Footnotes

*Testing (as described herein) not independently verified by AMD. GD-182

**Links to third party sites are provided for convenience and unless explicitly stated, AMD is not responsible for the contents of such linked sites and no endorsement is implied. GD-5

***The information contained in this blog represents the view the EBB Software as of the date published. EBB Software has no obligation to update any forward-looking content in the above blog. AMD is not responsible for the content of any third-party author and does not necessarily endorse the comments made therein. GD-84

Bojan Koprivica
Bojan Koprivica

Bojan Koprivica is a Programmer/DevOps in Ebb Software. He was responsible for Scorn platform development. He also developed CI tools used in the project.

Boris Usanovic
Boris Usanovic

Boris Usanovic is a Technical artist with over eight years of experience. On the Scorn project, he specialized in rendering and optimization for PC and XBOX platforms.

Enjoy this blog post? If you found it useful, why not share it with other game developers?

You may also like...

Getting started: AMD GPUOpen software

New or fairly new to AMD’s tools, libraries, and effects? This is the best place to get started on GPUOpen!

AMD GPUOpen Getting Started Development and Performance

Looking for tips on getting started with developing and/or optimizing your game, whether on AMD hardware or generally? We’ve got you covered!

GPUOpen Manuals

Don’t miss our manual documentation! And if slide decks are what you’re after, you’ll find 100+ of our finest presentations here.

AMD GPUOpen Technical blogs

Browse our technical blogs, and find valuable advice on developing with AMD hardware, ray tracing, Vulkan®, DirectX®, Unreal Engine, and lots more.

AMD GPUOpen videos

Words not enough? How about pictures? How about moving pictures? We have some amazing videos to share with you!

AMD GPUOpen Performance Guides

The home of great performance and optimization advice for AMD RDNA™ 2 GPUs, AMD Ryzen™ CPUs, and so much more.

AMD GPUOpen software blogs

Our handy software release blogs will help you make good use of our tools, SDKs, and effects, as well as sharing the latest features with new releases.

AMD GPUOpen publications

Discover our published publications.