diff --git a/.gitignore b/.gitignore
index 326331ad33975431b6c8305167496aa9b1ff4dd7..8ab30e60ef913a4d49762b7326c878fc130696cc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@
 .editorconfig
 
 # build directories
+bin/
 build/
 cmake-build-debug/
 cmake-build-release/
diff --git a/.gitmodules b/.gitmodules
index c9eb19ee6ba09ded88a81b5ad253b2ae8c73c814..d59470fa0626799afe59be1dadcdaf76a719ff1c 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -40,3 +40,6 @@
 [submodule "modules/algorithm/lib/FidelityFX-SPD"]
 	path = modules/algorithm/lib/FidelityFX-SPD
 	url = https://github.com/GPUOpen-Effects/FidelityFX-SPD.git
+[submodule "modules/upscaling/lib/FidelityFX-FSR2"]
+	path = modules/upscaling/lib/FidelityFX-FSR2
+	url = https://github.com/TheJackiMonster/FidelityFX-FSR2.git
diff --git a/modules/upscaling/CMakeLists.txt b/modules/upscaling/CMakeLists.txt
index 47a963d43f0444216a7a972987a7d584333a1fc8..4578f97fd654a5cad0e66549fbc944ab16051b11 100644
--- a/modules/upscaling/CMakeLists.txt
+++ b/modules/upscaling/CMakeLists.txt
@@ -20,6 +20,9 @@ set(vkcv_upscaling_sources
 		
 		${vkcv_upscaling_include}/vkcv/upscaling/NISUpscaling.hpp
 		${vkcv_upscaling_source}/vkcv/upscaling/NISUpscaling.cpp
+		
+		${vkcv_upscaling_include}/vkcv/upscaling/FSR2Upscaling.hpp
+		${vkcv_upscaling_source}/vkcv/upscaling/FSR2Upscaling.cpp
 )
 
 # Setup some path variables to load libraries
@@ -29,6 +32,9 @@ set(vkcv_upscaling_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_upscaling_lib})
 # Check and load FidelityFX_FSR
 include(config/FidelityFX_FSR.cmake)
 
+# Check and load FidelityFX_FSR2
+include(config/FidelityFX_FSR2.cmake)
+
 # Check and load NVIDIAImageScaling
 include(config/NVIDIAImageScaling.cmake)
 
