mirror of
				https://git.tardis.systems/mirrors/yuzu
				synced 2025-10-31 10:44:49 +01:00 
			
		
		
		
	vulkan: Implement FidelityFX Super Resolution
This commit is contained in:
		
							parent
							
								
									d4f5193bd3
								
							
						
					
					
						commit
						37cb0377ae
					
				| @ -64,6 +64,7 @@ enum class ScalingFilter : u32 { | ||||
|     Bilinear = 0, | ||||
|     Bicubic = 1, | ||||
|     ScaleForce = 2, | ||||
|     Fsr = 3, | ||||
| }; | ||||
| 
 | ||||
| struct ResolutionScalingInfo { | ||||
|  | ||||
| @ -132,6 +132,8 @@ add_library(video_core STATIC | ||||
|     renderer_vulkan/vk_descriptor_pool.h | ||||
|     renderer_vulkan/vk_fence_manager.cpp | ||||
|     renderer_vulkan/vk_fence_manager.h | ||||
|     renderer_vulkan/vk_fsr.cpp | ||||
|     renderer_vulkan/vk_fsr.h | ||||
|     renderer_vulkan/vk_graphics_pipeline.cpp | ||||
|     renderer_vulkan/vk_graphics_pipeline.h | ||||
|     renderer_vulkan/vk_master_semaphore.cpp | ||||
|  | ||||
| @ -1,3 +1,11 @@ | ||||
| set(FIDELITYFX_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/externals/FidelityFX-FSR/ffx-fsr) | ||||
| 
 | ||||
| set(GLSL_INCLUDES | ||||
|     fidelityfx_fsr.comp | ||||
|     ${FIDELITYFX_INCLUDE_DIR}/ffx_a.h | ||||
|     ${FIDELITYFX_INCLUDE_DIR}/ffx_fsr1.h | ||||
| ) | ||||
| 
 | ||||
| set(SHADER_FILES | ||||
|     astc_decoder.comp | ||||
|     block_linear_unswizzle_2d.comp | ||||
| @ -13,6 +21,8 @@ set(SHADER_FILES | ||||
|     present_bicubic.frag | ||||
|     vulkan_blit_color_float.frag | ||||
|     vulkan_blit_depth_stencil.frag | ||||
|     vulkan_fidelityfx_fsr_easu.comp | ||||
|     vulkan_fidelityfx_fsr_rcas.comp | ||||
|     vulkan_present.frag | ||||
|     vulkan_present.vert | ||||
|     vulkan_quad_indexed.comp | ||||
| @ -78,7 +88,7 @@ foreach(FILENAME IN ITEMS ${SHADER_FILES}) | ||||
|             OUTPUT | ||||
|                 ${SPIRV_HEADER_FILE} | ||||
|             COMMAND | ||||
|                 ${GLSLANGVALIDATOR} -V ${QUIET_FLAG} ${GLSL_FLAGS} --variable-name ${SPIRV_VARIABLE_NAME} -o ${SPIRV_HEADER_FILE} ${SOURCE_FILE} | ||||
|                 ${GLSLANGVALIDATOR} -V ${QUIET_FLAG} -I"${FIDELITYFX_INCLUDE_DIR}" ${GLSL_FLAGS} --variable-name ${SPIRV_VARIABLE_NAME} -o ${SPIRV_HEADER_FILE} ${SOURCE_FILE} | ||||
|             MAIN_DEPENDENCY | ||||
|                 ${SOURCE_FILE} | ||||
|         ) | ||||
| @ -86,9 +96,12 @@ foreach(FILENAME IN ITEMS ${SHADER_FILES}) | ||||
|     endif() | ||||
| endforeach() | ||||
| 
 | ||||
| set(SHADER_SOURCES ${SHADER_FILES}) | ||||
| list(APPEND SHADER_SOURCES ${GLSL_INCLUDES}) | ||||
| 
 | ||||
| add_custom_target(host_shaders | ||||
|     DEPENDS | ||||
|         ${SHADER_HEADERS} | ||||
|     SOURCES | ||||
|         ${SHADER_FILES} | ||||
|         ${SHADER_SOURCES} | ||||
| ) | ||||
|  | ||||
							
								
								
									
										114
									
								
								src/video_core/host_shaders/fidelityfx_fsr.comp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								src/video_core/host_shaders/fidelityfx_fsr.comp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,114 @@ | ||||
| // Copyright 2021 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
| 
 | ||||
| //!#version 460 core | ||||
| #extension GL_ARB_separate_shader_objects : enable | ||||
| #extension GL_ARB_shading_language_420pack : enable | ||||
| #extension GL_GOOGLE_include_directive : enable | ||||
| #extension GL_EXT_shader_explicit_arithmetic_types : require | ||||
| 
 | ||||
| // FidelityFX Super Resolution Sample | ||||
| // | ||||
| // Copyright (c) 2021 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. | ||||
| 
 | ||||
| layout( push_constant ) uniform constants { | ||||
|     u32vec2 input_size; | ||||
| }; | ||||
| 
 | ||||
| uvec4 Const0; | ||||
| uvec4 Const1; | ||||
| uvec4 Const2; | ||||
| uvec4 Const3; | ||||
| 
 | ||||
| #define A_GPU 1 | ||||
| #define A_GLSL 1 | ||||
| #define A_HALF | ||||
| 
 | ||||
| #include "ffx_a.h" | ||||
| 
 | ||||
| f16vec4 LinearToSRGB(f16vec4 linear) { | ||||
|     bvec4 selector = greaterThan(linear, f16vec4(0.00313066844250063)); | ||||
|     f16vec4 low = linear * float16_t(12.92); | ||||
|     f16vec4 high = float16_t(1.055) * pow(linear, f16vec4(1 / 2.4)) - float16_t(0.055); | ||||
|     return mix(low, high, selector); | ||||
| } | ||||
| 
 | ||||
| f16vec4 SRGBToLinear(f16vec4 srgb) { | ||||
|     bvec4 selector = greaterThan(srgb, f16vec4(0.0404482362771082)); | ||||
|     f16vec4 low = srgb * float16_t(1.0 / 12.92); | ||||
|     f16vec4 high = pow((srgb + float16_t(0.055)) * float16_t(1.0 / 1.055), f16vec4(2.4)); | ||||
|     return mix(low, high, selector); | ||||
| } | ||||
| 
 | ||||
| #if USE_EASU | ||||
|     #define FSR_EASU_H 1 | ||||
|     f16vec4 FsrEasuRH(vec2 p) { f16vec4 res = f16vec4(textureGather(InputTexture, p, 0)); return res; } | ||||
|     f16vec4 FsrEasuGH(vec2 p) { f16vec4 res = f16vec4(textureGather(InputTexture, p, 1)); return res; } | ||||
|     f16vec4 FsrEasuBH(vec2 p) { f16vec4 res = f16vec4(textureGather(InputTexture, p, 2)); return res; } | ||||
| #endif | ||||
| #if USE_RCAS | ||||
|     #define FSR_RCAS_H 1 | ||||
|     f16vec4 FsrRcasLoadH(ASW2 p) { return f16vec4(texelFetch(InputTexture, ASU2(p), 0)); } | ||||
|     void FsrRcasInputH(inout float16_t r, inout float16_t g, inout float16_t b) {} | ||||
| #endif | ||||
| 
 | ||||
| #include "ffx_fsr1.h" | ||||
| 
 | ||||
| void CurrFilter(u32vec2 pos) { | ||||
|     // For debugging | ||||
| #if USE_BILINEAR | ||||
|     vec2 pp = (vec2(pos) * vec2_AU2(Const0.xy) + vec2_AU2(Const0.zw)) * vec2_AU2(Const1.xy) + vec2(0.5, -0.5) * vec2_AU2(Const1.zw); | ||||
|     imageStore(OutputTexture, ivec2(pos), textureLod(InputTexture, pp, 0.0)); | ||||
| #endif | ||||
| #if USE_EASU | ||||
|     f16vec3 c; | ||||
|     FsrEasuH(c, pos, Const0, Const1, Const2, Const3); | ||||
|     imageStore(OutputTexture, ivec2(pos), f16vec4(c, 1)); | ||||
| #endif | ||||
| #if USE_RCAS | ||||
|     f16vec3 c; | ||||
|     FsrRcasH(c.r, c.g, c.b, pos, Const0); | ||||
|     imageStore(OutputTexture, ivec2(pos), f16vec4(c, 1)); | ||||
| #endif | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| layout(local_size_x=64) in; | ||||
| void main() { | ||||
| 
 | ||||
| #if USE_EASU || USE_BILINEAR | ||||
|     vec2 ires = vec2(input_size); | ||||
|     vec2 tres = textureSize(InputTexture, 0); | ||||
|     vec2 ores = imageSize(OutputTexture); | ||||
|     FsrEasuCon(Const0, Const1, Const2, Const3, ires.x, ires.y, tres.x, tres.y, ores.x, ores.y); | ||||
| #endif | ||||
| #if USE_RCAS | ||||
|     FsrRcasCon(Const0, 0.25f); | ||||
| #endif | ||||
| 
 | ||||
|     // Do remapping of local xy in workgroup for a more PS-like swizzle pattern. | ||||
|     AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u); | ||||
|     CurrFilter(gxy); | ||||
|     gxy.x += 8u; | ||||
|     CurrFilter(gxy); | ||||
|     gxy.y += 8u; | ||||
|     CurrFilter(gxy); | ||||
|     gxy.x -= 8u; | ||||
|     CurrFilter(gxy); | ||||
| } | ||||
							
								
								
									
										13
									
								
								src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu.comp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu.comp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| // Copyright 2021 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
| 
 | ||||
| #version 460 core | ||||
| #extension GL_GOOGLE_include_directive : enable | ||||
| 
 | ||||
| layout(set=0,binding=0) uniform sampler2D InputTexture; | ||||
| layout(set=0,binding=1,rgba16f) uniform image2D OutputTexture; | ||||
| 
 | ||||
| #define USE_EASU 1 | ||||
| 
 | ||||
| #include "fidelityfx_fsr.comp" | ||||
							
								
								
									
										13
									
								
								src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas.comp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas.comp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| // Copyright 2021 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
| 
 | ||||
| #version 460 core | ||||
| #extension GL_GOOGLE_include_directive : enable | ||||
| 
 | ||||
| layout(set=0,binding=0) uniform sampler2D InputTexture; | ||||
| layout(set=0,binding=1,rgba16f) uniform image2D OutputTexture; | ||||
| 
 | ||||
| #define USE_RCAS 1 | ||||
| 
 | ||||
| #include "fidelityfx_fsr.comp" | ||||
| @ -23,6 +23,7 @@ | ||||
| #include "video_core/host_shaders/vulkan_present_vert_spv.h" | ||||
| #include "video_core/renderer_vulkan/renderer_vulkan.h" | ||||
| #include "video_core/renderer_vulkan/vk_blit_screen.h" | ||||
| #include "video_core/renderer_vulkan/vk_fsr.h" | ||||
| #include "video_core/renderer_vulkan/vk_master_semaphore.h" | ||||
| #include "video_core/renderer_vulkan/vk_scheduler.h" | ||||
| #include "video_core/renderer_vulkan/vk_shader_util.h" | ||||
| @ -147,8 +148,12 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, | ||||
|     scheduler.Wait(resource_ticks[image_index]); | ||||
|     resource_ticks[image_index] = scheduler.CurrentTick(); | ||||
| 
 | ||||
|     UpdateDescriptorSet(image_index, | ||||
|                         use_accelerated ? screen_info.image_view : *raw_image_views[image_index]); | ||||
|     const VkImageView source_image_view = | ||||
|         use_accelerated ? screen_info.image_view : *raw_image_views[image_index]; | ||||
| 
 | ||||
|     if (!fsr) { | ||||
|         UpdateDescriptorSet(image_index, source_image_view); | ||||
|     } | ||||
| 
 | ||||
|     BufferData data; | ||||
|     SetUniformData(data, layout); | ||||
| @ -225,9 +230,26 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, | ||||
|                                    read_barrier); | ||||
|             cmdbuf.CopyBufferToImage(*buffer, image, VK_IMAGE_LAYOUT_GENERAL, copy); | ||||
|             cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, | ||||
|                                    VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, write_barrier); | ||||
|                                    VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | | ||||
|                                        VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, | ||||
|                                    0, write_barrier); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     if (fsr) { | ||||
|         auto crop_rect = framebuffer.crop_rect; | ||||
|         if (crop_rect.GetWidth() == 0) { | ||||
|             crop_rect.right = framebuffer.width; | ||||
|         } | ||||
|         if (crop_rect.GetHeight() == 0) { | ||||
|             crop_rect.bottom = framebuffer.height; | ||||
|         } | ||||
|         crop_rect = crop_rect.Scale(Settings::values.resolution_info.up_factor); | ||||
|         VkImageView fsr_image_view = | ||||
|             fsr->Draw(scheduler, image_index, source_image_view, crop_rect); | ||||
|         UpdateDescriptorSet(image_index, fsr_image_view); | ||||
|     } | ||||
| 
 | ||||
