From 65d6aa6303e6e03d00d370aa30394e16f8ad35d6 Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Wed, 12 Mar 2025 18:08:24 +0100 Subject: [PATCH] Added FSR 2.2 headers and static libraries for DX11 only --- ffx-fsr2-api/dx11/ffx_fsr2_dx11.h | 99 ++++ ffx-fsr2-api/ffx_assert.h | 132 +++++ ffx-fsr2-api/ffx_error.h | 59 ++ ffx-fsr2-api/ffx_fsr2.h | 454 ++++++++++++++++ ffx-fsr2-api/ffx_fsr2_interface.h | 395 ++++++++++++++ ffx-fsr2-api/ffx_fsr2_maximum_bias.h | 46 ++ ffx-fsr2-api/ffx_fsr2_private.h | 81 +++ ffx-fsr2-api/ffx_types.h | 364 +++++++++++++ ffx-fsr2-api/ffx_util.h | 78 +++ ffx-fsr2-api/shaders/ffx_fsr2_common.h | 565 ++++++++++++++++++++ ffx-fsr2-api/shaders/ffx_fsr2_resources.h | 105 ++++ lib/ffx_fsr2_api/ffx_fsr2_api_dx11_x64.lib | Bin 0 -> 2203568 bytes lib/ffx_fsr2_api/ffx_fsr2_api_dx11_x64d.lib | Bin 0 -> 3991124 bytes lib/ffx_fsr2_api/ffx_fsr2_api_x64.lib | Bin 0 -> 84986 bytes lib/ffx_fsr2_api/ffx_fsr2_api_x64d.lib | Bin 0 -> 180400 bytes 15 files changed, 2378 insertions(+) create mode 100644 ffx-fsr2-api/dx11/ffx_fsr2_dx11.h create mode 100644 ffx-fsr2-api/ffx_assert.h create mode 100644 ffx-fsr2-api/ffx_error.h create mode 100644 ffx-fsr2-api/ffx_fsr2.h create mode 100644 ffx-fsr2-api/ffx_fsr2_interface.h create mode 100644 ffx-fsr2-api/ffx_fsr2_maximum_bias.h create mode 100644 ffx-fsr2-api/ffx_fsr2_private.h create mode 100644 ffx-fsr2-api/ffx_types.h create mode 100644 ffx-fsr2-api/ffx_util.h create mode 100644 ffx-fsr2-api/shaders/ffx_fsr2_common.h create mode 100644 ffx-fsr2-api/shaders/ffx_fsr2_resources.h create mode 100644 lib/ffx_fsr2_api/ffx_fsr2_api_dx11_x64.lib create mode 100644 lib/ffx_fsr2_api/ffx_fsr2_api_dx11_x64d.lib create mode 100644 lib/ffx_fsr2_api/ffx_fsr2_api_x64.lib create mode 100644 lib/ffx_fsr2_api/ffx_fsr2_api_x64d.lib diff --git a/ffx-fsr2-api/dx11/ffx_fsr2_dx11.h b/ffx-fsr2-api/dx11/ffx_fsr2_dx11.h new file mode 100644 index 0000000..a8d4823 --- /dev/null +++ b/ffx-fsr2-api/dx11/ffx_fsr2_dx11.h @@ -0,0 +1,99 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +// +// 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. + +// @defgroup DX11 + +#pragma once + +#include +#include "../ffx_fsr2_interface.h" + +#if defined(__cplusplus) +extern "C" { +#endif // #if defined(__cplusplus) + +/// Query how much memory is required for the DirectX 11 backend's scratch buffer. +/// +/// @returns +/// The size (in bytes) of the required scratch memory buffer for the DX11 backend. +FFX_API size_t ffxFsr2GetScratchMemorySizeDX11(); + +/// Populate an interface with pointers for the DX11 backend. +/// +/// @param [out] fsr2Interface A pointer to a FfxFsr2Interface structure to populate with pointers. +/// @param [in] device A pointer to the DirectX11 device. +/// @param [in] scratchBuffer A pointer to a buffer of memory which can be used by the DirectX(R)11 backend. +/// @param [in] scratchBufferSize The size (in bytes) of the buffer pointed to by scratchBuffer. +/// +/// @retval +/// FFX_OK The operation completed successfully. +/// @retval +/// FFX_ERROR_CODE_INVALID_POINTER The interface pointer was NULL. +/// +/// @ingroup FSR2 DX11 +FFX_API FfxErrorCode ffxFsr2GetInterfaceDX11( + FfxFsr2Interface* fsr2Interface, + ID3D11Device* device, + void* scratchBuffer, + size_t scratchBufferSize); + +/// Create a FfxFsr2Device from a ID3D11Device. +/// +/// @param [in] device A pointer to the DirectX11 device. +/// +/// @returns +/// An abstract FidelityFX device. +/// +/// @ingroup FSR2 DX11 +FFX_API FfxDevice ffxGetDeviceDX11(ID3D11Device* device); + +/// Create a FfxResource from a ID3D11Resource. +/// +/// @param [in] fsr2Interface A pointer to a FfxFsr2Interface structure. +/// @param [in] resDx11 A pointer to the DirectX11 resource. +/// @param [in] name (optional) A name string to identify the resource in debug mode. +/// @param [in] state The state the resource is currently in. +/// +/// @returns +/// An abstract FidelityFX resources. +/// +/// @ingroup FSR2 DX11 +FFX_API FfxResource ffxGetResourceDX11( + FfxFsr2Context* context, + ID3D11Resource* resDx11, + const wchar_t* name = nullptr, + FfxResourceStates state = FFX_RESOURCE_STATE_COMPUTE_READ); + +/// Retrieve a ID3D11Resource pointer associated with a RESOURCE_IDENTIFIER. +/// Used for debug purposes when blitting internal surfaces. +/// +/// @param [in] context A pointer to a FfxFsr2Context structure. +/// @param [in] resId A resource. +/// +/// @returns +/// A ID3D11Resource pointer. +/// +/// @ingroup FSR2 DX11 +FFX_API ID3D11Resource* ffxGetDX11ResourcePtr(FfxFsr2Context* context, uint32_t resId); + +#if defined(__cplusplus) +} +#endif // #if defined(__cplusplus) diff --git a/ffx-fsr2-api/ffx_assert.h b/ffx-fsr2-api/ffx_assert.h new file mode 100644 index 0000000..ae32d2a --- /dev/null +++ b/ffx-fsr2-api/ffx_assert.h @@ -0,0 +1,132 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (c) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. +// +// 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_types.h" +#include "ffx_util.h" + +#ifdef __cplusplus +extern "C" { +#endif // #ifdef __cplusplus + +#ifdef _DEBUG +#ifdef _WIN32 + +#ifdef DISABLE_FFX_DEBUG_BREAK +#define FFX_DEBUG_BREAK \ + { \ + } +#else +/// Macro to force the debugger to break at this point in the code. +#define FFX_DEBUG_BREAK __debugbreak(); +#endif +#else +#define FFX_DEBUG_BREAK \ + { \ + } +#endif +#else +// don't allow debug break in release builds. +#define FFX_DEBUG_BREAK +#endif + +/// A typedef for the callback function for assert printing. +/// +/// This can be used to re-route printing of assert messages from the FFX backend +/// to another destination. For example instead of the default behaviour of printing +/// the assert messages to the debugger's TTY the message can be re-routed to a +/// MessageBox in a GUI application. +/// +/// @param [in] message The message generated by the assert. +/// +typedef void (*FfxAssertCallback)(const char* message); + +/// Function to report an assert. +/// +/// @param [in] file The name of the file as a string. +/// @param [in] line The index of the line in the file. +/// @param [in] condition The boolean condition that was tested. +/// @param [in] msg The optional message to print. +/// +/// @returns +/// Always returns true. +/// +FFX_API bool ffxAssertReport(const char* file, int32_t line, const char* condition, const char* msg); + +/// Provides the ability to set a callback for assert messages. +/// +/// @param [in] callback The callback function that will receive assert messages. +/// +FFX_API void ffxAssertSetPrintingCallback(FfxAssertCallback callback); + +#ifdef _DEBUG +/// Standard assert macro. +#define FFX_ASSERT(condition) \ + do \ + { \ + if (!(condition) && ffxAssertReport(__FILE__, __LINE__, #condition, NULL)) \ + FFX_DEBUG_BREAK \ + } while (0) + +/// Assert macro with message. +#define FFX_ASSERT_MESSAGE(condition, msg) \ + do \ + { \ + if (!(condition) && ffxAssertReport(__FILE__, __LINE__, #condition, msg)) \ + FFX_DEBUG_BREAK \ + } while (0) + +/// Assert macro that always fails. +#define FFX_ASSERT_FAIL(message) \ + do \ + { \ + ffxAssertReport(__FILE__, __LINE__, NULL, message); \ + FFX_DEBUG_BREAK \ + } while (0) +#else +// asserts disabled +#define FFX_ASSERT(condition) \ + do \ + { \ + FFX_UNUSED(condition); \ + } while (0) + +#define FFX_ASSERT_MESSAGE(condition, message) \ + do \ + { \ + FFX_UNUSED(condition); \ + FFX_UNUSED(message); \ + } while (0) + +#define FFX_ASSERT_FAIL(message) \ + do \ + { \ + FFX_UNUSED(message); \ + } while (0) +#endif // #if _DEBUG + +/// Simple static assert. +#define FFX_STATIC_ASSERT(condition) static_assert(condition, #condition) + +#ifdef __cplusplus +} +#endif // #ifdef __cplusplus diff --git a/ffx-fsr2-api/ffx_error.h b/ffx-fsr2-api/ffx_error.h new file mode 100644 index 0000000..7ba7d9c --- /dev/null +++ b/ffx-fsr2-api/ffx_error.h @@ -0,0 +1,59 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (c) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. +// +// 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_types.h" + +/// Typedef for error codes returned from functions in the FidelityFX SDK. +typedef int32_t FfxErrorCode; + +static const FfxErrorCode FFX_OK = 0; ///< The operation completed successfully. +static const FfxErrorCode FFX_ERROR_INVALID_POINTER = 0x80000000; ///< The operation failed due to an invalid pointer. +static const FfxErrorCode FFX_ERROR_INVALID_ALIGNMENT = 0x80000001; ///< The operation failed due to an invalid alignment. +static const FfxErrorCode FFX_ERROR_INVALID_SIZE = 0x80000002; ///< The operation failed due to an invalid size. +static const FfxErrorCode FFX_EOF = 0x80000003; ///< The end of the file was encountered. +static const FfxErrorCode FFX_ERROR_INVALID_PATH = 0x80000004; ///< The operation failed because the specified path was invalid. +static const FfxErrorCode FFX_ERROR_EOF = 0x80000005; ///< The operation failed because end of file was reached. +static const FfxErrorCode FFX_ERROR_MALFORMED_DATA = 0x80000006; ///< The operation failed because of some malformed data. +static const FfxErrorCode FFX_ERROR_OUT_OF_MEMORY = 0x80000007; ///< The operation failed because it ran out memory. +static const FfxErrorCode FFX_ERROR_INCOMPLETE_INTERFACE = 0x80000008; ///< The operation failed because the interface was not fully configured. +static const FfxErrorCode FFX_ERROR_INVALID_ENUM = 0x80000009; ///< The operation failed because of an invalid enumeration value. +static const FfxErrorCode FFX_ERROR_INVALID_ARGUMENT = 0x8000000a; ///< The operation failed because an argument was invalid. +static const FfxErrorCode FFX_ERROR_OUT_OF_RANGE = 0x8000000b; ///< The operation failed because a value was out of range. +static const FfxErrorCode FFX_ERROR_NULL_DEVICE = 0x8000000c; ///< The operation failed because a device was null. +static const FfxErrorCode FFX_ERROR_BACKEND_API_ERROR = 0x8000000d; ///< The operation failed because the backend API returned an error code. +static const FfxErrorCode FFX_ERROR_INSUFFICIENT_MEMORY = 0x8000000e; ///< The operation failed because there was not enough memory. + +/// Helper macro to return error code y from a function when a specific condition, x, is not met. +#define FFX_RETURN_ON_ERROR(x, y) \ + if (!(x)) \ + { \ + return (y); \ + } + +/// Helper macro to return error code x from a function when it is not FFX_OK. +#define FFX_VALIDATE(x) \ + { \ + FfxErrorCode ret = x; \ + FFX_RETURN_ON_ERROR(ret == FFX_OK, ret); \ + } + diff --git a/ffx-fsr2-api/ffx_fsr2.h b/ffx-fsr2-api/ffx_fsr2.h new file mode 100644 index 0000000..2a1c74a --- /dev/null +++ b/ffx-fsr2-api/ffx_fsr2.h @@ -0,0 +1,454 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (c) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. +// +// 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. + + +// @defgroup FSR2 + +#pragma once + +// Include the interface for the backend of the FSR2 API. +#include "ffx_fsr2_interface.h" + +/// FidelityFX Super Resolution 2 major version. +/// +/// @ingroup FSR2 +#define FFX_FSR2_VERSION_MAJOR (2) + +/// FidelityFX Super Resolution 2 minor version. +/// +/// @ingroup FSR2 +#define FFX_FSR2_VERSION_MINOR (2) + +/// FidelityFX Super Resolution 2 patch version. +/// +/// @ingroup FSR2 +#define FFX_FSR2_VERSION_PATCH (1) + +/// The size of the context specified in 32bit values. +/// +/// @ingroup FSR2 +#define FFX_FSR2_CONTEXT_SIZE (16536) + +#if defined(__cplusplus) +extern "C" { +#endif // #if defined(__cplusplus) + +/// An enumeration of all the quality modes supported by FidelityFX Super +/// Resolution 2 upscaling. +/// +/// In order to provide a consistent user experience across multiple +/// applications which implement FSR2. It is strongly recommended that the +/// following preset scaling factors are made available through your +/// application's user interface. +/// +/// If your application does not expose the notion of preset scaling factors +/// for upscaling algorithms (perhaps instead implementing a fixed ratio which +/// is immutable) or implementing a more dynamic scaling scheme (such as +/// dynamic resolution scaling), then there is no need to use these presets. +/// +/// Please note that FFX_FSR2_QUALITY_MODE_ULTRA_PERFORMANCE is +/// an optional mode which may introduce significant quality degradation in the +/// final image. As such it is recommended that you evaluate the final results +/// of using this scaling mode before deciding if you should include it in your +/// application. +/// +/// @ingroup FSR2 +typedef enum FfxFsr2QualityMode { + + FFX_FSR2_QUALITY_MODE_QUALITY = 1, ///< Perform upscaling with a per-dimension upscaling ratio of 1.5x. + FFX_FSR2_QUALITY_MODE_BALANCED = 2, ///< Perform upscaling with a per-dimension upscaling ratio of 1.7x. + FFX_FSR2_QUALITY_MODE_PERFORMANCE = 3, ///< Perform upscaling with a per-dimension upscaling ratio of 2.0x. + FFX_FSR2_QUALITY_MODE_ULTRA_PERFORMANCE = 4 ///< Perform upscaling with a per-dimension upscaling ratio of 3.0x. +} FfxFsr2QualityMode; + +/// An enumeration of bit flags used when creating a +/// FfxFsr2Context. See FfxFsr2ContextDescription. +/// +/// @ingroup FSR2 +typedef enum FfxFsr2InitializationFlagBits { + + FFX_FSR2_ENABLE_HIGH_DYNAMIC_RANGE = (1<<0), ///< A bit indicating if the input color data provided is using a high-dynamic range. + FFX_FSR2_ENABLE_DISPLAY_RESOLUTION_MOTION_VECTORS = (1<<1), ///< A bit indicating if the motion vectors are rendered at display resolution. + FFX_FSR2_ENABLE_MOTION_VECTORS_JITTER_CANCELLATION = (1<<2), ///< A bit indicating that the motion vectors have the jittering pattern applied to them. + FFX_FSR2_ENABLE_DEPTH_INVERTED = (1<<3), ///< A bit indicating that the input depth buffer data provided is inverted [1..0]. + FFX_FSR2_ENABLE_DEPTH_INFINITE = (1<<4), ///< A bit indicating that the input depth buffer data provided is using an infinite far plane. + FFX_FSR2_ENABLE_AUTO_EXPOSURE = (1<<5), ///< A bit indicating if automatic exposure should be applied to input color data. + FFX_FSR2_ENABLE_DYNAMIC_RESOLUTION = (1<<6), ///< A bit indicating that the application uses dynamic resolution scaling. + FFX_FSR2_ENABLE_TEXTURE1D_USAGE = (1<<7), ///< A bit indicating that the backend should use 1D textures. + FFX_FSR2_ENABLE_DEBUG_CHECKING = (1<<8), ///< A bit indicating that the runtime should check some API values and report issues. +} FfxFsr2InitializationFlagBits; + +/// A structure encapsulating the parameters required to initialize FidelityFX +/// Super Resolution 2 upscaling. +/// +/// @ingroup FSR2 +typedef struct FfxFsr2ContextDescription { + + uint32_t flags; ///< A collection of FfxFsr2InitializationFlagBits. + FfxDimensions2D maxRenderSize; ///< The maximum size that rendering will be performed at. + FfxDimensions2D displaySize; ///< The size of the presentation resolution targeted by the upscaling process. + FfxFsr2Interface callbacks; ///< A set of pointers to the backend implementation for FSR 2.0. + FfxDevice device; ///< The abstracted device which is passed to some callback functions. + + FfxFsr2Message fpMessage; ///< A pointer to a function that can recieve messages from the runtime. +} FfxFsr2ContextDescription; + +/// A structure encapsulating the parameters for dispatching the various passes +/// of FidelityFX Super Resolution 2. +/// +/// @ingroup FSR2 +typedef struct FfxFsr2DispatchDescription { + + FfxCommandList commandList; ///< The FfxCommandList to record FSR2 rendering commands into. + FfxResource color; ///< A FfxResource containing the color buffer for the current frame (at render resolution). + FfxResource depth; ///< A FfxResource containing 32bit depth values for the current frame (at render resolution). + FfxResource motionVectors; ///< A FfxResource containing 2-dimensional motion vectors (at render resolution if FFX_FSR2_ENABLE_DISPLAY_RESOLUTION_MOTION_VECTORS is not set). + FfxResource exposure; ///< A optional FfxResource containing a 1x1 exposure value. + FfxResource reactive; ///< A optional FfxResource containing alpha value of reactive objects in the scene. + FfxResource transparencyAndComposition; ///< A optional FfxResource containing alpha value of special objects in the scene. + FfxResource output; ///< A FfxResource containing the output color buffer for the current frame (at presentation resolution). + FfxFloatCoords2D jitterOffset; ///< The subpixel jitter offset applied to the camera. + FfxFloatCoords2D motionVectorScale; ///< The scale factor to apply to motion vectors. + FfxDimensions2D renderSize; ///< The resolution that was used for rendering the input resources. + 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 + + // EXPERIMENTAL reactive mask generation parameters + bool enableAutoReactive; ///< A boolean value to indicate internal reactive autogeneration should be used + FfxResource colorOpaqueOnly; ///< A FfxResource containing the opaque only color buffer for the current frame (at render resolution). + float autoTcThreshold; ///< Cutoff value for TC + float autoTcScale; ///< A value to scale the transparency and composition mask + float autoReactiveScale; ///< A value to scale the reactive mask + float autoReactiveMax; ///< A value to clamp the reactive mask + +} FfxFsr2DispatchDescription; + +/// A structure encapsulating the parameters for automatic generation of a reactive mask +/// +/// @ingroup FSR2 +typedef struct FfxFsr2GenerateReactiveDescription { + + FfxCommandList commandList; ///< The FfxCommandList to record FSR2 rendering commands into. + FfxResource colorOpaqueOnly; ///< A FfxResource containing the opaque only color buffer for the current frame (at render resolution). + FfxResource colorPreUpscale; ///< A FfxResource containing the opaque+translucent color buffer for the current frame (at render resolution). + FfxResource outReactive; ///< A FfxResource containing the surface to generate the reactive mask into. + FfxDimensions2D 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 +} FfxFsr2GenerateReactiveDescription; + +/// A structure encapsulating the FidelityFX Super Resolution 2 context. +/// +/// This sets up an object which contains all persistent internal data and +/// resources that are required by FSR2. +/// +/// The FfxFsr2Context object should have a lifetime matching +/// your use of FSR2. Before destroying the FSR2 context care should be taken +/// to ensure the GPU is not accessing the resources created or used by FSR2. +/// It is therefore recommended that the GPU is idle before destroying the +/// FSR2 context. +/// +/// @ingroup FSR2 +typedef struct FfxFsr2Context { + + uint32_t data[FFX_FSR2_CONTEXT_SIZE]; ///< An opaque set of uint32_t which contain the data for the context. +} FfxFsr2Context; + +/// Create a FidelityFX Super Resolution 2 context from the parameters +/// programmed to the FfxFsr2CreateParams structure. +/// +/// The context structure is the main object used to interact with the FSR2 +/// API, and is responsible for the management of the internal resources used +/// by the FSR2 algorithm. When this API is called, multiple calls will be +/// made via the pointers contained in the callbacks structure. +/// These callbacks will attempt to retreive the device capabilities, and +/// create the internal resources, and pipelines required by FSR2's +/// frame-to-frame function. Depending on the precise configuration used when +/// creating the FfxFsr2Context a different set of resources and +/// pipelines might be requested via the callback functions. +/// +/// The flags included in the flags field of +/// FfxFsr2Context how match the configuration of your +/// application as well as the intended use of FSR2. It is important that these +/// flags are set correctly (as well as a correct programmed +/// FfxFsr2DispatchDescription) to ensure correct operation. It is +/// recommended to consult the overview documentation for further details on +/// how FSR2 should be integerated into an application. +/// +/// When the FfxFsr2Context is created, you should use the +/// ffxFsr2ContextDispatch function each frame where FSR2 +/// upscaling should be applied. See the documentation of +/// ffxFsr2ContextDispatch for more details. +/// +/// The FfxFsr2Context should be destroyed when use of it is +/// completed, typically when an application is unloaded or FSR2 upscaling is +/// disabled by a user. To destroy the FSR2 context you should call +/// ffxFsr2ContextDestroy. +/// +/// @param [out] context A pointer to a FfxFsr2Context structure to populate. +/// @param [in] contextDescription A pointer to a FfxFsr2ContextDescription structure. +/// +/// @retval +/// FFX_OK The operation completed successfully. +/// @retval +/// FFX_ERROR_CODE_NULL_POINTER The operation failed because either context or contextDescription was NULL. +/// @retval +/// FFX_ERROR_INCOMPLETE_INTERFACE The operation failed because the FfxFsr2ContextDescription.callbacks was not fully specified. +/// @retval +/// FFX_ERROR_BACKEND_API_ERROR The operation failed because of an error returned from the backend. +/// +/// @ingroup FSR2 +FFX_API FfxErrorCode ffxFsr2ContextCreate(FfxFsr2Context* context, const FfxFsr2ContextDescription* contextDescription); + +/// Dispatch the various passes that constitute FidelityFX Super Resolution 2. +/// +/// FSR2 is a composite effect, meaning that it is compromised of multiple +/// constituent passes (implemented as one or more clears, copies and compute +/// dispatches). The ffxFsr2ContextDispatch function is the +/// function which (via the use of the functions contained in the +/// callbacks field of the FfxFsr2Context +/// structure) utlimately generates the sequence of graphics API calls required +/// each frame. +/// +/// As with the creation of the FfxFsr2Context correctly +/// programming the FfxFsr2DispatchDescription is key to ensuring +/// the correct operation of FSR2. It is particularly important to ensure that +/// camera jitter is correctly applied to your application's projection matrix +/// (or camera origin for raytraced applications). FSR2 provides the +/// ffxFsr2GetJitterPhaseCount and +/// ffxFsr2GetJitterOffset entry points to help applications +/// correctly compute the camera jitter. Whatever jitter pattern is used by the +/// application it should be correctly programmed to the +/// jitterOffset field of the dispatchDescription +/// structure. For more guidance on camera jitter please consult the +/// documentation for ffxFsr2GetJitterOffset as well as the +/// accompanying overview documentation for FSR2. +/// +/// @param [in] context A pointer to a FfxFsr2Context structure. +/// @param [in] dispatchDescription A pointer to a FfxFsr2DispatchDescription structure. +/// +/// @retval +/// FFX_OK The operation completed successfully. +/// @retval +/// FFX_ERROR_CODE_NULL_POINTER The operation failed because either context or dispatchDescription was NULL. +/// @retval +/// FFX_ERROR_OUT_OF_RANGE The operation failed because dispatchDescription.renderSize was larger than the maximum render resolution. +/// @retval +/// FFX_ERROR_NULL_DEVICE The operation failed because the device inside the context was NULL. +/// @retval +/// FFX_ERROR_BACKEND_API_ERROR The operation failed because of an error returned from the backend. +/// +/// @ingroup FSR2 +FFX_API FfxErrorCode ffxFsr2ContextDispatch(FfxFsr2Context* context, const FfxFsr2DispatchDescription* dispatchDescription); + +/// A helper function generate a Reactive mask from an opaque only texure and one containing translucent objects. +/// +/// @param [in] context A pointer to a FfxFsr2Context structure. +/// @param [in] params A pointer to a FfxFsr2GenerateReactiveDescription structure +/// +/// @retval +/// FFX_OK The operation completed successfully. +/// +/// @ingroup FSR2 +FFX_API FfxErrorCode ffxFsr2ContextGenerateReactiveMask(FfxFsr2Context* context, const FfxFsr2GenerateReactiveDescription* params); + +/// Destroy the FidelityFX Super Resolution context. +/// +/// @param [out] context A pointer to a FfxFsr2Context structure to destroy. +/// +/// @retval +/// FFX_OK The operation completed successfully. +/// @retval +/// FFX_ERROR_CODE_NULL_POINTER The operation failed because either context was NULL. +/// +/// @ingroup FSR2 +FFX_API FfxErrorCode ffxFsr2ContextDestroy(FfxFsr2Context* context); + +/// Get the upscale ratio from the quality mode. +/// +/// The following table enumerates the mapping of the quality modes to +/// per-dimension scaling ratios. +/// +/// Quality preset | Scale factor +/// ----------------------------------------------------- | ------------- +/// FFX_FSR2_QUALITY_MODE_QUALITY | 1.5x +/// FFX_FSR2_QUALITY_MODE_BALANCED | 1.7x +/// FFX_FSR2_QUALITY_MODE_PERFORMANCE | 2.0x +/// FFX_FSR2_QUALITY_MODE_ULTRA_PERFORMANCE | 3.0x +/// +/// Passing an invalid qualityMode will return 0.0f. +/// +/// @param [in] qualityMode The quality mode preset. +/// +/// @returns +/// The upscaling the per-dimension upscaling ratio for +/// qualityMode according to the table above. +/// +/// @ingroup FSR2 +FFX_API float ffxFsr2GetUpscaleRatioFromQualityMode(FfxFsr2QualityMode qualityMode); + +/// A helper function to calculate the rendering resolution from a target +/// resolution and desired quality level. +/// +/// This function applies the scaling factor returned by +/// ffxFsr2GetUpscaleRatioFromQualityMode to each dimension. +/// +/// @param [out] renderWidth A pointer to a uint32_t which will hold the calculated render resolution width. +/// @param [out] renderHeight A pointer to a uint32_t which will hold the calculated render resolution height. +/// @param [in] displayWidth The target display resolution width. +/// @param [in] displayHeight The target display resolution height. +/// @param [in] qualityMode The desired quality mode for FSR 2 upscaling. +/// +/// @retval +/// FFX_OK The operation completed successfully. +/// @retval +/// FFX_ERROR_INVALID_POINTER Either renderWidth or renderHeight was NULL. +/// @retval +/// FFX_ERROR_INVALID_ENUM An invalid quality mode was specified. +/// +/// @ingroup FSR2 +FFX_API FfxErrorCode ffxFsr2GetRenderResolutionFromQualityMode( + uint32_t* renderWidth, + uint32_t* renderHeight, + uint32_t displayWidth, + uint32_t displayHeight, + FfxFsr2QualityMode qualityMode); + +/// A helper function to calculate the jitter phase count from display +/// resolution. +/// +/// For more detailed information about the application of camera jitter to +/// your application's rendering please refer to the +/// ffxFsr2GetJitterOffset function. +/// +/// The table below shows the jitter phase count which this function +/// would return for each of the quality presets. +/// +/// Quality preset | Scale factor | Phase count +/// ----------------------------------------------------- | ------------- | --------------- +/// FFX_FSR2_QUALITY_MODE_QUALITY | 1.5x | 18 +/// FFX_FSR2_QUALITY_MODE_BALANCED | 1.7x | 23 +/// FFX_FSR2_QUALITY_MODE_PERFORMANCE | 2.0x | 32 +/// FFX_FSR2_QUALITY_MODE_ULTRA_PERFORMANCE | 3.0x | 72 +/// Custom | [1..n]x | ceil(8*n^2) +/// +/// @param [in] renderWidth The render resolution width. +/// @param [in] displayWidth The display resolution width. +/// +/// @returns +/// The jitter phase count for the scaling factor between renderWidth and displayWidth. +/// +/// @ingroup FSR2 +FFX_API int32_t ffxFsr2GetJitterPhaseCount(int32_t renderWidth, int32_t displayWidth); + +/// A helper function to calculate the subpixel jitter offset. +/// +/// FSR2 relies on the application to apply sub-pixel jittering while rendering. +/// This is typically included in the projection matrix of the camera. To make +/// the application of camera jitter simple, the FSR2 API provides a small set +/// of utility function which computes the sub-pixel jitter offset for a +/// particular frame within a sequence of separate jitter offsets. To begin, the +/// index within the jitter phase must be computed. To calculate the +/// sequence's length, you can call the ffxFsr2GetJitterPhaseCount +/// function. The index should be a value which is incremented each frame modulo +/// the length of the sequence computed by ffxFsr2GetJitterPhaseCount. +/// The index within the jitter phase is passed to +/// ffxFsr2GetJitterOffset via the index parameter. +/// +/// This function uses a Halton(2,3) sequence to compute the jitter offset. +/// The ultimate index used for the sequence is index % +/// phaseCount. +/// +/// It is important to understand that the values returned from the +/// ffxFsr2GetJitterOffset function are in unit pixel space, and +/// in order to composite this correctly into a projection matrix we must +/// convert them into projection offsets. This is done as per the pseudo code +/// listing which is shown below. +/// +/// const int32_t jitterPhaseCount = ffxFsr2GetJitterPhaseCount(renderWidth, displayWidth); +/// +/// float jitterX = 0; +/// float jitterY = 0; +/// ffxFsr2GetJitterOffset(&jitterX, &jitterY, index, jitterPhaseCount); +/// +/// const float jitterX = 2.0f * jitterX / (float)renderWidth; +/// const float jitterY = -2.0f * jitterY / (float)renderHeight; +/// const Matrix4 jitterTranslationMatrix = translateMatrix(Matrix3::identity, Vector3(jitterX, jitterY, 0)); +/// const Matrix4 jitteredProjectionMatrix = jitterTranslationMatrix * projectionMatrix; +/// +/// Jitter should be applied to all rendering. This includes opaque, alpha +/// transparent, and raytraced objects. For rasterized objects, the sub-pixel +/// jittering values calculated by the iffxFsr2GetJitterOffset +/// function can be applied to the camera projection matrix which is ultimately +/// used to perform transformations during vertex shading. For raytraced +/// rendering, the sub-pixel jitter should be applied to the ray's origin, +/// often the camera's position. +/// +/// Whether you elect to use the ffxFsr2GetJitterOffset function +/// or your own sequence generator, you must program the +/// jitterOffset field of the +/// FfxFsr2DispatchParameters structure in order to inform FSR2 +/// of the jitter offset that has been applied in order to render each frame. +/// +/// If not using the recommended ffxFsr2GetJitterOffset function, +/// care should be taken that your jitter sequence never generates a null vector; +/// that is value of 0 in both the X and Y dimensions. +/// +/// @param [out] outX A pointer to a float which will contain the subpixel jitter offset for the x dimension. +/// @param [out] outY A pointer to a float which will contain the subpixel jitter offset for the y dimension. +/// @param [in] index The index within the jitter sequence. +/// @param [in] phaseCount The length of jitter phase. See ffxFsr2GetJitterPhaseCount. +/// +/// @retval +/// FFX_OK The operation completed successfully. +/// @retval +/// FFX_ERROR_INVALID_POINTER Either outX or outY was NULL. +/// @retval +/// FFX_ERROR_INVALID_ARGUMENT Argument phaseCount must be greater than 0. +/// +/// @ingroup FSR2 +FFX_API FfxErrorCode ffxFsr2GetJitterOffset(float* outX, float* outY, int32_t index, int32_t phaseCount); + +/// A helper function to check if a resource is +/// FFX_FSR2_RESOURCE_IDENTIFIER_NULL. +/// +/// @param [in] resource A FfxResource. +/// +/// @returns +/// true The resource was not FFX_FSR2_RESOURCE_IDENTIFIER_NULL. +/// @returns +/// false The resource was FFX_FSR2_RESOURCE_IDENTIFIER_NULL. +/// +/// @ingroup FSR2 +FFX_API bool ffxFsr2ResourceIsNull(FfxResource resource); + +#if defined(__cplusplus) +} +#endif // #if defined(__cplusplus) diff --git a/ffx-fsr2-api/ffx_fsr2_interface.h b/ffx-fsr2-api/ffx_fsr2_interface.h new file mode 100644 index 0000000..b6be976 --- /dev/null +++ b/ffx-fsr2-api/ffx_fsr2_interface.h @@ -0,0 +1,395 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (c) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. +// +// 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_assert.h" +#include "ffx_types.h" +#include "ffx_error.h" + +// Include the FSR2 resources defined in the HLSL code. This shared here to avoid getting out of sync. +#define FFX_CPU +#include "shaders/ffx_fsr2_resources.h" +#include "shaders/ffx_fsr2_common.h" + +#if defined(__cplusplus) +extern "C" { +#endif // #if defined(__cplusplus) + +FFX_FORWARD_DECLARE(FfxFsr2Interface); + +/// An enumeration of all the passes which constitute the FSR2 algorithm. +/// +/// FSR2 is implemented as a composite of several compute passes each +/// computing a key part of the final result. Each call to the +/// FfxFsr2ScheduleGpuJobFunc callback function will +/// correspond to a single pass included in FfxFsr2Pass. For a +/// more comprehensive description of each pass, please refer to the FSR2 +/// reference documentation. +/// +/// Please note in some cases e.g.: FFX_FSR2_PASS_ACCUMULATE +/// and FFX_FSR2_PASS_ACCUMULATE_SHARPEN either one pass or the +/// other will be used (they are mutually exclusive). The choice of which will +/// depend on the way the FfxFsr2Context is created and the +/// precise contents of FfxFsr2DispatchParamters each time a call +/// is made to ffxFsr2ContextDispatch. +/// +/// @ingroup FSR2 +typedef enum FfxFsr2Pass { + + FFX_FSR2_PASS_DEPTH_CLIP = 0, ///< A pass which performs depth clipping. + FFX_FSR2_PASS_RECONSTRUCT_PREVIOUS_DEPTH = 1, ///< A pass which performs reconstruction of previous frame's depth. + FFX_FSR2_PASS_LOCK = 2, ///< A pass which calculates pixel locks. + FFX_FSR2_PASS_ACCUMULATE = 3, ///< A pass which performs upscaling. + FFX_FSR2_PASS_ACCUMULATE_SHARPEN = 4, ///< A pass which performs upscaling when sharpening is used. + FFX_FSR2_PASS_RCAS = 5, ///< A pass which performs sharpening. + FFX_FSR2_PASS_COMPUTE_LUMINANCE_PYRAMID = 6, ///< A pass which generates the luminance mipmap chain for the current frame. + FFX_FSR2_PASS_GENERATE_REACTIVE = 7, ///< An optional pass to generate a reactive mask + FFX_FSR2_PASS_TCR_AUTOGENERATE = 8, ///< An optional pass to generate a texture-and-composition and reactive masks + + FFX_FSR2_PASS_COUNT ///< The number of passes performed by FSR2. +} FfxFsr2Pass; + +typedef enum FfxFsr2MsgType { + FFX_FSR2_MESSAGE_TYPE_ERROR = 0, + FFX_FSR2_MESSAGE_TYPE_WARNING = 1, + FFX_FSR2_MESSAGE_TYPE_COUNT +} FfxFsr2MsgType; + +/// Create and initialize the backend context. +/// +/// The callback function sets up the backend context for rendering. +/// It will create or reference the device and create required internal data structures. +/// +/// @param [in] backendInterface A pointer to the backend interface. +/// @param [in] device The FfxDevice obtained by ffxGetDevice(DX12/VK/...). +/// +/// @retval +/// FFX_OK The operation completed successfully. +/// @retval +/// Anything else The operation failed. +/// +/// @ingroup FSR2 +typedef FfxErrorCode (*FfxFsr2CreateBackendContextFunc)( + FfxFsr2Interface* backendInterface, + FfxDevice device); + +/// Get a list of capabilities of the device. +/// +/// When creating an FfxFsr2Context it is desirable for the FSR2 +/// core implementation to be aware of certain characteristics of the platform +/// that is being targetted. This is because some optimizations which FSR2 +/// attempts to perform are more effective on certain classes of hardware than +/// others, or are not supported by older hardware. In order to avoid cases +/// where optimizations actually have the effect of decreasing performance, or +/// reduce the breadth of support provided by FSR2, FSR2 queries the +/// capabilities of the device to make such decisions. +/// +/// For target platforms with fixed hardware support you need not implement +/// this callback function by querying the device, but instead may hardcore +/// what features are available on the platform. +/// +/// @param [in] backendInterface A pointer to the backend interface. +/// @param [out] outDeviceCapabilities The device capabilities structure to fill out. +/// @param [in] device The device to query for capabilities. +/// +/// @retval +/// FFX_OK The operation completed successfully. +/// @retval +/// Anything else The operation failed. +/// +/// @ingroup FSR2 +typedef FfxErrorCode(*FfxFsr2GetDeviceCapabilitiesFunc)( + FfxFsr2Interface* backendInterface, + FfxDeviceCapabilities* outDeviceCapabilities, + FfxDevice device); + +/// Destroy the backend context and dereference the device. +/// +/// This function is called when the FfxFsr2Context is destroyed. +/// +/// @param [in] backendInterface A pointer to the backend interface. +/// +/// @retval +/// FFX_OK The operation completed successfully. +/// @retval +/// Anything else The operation failed. +/// +/// @ingroup FSR2 +typedef FfxErrorCode(*FfxFsr2DestroyBackendContextFunc)( + FfxFsr2Interface* backendInterface); + +/// Create a resource. +/// +/// This callback is intended for the backend to create internal resources. +/// +/// Please note: It is also possible that the creation of resources might +/// itself cause additional resources to be created by simply calling the +/// FfxFsr2CreateResourceFunc function pointer again. This is +/// useful when handling the initial creation of resources which must be +/// initialized. The flow in such a case would be an initial call to create the +/// CPU-side resource, another to create the GPU-side resource, and then a call +/// to schedule a copy render job to move the data between the two. Typically +/// this type of function call flow is only seen during the creation of an +/// FfxFsr2Context. +/// +/// @param [in] backendInterface A pointer to the backend interface. +/// @param [in] createResourceDescription A pointer to a FfxCreateResourceDescription. +/// @param [out] outResource A pointer to a FfxResource object. +/// +/// @retval +/// FFX_OK The operation completed successfully. +/// @retval +/// Anything else The operation failed. +/// +/// @ingroup FSR2 +typedef FfxErrorCode (*FfxFsr2CreateResourceFunc)( + FfxFsr2Interface* backendInterface, + const FfxCreateResourceDescription* createResourceDescription, + FfxResourceInternal* outResource); + +/// Register a resource in the backend for the current frame. +/// +/// Since FSR2 and the backend are not aware how many different +/// resources will get passed to FSR2 over time, it's not safe +/// to register all resources simultaneously in the backend. +/// Also passed resources may not be valid after the dispatch call. +/// As a result it's safest to register them as FfxResourceInternal +/// and clear them at the end of the dispatch call. +/// +/// @param [in] backendInterface A pointer to the backend interface. +/// @param [in] inResource A pointer to a FfxResource. +/// @param [out] outResource A pointer to a FfxResourceInternal object. +/// +/// @retval +/// FFX_OK The operation completed successfully. +/// @retval +/// Anything else The operation failed. +/// +/// @ingroup FSR2 +typedef FfxErrorCode(*FfxFsr2RegisterResourceFunc)( + FfxFsr2Interface* backendInterface, + const FfxResource* inResource, + FfxResourceInternal* outResource); + +/// Unregister all temporary FfxResourceInternal from the backend. +/// +/// Unregister FfxResourceInternal referencing resources passed to +/// a function as a parameter. +/// +/// @param [in] backendInterface A pointer to the backend interface. +/// +/// @retval +/// FFX_OK The operation completed successfully. +/// @retval +/// Anything else The operation failed. +/// +/// @ingroup FSR2 +typedef FfxErrorCode(*FfxFsr2UnregisterResourcesFunc)( + FfxFsr2Interface* backendInterface); + +/// Retrieve a FfxResourceDescription matching a +/// FfxResource structure. +/// +/// @param [in] backendInterface A pointer to the backend interface. +/// @param [in] resource A pointer to a FfxResource object. +/// +/// @returns +/// A description of the resource. +/// +/// @ingroup FSR2 +typedef FfxResourceDescription (*FfxFsr2GetResourceDescriptionFunc)( + FfxFsr2Interface* backendInterface, + FfxResourceInternal resource); + +/// Destroy a resource +/// +/// This callback is intended for the backend to release an internal resource. +/// +/// @param [in] backendInterface A pointer to the backend interface. +/// @param [in] resource A pointer to a FfxResource object. +/// +/// @retval +/// FFX_OK The operation completed successfully. +/// @retval +/// Anything else The operation failed. +/// +/// @ingroup FSR2 +typedef FfxErrorCode (*FfxFsr2DestroyResourceFunc)( + FfxFsr2Interface* backendInterface, + FfxResourceInternal resource); + +/// Create a render pipeline. +/// +/// A rendering pipeline contains the shader as well as resource bindpoints +/// and samplers. +/// +/// @param [in] backendInterface A pointer to the backend interface. +/// @param [in] pass The identifier for the pass. +/// @param [in] pipelineDescription A pointer to a FfxPipelineDescription describing the pipeline to be created. +/// @param [out] outPipeline A pointer to a FfxPipelineState structure which should be populated. +/// +/// @retval +/// FFX_OK The operation completed successfully. +/// @retval +/// Anything else The operation failed. +/// +/// @ingroup FSR2 +typedef FfxErrorCode (*FfxFsr2CreatePipelineFunc)( + FfxFsr2Interface* backendInterface, + FfxFsr2Pass pass, + const FfxPipelineDescription* pipelineDescription, + FfxPipelineState* outPipeline); + +/// Destroy a render pipeline. +/// +/// @param [in] backendInterface A pointer to the backend interface. +/// @param [out] pipeline A pointer to a FfxPipelineState structure which should be released. +/// +/// @retval +/// FFX_OK The operation completed successfully. +/// @retval +/// Anything else The operation failed. +/// +/// @ingroup FSR2 +typedef FfxErrorCode (*FfxFsr2DestroyPipelineFunc)( + FfxFsr2Interface* backendInterface, + FfxPipelineState* pipeline); + +/// Schedule a render job to be executed on the next call of +/// FfxFsr2ExecuteGpuJobsFunc. +/// +/// Render jobs can perform one of three different tasks: clear, copy or +/// compute dispatches. +/// +/// @param [in] backendInterface A pointer to the backend interface. +/// @param [in] job A pointer to a FfxGpuJobDescription structure. +/// +/// @retval +/// FFX_OK The operation completed successfully. +/// @retval +/// Anything else The operation failed. +/// +/// @ingroup FSR2 +typedef FfxErrorCode (*FfxFsr2ScheduleGpuJobFunc)( + FfxFsr2Interface* backendInterface, + const FfxGpuJobDescription* job); + +/// Execute scheduled render jobs on the comandList provided. +/// +/// The recording of the graphics API commands should take place in this +/// callback function, the render jobs which were previously enqueued (via +/// callbacks made to FfxFsr2ScheduleGpuJobFunc) should be +/// processed in the order they were received. Advanced users might choose to +/// reorder the rendering jobs, but should do so with care to respect the +/// resource dependencies. +/// +/// Depending on the precise contents of FfxFsr2DispatchDescription a +/// different number of render jobs might have previously been enqueued (for +/// example if sharpening is toggled on and off). +/// +/// @param [in] backendInterface A pointer to the backend interface. +/// @param [in] commandList A pointer to a FfxCommandList structure. +/// +/// @retval +/// FFX_OK The operation completed successfully. +/// @retval +/// Anything else The operation failed. +/// +/// @ingroup FSR2 +typedef FfxErrorCode (*FfxFsr2ExecuteGpuJobsFunc)( + FfxFsr2Interface* backendInterface, + FfxCommandList commandList); + +/// Pass a string message +/// +/// Used for debug messages. +/// +/// @param [in] type The type of message. +/// @param [in] message A string message to pass. +/// +/// +/// @ingroup FSR2 +typedef void(*FfxFsr2Message)( + FfxFsr2MsgType type, + const wchar_t* message); + +/// A structure encapsulating the interface between the core implentation of +/// the FSR2 algorithm and any graphics API that it should ultimately call. +/// +/// This set of functions serves as an abstraction layer between FSR2 and the +/// API used to implement it. While FSR2 ships with backends for DirectX12 and +/// Vulkan, it is possible to implement your own backend for other platforms or +/// which sits ontop of your engine's own abstraction layer. For details on the +/// expectations of what each function should do you should refer the +/// description of the following function pointer types: +/// +/// FfxFsr2CreateDeviceFunc +/// FfxFsr2GetDeviceCapabilitiesFunc +/// FfxFsr2DestroyDeviceFunc +/// FfxFsr2CreateResourceFunc +/// FfxFsr2GetResourceDescriptionFunc +/// FfxFsr2DestroyResourceFunc +/// FfxFsr2CreatePipelineFunc +/// FfxFsr2DestroyPipelineFunc +/// FfxFsr2ScheduleGpuJobFunc +/// FfxFsr2ExecuteGpuJobsFunc +/// +/// Depending on the graphics API that is abstracted by the backend, it may be +/// required that the backend is to some extent stateful. To ensure that +/// applications retain full control to manage the memory used by FSR2, the +/// scratchBuffer and scratchBufferSize fields are +/// provided. A backend should provide a means of specifying how much scratch +/// memory is required for its internal implementation (e.g: via a function +/// or constant value). The application is that responsible for allocating that +/// memory and providing it when setting up the FSR2 backend. Backends provided +/// with FSR2 do not perform dynamic memory allocations, and instead +/// suballocate all memory from the scratch buffers provided. +/// +/// The scratchBuffer and scratchBufferSize fields +/// should be populated according to the requirements of each backend. For +/// example, if using the DirectX 12 backend you should call the +/// ffxFsr2GetScratchMemorySizeDX12 function. It is not required +/// that custom backend implementations use a scratch buffer. +/// +/// @ingroup FSR2 +typedef struct FfxFsr2Interface { + + FfxFsr2CreateBackendContextFunc fpCreateBackendContext; ///< A callback function to create and initialize the backend context. + FfxFsr2GetDeviceCapabilitiesFunc fpGetDeviceCapabilities; ///< A callback function to query device capabilites. + FfxFsr2DestroyBackendContextFunc fpDestroyBackendContext; ///< A callback function to destroy the backendcontext. This also dereferences the device. + FfxFsr2CreateResourceFunc fpCreateResource; ///< A callback function to create a resource. + FfxFsr2RegisterResourceFunc fpRegisterResource; ///< A callback function to register an external resource. + FfxFsr2UnregisterResourcesFunc fpUnregisterResources; ///< A callback function to unregister external resource. + FfxFsr2GetResourceDescriptionFunc fpGetResourceDescription; ///< A callback function to retrieve a resource description. + FfxFsr2DestroyResourceFunc fpDestroyResource; ///< A callback function to destroy a resource. + FfxFsr2CreatePipelineFunc fpCreatePipeline; ///< A callback function to create a render or compute pipeline. + FfxFsr2DestroyPipelineFunc fpDestroyPipeline; ///< A callback function to destroy a render or compute pipeline. + FfxFsr2ScheduleGpuJobFunc fpScheduleGpuJob; ///< A callback function to schedule a render job. + FfxFsr2ExecuteGpuJobsFunc fpExecuteGpuJobs; ///< A callback function to execute all queued render jobs. + + void* scratchBuffer; ///< A preallocated buffer for memory utilized internally by the backend. + size_t scratchBufferSize; ///< Size of the buffer pointed to by scratchBuffer. +} FfxFsr2Interface; + +#if defined(__cplusplus) +} +#endif // #if defined(__cplusplus) diff --git a/ffx-fsr2-api/ffx_fsr2_maximum_bias.h b/ffx-fsr2-api/ffx_fsr2_maximum_bias.h new file mode 100644 index 0000000..5fdbd0c --- /dev/null +++ b/ffx-fsr2-api/ffx_fsr2_maximum_bias.h @@ -0,0 +1,46 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (c) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. +// +// 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. + +// @internal + +#pragma once + +static const int FFX_FSR2_MAXIMUM_BIAS_TEXTURE_WIDTH = 16; +static const int FFX_FSR2_MAXIMUM_BIAS_TEXTURE_HEIGHT = 16; +static const float ffxFsr2MaximumBias[] = { + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.876f, 1.809f, 1.772f, 1.753f, 1.748f, + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.869f, 1.801f, 1.764f, 1.745f, 1.739f, + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.976f, 1.841f, 1.774f, 1.737f, 1.716f, 1.71f, + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.914f, 1.784f, 1.716f, 1.673f, 1.649f, 1.641f, + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.793f, 1.676f, 1.604f, 1.562f, 1.54f, 1.533f, + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.802f, 1.619f, 1.536f, 1.492f, 1.467f, 1.454f, 1.449f, + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.812f, 1.575f, 1.496f, 1.456f, 1.432f, 1.416f, 1.408f, 1.405f, + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.555f, 1.479f, 1.438f, 1.413f, 1.398f, 1.387f, 1.381f, 1.379f, + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.812f, 1.555f, 1.474f, 1.43f, 1.404f, 1.387f, 1.376f, 1.368f, 1.363f, 1.362f, + 2.0f, 2.0f, 2.0f, 2.0f, 2.0f, 1.802f, 1.575f, 1.479f, 1.43f, 1.401f, 1.382f, 1.369f, 1.36f, 1.354f, 1.351f, 1.35f, + 2.0f, 2.0f, 1.976f, 1.914f, 1.793f, 1.619f, 1.496f, 1.438f, 1.404f, 1.382f, 1.367f, 1.357f, 1.349f, 1.344f, 1.341f, 1.34f, + 1.876f, 1.869f, 1.841f, 1.784f, 1.676f, 1.536f, 1.456f, 1.413f, 1.387f, 1.369f, 1.357f, 1.347f, 1.341f, 1.336f, 1.333f, 1.332f, + 1.809f, 1.801f, 1.774f, 1.716f, 1.604f, 1.492f, 1.432f, 1.398f, 1.376f, 1.36f, 1.349f, 1.341f, 1.335f, 1.33f, 1.328f, 1.327f, + 1.772f, 1.764f, 1.737f, 1.673f, 1.562f, 1.467f, 1.416f, 1.387f, 1.368f, 1.354f, 1.344f, 1.336f, 1.33f, 1.326f, 1.323f, 1.323f, + 1.753f, 1.745f, 1.716f, 1.649f, 1.54f, 1.454f, 1.408f, 1.381f, 1.363f, 1.351f, 1.341f, 1.333f, 1.328f, 1.323f, 1.321f, 1.32f, + 1.748f, 1.739f, 1.71f, 1.641f, 1.533f, 1.449f, 1.405f, 1.379f, 1.362f, 1.35f, 1.34f, 1.332f, 1.327f, 1.323f, 1.32f, 1.319f, + +}; diff --git a/ffx-fsr2-api/ffx_fsr2_private.h b/ffx-fsr2-api/ffx_fsr2_private.h new file mode 100644 index 0000000..6b5fbc5 --- /dev/null +++ b/ffx-fsr2-api/ffx_fsr2_private.h @@ -0,0 +1,81 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (c) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. +// +// 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 + +// Constants for FSR2 DX12 dispatches. Must be kept in sync with cbFSR2 in ffx_fsr2_callbacks_hlsl.h +typedef struct Fsr2Constants { + + int32_t renderSize[2]; + int32_t maxRenderSize[2]; + int32_t displaySize[2]; + int32_t inputColorResourceDimensions[2]; + int32_t lumaMipDimensions[2]; + int32_t lumaMipLevelToUse; + int32_t frameIndex; + + float deviceToViewDepth[4]; + float jitterOffset[2]; + float motionVectorScale[2]; + float downscaleFactor[2]; + float motionVectorJitterCancellation[2]; + float preExposure; + float previousFramePreExposure; + float tanHalfFOV; + float jitterPhaseCount; + float deltaTime; + float dynamicResChangeFactor; + float viewSpaceToMetersFactor; +} Fsr2Constants; + +struct FfxFsr2ContextDescription; +struct FfxDeviceCapabilities; +struct FfxPipelineState; +struct FfxResource; + +// FfxFsr2Context_Private +// The private implementation of the FSR2 context. +typedef struct FfxFsr2Context_Private { + + FfxFsr2ContextDescription contextDescription; + Fsr2Constants constants; + FfxDevice device; + FfxDeviceCapabilities deviceCapabilities; + FfxPipelineState pipelineDepthClip; + FfxPipelineState pipelineReconstructPreviousDepth; + FfxPipelineState pipelineLock; + FfxPipelineState pipelineAccumulate; + FfxPipelineState pipelineAccumulateSharpen; + FfxPipelineState pipelineRCAS; + FfxPipelineState pipelineComputeLuminancePyramid; + FfxPipelineState pipelineGenerateReactive; + FfxPipelineState pipelineTcrAutogenerate; + + // 2 arrays of resources, as e.g. FFX_FSR2_RESOURCE_IDENTIFIER_LOCK_STATUS will use different resources when bound as SRV vs when bound as UAV + FfxResourceInternal srvResources[FFX_FSR2_RESOURCE_IDENTIFIER_COUNT]; + FfxResourceInternal uavResources[FFX_FSR2_RESOURCE_IDENTIFIER_COUNT]; + + bool firstExecution; + bool refreshPipelineStates; + uint32_t resourceFrameIndex; + float previousJitterOffset[2]; + int32_t jitterPhaseCountRemaining; +} FfxFsr2Context_Private; diff --git a/ffx-fsr2-api/ffx_types.h b/ffx-fsr2-api/ffx_types.h new file mode 100644 index 0000000..74edd19 --- /dev/null +++ b/ffx-fsr2-api/ffx_types.h @@ -0,0 +1,364 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (c) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. +// +// 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 + +#if defined (FFX_GCC) +/// FidelityFX exported functions +#define FFX_API +#else +/// FidelityFX exported functions +#define FFX_API __declspec(dllexport) +#endif // #if defined (FFX_GCC) + +/// Maximum supported number of simultaneously bound SRVs. +#define FFX_MAX_NUM_SRVS 16 + +/// Maximum supported number of simultaneously bound UAVs. +#define FFX_MAX_NUM_UAVS 8 + +/// Maximum number of constant buffers bound. +#define FFX_MAX_NUM_CONST_BUFFERS 2 + +/// Maximum size of bound constant buffers. +#define FFX_MAX_CONST_SIZE 64 + +/// Off by default warnings +#if defined(_MSC_VER) +#pragma warning(disable : 4365 4710 4820 5039) +#elif defined(__clang__) +#pragma clang diagnostic ignored "-Wunused-parameter" +#pragma clang diagnostic ignored "-Wmissing-field-initializers" +#pragma clang diagnostic ignored "-Wsign-compare" +#pragma clang diagnostic ignored "-Wunused-function" +#pragma clang diagnostic ignored "-Wignored-qualifiers" +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wunused-function" +#endif + +#ifdef __cplusplus +extern "C" { +#endif // #ifdef __cplusplus + +/// An enumeration of surface formats. +typedef enum FfxSurfaceFormat { + + FFX_SURFACE_FORMAT_UNKNOWN, ///< Unknown format + FFX_SURFACE_FORMAT_R32G32B32A32_TYPELESS, ///< 32 bit per channel, 4 channel typeless format + FFX_SURFACE_FORMAT_R32G32B32A32_FLOAT, ///< 32 bit per channel, 4 channel float format + FFX_SURFACE_FORMAT_R16G16B16A16_FLOAT, ///< 16 bit per channel, 4 channel float format + FFX_SURFACE_FORMAT_R16G16B16A16_UNORM, ///< 16 bit per channel, 4 channel unsigned normalized format + FFX_SURFACE_FORMAT_R32G32_FLOAT, ///< 32 bit per channel, 2 channel float format + FFX_SURFACE_FORMAT_R32_UINT, ///< 32 bit per channel, 1 channel float format + FFX_SURFACE_FORMAT_R8G8B8A8_TYPELESS, ///< 8 bit per channel, 4 channel float format + FFX_SURFACE_FORMAT_R8G8B8A8_UNORM, ///< 8 bit per channel, 4 channel unsigned normalized format + FFX_SURFACE_FORMAT_R11G11B10_FLOAT, ///< 32 bit 3 channel float format + FFX_SURFACE_FORMAT_R16G16_FLOAT, ///< 16 bit per channel, 2 channel float format + FFX_SURFACE_FORMAT_R16G16_UINT, ///< 16 bit per channel, 2 channel unsigned int format + FFX_SURFACE_FORMAT_R16_FLOAT, ///< 16 bit per channel, 1 channel float format + FFX_SURFACE_FORMAT_R16_UINT, ///< 16 bit per channel, 1 channel unsigned int format + FFX_SURFACE_FORMAT_R16_UNORM, ///< 16 bit per channel, 1 channel unsigned normalized format + FFX_SURFACE_FORMAT_R16_SNORM, ///< 16 bit per channel, 1 channel signed normalized format + FFX_SURFACE_FORMAT_R8_UNORM, ///< 8 bit per channel, 1 channel unsigned normalized format + FFX_SURFACE_FORMAT_R8_UINT, ///< 8 bit per channel, 1 channel unsigned int format + FFX_SURFACE_FORMAT_R8G8_UNORM, ///< 8 bit per channel, 2 channel unsigned normalized format + FFX_SURFACE_FORMAT_R32_FLOAT ///< 32 bit per channel, 1 channel float format +} FfxSurfaceFormat; + +/// An enumeration of resource usage. +typedef enum FfxResourceUsage { + + FFX_RESOURCE_USAGE_READ_ONLY = 0, ///< No usage flags indicate a resource is read only. + FFX_RESOURCE_USAGE_RENDERTARGET = (1<<0), ///< Indicates a resource will be used as render target. + FFX_RESOURCE_USAGE_UAV = (1<<1), ///< Indicates a resource will be used as UAV. +} FfxResourceUsage; + +/// An enumeration of resource states. +typedef enum FfxResourceStates { + + FFX_RESOURCE_STATE_UNORDERED_ACCESS = (1<<0), ///< Indicates a resource is in the state to be used as UAV. + FFX_RESOURCE_STATE_COMPUTE_READ = (1 << 1), ///< Indicates a resource is in the state to be read by compute shaders. + FFX_RESOURCE_STATE_COPY_SRC = (1 << 2), ///< Indicates a resource is in the state to be used as source in a copy command. + FFX_RESOURCE_STATE_COPY_DEST = (1 << 3), ///< Indicates a resource is in the state to be used as destination in a copy command. + FFX_RESOURCE_STATE_GENERIC_READ = (FFX_RESOURCE_STATE_COPY_SRC | FFX_RESOURCE_STATE_COMPUTE_READ), ///< Indicates a resource is in generic (slow) read state. +} FfxResourceStates; + +/// An enumeration of surface dimensions. +typedef enum FfxResourceDimension { + + FFX_RESOURCE_DIMENSION_TEXTURE_1D, ///< A resource with a single dimension. + FFX_RESOURCE_DIMENSION_TEXTURE_2D, ///< A resource with two dimensions. +} FfxResourceDimension; + +/// An enumeration of surface dimensions. +typedef enum FfxResourceFlags { + + FFX_RESOURCE_FLAGS_NONE = 0, ///< No flags. + FFX_RESOURCE_FLAGS_ALIASABLE = (1<<0), ///< A bit indicating a resource does not need to persist across frames. +} FfxResourceFlags; + +/// An enumeration of all resource view types. +typedef enum FfxResourceViewType { + + FFX_RESOURCE_VIEW_UNORDERED_ACCESS, ///< The resource view is an unordered access view (UAV). + FFX_RESOURCE_VIEW_SHADER_READ, ///< The resource view is a shader resource view (SRV). +} FfxResourceViewType; + +/// The type of filtering to perform when reading a texture. +typedef enum FfxFilterType { + + FFX_FILTER_TYPE_POINT, ///< Point sampling. + FFX_FILTER_TYPE_LINEAR ///< Sampling with interpolation. +} FfxFilterType; + +/// An enumeration of all supported shader models. +typedef enum FfxShaderModel { + + FFX_SHADER_MODEL_5_1, ///< Shader model 5.1. + FFX_SHADER_MODEL_6_0, ///< Shader model 6.0. + FFX_SHADER_MODEL_6_1, ///< Shader model 6.1. + FFX_SHADER_MODEL_6_2, ///< Shader model 6.2. + FFX_SHADER_MODEL_6_3, ///< Shader model 6.3. + FFX_SHADER_MODEL_6_4, ///< Shader model 6.4. + FFX_SHADER_MODEL_6_5, ///< Shader model 6.5. + FFX_SHADER_MODEL_6_6, ///< Shader model 6.6. + FFX_SHADER_MODEL_6_7, ///< Shader model 6.7. +} FfxShaderModel; + +// An enumeration for different resource types +typedef enum FfxResourceType { + + FFX_RESOURCE_TYPE_BUFFER, ///< The resource is a buffer. + FFX_RESOURCE_TYPE_TEXTURE1D, ///< The resource is a 1-dimensional texture. + FFX_RESOURCE_TYPE_TEXTURE2D, ///< The resource is a 2-dimensional texture. + FFX_RESOURCE_TYPE_TEXTURE3D, ///< The resource is a 3-dimensional texture. +} FfxResourceType; + +/// An enumeration for different heap types +typedef enum FfxHeapType { + + FFX_HEAP_TYPE_DEFAULT = 0, ///< Local memory. + FFX_HEAP_TYPE_UPLOAD ///< Heap used for uploading resources. +} FfxHeapType; + +/// An enumberation for different render job types +typedef enum FfxGpuJobType { + + FFX_GPU_JOB_CLEAR_FLOAT = 0, ///< The GPU job is performing a floating-point clear. + FFX_GPU_JOB_COPY = 1, ///< The GPU job is performing a copy. + FFX_GPU_JOB_COMPUTE = 2, ///< The GPU job is performing a compute dispatch. +} FfxGpuJobType; + +/// A typedef representing the graphics device. +typedef void* FfxDevice; + +/// A typedef representing a command list or command buffer. +typedef void* FfxCommandList; + +/// A typedef for a root signature. +typedef void* FfxRootSignature; + +/// A typedef for a pipeline state object. +typedef void* FfxPipeline; + +/// A structure encapasulating a collection of device capabilities. +typedef struct FfxDeviceCapabilities { + + FfxShaderModel minimumSupportedShaderModel; ///< The minimum shader model supported by the device. + uint32_t waveLaneCountMin; ///< The minimum supported wavefront width. + uint32_t waveLaneCountMax; ///< The maximum supported wavefront width. + bool fp16Supported; ///< The device supports FP16 in hardware. + bool raytracingSupported; ///< The device supports raytracing. +} FfxDeviceCapabilities; + +/// A structure encapsulating a 2-dimensional point, using 32bit unsigned integers. +typedef struct FfxDimensions2D { + + uint32_t width; ///< The width of a 2-dimensional range. + uint32_t height; ///< The height of a 2-dimensional range. +} FfxDimensions2D; + +/// A structure encapsulating a 2-dimensional point, +typedef struct FfxIntCoords2D { + + int32_t x; ///< The x coordinate of a 2-dimensional point. + int32_t y; ///< The y coordinate of a 2-dimensional point. +} FfxIntCoords2D; + +/// A structure encapsulating a 2-dimensional set of floating point coordinates. +typedef struct FfxFloatCoords2D { + + float x; ///< The x coordinate of a 2-dimensional point. + float y; ///< The y coordinate of a 2-dimensional point. +} FfxFloatCoords2D; + +/// A structure describing a resource. +typedef struct FfxResourceDescription { + + FfxResourceType type; ///< The type of the resource. + FfxSurfaceFormat format; ///< The surface format. + uint32_t width; ///< The width of the resource. + uint32_t height; ///< The height of the resource. + uint32_t depth; ///< The depth of the resource. + uint32_t mipCount; ///< Number of mips (or 0 for full mipchain). + FfxResourceFlags flags; ///< A set of FfxResourceFlags flags. +} FfxResourceDescription; + +/// An outward facing structure containing a resource +typedef struct FfxResource { + void* resource; ///< pointer to the resource. + wchar_t name[64]; + FfxResourceDescription description; + FfxResourceStates state; + bool isDepth; + uint64_t descriptorData; +} FfxResource; + +/// An internal structure containing a handle to a resource and resource views +typedef struct FfxResourceInternal { + int32_t internalIndex; ///< The index of the resource. +} FfxResourceInternal; + + +/// A structure defining a resource bind point +typedef struct FfxResourceBinding +{ + uint32_t slotIndex; + uint32_t resourceIdentifier; + wchar_t name[64]; +}FfxResourceBinding; + +/// A structure encapsulating a single pass of an algorithm. +typedef struct FfxPipelineState { + + FfxRootSignature rootSignature; ///< The pipelines rootSignature + FfxPipeline pipeline; ///< The pipeline object + uint32_t uavCount; ///< Count of UAVs used in this pipeline + uint32_t srvCount; ///< Count of SRVs used in this pipeline + uint32_t constCount; ///< Count of constant buffers used in this pipeline + + FfxResourceBinding uavResourceBindings[FFX_MAX_NUM_UAVS]; ///< Array of ResourceIdentifiers bound as UAVs + FfxResourceBinding srvResourceBindings[FFX_MAX_NUM_SRVS]; ///< Array of ResourceIdentifiers bound as SRVs + FfxResourceBinding cbResourceBindings[FFX_MAX_NUM_CONST_BUFFERS]; ///< Array of ResourceIdentifiers bound as CBs +} FfxPipelineState; + +/// A structure containing the data required to create a resource. +typedef struct FfxCreateResourceDescription { + + FfxHeapType heapType; ///< The heap type to hold the resource, typically FFX_HEAP_TYPE_DEFAULT. + FfxResourceDescription resourceDescription; ///< A resource description. + FfxResourceStates initalState; ///< The initial resource state. + uint32_t initDataSize; ///< Size of initial data buffer. + void* initData; ///< Buffer containing data to fill the resource. + const wchar_t* name; ///< Name of the resource. + FfxResourceUsage usage; ///< Resource usage flags. + uint32_t id; ///< Internal resource ID. +} FfxCreateResourceDescription; + +/// A structure containing the description used to create a +/// FfxPipeline structure. +/// +/// A pipeline is the name given to a shader and the collection of state that +/// is required to dispatch it. In the context of FSR2 and its architecture +/// this means that a FfxPipelineDescription will map to either a +/// monolithic object in an explicit API (such as a +/// PipelineStateObject in DirectX 12). Or a shader and some +/// ancillary API objects (in something like DirectX 11). +/// +/// The contextFlags field contains a copy of the flags passed +/// to ffxFsr2ContextCreate via the flags field of +/// the FfxFsr2InitializationParams structure. These flags are +/// used to determine which permutation of a pipeline for a specific +/// FfxFsr2Pass should be used to implement the features required +/// by each application, as well as to acheive the best performance on specific +/// target hardware configurations. +/// +/// When using one of the provided backends for FSR2 (such as DirectX 12 or +/// Vulkan) the data required to create a pipeline is compiled offline and +/// included into the backend library that you are using. For cases where the +/// backend interface is overriden by providing custom callback function +/// implementations care should be taken to respect the contents of the +/// contextFlags field in order to correctly support the options +/// provided by FSR2, and acheive best performance. +/// +/// @ingroup FSR2 +typedef struct FfxPipelineDescription { + + uint32_t contextFlags; ///< A collection of FfxFsr2InitializationFlagBits which were passed to the context. + FfxFilterType* samplers; ///< Array of static samplers. + size_t samplerCount; ///< The number of samples contained inside samplers. + const uint32_t* rootConstantBufferSizes; ///< Array containing the sizes of the root constant buffers (count of 32 bit elements). + uint32_t rootConstantBufferCount; ///< The number of root constants contained within rootConstantBufferSizes. +} FfxPipelineDescription; + +/// A structure containing a constant buffer. +typedef struct FfxConstantBuffer { + + uint32_t uint32Size; ///< Size of 32 bit chunks used in the constant buffer + uint32_t data[FFX_MAX_CONST_SIZE]; ///< Constant buffer data +}FfxConstantBuffer; + +/// A structure describing a clear render job. +typedef struct FfxClearFloatJobDescription { + + float color[4]; ///< The clear color of the resource. + FfxResourceInternal target; ///< The resource to be cleared. +} FfxClearFloatJobDescription; + +/// A structure describing a compute render job. +typedef struct FfxComputeJobDescription { + + FfxPipelineState pipeline; ///< Compute pipeline for the render job. + uint32_t dimensions[3]; ///< Dispatch dimensions. + FfxResourceInternal srvs[FFX_MAX_NUM_SRVS]; ///< SRV resources to be bound in the compute job. + wchar_t srvNames[FFX_MAX_NUM_SRVS][64]; + FfxResourceInternal uavs[FFX_MAX_NUM_UAVS]; ///< UAV resources to be bound in the compute job. + uint32_t uavMip[FFX_MAX_NUM_UAVS]; ///< Mip level of UAV resources to be bound in the compute job. + wchar_t uavNames[FFX_MAX_NUM_UAVS][64]; + FfxConstantBuffer cbs[FFX_MAX_NUM_CONST_BUFFERS]; ///< Constant buffers to be bound in the compute job. + wchar_t cbNames[FFX_MAX_NUM_CONST_BUFFERS][64]; + uint32_t cbSlotIndex[FFX_MAX_NUM_CONST_BUFFERS]; ///< Slot index in the descriptor table +} FfxComputeJobDescription; + +/// A structure describing a copy render job. +typedef struct FfxCopyJobDescription +{ + FfxResourceInternal src; ///< Source resource for the copy. + FfxResourceInternal dst; ///< Destination resource for the copy. +} FfxCopyJobDescription; + +/// A structure describing a single render job. +typedef struct FfxGpuJobDescription{ + + FfxGpuJobType jobType; ///< Type of the job. + + union { + FfxClearFloatJobDescription clearJobDescriptor; ///< Clear job descriptor. Valid when jobType is FFX_RENDER_JOB_CLEAR_FLOAT. + FfxCopyJobDescription copyJobDescriptor; ///< Copy job descriptor. Valid when jobType is FFX_RENDER_JOB_COPY. + FfxComputeJobDescription computeJobDescriptor; ///< Compute job descriptor. Valid when jobType is FFX_RENDER_JOB_COMPUTE. + }; +} FfxGpuJobDescription; + +#ifdef __cplusplus +} +#endif // #ifdef __cplusplus diff --git a/ffx-fsr2-api/ffx_util.h b/ffx-fsr2-api/ffx_util.h new file mode 100644 index 0000000..ca4324e --- /dev/null +++ b/ffx-fsr2-api/ffx_util.h @@ -0,0 +1,78 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (c) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. +// +// 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_types.h" + +/// The value of Pi. +const float FFX_PI = 3.141592653589793f; + +/// An epsilon value for floating point numbers. +const float FFX_EPSILON = 1e-06f; + +/// Helper macro to create the version number. +#define FFX_MAKE_VERSION(major, minor, patch) ((major << 22) | (minor << 12) | patch) + +///< Use this to specify no version. +#define FFX_UNSPECIFIED_VERSION 0xFFFFAD00 + +/// Helper macro to avoid warnings about unused variables. +#define FFX_UNUSED(x) ((void)(x)) + +/// Helper macro to align an integer to the specified power of 2 boundary +#define FFX_ALIGN_UP(x, y) (((x) + ((y)-1)) & ~((y)-1)) + +/// Helper macro to check if a value is aligned. +#define FFX_IS_ALIGNED(x) (((x) != 0) && ((x) & ((x)-1))) + +/// Helper macro to stringify a value. +#define FFX_STR(s) FFX_XSTR(s) +#define FFX_XSTR(s) #s + +/// Helper macro to forward declare a structure. +#define FFX_FORWARD_DECLARE(x) typedef struct x x + +/// Helper macro to return the maximum of two values. +#define FFX_MAXIMUM(x, y) (((x) > (y)) ? (x) : (y)) + +/// Helper macro to return the minimum of two values. +#define FFX_MINIMUM(x, y) (((x) < (y)) ? (x) : (y)) + +/// Helper macro to do safe free on a pointer. +#define FFX_SAFE_FREE(x) \ + if (x) \ + free(x) + +/// Helper macro to return the abs of an integer value. +#define FFX_ABSOLUTE(x) (((x) < 0) ? (-(x)) : (x)) + +/// Helper macro to return sign of a value. +#define FFX_SIGN(x) (((x) < 0) ? -1 : 1) + +/// Helper macro to work out the number of elements in an array. +#define FFX_ARRAY_ELEMENTS(x) (int32_t)((sizeof(x) / sizeof(0 [x])) / ((size_t)(!(sizeof(x) % sizeof(0 [x]))))) + +/// The maximum length of a path that can be specified to the FidelityFX API. +#define FFX_MAXIMUM_PATH (260) + +/// Helper macro to check if the specified key is set in a bitfield. +#define FFX_CONTAINS_FLAG(options, key) ((options & key) == key) diff --git a/ffx-fsr2-api/shaders/ffx_fsr2_common.h b/ffx-fsr2-api/shaders/ffx_fsr2_common.h new file mode 100644 index 0000000..0c72aa8 --- /dev/null +++ b/ffx-fsr2-api/shaders/ffx_fsr2_common.h @@ -0,0 +1,565 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (c) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. +// +// 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. + +#if !defined(FFX_FSR2_COMMON_H) +#define FFX_FSR2_COMMON_H + +#if defined(FFX_CPU) || defined(FFX_GPU) +//Locks +#define LOCK_LIFETIME_REMAINING 0 +#define LOCK_TEMPORAL_LUMA 1 +#endif // #if defined(FFX_CPU) || defined(FFX_GPU) + +#if defined(FFX_GPU) +FFX_STATIC const FfxFloat32 FSR2_FP16_MIN = 6.10e-05f; +FFX_STATIC const FfxFloat32 FSR2_FP16_MAX = 65504.0f; +FFX_STATIC const FfxFloat32 FSR2_EPSILON = 1e-03f; +FFX_STATIC const FfxFloat32 FSR2_TONEMAP_EPSILON = 1.0f / FSR2_FP16_MAX; +FFX_STATIC const FfxFloat32 FSR2_FLT_MAX = 3.402823466e+38f; +FFX_STATIC const FfxFloat32 FSR2_FLT_MIN = 1.175494351e-38f; + +// treat vector truncation warnings as errors +#pragma warning(error: 3206) + +// suppress warnings +#pragma warning(disable: 3205) // conversion from larger type to smaller +#pragma warning(disable: 3571) // in ffxPow(f, e), f could be negative + +// Reconstructed depth usage +FFX_STATIC const FfxFloat32 fReconstructedDepthBilinearWeightThreshold = 0.01f; + +// Accumulation +FFX_STATIC const FfxFloat32 fUpsampleLanczosWeightScale = 1.0f / 12.0f; +FFX_STATIC const FfxFloat32 fMaxAccumulationLanczosWeight = 1.0f; +FFX_STATIC const FfxFloat32 fAverageLanczosWeightPerFrame = 0.74f * fUpsampleLanczosWeightScale; // Average lanczos weight for jitter accumulated samples +FFX_STATIC const FfxFloat32 fAccumulationMaxOnMotion = 3.0f * fUpsampleLanczosWeightScale; + +// Auto exposure +FFX_STATIC const FfxFloat32 resetAutoExposureAverageSmoothing = 1e8f; + +struct AccumulationPassCommonParams +{ + FfxInt32x2 iPxHrPos; + FfxFloat32x2 fHrUv; + FfxFloat32x2 fLrUv_HwSampler; + FfxFloat32x2 fMotionVector; + FfxFloat32x2 fReprojectedHrUv; + FfxFloat32 fHrVelocity; + FfxFloat32 fDepthClipFactor; + FfxFloat32 fDilatedReactiveFactor; + FfxFloat32 fAccumulationMask; + + FfxBoolean bIsResetFrame; + FfxBoolean bIsExistingSample; + FfxBoolean bIsNewSample; +}; + +struct LockState +{ + FfxBoolean NewLock; //Set for both unique new and re-locked new + FfxBoolean WasLockedPrevFrame; //Set to identify if the pixel was already locked (relock) +}; + +void InitializeNewLockSample(FFX_PARAMETER_OUT FfxFloat32x2 fLockStatus) +{ + fLockStatus = FfxFloat32x2(0, 0); +} + +#if FFX_HALF +void InitializeNewLockSample(FFX_PARAMETER_OUT FFX_MIN16_F2 fLockStatus) +{ + fLockStatus = FFX_MIN16_F2(0, 0); +} +#endif + + +void KillLock(FFX_PARAMETER_INOUT FfxFloat32x2 fLockStatus) +{ + fLockStatus[LOCK_LIFETIME_REMAINING] = 0; +} + +#if FFX_HALF +void KillLock(FFX_PARAMETER_INOUT FFX_MIN16_F2 fLockStatus) +{ + fLockStatus[LOCK_LIFETIME_REMAINING] = FFX_MIN16_F(0); +} +#endif + +struct RectificationBox +{ + FfxFloat32x3 boxCenter; + FfxFloat32x3 boxVec; + FfxFloat32x3 aabbMin; + FfxFloat32x3 aabbMax; + FfxFloat32 fBoxCenterWeight; +}; +#if FFX_HALF +struct RectificationBoxMin16 +{ + FFX_MIN16_F3 boxCenter; + FFX_MIN16_F3 boxVec; + FFX_MIN16_F3 aabbMin; + FFX_MIN16_F3 aabbMax; + FFX_MIN16_F fBoxCenterWeight; +}; +#endif + +void RectificationBoxReset(FFX_PARAMETER_INOUT RectificationBox rectificationBox) +{ + rectificationBox.fBoxCenterWeight = FfxFloat32(0); + + rectificationBox.boxCenter = FfxFloat32x3(0, 0, 0); + rectificationBox.boxVec = FfxFloat32x3(0, 0, 0); + rectificationBox.aabbMin = FfxFloat32x3(FSR2_FLT_MAX, FSR2_FLT_MAX, FSR2_FLT_MAX); + rectificationBox.aabbMax = -FfxFloat32x3(FSR2_FLT_MAX, FSR2_FLT_MAX, FSR2_FLT_MAX); +} +#if FFX_HALF +void RectificationBoxReset(FFX_PARAMETER_INOUT RectificationBoxMin16 rectificationBox) +{ + rectificationBox.fBoxCenterWeight = FFX_MIN16_F(0); + + rectificationBox.boxCenter = FFX_MIN16_F3(0, 0, 0); + rectificationBox.boxVec = FFX_MIN16_F3(0, 0, 0); + rectificationBox.aabbMin = FFX_MIN16_F3(FSR2_FP16_MAX, FSR2_FP16_MAX, FSR2_FP16_MAX); + rectificationBox.aabbMax = -FFX_MIN16_F3(FSR2_FP16_MAX, FSR2_FP16_MAX, FSR2_FP16_MAX); +} +#endif + +void RectificationBoxAddInitialSample(FFX_PARAMETER_INOUT RectificationBox rectificationBox, const FfxFloat32x3 colorSample, const FfxFloat32 fSampleWeight) +{ + rectificationBox.aabbMin = colorSample; + rectificationBox.aabbMax = colorSample; + + FfxFloat32x3 weightedSample = colorSample * fSampleWeight; + rectificationBox.boxCenter = weightedSample; + rectificationBox.boxVec = colorSample * weightedSample; + rectificationBox.fBoxCenterWeight = fSampleWeight; +} + +void RectificationBoxAddSample(FfxBoolean bInitialSample, FFX_PARAMETER_INOUT RectificationBox rectificationBox, const FfxFloat32x3 colorSample, const FfxFloat32 fSampleWeight) +{ + if (bInitialSample) { + RectificationBoxAddInitialSample(rectificationBox, colorSample, fSampleWeight); + } else { + rectificationBox.aabbMin = ffxMin(rectificationBox.aabbMin, colorSample); + rectificationBox.aabbMax = ffxMax(rectificationBox.aabbMax, colorSample); + + FfxFloat32x3 weightedSample = colorSample * fSampleWeight; + rectificationBox.boxCenter += weightedSample; + rectificationBox.boxVec += colorSample * weightedSample; + rectificationBox.fBoxCenterWeight += fSampleWeight; + } +} +#if FFX_HALF +void RectificationBoxAddInitialSample(FFX_PARAMETER_INOUT RectificationBoxMin16 rectificationBox, const FFX_MIN16_F3 colorSample, const FFX_MIN16_F fSampleWeight) +{ + rectificationBox.aabbMin = colorSample; + rectificationBox.aabbMax = colorSample; + + FFX_MIN16_F3 weightedSample = colorSample * fSampleWeight; + rectificationBox.boxCenter = weightedSample; + rectificationBox.boxVec = colorSample * weightedSample; + rectificationBox.fBoxCenterWeight = fSampleWeight; +} + +void RectificationBoxAddSample(FfxBoolean bInitialSample, FFX_PARAMETER_INOUT RectificationBoxMin16 rectificationBox, const FFX_MIN16_F3 colorSample, const FFX_MIN16_F fSampleWeight) +{ + if (bInitialSample) { + RectificationBoxAddInitialSample(rectificationBox, colorSample, fSampleWeight); + } else { + rectificationBox.aabbMin = ffxMin(rectificationBox.aabbMin, colorSample); + rectificationBox.aabbMax = ffxMax(rectificationBox.aabbMax, colorSample); + + FFX_MIN16_F3 weightedSample = colorSample * fSampleWeight; + rectificationBox.boxCenter += weightedSample; + rectificationBox.boxVec += colorSample * weightedSample; + rectificationBox.fBoxCenterWeight += fSampleWeight; + } +} +#endif + +void RectificationBoxComputeVarianceBoxData(FFX_PARAMETER_INOUT RectificationBox rectificationBox) +{ + rectificationBox.fBoxCenterWeight = (abs(rectificationBox.fBoxCenterWeight) > FfxFloat32(FSR2_EPSILON) ? rectificationBox.fBoxCenterWeight : FfxFloat32(1.f)); + rectificationBox.boxCenter /= rectificationBox.fBoxCenterWeight; + rectificationBox.boxVec /= rectificationBox.fBoxCenterWeight; + FfxFloat32x3 stdDev = sqrt(abs(rectificationBox.boxVec - rectificationBox.boxCenter * rectificationBox.boxCenter)); + rectificationBox.boxVec = stdDev; +} +#if FFX_HALF +void RectificationBoxComputeVarianceBoxData(FFX_PARAMETER_INOUT RectificationBoxMin16 rectificationBox) +{ + rectificationBox.fBoxCenterWeight = (abs(rectificationBox.fBoxCenterWeight) > FFX_MIN16_F(FSR2_EPSILON) ? rectificationBox.fBoxCenterWeight : FFX_MIN16_F(1.f)); + rectificationBox.boxCenter /= rectificationBox.fBoxCenterWeight; + rectificationBox.boxVec /= rectificationBox.fBoxCenterWeight; + FFX_MIN16_F3 stdDev = sqrt(abs(rectificationBox.boxVec - rectificationBox.boxCenter * rectificationBox.boxCenter)); + rectificationBox.boxVec = stdDev; +} +#endif + +FfxFloat32x3 SafeRcp3(FfxFloat32x3 v) +{ + return (all(FFX_NOT_EQUAL(v, FfxFloat32x3(0, 0, 0)))) ? (FfxFloat32x3(1, 1, 1) / v) : FfxFloat32x3(0, 0, 0); +} +#if FFX_HALF +FFX_MIN16_F3 SafeRcp3(FFX_MIN16_F3 v) +{ + return (all(FFX_NOT_EQUAL(v, FFX_MIN16_F3(0, 0, 0)))) ? (FFX_MIN16_F3(1, 1, 1) / v) : FFX_MIN16_F3(0, 0, 0); +} +#endif + +FfxFloat32 MinDividedByMax(const FfxFloat32 v0, const FfxFloat32 v1) +{ + const FfxFloat32 m = ffxMax(v0, v1); + return m != 0 ? ffxMin(v0, v1) / m : 0; +} + +#if FFX_HALF +FFX_MIN16_F MinDividedByMax(const FFX_MIN16_F v0, const FFX_MIN16_F v1) +{ + const FFX_MIN16_F m = ffxMax(v0, v1); + return m != FFX_MIN16_F(0) ? ffxMin(v0, v1) / m : FFX_MIN16_F(0); +} +#endif + +FfxFloat32x3 YCoCgToRGB(FfxFloat32x3 fYCoCg) +{ + FfxFloat32x3 fRgb; + + fRgb = FfxFloat32x3( + fYCoCg.x + fYCoCg.y - fYCoCg.z, + fYCoCg.x + fYCoCg.z, + fYCoCg.x - fYCoCg.y - fYCoCg.z); + + return fRgb; +} +#if FFX_HALF +FFX_MIN16_F3 YCoCgToRGB(FFX_MIN16_F3 fYCoCg) +{ + FFX_MIN16_F3 fRgb; + + fRgb = FFX_MIN16_F3( + fYCoCg.x + fYCoCg.y - fYCoCg.z, + fYCoCg.x + fYCoCg.z, + fYCoCg.x - fYCoCg.y - fYCoCg.z); + + return fRgb; +} +#endif + +FfxFloat32x3 RGBToYCoCg(FfxFloat32x3 fRgb) +{ + FfxFloat32x3 fYCoCg; + + fYCoCg = FfxFloat32x3( + 0.25f * fRgb.r + 0.5f * fRgb.g + 0.25f * fRgb.b, + 0.5f * fRgb.r - 0.5f * fRgb.b, + -0.25f * fRgb.r + 0.5f * fRgb.g - 0.25f * fRgb.b); + + return fYCoCg; +} +#if FFX_HALF +FFX_MIN16_F3 RGBToYCoCg(FFX_MIN16_F3 fRgb) +{ + FFX_MIN16_F3 fYCoCg; + + fYCoCg = FFX_MIN16_F3( + 0.25 * fRgb.r + 0.5 * fRgb.g + 0.25 * fRgb.b, + 0.5 * fRgb.r - 0.5 * fRgb.b, + -0.25 * fRgb.r + 0.5 * fRgb.g - 0.25 * fRgb.b); + + return fYCoCg; +} +#endif + +FfxFloat32 RGBToLuma(FfxFloat32x3 fLinearRgb) +{ + return dot(fLinearRgb, FfxFloat32x3(0.2126f, 0.7152f, 0.0722f)); +} +#if FFX_HALF +FFX_MIN16_F RGBToLuma(FFX_MIN16_F3 fLinearRgb) +{ + return dot(fLinearRgb, FFX_MIN16_F3(0.2126f, 0.7152f, 0.0722f)); +} +#endif + +FfxFloat32 RGBToPerceivedLuma(FfxFloat32x3 fLinearRgb) +{ + FfxFloat32 fLuminance = RGBToLuma(fLinearRgb); + + FfxFloat32 fPercievedLuminance = 0; + if (fLuminance <= 216.0f / 24389.0f) { + fPercievedLuminance = fLuminance * (24389.0f / 27.0f); + } + else { + fPercievedLuminance = ffxPow(fLuminance, 1.0f / 3.0f) * 116.0f - 16.0f; + } + + return fPercievedLuminance * 0.01f; +} +#if FFX_HALF +FFX_MIN16_F RGBToPerceivedLuma(FFX_MIN16_F3 fLinearRgb) +{ + FFX_MIN16_F fLuminance = RGBToLuma(fLinearRgb); + + FFX_MIN16_F fPercievedLuminance = FFX_MIN16_F(0); + if (fLuminance <= FFX_MIN16_F(216.0f / 24389.0f)) { + fPercievedLuminance = fLuminance * FFX_MIN16_F(24389.0f / 27.0f); + } + else { + fPercievedLuminance = ffxPow(fLuminance, FFX_MIN16_F(1.0f / 3.0f)) * FFX_MIN16_F(116.0f) - FFX_MIN16_F(16.0f); + } + + return fPercievedLuminance * FFX_MIN16_F(0.01f); +} +#endif + +FfxFloat32x3 Tonemap(FfxFloat32x3 fRgb) +{ + return fRgb / (ffxMax(ffxMax(0.f, fRgb.r), ffxMax(fRgb.g, fRgb.b)) + 1.f).xxx; +} + +FfxFloat32x3 InverseTonemap(FfxFloat32x3 fRgb) +{ + return fRgb / ffxMax(FSR2_TONEMAP_EPSILON, 1.f - ffxMax(fRgb.r, ffxMax(fRgb.g, fRgb.b))).xxx; +} + +#if FFX_HALF +FFX_MIN16_F3 Tonemap(FFX_MIN16_F3 fRgb) +{ + return fRgb / (ffxMax(ffxMax(FFX_MIN16_F(0.f), fRgb.r), ffxMax(fRgb.g, fRgb.b)) + FFX_MIN16_F(1.f)).xxx; +} + +FFX_MIN16_F3 InverseTonemap(FFX_MIN16_F3 fRgb) +{ + return fRgb / ffxMax(FFX_MIN16_F(FSR2_TONEMAP_EPSILON), FFX_MIN16_F(1.f) - ffxMax(fRgb.r, ffxMax(fRgb.g, fRgb.b))).xxx; +} +#endif + +FfxInt32x2 ClampLoad(FfxInt32x2 iPxSample, FfxInt32x2 iPxOffset, FfxInt32x2 iTextureSize) +{ + FfxInt32x2 result = iPxSample + iPxOffset; + result.x = (iPxOffset.x < 0) ? ffxMax(result.x, 0) : result.x; + result.x = (iPxOffset.x > 0) ? ffxMin(result.x, iTextureSize.x - 1) : result.x; + result.y = (iPxOffset.y < 0) ? ffxMax(result.y, 0) : result.y; + result.y = (iPxOffset.y > 0) ? ffxMin(result.y, iTextureSize.y - 1) : result.y; + return result; + + // return ffxMed3(iPxSample + iPxOffset, FfxInt32x2(0, 0), iTextureSize - FfxInt32x2(1, 1)); +} +#if FFX_HALF +FFX_MIN16_I2 ClampLoad(FFX_MIN16_I2 iPxSample, FFX_MIN16_I2 iPxOffset, FFX_MIN16_I2 iTextureSize) +{ + FFX_MIN16_I2 result = iPxSample + iPxOffset; + result.x = (iPxOffset.x < 0) ? ffxMax(result.x, FFX_MIN16_I(0)) : result.x; + result.x = (iPxOffset.x > 0) ? ffxMin(result.x, iTextureSize.x - FFX_MIN16_I(1)) : result.x; + result.y = (iPxOffset.y < 0) ? ffxMax(result.y, FFX_MIN16_I(0)) : result.y; + result.y = (iPxOffset.y > 0) ? ffxMin(result.y, iTextureSize.y - FFX_MIN16_I(1)) : result.y; + return result; + + // return ffxMed3Half(iPxSample + iPxOffset, FFX_MIN16_I2(0, 0), iTextureSize - FFX_MIN16_I2(1, 1)); +} +#endif + +FfxFloat32x2 ClampUv(FfxFloat32x2 fUv, FfxInt32x2 iTextureSize, FfxInt32x2 iResourceSize) +{ + const FfxFloat32x2 fSampleLocation = fUv * iTextureSize; + const FfxFloat32x2 fClampedLocation = ffxMax(FfxFloat32x2(0.5f, 0.5f), ffxMin(fSampleLocation, FfxFloat32x2(iTextureSize) - FfxFloat32x2(0.5f, 0.5f))); + const FfxFloat32x2 fClampedUv = fClampedLocation / FfxFloat32x2(iResourceSize); + + return fClampedUv; +} + +FfxBoolean IsOnScreen(FfxInt32x2 pos, FfxInt32x2 size) +{ + return all(FFX_LESS_THAN(FfxUInt32x2(pos), FfxUInt32x2(size))); +} +#if FFX_HALF +FfxBoolean IsOnScreen(FFX_MIN16_I2 pos, FFX_MIN16_I2 size) +{ + return all(FFX_LESS_THAN(FFX_MIN16_U2(pos), FFX_MIN16_U2(size))); +} +#endif + +FfxFloat32 ComputeAutoExposureFromLavg(FfxFloat32 Lavg) +{ + Lavg = exp(Lavg); + + const FfxFloat32 S = 100.0f; //ISO arithmetic speed + const FfxFloat32 K = 12.5f; + FfxFloat32 ExposureISO100 = log2((Lavg * S) / K); + + const FfxFloat32 q = 0.65f; + FfxFloat32 Lmax = (78.0f / (q * S)) * ffxPow(2.0f, ExposureISO100); + + return 1 / Lmax; +} +#if FFX_HALF +FFX_MIN16_F ComputeAutoExposureFromLavg(FFX_MIN16_F Lavg) +{ + Lavg = exp(Lavg); + + const FFX_MIN16_F S = FFX_MIN16_F(100.0f); //ISO arithmetic speed + const FFX_MIN16_F K = FFX_MIN16_F(12.5f); + const FFX_MIN16_F ExposureISO100 = log2((Lavg * S) / K); + + const FFX_MIN16_F q = FFX_MIN16_F(0.65f); + const FFX_MIN16_F Lmax = (FFX_MIN16_F(78.0f) / (q * S)) * ffxPow(FFX_MIN16_F(2.0f), ExposureISO100); + + return FFX_MIN16_F(1) / Lmax; +} +#endif + +FfxInt32x2 ComputeHrPosFromLrPos(FfxInt32x2 iPxLrPos) +{ + FfxFloat32x2 fSrcJitteredPos = FfxFloat32x2(iPxLrPos) + 0.5f - Jitter(); + FfxFloat32x2 fLrPosInHr = (fSrcJitteredPos / RenderSize()) * DisplaySize(); + FfxInt32x2 iPxHrPos = FfxInt32x2(floor(fLrPosInHr)); + return iPxHrPos; +} +#if FFX_HALF +FFX_MIN16_I2 ComputeHrPosFromLrPos(FFX_MIN16_I2 iPxLrPos) +{ + FFX_MIN16_F2 fSrcJitteredPos = FFX_MIN16_F2(iPxLrPos) + FFX_MIN16_F(0.5f) - FFX_MIN16_F2(Jitter()); + FFX_MIN16_F2 fLrPosInHr = (fSrcJitteredPos / FFX_MIN16_F2(RenderSize())) * FFX_MIN16_F2(DisplaySize()); + FFX_MIN16_I2 iPxHrPos = FFX_MIN16_I2(floor(fLrPosInHr)); + return iPxHrPos; +} +#endif + +FfxFloat32x2 ComputeNdc(FfxFloat32x2 fPxPos, FfxInt32x2 iSize) +{ + return fPxPos / FfxFloat32x2(iSize) * FfxFloat32x2(2.0f, -2.0f) + FfxFloat32x2(-1.0f, 1.0f); +} + +FfxFloat32 GetViewSpaceDepth(FfxFloat32 fDeviceDepth) +{ + const FfxFloat32x4 fDeviceToViewDepth = DeviceToViewSpaceTransformFactors(); + + // fDeviceToViewDepth details found in ffx_fsr2.cpp + return (fDeviceToViewDepth[1] / (fDeviceDepth - fDeviceToViewDepth[0])); +} + +FfxFloat32 GetViewSpaceDepthInMeters(FfxFloat32 fDeviceDepth) +{ + return GetViewSpaceDepth(fDeviceDepth) * ViewSpaceToMetersFactor(); +} + +FfxFloat32x3 GetViewSpacePosition(FfxInt32x2 iViewportPos, FfxInt32x2 iViewportSize, FfxFloat32 fDeviceDepth) +{ + const FfxFloat32x4 fDeviceToViewDepth = DeviceToViewSpaceTransformFactors(); + + const FfxFloat32 Z = GetViewSpaceDepth(fDeviceDepth); + + const FfxFloat32x2 fNdcPos = ComputeNdc(iViewportPos, iViewportSize); + const FfxFloat32 X = fDeviceToViewDepth[2] * fNdcPos.x * Z; + const FfxFloat32 Y = fDeviceToViewDepth[3] * fNdcPos.y * Z; + + return FfxFloat32x3(X, Y, Z); +} + +FfxFloat32x3 GetViewSpacePositionInMeters(FfxInt32x2 iViewportPos, FfxInt32x2 iViewportSize, FfxFloat32 fDeviceDepth) +{ + return GetViewSpacePosition(iViewportPos, iViewportSize, fDeviceDepth) * ViewSpaceToMetersFactor(); +} + +FfxFloat32 GetMaxDistanceInMeters() +{ +#if FFX_FSR2_OPTION_INVERTED_DEPTH + return GetViewSpaceDepth(0.0f) * ViewSpaceToMetersFactor(); +#else + return GetViewSpaceDepth(1.0f) * ViewSpaceToMetersFactor(); +#endif +} + +FfxFloat32x3 PrepareRgb(FfxFloat32x3 fRgb, FfxFloat32 fExposure, FfxFloat32 fPreExposure) +{ + fRgb /= fPreExposure; + fRgb *= fExposure; + + fRgb = clamp(fRgb, 0.0f, FSR2_FP16_MAX); + + return fRgb; +} + +FfxFloat32x3 UnprepareRgb(FfxFloat32x3 fRgb, FfxFloat32 fExposure) +{ + fRgb /= fExposure; + fRgb *= PreExposure(); + + return fRgb; +} + + +struct BilinearSamplingData +{ + FfxInt32x2 iOffsets[4]; + FfxFloat32 fWeights[4]; + FfxInt32x2 iBasePos; +}; + +BilinearSamplingData GetBilinearSamplingData(FfxFloat32x2 fUv, FfxInt32x2 iSize) +{ + BilinearSamplingData data; + + FfxFloat32x2 fPxSample = (fUv * iSize) - FfxFloat32x2(0.5f, 0.5f); + data.iBasePos = FfxInt32x2(floor(fPxSample)); + FfxFloat32x2 fPxFrac = ffxFract(fPxSample); + + data.iOffsets[0] = FfxInt32x2(0, 0); + data.iOffsets[1] = FfxInt32x2(1, 0); + data.iOffsets[2] = FfxInt32x2(0, 1); + data.iOffsets[3] = FfxInt32x2(1, 1); + + data.fWeights[0] = (1 - fPxFrac.x) * (1 - fPxFrac.y); + data.fWeights[1] = (fPxFrac.x) * (1 - fPxFrac.y); + data.fWeights[2] = (1 - fPxFrac.x) * (fPxFrac.y); + data.fWeights[3] = (fPxFrac.x) * (fPxFrac.y); + + return data; +} + +struct PlaneData +{ + FfxFloat32x3 fNormal; + FfxFloat32 fDistanceFromOrigin; +}; + +PlaneData GetPlaneFromPoints(FfxFloat32x3 fP0, FfxFloat32x3 fP1, FfxFloat32x3 fP2) +{ + PlaneData plane; + + FfxFloat32x3 v0 = fP0 - fP1; + FfxFloat32x3 v1 = fP0 - fP2; + plane.fNormal = normalize(cross(v0, v1)); + plane.fDistanceFromOrigin = -dot(fP0, plane.fNormal); + + return plane; +} + +FfxFloat32 PointToPlaneDistance(PlaneData plane, FfxFloat32x3 fPoint) +{ + return abs(dot(plane.fNormal, fPoint) + plane.fDistanceFromOrigin); +} + +#endif // #if defined(FFX_GPU) + +#endif //!defined(FFX_FSR2_COMMON_H) diff --git a/ffx-fsr2-api/shaders/ffx_fsr2_resources.h b/ffx-fsr2-api/shaders/ffx_fsr2_resources.h new file mode 100644 index 0000000..535dbc3 --- /dev/null +++ b/ffx-fsr2-api/shaders/ffx_fsr2_resources.h @@ -0,0 +1,105 @@ +// This file is part of the FidelityFX SDK. +// +// Copyright (c) 2022-2023 Advanced Micro Devices, Inc. All rights reserved. +// +// 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. + +#ifndef FFX_FSR2_RESOURCES_H +#define FFX_FSR2_RESOURCES_H + +#if defined(FFX_CPU) || defined(FFX_GPU) +#define FFX_FSR2_RESOURCE_IDENTIFIER_NULL 0 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INPUT_OPAQUE_ONLY 1 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INPUT_COLOR 2 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INPUT_MOTION_VECTORS 3 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INPUT_DEPTH 4 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INPUT_EXPOSURE 5 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INPUT_REACTIVE_MASK 6 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INPUT_TRANSPARENCY_AND_COMPOSITION_MASK 7 +#define FFX_FSR2_RESOURCE_IDENTIFIER_RECONSTRUCTED_PREVIOUS_NEAREST_DEPTH 8 +#define FFX_FSR2_RESOURCE_IDENTIFIER_DILATED_MOTION_VECTORS 9 +#define FFX_FSR2_RESOURCE_IDENTIFIER_DILATED_DEPTH 10 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR 11 +#define FFX_FSR2_RESOURCE_IDENTIFIER_LOCK_STATUS 12 +#define FFX_FSR2_RESOURCE_IDENTIFIER_NEW_LOCKS 13 +#define FFX_FSR2_RESOURCE_IDENTIFIER_PREPARED_INPUT_COLOR 14 +#define FFX_FSR2_RESOURCE_IDENTIFIER_LUMA_HISTORY 15 +#define FFX_FSR2_RESOURCE_IDENTIFIER_DEBUG_OUTPUT 16 +#define FFX_FSR2_RESOURCE_IDENTIFIER_LANCZOS_LUT 17 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SPD_ATOMIC_COUNT 18 +#define FFX_FSR2_RESOURCE_IDENTIFIER_UPSCALED_OUTPUT 19 +#define FFX_FSR2_RESOURCE_IDENTIFIER_RCAS_INPUT 20 +#define FFX_FSR2_RESOURCE_IDENTIFIER_LOCK_STATUS_1 21 +#define FFX_FSR2_RESOURCE_IDENTIFIER_LOCK_STATUS_2 22 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR_1 23 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INTERNAL_UPSCALED_COLOR_2 24 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INTERNAL_DEFAULT_REACTIVITY 25 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INTERNAL_DEFAULT_TRANSPARENCY_AND_COMPOSITION 26 +#define FFX_FSR2_RESOURCE_IDENTITIER_UPSAMPLE_MAXIMUM_BIAS_LUT 27 +#define FFX_FSR2_RESOURCE_IDENTIFIER_DILATED_REACTIVE_MASKS 28 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE 29 // same as FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_0 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_0 29 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_1 30 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_2 31 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_3 32 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_4 33 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_5 34 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_6 35 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_7 36 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_8 37 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_9 38 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_10 39 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_11 40 +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_12 41 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INTERNAL_DEFAULT_EXPOSURE 42 +#define FFX_FSR2_RESOURCE_IDENTIFIER_AUTO_EXPOSURE 43 +#define FFX_FSR2_RESOURCE_IDENTIFIER_AUTOREACTIVE 44 +#define FFX_FSR2_RESOURCE_IDENTIFIER_AUTOCOMPOSITION 45 + +#define FFX_FSR2_RESOURCE_IDENTIFIER_PREV_PRE_ALPHA_COLOR 46 +#define FFX_FSR2_RESOURCE_IDENTIFIER_PREV_POST_ALPHA_COLOR 47 +#define FFX_FSR2_RESOURCE_IDENTIFIER_PREV_PRE_ALPHA_COLOR_1 48 +#define FFX_FSR2_RESOURCE_IDENTIFIER_PREV_POST_ALPHA_COLOR_1 49 +#define FFX_FSR2_RESOURCE_IDENTIFIER_PREV_PRE_ALPHA_COLOR_2 50 +#define FFX_FSR2_RESOURCE_IDENTIFIER_PREV_POST_ALPHA_COLOR_2 51 +#define FFX_FSR2_RESOURCE_IDENTIFIER_PREVIOUS_DILATED_MOTION_VECTORS 52 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INTERNAL_DILATED_MOTION_VECTORS_1 53 +#define FFX_FSR2_RESOURCE_IDENTIFIER_INTERNAL_DILATED_MOTION_VECTORS_2 54 +#define FFX_FSR2_RESOURCE_IDENTIFIER_LUMA_HISTORY_1 55 +#define FFX_FSR2_RESOURCE_IDENTIFIER_LUMA_HISTORY_2 56 +#define FFX_FSR2_RESOURCE_IDENTIFIER_LOCK_INPUT_LUMA 57 + +// Shading change detection mip level setting, value must be in the range [FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_0, FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_12] +#define FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_SHADING_CHANGE FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_4 +#define FFX_FSR2_SHADING_CHANGE_MIP_LEVEL (FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE_MIPMAP_SHADING_CHANGE - FFX_FSR2_RESOURCE_IDENTIFIER_SCENE_LUMINANCE) + +#define FFX_FSR2_RESOURCE_IDENTIFIER_COUNT 58 + +#define FFX_FSR2_CONSTANTBUFFER_IDENTIFIER_FSR2 0 +#define FFX_FSR2_CONSTANTBUFFER_IDENTIFIER_SPD 1 +#define FFX_FSR2_CONSTANTBUFFER_IDENTIFIER_RCAS 2 +#define FFX_FSR2_CONSTANTBUFFER_IDENTIFIER_GENREACTIVE 3 + +#define FFX_FSR2_AUTOREACTIVEFLAGS_APPLY_TONEMAP 1 +#define FFX_FSR2_AUTOREACTIVEFLAGS_APPLY_INVERSETONEMAP 2 +#define FFX_FSR2_AUTOREACTIVEFLAGS_APPLY_THRESHOLD 4 +#define FFX_FSR2_AUTOREACTIVEFLAGS_USE_COMPONENTS_MAX 8 + +#endif // #if defined(FFX_CPU) || defined(FFX_GPU) + +#endif //!defined( FFX_FSR2_RESOURCES_H ) diff --git a/lib/ffx_fsr2_api/ffx_fsr2_api_dx11_x64.lib b/lib/ffx_fsr2_api/ffx_fsr2_api_dx11_x64.lib new file mode 100644 index 0000000000000000000000000000000000000000..30ffa6b0b473d2f49537509a57958ffe78a27415 GIT binary patch literal 2203568 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