diff --git a/modules/upscaling/config/FidelityFX_FSR2.cmake b/modules/upscaling/config/FidelityFX_FSR2.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..3dbc43e6721ae139f9ceb76c9f05b6fbce010fba
--- /dev/null
+++ b/modules/upscaling/config/FidelityFX_FSR2.cmake
@@ -0,0 +1,14 @@
+
+use_git_submodule("${vkcv_upscaling_lib_path}/FidelityFX-FSR2" ffx_fsr2_status)
+
+if (${ffx_fsr2_status})
+	set(FFX_FSR2_API_DX12 OFF CACHE INTERNAL "")
+	set(FFX_FSR2_API_VK ON CACHE INTERNAL "")
+	
+	add_subdirectory(${vkcv_upscaling_lib}/FidelityFX-FSR2/src/ffx-fsr2-api)
+	
+	list(APPEND vkcv_upscaling_libraries ${FFX_FSR2_API} ${FFX_FSR2_API_VK})
+	
+	list(APPEND vkcv_upscaling_includes ${vkcv_upscaling_lib}/FidelityFX-FSR2/src/ffx-fsr2-api)
+	list(APPEND vkcv_upscaling_includes ${vkcv_upscaling_lib}/FidelityFX-FSR2/src/ffx-fsr2-api/vk)
+endif ()
diff --git a/modules/upscaling/include/vkcv/upscaling/FSR2Upscaling.hpp b/modules/upscaling/include/vkcv/upscaling/FSR2Upscaling.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9b1b702147034a782d5c7f63674ff0304d090d04
--- /dev/null
+++ b/modules/upscaling/include/vkcv/upscaling/FSR2Upscaling.hpp
@@ -0,0 +1,162 @@
+#pragma once
+
+#include "Upscaling.hpp"
+
+#include <vector>
+
+#define FFX_GCC
+#include <ffx_fsr2.h>
+#undef FFX_GCC
+
+namespace vkcv::upscaling {
+
+/**
+     * @addtogroup vkcv_upscaling
+     * @{
+     */
+
+	/**
+     * A class to handle upscaling via FidelityFX Super Resolution.
+     * https://github.com/GPUOpen-Effects/FidelityFX-FSR2
+     */
+	class FSR2Upscaling : public Upscaling {
+	private:
+		std::vector<char> m_scratchBuffer;
+		
+		FfxFsr2ContextDescription m_description;
+		FfxFsr2Context m_context;
+		
+		ImageHandle m_depth;
+		ImageHandle m_velocity;
+		
+		float m_frameDeltaTime;
+		bool m_reset;
+		
+		float m_near;
+		float m_far;
+		float m_fov;
+		
+		/**
+		* Current state of HDR support.
+		*/
+		bool m_hdr;
+		
+		/**
+		 * Sharpness will improve the upscaled image quality with
+		 * a factor between 0.0f for no sharpening and 1.0f for
+		 * maximum sharpening.
+		 *
+		 * The default value for sharpness should be 0.875f.
+		 *
+		 * Beware that 0.0f or any negative value of sharpness will
+		 * disable the sharpening pass completely.
+		 */
+		float m_sharpness;
+		
+		void createFSR2Context(uint32_t displayWidth,
+							   uint32_t displayHeight,
+							   uint32_t renderWidth,
+							   uint32_t renderHeight);
+		
+		void destroyFSR2Context();
+		
+	public:
+		/**
+         * Constructor to create an instance for FSR upscaling.
+         *
+         * @param[in,out] core Reference to a Core instance
+         */
+		explicit FSR2Upscaling(Core& core);
+		
+		/**
+		 * Destructor to free the instance for FSR upscaling.
+		 */
+		~FSR2Upscaling();
+		
+		/**
+		 * Update the upscaling instance with current frame
+		 * delta time and whether the temporal data needs to
+		 * be reset (for example because the camera switched).
+		 *
+		 * @param[in] deltaTime Current frame delta time
+		 * @param[in] reset Reset temporal frame data
+		 */
+		void update(float deltaTime, bool reset = false);
+		
+		/**
+		 * Bind the depth buffer image to use with the FSR2
+		 * upscaling instance for utilizing depth information.
+		 *
+		 * @param[in] depthInput Depth input image handle
+		 */
+		void bindDepthBuffer(const ImageHandle& depthInput);
+		
+		/**
+		 * Bind the velocity buffer image to use with the FSR2
+		 * upscaling instance for utilizing 2D motion vectors.
+		 *
+		 * @param[in] velocityInput Velocity input image handle
+		 */
+		void bindVelocityBuffer(const ImageHandle& velocityInput);
+		
+		/**
+		 * Record the comands of the FSR2 upscaling instance to
+		 * scale the image of the input handle to the resolution of
+		 * the output image handle via FidelityFX Super Resolution.
+		 *
+		 * @param[in] cmdStream Command stream handle to record commands
+		 * @param[in] colorInput Color input image handle
+		 * @param[in] output Output image handle
+		 */
+		void recordUpscaling(const CommandStreamHandle& cmdStream,
+							 const ImageHandle& colorInput,
+							 const ImageHandle& output) override;
+		
+		/**
+		 * Set the required camera values for the FSR2 upscaling
+		 * instance including near- and far-plane as well as
+		 * the FOV angle vertical.
+		 *
+		 * @param[in] near Camera near plane
+		 * @param[in] far Camera far plane
+		 * @param[in] fov Camera field of view angle vertical
+		 */
+		void setCamera(float near, float far, float fov);
+		
+		/**
+         * Checks if HDR support is enabled and returns the status as boolean.
+         *
+         * @return true if HDR is supported, otherwise false
+         */
+		[[nodiscard]]
+		bool isHdrEnabled() const;
+		
+		/**
+		 * Changes the status of HDR support of the FSR upscaling instance.
+		 *
+		 * @param[in] enabled New status of HDR support
+		 */
+		void setHdrEnabled(bool enabled);
+		
+		/**
+         * Returns the amount of sharpness the FSR upscaling instance is using.
+         *
+         * @return The amount of sharpness
+         */
+		[[nodiscard]]
+		float getSharpness() const;
+		
+		/**
+		 * Changes the amount of sharpness of the FSR upscaling instance.
+		 * The new sharpness value is restricted by 0.0f as lower and 1.0f
+		 * as upper boundary.
+		 *
+		 * @param[in] sharpness New sharpness value
+		 */
+		void setSharpness(float sharpness);
+		
+	};
+	
+	/** @} */
+
+}
\ No newline at end of file
diff --git a/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp b/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp
index 64bc78aca4fe42ff813d05fc016a70c7108bd557..ee2572da2d01a8234f03ced018704b48686e1a9b 100644
--- a/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp
+++ b/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp
@@ -44,7 +44,13 @@ namespace vkcv::upscaling {
          * Low quality of FSR upscaling:
          * 2.0x per dimension
          */
-		PERFORMANCE = 4
+		PERFORMANCE = 4,
+		
+		/**
+         * Lowest quality of FSR upscaling:
+         * 3.0x per dimension
+         */
+		ULTRA_PERFORMANCE = 5,
 	};
 
     /**
diff --git a/modules/upscaling/lib/FidelityFX-FSR2 b/modules/upscaling/lib/FidelityFX-FSR2
new file mode 160000
index 0000000000000000000000000000000000000000..0ce4ff5c5a0210273be7e3085bb4b15d0590431c
--- /dev/null
+++ b/modules/upscaling/lib/FidelityFX-FSR2
@@ -0,0 +1 @@
+Subproject commit 0ce4ff5c5a0210273be7e3085bb4b15d0590431c
diff --git a/modules/upscaling/src/vkcv/upscaling/FSR2Upscaling.cpp b/modules/upscaling/src/vkcv/upscaling/FSR2Upscaling.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4ef213e3d89ba57f7be6958a8d74a49a8c4cf4bd
--- /dev/null
+++ b/modules/upscaling/src/vkcv/upscaling/FSR2Upscaling.cpp
@@ -0,0 +1,223 @@
+
+#include "vkcv/upscaling/FSR2Upscaling.hpp"
+
+#define FFX_GCC
+#include <ffx_fsr2_vk.h>
+#undef FFX_GCC
+
+namespace vkcv::upscaling {
+	
+	void FSR2Upscaling::createFSR2Context(uint32_t displayWidth,
+									 uint32_t displayHeight,
+									 uint32_t renderWidth,
+									 uint32_t renderHeight) {
+		m_description.displaySize.width = displayWidth;
+		m_description.displaySize.height = displayHeight;
+		
+		m_description.maxRenderSize.width = renderWidth;
+		m_description.maxRenderSize.height = renderHeight;
+		
+		m_description.flags = FFX_FSR2_ENABLE_AUTO_EXPOSURE;
+		
+		if (m_hdr) {
+			m_description.flags |= FFX_FSR2_ENABLE_HIGH_DYNAMIC_RANGE;
+		}
+		
+		assert(ffxFsr2ContextCreate(&m_context, &m_description) == FFX_OK);
+	}
+	
+	void FSR2Upscaling::destroyFSR2Context() {
+		m_core.getContext().getDevice().waitIdle();
+		
+		assert(ffxFsr2ContextDestroy(&m_context) == FFX_OK);
+	}
+	
+	FSR2Upscaling::FSR2Upscaling(Core &core) : Upscaling(core) {
+		const auto& physicalDevice = core.getContext().getPhysicalDevice();
+		
+		memset(&m_description, 0, sizeof(m_description));
+		
+		m_scratchBuffer.resize(ffxFsr2GetScratchMemorySizeVK(physicalDevice));
+		
+		assert(ffxFsr2GetInterfaceVK(
+				&(m_description.callbacks),
+				m_scratchBuffer.data(),
+				m_scratchBuffer.size(),
+				physicalDevice,
+				vkGetDeviceProcAddr
+		) == FFX_OK);
+		
+		m_description.device = ffxGetDeviceVK(core.getContext().getDevice());
+		
+		createFSR2Context(0, 0, 0, 0);
+	}
+	
+	FSR2Upscaling::~FSR2Upscaling() {
+		destroyFSR2Context();
+		
+		m_scratchBuffer.clear();
+		m_description.callbacks.scratchBuffer = nullptr;
+	}
+	
+	void FSR2Upscaling::update(float deltaTime, bool reset) {
+		m_frameDeltaTime = deltaTime;
+		m_reset = reset;
+	}
+	
+	void FSR2Upscaling::bindDepthBuffer(const ImageHandle &depthInput) {
+		m_depth = depthInput;
+	}
+	
+	void FSR2Upscaling::bindVelocityBuffer(const ImageHandle &velocityInput) {
+		m_velocity = velocityInput;
+	}
+	
+	void FSR2Upscaling::recordUpscaling(const CommandStreamHandle &cmdStream, const ImageHandle &colorInput,
+										const ImageHandle &output) {
+		FfxFsr2DispatchDescription dispatch;
+		memset(&dispatch, 0, sizeof(dispatch));
+		
+		const uint32_t inputWidth = m_core.getImageWidth(colorInput);
+		const uint32_t inputHeight = m_core.getImageHeight(colorInput);
+		
+		const uint32_t outputWidth = m_core.getImageWidth(output);
+		const uint32_t outputHeight = m_core.getImageHeight(output);
+		
+		if ((m_description.displaySize.width != outputWidth) ||
+			(m_description.displaySize.height != outputHeight) ||
+			(m_description.maxRenderSize.width < inputWidth) ||
+			(m_description.maxRenderSize.height < inputHeight) ||
+			(m_hdr != ((m_description.flags & FFX_FSR2_ENABLE_HIGH_DYNAMIC_RANGE) != 0))) {
+			destroyFSR2Context();
+			
+			createFSR2Context(
+					outputWidth,
+					outputHeight,
+					inputWidth,
+					inputHeight
+			);
+		}
+		
+		const bool sharpeningEnabled = (
+				(m_sharpness > +0.0f) &&
+				((inputWidth < outputWidth) || (inputHeight < outputHeight))
+		);
+		
+		dispatch.color = ffxGetTextureResourceVK(
+				&m_context,
+				m_core.getVulkanImage(colorInput),
+				m_core.getVulkanImageView(colorInput),
+				inputWidth,
+				inputHeight,
+				static_cast<VkFormat>(m_core.getImageFormat(colorInput))
+		);
+		
+		dispatch.depth = ffxGetTextureResourceVK(
+				&m_context,
+				m_core.getVulkanImage(m_depth),
+				m_core.getVulkanImageView(m_depth),
+				m_core.getImageWidth(m_depth),
+				m_core.getImageHeight(m_depth),
+				static_cast<VkFormat>(m_core.getImageFormat(m_depth))
+		);
+		
+		dispatch.motionVectors = ffxGetTextureResourceVK(
+				&m_context,
+				m_core.getVulkanImage(m_velocity),
+				m_core.getVulkanImageView(m_velocity),
+				m_core.getImageWidth(m_velocity),
+				m_core.getImageHeight(m_velocity),
+				static_cast<VkFormat>(m_core.getImageFormat(m_velocity))
+		);
+		
+		dispatch.exposure = ffxGetTextureResourceVK(
+				&m_context,
+				nullptr,
+				nullptr,
+				1,
+				1,
+				VK_FORMAT_UNDEFINED
+		);
+		
+		dispatch.reactive = ffxGetTextureResourceVK(
+				&m_context,
+				nullptr,
+				nullptr,
+				1,
+				1,
+				VK_FORMAT_UNDEFINED
+		);
+		
+		dispatch.transparencyAndComposition = ffxGetTextureResourceVK(
+				&m_context,
+				nullptr,
+				nullptr,
+				1,
+				1,
+				VK_FORMAT_UNDEFINED
+		);
+		
+		dispatch.output = ffxGetTextureResourceVK(
+				&m_context,
+				m_core.getVulkanImage(output),
+				m_core.getVulkanImageView(output),
+				outputWidth,
+				outputHeight,
+				static_cast<VkFormat>(m_core.getImageFormat(output))
+		);
+		
+		dispatch.jitterOffset.x = 0;
+		dispatch.jitterOffset.y = 0;
+		
+		dispatch.motionVectorScale.x = 0;
+		dispatch.motionVectorScale.y = 0;
+		
+		dispatch.renderSize.width = inputWidth;
+		dispatch.renderSize.height = inputHeight;
+		
+		dispatch.enableSharpening = sharpeningEnabled;
+		dispatch.sharpness = m_sharpness;
+		
+		dispatch.frameTimeDelta = m_frameDeltaTime * 1000.0f; // from seconds to milliseconds
+		dispatch.preExposure = 1.0f;
+		dispatch.reset = m_reset;
+		
+		dispatch.cameraNear = m_near;
+		dispatch.cameraFar = m_far;
+		dispatch.cameraFovAngleVertical = m_fov;
+		
+		m_core.recordCommandsToStream(cmdStream, [&dispatch](const vk::CommandBuffer& cmdBuffer) {
+			dispatch.commandList = ffxGetCommandListVK(cmdBuffer);
+		}, [&]() {
+			assert(ffxFsr2ContextDispatch(
+					&m_context,
+					&dispatch
+			) == FFX_OK);
+			
+			m_reset = false;
+		});
+	}
+	
+	void FSR2Upscaling::setCamera(float near, float far, float fov) {
+		m_near = near;
+		m_far = far;
+		m_fov = fov;
+	}
+	
+	bool FSR2Upscaling::isHdrEnabled() const {
+		return m_hdr;
+	}
+	
+	void FSR2Upscaling::setHdrEnabled(bool enabled) {
+		m_hdr = enabled;
+	}
+	
+	float FSR2Upscaling::getSharpness() const {
+		return m_sharpness;
+	}
+	
+	void FSR2Upscaling::setSharpness(float sharpness) {
+		m_sharpness = (sharpness < 0.0f ? 0.0f : (sharpness > 1.0f ? 1.0f : sharpness));
+	}
+	
+}
\ No newline at end of file
diff --git a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
index 247161e35718cde352da5461b51f30c4fe8570c7..2f37765e270c342a7aea3d52d37f1c9d4828cd89 100644
--- a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
+++ b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp
@@ -36,6 +36,9 @@ namespace vkcv::upscaling {
 			case FSRQualityMode::PERFORMANCE:
 				scale = 2.0f;
 				break;
+			case FSRQualityMode::ULTRA_PERFORMANCE:
+				scale = 3.0f;
+				break;
 			default:
 				scale = 1.0f;
 				break;
@@ -60,6 +63,8 @@ namespace vkcv::upscaling {
 				return -0.79f;
 			case FSRQualityMode::PERFORMANCE:
 				return -1.0f;
+			case FSRQualityMode::ULTRA_PERFORMANCE:
+				return -2.0f;
 			default:
 				return 0.0f;
 		}
@@ -333,7 +338,7 @@ namespace vkcv::upscaling {
 					cmdStream,
 					m_easuPipeline,
 					dispatch,
-					{DescriptorSetUsage(0, m_easuDescriptorSet, { 0 })},
+					{vkcv::useDescriptorSet(0, m_easuDescriptorSet, { 0 })},
 					PushConstants(0)
 			);
 			
@@ -353,7 +358,7 @@ namespace vkcv::upscaling {
 					cmdStream,
 					m_rcasPipeline,
 					dispatch,
-					{DescriptorSetUsage(0,m_rcasDescriptorSet, { 0 })},
+					{vkcv::useDescriptorSet(0,m_rcasDescriptorSet, { 0 })},
 					PushConstants(0)
 			);
 			
@@ -371,7 +376,7 @@ namespace vkcv::upscaling {
 					cmdStream,
 					m_easuPipeline,
 					dispatch,
-					{DescriptorSetUsage(0, m_easuDescriptorSet, { 0 })},
+					{vkcv::useDescriptorSet(0, m_easuDescriptorSet, { 0 })},
 					PushConstants(0)
 			);
 		}
diff --git a/src/vkcv/DescriptorSetUsage.cpp b/src/vkcv/DescriptorSetUsage.cpp
index 70fc7811791e02d57cb3a4de3d1c2f75db6e9b3a..cfa2ba1998e124cec0f1fc851356d28009288e1b 100644
--- a/src/vkcv/DescriptorSetUsage.cpp
+++ b/src/vkcv/DescriptorSetUsage.cpp
@@ -5,7 +5,10 @@ namespace vkcv {
 
 	DescriptorSetUsage useDescriptorSet(uint32_t location, const DescriptorSetHandle &descriptorSet,
 										const std::vector<uint32_t> &dynamicOffsets) {
-		DescriptorSetUsage usage(location, descriptorSet, dynamicOffsets);
+		DescriptorSetUsage usage;
+		usage.location = location;
+		usage.descriptorSet = descriptorSet;
+		usage.dynamicOffsets = dynamicOffsets;
 		return usage;
 	}
 
diff --git a/src/vkcv/VertexData.cpp b/src/vkcv/VertexData.cpp
index 9b9db90166a0be6f0e3a4a5666f837ceba293690..a1fcfb3692fcc6051e237e759ce29c38e4c688f4 100644
--- a/src/vkcv/VertexData.cpp
+++ b/src/vkcv/VertexData.cpp
@@ -4,7 +4,9 @@
 namespace vkcv {
 
 	VertexBufferBinding vertexBufferBinding(const BufferHandle &buffer, size_t offset) {
-		VertexBufferBinding binding(buffer, offset);
+		VertexBufferBinding binding;
+		binding.buffer = buffer;
+		binding.offset = offset;
 		return binding;
 	}