|     scheduler.Record( | ||||
|         [this, host_framebuffer, image_index, size = render_area](vk::CommandBuffer cmdbuf) { | ||||
|             const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; | ||||
| @ -325,6 +347,13 @@ void VKBlitScreen::CreateDynamicResources() { | ||||
|     CreateRenderPass(); | ||||
|     CreateFramebuffers(); | ||||
|     CreateGraphicsPipeline(); | ||||
|     fsr.reset(); | ||||
|     if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { | ||||
|         const auto& layout = render_window.GetFramebufferLayout(); | ||||
|         fsr = std::make_unique<FSR>( | ||||
|             device, memory_allocator, image_count, | ||||
|             VkExtent2D{.width = layout.screen.GetWidth(), .height = layout.screen.GetHeight()}); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void VKBlitScreen::RefreshResources(const Tegra::FramebufferConfig& framebuffer) { | ||||
| @ -716,13 +745,14 @@ void VKBlitScreen::CreateGraphicsPipeline() { | ||||
| } | ||||
| 
 | ||||
| void VKBlitScreen::CreateSampler() { | ||||
|     bool linear = Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::Fsr; | ||||
|     const VkSamplerCreateInfo ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .magFilter = VK_FILTER_LINEAR, | ||||
|         .minFilter = VK_FILTER_LINEAR, | ||||
|         .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, | ||||
|         .magFilter = linear ? VK_FILTER_LINEAR : VK_FILTER_NEAREST, | ||||
|         .minFilter = linear ? VK_FILTER_LINEAR : VK_FILTER_NEAREST, | ||||
|         .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, | ||||
|         .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, | ||||
|         .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, | ||||
|         .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, | ||||
| @ -905,17 +935,19 @@ void VKBlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfi | ||||
|     UNIMPLEMENTED_IF(framebuffer_crop_rect.top != 0); | ||||
|     UNIMPLEMENTED_IF(framebuffer_crop_rect.left != 0); | ||||
| 
 | ||||
|     // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
 | ||||
|     // (e.g. handheld mode) on a 1920x1080 framebuffer.
 | ||||
|     f32 scale_u = 1.0f; | ||||
|     f32 scale_v = 1.0f; | ||||
|     if (framebuffer_crop_rect.GetWidth() > 0) { | ||||
|         scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) / | ||||
|                   static_cast<f32>(screen_info.width); | ||||
|     } | ||||
|     if (framebuffer_crop_rect.GetHeight() > 0) { | ||||
|         scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) / | ||||
|                   static_cast<f32>(screen_info.height); | ||||
|     // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering
 | ||||
|     // (e.g. handheld mode) on a 1920x1080 framebuffer.
 | ||||
|     if (!fsr) { | ||||
|         if (framebuffer_crop_rect.GetWidth() > 0) { | ||||
|             scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) / | ||||
|                       static_cast<f32>(screen_info.width); | ||||
|         } | ||||
|         if (framebuffer_crop_rect.GetHeight() > 0) { | ||||
|             scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) / | ||||
|                       static_cast<f32>(screen_info.height); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     const auto& screen = layout.screen; | ||||
|  | ||||
| @ -38,6 +38,8 @@ class RasterizerVulkan; | ||||
| class VKScheduler; | ||||
| class VKSwapchain; | ||||
| 
 | ||||
| class FSR; | ||||
| 
 | ||||
| struct VKScreenInfo { | ||||
|     VkImageView image_view{}; | ||||
|     u32 width{}; | ||||
| @ -132,6 +134,8 @@ private: | ||||
|     std::vector<MemoryCommit> raw_buffer_commits; | ||||
|     u32 raw_width = 0; | ||||
|     u32 raw_height = 0; | ||||
| 
 | ||||
|     std::unique_ptr<FSR> fsr; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Vulkan
 | ||||
|  | ||||
							
								
								
									
										375
									
								
								src/video_core/renderer_vulkan/vk_fsr.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										375
									
								
								src/video_core/renderer_vulkan/vk_fsr.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,375 @@ | ||||
| // Copyright 2021 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "common/div_ceil.h" | ||||
| #include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_comp_spv.h" | ||||
| #include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_comp_spv.h" | ||||
| #include "video_core/renderer_vulkan/vk_fsr.h" | ||||
| #include "video_core/renderer_vulkan/vk_scheduler.h" | ||||
| #include "video_core/renderer_vulkan/vk_shader_util.h" | ||||
| #include "video_core/vulkan_common/vulkan_device.h" | ||||
| 
 | ||||
| namespace Vulkan { | ||||
| 
 | ||||
| FSR::FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, | ||||
|          VkExtent2D output_size) | ||||
|     : device{device}, memory_allocator{memory_allocator}, image_count{image_count}, | ||||
|       output_size{output_size} { | ||||
| 
 | ||||
|     CreateImages(); | ||||
|     CreateSampler(); | ||||
|     CreateShaders(); | ||||
|     CreateDescriptorPool(); | ||||
|     CreateDescriptorSetLayout(); | ||||
|     CreateDescriptorSets(); | ||||
|     CreatePipelineLayout(); | ||||
|     CreatePipeline(); | ||||
| } | ||||
| 
 | ||||
| VkImageView FSR::Draw(VKScheduler& scheduler, size_t image_index, VkImageView image_view, | ||||
|                       const Common::Rectangle<int>& crop_rect) { | ||||
| 
 | ||||
|     UpdateDescriptorSet(image_index, image_view); | ||||
| 
 | ||||
|     scheduler.Record([this, image_index, crop_rect](vk::CommandBuffer cmdbuf) { | ||||
|         const VkImageMemoryBarrier base_barrier{ | ||||
|             .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||||
|             .pNext = nullptr, | ||||
|             .srcAccessMask = 0, | ||||
|             .dstAccessMask = 0, | ||||
|             .oldLayout = VK_IMAGE_LAYOUT_GENERAL, | ||||
|             .newLayout = VK_IMAGE_LAYOUT_GENERAL, | ||||
|             .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||||
|             .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||||
|             .image = {}, | ||||
|             .subresourceRange = | ||||
|                 { | ||||
|                     .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||||
|                     .baseMipLevel = 0, | ||||
|                     .levelCount = 1, | ||||
|                     .baseArrayLayer = 0, | ||||
|                     .layerCount = 1, | ||||
|                 }, | ||||
|         }; | ||||
| 
 | ||||
|         // TODO: Support clear color
 | ||||
|         cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *easu_pipeline); | ||||
|         cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, | ||||
|                              VkExtent2D{ | ||||
|                                  .width = static_cast<u32>(crop_rect.GetWidth()), | ||||
|                                  .height = static_cast<u32>(crop_rect.GetHeight()), | ||||
|                              }); | ||||
| 
 | ||||
|         { | ||||
|             VkImageMemoryBarrier fsr_write_barrier = base_barrier; | ||||
|             fsr_write_barrier.image = *images[image_index], | ||||
|             fsr_write_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; | ||||
| 
 | ||||
|             cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, | ||||
|                                    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, fsr_write_barrier); | ||||
|         } | ||||
| 
 | ||||
|         cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline_layout, 0, | ||||
|                                   descriptor_sets[image_index * 2], {}); | ||||
|         cmdbuf.Dispatch(Common::DivCeil(output_size.width, 16u), | ||||
|                         Common::DivCeil(output_size.height, 16u), 1); | ||||
| 
 | ||||
|         cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *rcas_pipeline); | ||||
|         cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, output_size); | ||||
| 
 | ||||
|         { | ||||
|             std::array<VkImageMemoryBarrier, 2> barriers; | ||||
|             auto& fsr_read_barrier = barriers[0]; | ||||
|             auto& blit_write_barrier = barriers[1]; | ||||
| 
 | ||||
|             fsr_read_barrier = base_barrier; | ||||
|             fsr_read_barrier.image = *images[image_index]; | ||||
|             fsr_read_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; | ||||
|             fsr_read_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; | ||||
| 
 | ||||
|             blit_write_barrier = base_barrier; | ||||
|             blit_write_barrier.image = *images[image_count + image_index]; | ||||
|             blit_write_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; | ||||
|             blit_write_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; | ||||
| 
 | ||||
|             cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, | ||||
|                                    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, {}, {}, barriers); | ||||
|         } | ||||
| 
 | ||||
|         cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline_layout, 0, | ||||
|                                   descriptor_sets[image_index * 2 + 1], {}); | ||||
|         cmdbuf.Dispatch(Common::DivCeil(output_size.width, 16u), | ||||
|                         Common::DivCeil(output_size.height, 16u), 1); | ||||
| 
 | ||||
|         { | ||||
|             std::array<VkImageMemoryBarrier, 1> barriers; | ||||
|             auto& blit_read_barrier = barriers[0]; | ||||
| 
 | ||||
|             blit_read_barrier = base_barrier; | ||||
|             blit_read_barrier.image = *images[image_count + image_index]; | ||||
|             blit_read_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; | ||||
|             blit_read_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; | ||||
| 
 | ||||
|             cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, | ||||
|                                    VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, {}, {}, barriers); | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|     return *image_views[image_count + image_index]; | ||||
| } | ||||
| 
 | ||||
