diff --git a/src/common/settings.h b/src/common/settings.h index f6acf5bdf..830030efd 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -67,8 +67,9 @@ enum class ScalingFilter : u32 { NearestNeighbor = 0, Bilinear = 1, Bicubic = 2, - ScaleForce = 3, - Fsr = 4, + Gaussian = 3, + ScaleForce = 4, + Fsr = 5, }; struct ResolutionScalingInfo { diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 32e2ab500..b0e15773c 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -19,6 +19,7 @@ set(SHADER_FILES pitch_unswizzle.comp present_scaleforce.frag present_bicubic.frag + present_gaussian.frag vulkan_blit_color_float.frag vulkan_blit_depth_stencil.frag vulkan_fidelityfx_fsr_easu.comp diff --git a/src/video_core/host_shaders/present_gaussian.frag b/src/video_core/host_shaders/present_gaussian.frag new file mode 100644 index 000000000..d5e2b1781 --- /dev/null +++ b/src/video_core/host_shaders/present_gaussian.frag @@ -0,0 +1,74 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#version 460 core + +#ifdef VULKAN + +#define BINDING_COLOR_TEXTURE 1 + +#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv + +#define BINDING_COLOR_TEXTURE 0 + +#endif + +layout (location = 0) in vec2 frag_tex_coord; + +layout (location = 0) out vec4 color; + +layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D color_texture; + +const float offset[3] = float[](0.0, 1.3846153846, 3.2307692308); +const float weight[3] = float[](0.2270270270, 0.3162162162, 0.0702702703); + +vec4 blurVertical(sampler2D textureSampler, vec2 coord, vec2 norm) { + vec4 result = vec4(0.0f); + for (int i=1; i<3; i++) { + result += + texture(textureSampler, vec2(coord) + (vec2(0.0, offset[i]) * norm)) + * weight[i]; + result += + texture(textureSampler, vec2(coord) - (vec2(0.0, offset[i]) * norm)) + * weight[i]; + } + return result; +} + +vec4 blurHorizontal(sampler2D textureSampler, vec2 coord, vec2 norm) { + vec4 result = vec4(0.0f); + for (int i=1; i<3; i++) { + result += + texture(textureSampler, vec2(coord) + (vec2(offset[i], 0.0) * norm)) + * weight[i]; + result += + texture(textureSampler, vec2(coord) - (vec2(offset[i], 0.0) * norm)) + * weight[i]; + } + return result; +} + +vec4 blurDiagonal(sampler2D textureSampler, vec2 coord, vec2 norm) { + vec4 result = vec4(0.0f); + for (int i=1; i<3; i++) { + result += + texture(textureSampler, vec2(coord) + (vec2(offset[i], offset[i]) * norm)) + * weight[i]; + result += + texture(textureSampler, vec2(coord) - (vec2(offset[i], offset[i]) * norm)) + * weight[i]; + } + return result; +} + +void main() { + vec3 base = texture(color_texture, vec2(frag_tex_coord)).rgb * weight[0]; + vec2 tex_offset = 1.0f / textureSize(color_texture, 0); + vec3 horizontal = blurHorizontal(color_texture, frag_tex_coord, tex_offset).rgb; + vec3 vertical = blurVertical(color_texture, frag_tex_coord, tex_offset).rgb; + vec3 diagonalA = blurVertical(color_texture, frag_tex_coord, tex_offset).rgb; + vec3 diagonalB = blurVertical(color_texture, frag_tex_coord, -tex_offset).rgb; + vec3 combination = mix(mix(horizontal, vertical, 0.5f), mix(diagonalA, diagonalB, 0.5f), 0.5f); + color = vec4(combination + base, 1.0f); +} diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 68423601c..6132b3c49 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -256,6 +256,8 @@ void RendererOpenGL::InitOpenGLObjects() { present_vertex = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER); present_bilinear_fragment = CreateProgram(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER); present_bicubic_fragment = CreateProgram(HostShaders::PRESENT_BICUBIC_FRAG, GL_FRAGMENT_SHADER); + present_gaussian_fragment = + CreateProgram(HostShaders::PRESENT_GAUSSIAN_FRAG, GL_FRAGMENT_SHADER); present_scaleforce_fragment = CreateProgram(HostShaders::PRESENT_SCALEFORCE_FRAG, GL_FRAGMENT_SHADER); @@ -359,6 +361,9 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { case Settings::ScalingFilter::Bicubic: fragment_handle = present_bicubic_fragment.handle; break; + case Settings::ScalingFilter::Gaussian: + fragment_handle = present_gaussian_fragment.handle; + break; case Settings::ScalingFilter::ScaleForce: fragment_handle = present_scaleforce_fragment.handle; break; diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 504ddbe7b..62a746e41 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -114,6 +114,7 @@ private: OGLProgram present_vertex; OGLProgram present_bilinear_fragment; OGLProgram present_bicubic_fragment; + OGLProgram present_gaussian_fragment; OGLProgram present_scaleforce_fragment; OGLFramebuffer screenshot_framebuffer; diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index ccf721008..0d6bce214 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -18,6 +18,7 @@ #include "core/memory.h" #include "video_core/gpu.h" #include "video_core/host_shaders/present_bicubic_frag_spv.h" +#include "video_core/host_shaders/present_gaussian_frag_spv.h" #include "video_core/host_shaders/present_scaleforce_frag_spv.h" #include "video_core/host_shaders/vulkan_present_frag_spv.h" #include "video_core/host_shaders/vulkan_present_vert_spv.h" @@ -297,6 +298,9 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, case Settings::ScalingFilter::Bicubic: cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bicubic_pipeline); break; + case Settings::ScalingFilter::Gaussian: + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *gaussian_pipeline); + break; case Settings::ScalingFilter::ScaleForce: cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *scaleforce_pipeline); break; @@ -388,6 +392,7 @@ void VKBlitScreen::CreateShaders() { vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV); bilinear_fragment_shader = BuildShader(device, VULKAN_PRESENT_FRAG_SPV); bicubic_fragment_shader = BuildShader(device, PRESENT_BICUBIC_FRAG_SPV); + gaussian_fragment_shader = BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV); scaleforce_fragment_shader = BuildShader(device, PRESENT_SCALEFORCE_FRAG_SPV); } @@ -574,6 +579,27 @@ void VKBlitScreen::CreateGraphicsPipeline() { }, }}; + const std::array gaussian_shader_stages{{ + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stage = VK_SHADER_STAGE_VERTEX_BIT, + .module = *vertex_shader, + .pName = "main", + .pSpecializationInfo = nullptr, + }, + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stage = VK_SHADER_STAGE_FRAGMENT_BIT, + .module = *gaussian_fragment_shader, + .pName = "main", + .pSpecializationInfo = nullptr, + }, + }}; + const std::array scaleforce_shader_stages{{ { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, @@ -733,6 +759,28 @@ void VKBlitScreen::CreateGraphicsPipeline() { .basePipelineIndex = 0, }; + const VkGraphicsPipelineCreateInfo gaussian_pipeline_ci{ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stageCount = static_cast(gaussian_shader_stages.size()), + .pStages = gaussian_shader_stages.data(), + .pVertexInputState = &vertex_input_ci, + .pInputAssemblyState = &input_assembly_ci, + .pTessellationState = nullptr, + .pViewportState = &viewport_state_ci, + .pRasterizationState = &rasterization_ci, + .pMultisampleState = &multisampling_ci, + .pDepthStencilState = nullptr, + .pColorBlendState = &color_blend_ci, + .pDynamicState = &dynamic_state_ci, + .layout = *pipeline_layout, + .renderPass = *renderpass, + .subpass = 0, + .basePipelineHandle = 0, + .basePipelineIndex = 0, + }; + const VkGraphicsPipelineCreateInfo scaleforce_pipeline_ci{ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .pNext = nullptr, @@ -757,6 +805,7 @@ void VKBlitScreen::CreateGraphicsPipeline() { bilinear_pipeline = device.GetLogical().CreateGraphicsPipeline(bilinear_pipeline_ci); bicubic_pipeline = device.GetLogical().CreateGraphicsPipeline(bicubic_pipeline_ci); + gaussian_pipeline = device.GetLogical().CreateGraphicsPipeline(gaussian_pipeline_ci); scaleforce_pipeline = device.GetLogical().CreateGraphicsPipeline(scaleforce_pipeline_ci); } diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h index 448a2fbe6..96a5598ad 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.h +++ b/src/video_core/renderer_vulkan/vk_blit_screen.h @@ -111,6 +111,7 @@ private: vk::ShaderModule vertex_shader; vk::ShaderModule bilinear_fragment_shader; vk::ShaderModule bicubic_fragment_shader; + vk::ShaderModule gaussian_fragment_shader; vk::ShaderModule scaleforce_fragment_shader; vk::DescriptorPool descriptor_pool; vk::DescriptorSetLayout descriptor_set_layout; @@ -118,6 +119,7 @@ private: vk::Pipeline nearest_neightbor_pipeline; vk::Pipeline bilinear_pipeline; vk::Pipeline bicubic_pipeline; + vk::Pipeline gaussian_pipeline; vk::Pipeline scaleforce_pipeline; vk::RenderPass renderpass; std::vector framebuffers; diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index c1d7e8349..848ee2d08 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui @@ -417,6 +417,11 @@ Bicubic + + + Gaussian + + ScaleForce