| void FSR::CreateDescriptorPool() { | ||||
|     const std::array<VkDescriptorPoolSize, 2> pool_sizes{{ | ||||
|         { | ||||
|             .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||||
|             .descriptorCount = static_cast<u32>(image_count * 2), | ||||
|         }, | ||||
|         { | ||||
|             .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, | ||||
|             .descriptorCount = static_cast<u32>(image_count * 2), | ||||
|         }, | ||||
|     }}; | ||||
| 
 | ||||
|     const VkDescriptorPoolCreateInfo ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, | ||||
|         .maxSets = static_cast<u32>(image_count * 2), | ||||
|         .poolSizeCount = static_cast<u32>(pool_sizes.size()), | ||||
|         .pPoolSizes = pool_sizes.data(), | ||||
|     }; | ||||
|     descriptor_pool = device.GetLogical().CreateDescriptorPool(ci); | ||||
| } | ||||
| 
 | ||||
| void FSR::CreateDescriptorSetLayout() { | ||||
|     const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings{{ | ||||
|         { | ||||
|             .binding = 0, | ||||
|             .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||||
|             .descriptorCount = 1, | ||||
|             .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, | ||||
|             .pImmutableSamplers = sampler.address(), | ||||
|         }, | ||||
|         { | ||||
|             .binding = 1, | ||||
|             .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, | ||||
|             .descriptorCount = 1, | ||||
|             .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, | ||||
|             .pImmutableSamplers = sampler.address(), | ||||
|         }, | ||||
|     }}; | ||||
| 
 | ||||
|     const VkDescriptorSetLayoutCreateInfo ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .bindingCount = static_cast<u32>(layout_bindings.size()), | ||||
|         .pBindings = layout_bindings.data(), | ||||
|     }; | ||||
| 
 | ||||
|     descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci); | ||||
| } | ||||
| 
 | ||||
| void FSR::CreateDescriptorSets() { | ||||
|     const u32 sets = static_cast<u32>(image_count * 2); | ||||
|     const std::vector layouts(sets, *descriptor_set_layout); | ||||
| 
 | ||||
|     const VkDescriptorSetAllocateInfo ai{ | ||||
|         .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .descriptorPool = *descriptor_pool, | ||||
|         .descriptorSetCount = sets, | ||||
|         .pSetLayouts = layouts.data(), | ||||
|     }; | ||||
| 
 | ||||
|     descriptor_sets = descriptor_pool.Allocate(ai); | ||||
| } | ||||
| 
 | ||||
| void FSR::CreateImages() { | ||||
|     images.resize(image_count * 2); | ||||
|     image_views.resize(image_count * 2); | ||||
|     buffer_commits.resize(image_count * 2); | ||||
| 
 | ||||
|     for (size_t i = 0; i < image_count * 2; ++i) { | ||||
|         images[i] = device.GetLogical().CreateImage(VkImageCreateInfo{ | ||||
|             .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, | ||||
|             .pNext = nullptr, | ||||
|             .flags = 0, | ||||
|             .imageType = VK_IMAGE_TYPE_2D, | ||||
|             .format = VK_FORMAT_R16G16B16A16_SFLOAT, | ||||
|             .extent = | ||||
|                 { | ||||
|                     .width = output_size.width, | ||||
|                     .height = output_size.height, | ||||
|                     .depth = 1, | ||||
|                 }, | ||||
|             .mipLevels = 1, | ||||
|             .arrayLayers = 1, | ||||
|             .samples = VK_SAMPLE_COUNT_1_BIT, | ||||
|             .tiling = VK_IMAGE_TILING_OPTIMAL, | ||||
|             .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT | | ||||
|                      VK_IMAGE_USAGE_SAMPLED_BIT, | ||||
|             .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||||
|             .queueFamilyIndexCount = 0, | ||||
|             .pQueueFamilyIndices = nullptr, | ||||
|             .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, | ||||
|         }); | ||||
|         buffer_commits[i] = memory_allocator.Commit(images[i], MemoryUsage::DeviceLocal); | ||||
|         image_views[i] = device.GetLogical().CreateImageView(VkImageViewCreateInfo{ | ||||
|             .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | ||||
|             .pNext = nullptr, | ||||
|             .flags = 0, | ||||
|             .image = *images[i], | ||||
|             .viewType = VK_IMAGE_VIEW_TYPE_2D, | ||||
|             .format = VK_FORMAT_R16G16B16A16_SFLOAT, | ||||
|             .components = | ||||
|                 { | ||||
|                     .r = VK_COMPONENT_SWIZZLE_IDENTITY, | ||||
|                     .g = VK_COMPONENT_SWIZZLE_IDENTITY, | ||||
|                     .b = VK_COMPONENT_SWIZZLE_IDENTITY, | ||||
|                     .a = VK_COMPONENT_SWIZZLE_IDENTITY, | ||||
|                 }, | ||||
|             .subresourceRange = | ||||
|                 { | ||||
|                     .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||||
|                     .baseMipLevel = 0, | ||||
|                     .levelCount = 1, | ||||
|                     .baseArrayLayer = 0, | ||||
|                     .layerCount = 1, | ||||
|                 }, | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void FSR::CreatePipelineLayout() { | ||||
|     VkPushConstantRange push_const{ | ||||
|         .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, | ||||
|         .offset = 0, | ||||
|         .size = sizeof(std::array<u32, 2>), | ||||
|     }; | ||||
|     VkPipelineLayoutCreateInfo ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .setLayoutCount = 1, | ||||
|         .pSetLayouts = descriptor_set_layout.address(), | ||||
|         .pushConstantRangeCount = 1, | ||||
|         .pPushConstantRanges = &push_const, | ||||
|     }; | ||||
| 
 | ||||
|     pipeline_layout = device.GetLogical().CreatePipelineLayout(ci); | ||||
| } | ||||
| 
 | ||||
| void FSR::UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const { | ||||
|     const auto fsr_image_view = *image_views[image_index]; | ||||
|     const auto blit_image_view = *image_views[image_count + image_index]; | ||||
| 
 | ||||
|     const VkDescriptorImageInfo image_info{ | ||||
|         .imageView = image_view, | ||||
|         .imageLayout = VK_IMAGE_LAYOUT_GENERAL, | ||||
|     }; | ||||
|     const VkDescriptorImageInfo fsr_image_info{ | ||||
|         .imageView = fsr_image_view, | ||||
|         .imageLayout = VK_IMAGE_LAYOUT_GENERAL, | ||||
|     }; | ||||
|     const VkDescriptorImageInfo blit_image_info{ | ||||
|         .imageView = blit_image_view, | ||||
|         .imageLayout = VK_IMAGE_LAYOUT_GENERAL, | ||||
|     }; | ||||
| 
 | ||||
|     VkWriteDescriptorSet sampler_write{ | ||||
|         .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, | ||||
|         .pNext = nullptr, | ||||
|         .dstSet = descriptor_sets[image_index * 2], | ||||
|         .dstBinding = 0, | ||||
|         .dstArrayElement = 0, | ||||
|         .descriptorCount = 1, | ||||
|         .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||||
|         .pImageInfo = &image_info, | ||||
|         .pBufferInfo = nullptr, | ||||
|         .pTexelBufferView = nullptr, | ||||
|     }; | ||||
| 
 | ||||
|     VkWriteDescriptorSet output_write{ | ||||
|         .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, | ||||
|         .pNext = nullptr, | ||||
|         .dstSet = descriptor_sets[image_index * 2], | ||||
|         .dstBinding = 1, | ||||
|         .dstArrayElement = 0, | ||||
|         .descriptorCount = 1, | ||||
|         .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, | ||||
|         .pImageInfo = &fsr_image_info, | ||||
|         .pBufferInfo = nullptr, | ||||
|         .pTexelBufferView = nullptr, | ||||
|     }; | ||||
| 
 | ||||
|     device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, output_write}, {}); | ||||
| 
 | ||||
|     sampler_write.dstSet = descriptor_sets[image_index * 2 + 1]; | ||||
|     sampler_write.pImageInfo = &fsr_image_info; | ||||
|     output_write.dstSet = descriptor_sets[image_index * 2 + 1]; | ||||
|     output_write.pImageInfo = &blit_image_info; | ||||
| 
 | ||||
|     device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, output_write}, {}); | ||||
| } | ||||
| 
 | ||||
| void FSR::CreateSampler() { | ||||
|     const VkSamplerCreateInfo ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .magFilter = VK_FILTER_LINEAR, | ||||
|         .minFilter = VK_FILTER_LINEAR, | ||||
|         .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, | ||||
|         .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, | ||||
|         .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, | ||||
|         .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, | ||||
|         .mipLodBias = 0.0f, | ||||
|         .anisotropyEnable = VK_FALSE, | ||||
|         .maxAnisotropy = 0.0f, | ||||
|         .compareEnable = VK_FALSE, | ||||
|         .compareOp = VK_COMPARE_OP_NEVER, | ||||
|         .minLod = 0.0f, | ||||
|         .maxLod = 0.0f, | ||||
|         .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, | ||||
|         .unnormalizedCoordinates = VK_FALSE, | ||||
|     }; | ||||
| 
 | ||||
|     sampler = device.GetLogical().CreateSampler(ci); | ||||
| } | ||||
| 
 | ||||
| void FSR::CreateShaders() { | ||||
|     easu_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_EASU_COMP_SPV); | ||||
|     rcas_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_EASU_COMP_SPV); | ||||
| } | ||||
| 
 | ||||
| void FSR::CreatePipeline() { | ||||
|     VkPipelineShaderStageCreateInfo shader_stage{ | ||||
| 
 | ||||
|         .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .stage = VK_SHADER_STAGE_COMPUTE_BIT, | ||||
|         .pName = "main", | ||||
|         .pSpecializationInfo = nullptr, | ||||
|     }; | ||||
| 
 | ||||
|     VkComputePipelineCreateInfo pipeline_ci{ | ||||
|         .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, | ||||
|         .pNext = nullptr, | ||||
|         .flags = 0, | ||||
|         .layout = *pipeline_layout, | ||||
|         .basePipelineIndex = 0, | ||||
|     }; | ||||
| 
 | ||||
|     shader_stage.module = *easu_shader; | ||||
|     pipeline_ci.stage = shader_stage; | ||||
|     easu_pipeline = device.GetLogical().CreateComputePipeline(pipeline_ci); | ||||
| 
 | ||||
|     shader_stage.module = *rcas_shader; | ||||
|     pipeline_ci.stage = shader_stage; | ||||
|     rcas_pipeline = device.GetLogical().CreateComputePipeline(pipeline_ci); | ||||
| } | ||||
| 
 | ||||
| } // namespace Vulkan
 | ||||
							
								
								
									
										54
									
								
								src/video_core/renderer_vulkan/vk_fsr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/video_core/renderer_vulkan/vk_fsr.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | ||||
| // Copyright 2021 yuzu Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/math_util.h" | ||||
| #include "video_core/vulkan_common/vulkan_memory_allocator.h" | ||||
| #include "video_core/vulkan_common/vulkan_wrapper.h" | ||||
| 
 | ||||
| namespace Vulkan { | ||||
| 
 | ||||
| class Device; | ||||
| class VKScheduler; | ||||
| 
 | ||||
| class FSR { | ||||
| public: | ||||
|     explicit FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, | ||||
|                  VkExtent2D output_size); | ||||
|     VkImageView Draw(VKScheduler& scheduler, size_t image_index, VkImageView image_view, | ||||
|                      const Common::Rectangle<int>& crop_rect); | ||||
| 
 | ||||
| private: | ||||
|     void CreateDescriptorPool(); | ||||
|     void CreateDescriptorSetLayout(); | ||||
|     void CreateDescriptorSets(); | ||||
|     void CreateImages(); | ||||
|     void CreateSampler(); | ||||
|     void CreateShaders(); | ||||
|     void CreatePipeline(); | ||||
|     void CreatePipelineLayout(); | ||||
| 
 | ||||
|     void UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const; | ||||
| 
 | ||||
|     const Device& device; | ||||
|     MemoryAllocator& memory_allocator; | ||||
|     size_t image_count; | ||||
|     VkExtent2D output_size; | ||||
| 
 | ||||
|     vk::DescriptorPool descriptor_pool; | ||||
|     vk::DescriptorSetLayout descriptor_set_layout; | ||||
|     vk::DescriptorSets descriptor_sets; | ||||
|     vk::PipelineLayout pipeline_layout; | ||||
|     vk::ShaderModule easu_shader; | ||||
|     vk::ShaderModule rcas_shader; | ||||
|     vk::Pipeline easu_pipeline; | ||||
|     vk::Pipeline rcas_pipeline; | ||||
|     vk::Sampler sampler; | ||||
|     std::vector<vk::Image> images; | ||||
|     std::vector<vk::ImageView> image_views; | ||||
|     std::vector<MemoryCommit> buffer_commits; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Vulkan
 | ||||
| @ -402,6 +402,11 @@ | ||||
|                <string>ScaleForce</string> | ||||
|               </property> | ||||
|              </item> | ||||
|              <item> | ||||
|               <property name="text"> | ||||
|                <string>FidelityFX Super Resolution</string> | ||||
|               </property> | ||||
|              </item> | ||||
|             </widget> | ||||
|            </item> | ||||
|           </layout> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user