diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/settings.cpp | 10 | ||||
| -rw-r--r-- | src/common/settings.h | 17 | ||||
| -rw-r--r-- | src/video_core/host_shaders/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/video_core/host_shaders/opengl_present_bicubic.frag | 56 | ||||
| -rw-r--r-- | src/video_core/host_shaders/opengl_present_scaleforce.frag | 135 | ||||
| -rw-r--r-- | src/video_core/host_shaders/vulkan_present_bicubic.frag | 56 | ||||
| -rw-r--r-- | src/video_core/host_shaders/vulkan_present_scaleforce.frag | 137 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.cpp | 27 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.h | 4 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_blit_screen.cpp | 123 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_blit_screen.h | 8 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.cpp | 5 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.h | 1 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_graphics.cpp | 25 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_graphics.ui | 46 |
15 files changed, 620 insertions, 34 deletions
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 4b7fa4b82..f0686a7c5 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -48,6 +48,7 @@ void LogSettings() { | |||
| 48 | log_setting("Core_UseMultiCore", values.use_multi_core.GetValue()); | 48 | log_setting("Core_UseMultiCore", values.use_multi_core.GetValue()); |
| 49 | log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue()); | 49 | log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue()); |
| 50 | log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue()); | 50 | log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue()); |
| 51 | log_setting("Renderer_ScalingFilter", values.scaling_filter.GetValue()); | ||
| 51 | log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue()); | 52 | log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue()); |
| 52 | log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue()); | 53 | log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue()); |
| 53 | log_setting("Renderer_UseDiskShaderCache", values.use_disk_shader_cache.GetValue()); | 54 | log_setting("Renderer_UseDiskShaderCache", values.use_disk_shader_cache.GetValue()); |
| @@ -113,19 +114,10 @@ void UpdateRescalingInfo() { | |||
| 113 | info.up_scale = 1; | 114 | info.up_scale = 1; |
| 114 | info.down_shift = 1; | 115 | info.down_shift = 1; |
| 115 | break; | 116 | break; |
| 116 | case ResolutionSetup::Res3_4X: | ||
| 117 | info.up_scale = 3; | ||
| 118 | info.down_shift = 2; | ||
| 119 | break; | ||
| 120 | case ResolutionSetup::Res1X: | 117 | case ResolutionSetup::Res1X: |
| 121 | info.up_scale = 1; | 118 | info.up_scale = 1; |
| 122 | info.down_shift = 0; | 119 | info.down_shift = 0; |
| 123 | break; | 120 | break; |
| 124 | case ResolutionSetup::Res3_2X: { | ||
| 125 | info.up_scale = 3; | ||
| 126 | info.down_shift = 1; | ||
| 127 | break; | ||
| 128 | } | ||
| 129 | case ResolutionSetup::Res2X: | 121 | case ResolutionSetup::Res2X: |
| 130 | info.up_scale = 2; | 122 | info.up_scale = 2; |
| 131 | info.down_shift = 0; | 123 | info.down_shift = 0; |
diff --git a/src/common/settings.h b/src/common/settings.h index ca88c086b..f629c7c56 100644 --- a/src/common/settings.h +++ b/src/common/settings.h | |||
| @@ -54,12 +54,16 @@ enum class NvdecEmulation : u32 { | |||
| 54 | 54 | ||
| 55 | enum class ResolutionSetup : u32 { | 55 | enum class ResolutionSetup : u32 { |
| 56 | Res1_2X = 0, | 56 | Res1_2X = 0, |
| 57 | Res3_4X = 1, | 57 | Res1X = 1, |
| 58 | Res1X = 2, | 58 | Res2X = 2, |
| 59 | Res3_2X = 3, | 59 | Res3X = 3, |
| 60 | Res2X = 4, | 60 | Res4X = 4, |
| 61 | Res3X = 5, | 61 | }; |
| 62 | Res4X = 6, | 62 | |
| 63 | enum class ScalingFilter : u32 { | ||
| 64 | Bilinear = 0, | ||
| 65 | Bicubic = 1, | ||
| 66 | ScaleForce = 2, | ||
| 63 | }; | 67 | }; |
| 64 | 68 | ||
| 65 | struct ResolutionScalingInfo { | 69 | struct ResolutionScalingInfo { |
| @@ -471,6 +475,7 @@ struct Values { | |||
| 471 | 475 | ||
| 472 | ResolutionScalingInfo resolution_info{}; | 476 | ResolutionScalingInfo resolution_info{}; |
| 473 | Setting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"}; | 477 | Setting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"}; |
| 478 | Setting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"}; | ||
| 474 | // *nix platforms may have issues with the borderless windowed fullscreen mode. | 479 | // *nix platforms may have issues with the borderless windowed fullscreen mode. |
| 475 | // Default to exclusive fullscreen on these platforms for now. | 480 | // Default to exclusive fullscreen on these platforms for now. |
| 476 | RangedSetting<FullscreenMode> fullscreen_mode{ | 481 | RangedSetting<FullscreenMode> fullscreen_mode{ |
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 20d748c12..835b37944 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt | |||
| @@ -6,11 +6,15 @@ set(SHADER_FILES | |||
| 6 | convert_float_to_depth.frag | 6 | convert_float_to_depth.frag |
| 7 | full_screen_triangle.vert | 7 | full_screen_triangle.vert |
| 8 | opengl_copy_bc4.comp | 8 | opengl_copy_bc4.comp |
| 9 | opengl_present_scaleforce.frag | ||
| 10 | opengl_present_bicubic.frag | ||
| 9 | opengl_present.frag | 11 | opengl_present.frag |
| 10 | opengl_present.vert | 12 | opengl_present.vert |
| 11 | pitch_unswizzle.comp | 13 | pitch_unswizzle.comp |
| 12 | vulkan_blit_color_float.frag | 14 | vulkan_blit_color_float.frag |
| 13 | vulkan_blit_depth_stencil.frag | 15 | vulkan_blit_depth_stencil.frag |
| 16 | vulkan_present_bicubic.frag | ||
| 17 | vulkan_present_scaleforce.frag | ||
| 14 | vulkan_present.frag | 18 | vulkan_present.frag |
| 15 | vulkan_present.vert | 19 | vulkan_present.vert |
| 16 | vulkan_quad_indexed.comp | 20 | vulkan_quad_indexed.comp |
diff --git a/src/video_core/host_shaders/opengl_present_bicubic.frag b/src/video_core/host_shaders/opengl_present_bicubic.frag new file mode 100644 index 000000000..17772095a --- /dev/null +++ b/src/video_core/host_shaders/opengl_present_bicubic.frag | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #version 460 core | ||
| 6 | |||
| 7 | layout (location = 0) in vec2 frag_tex_coord; | ||
| 8 | |||
| 9 | layout (location = 0) out vec4 color; | ||
| 10 | |||
| 11 | layout (binding = 1) uniform sampler2D color_texture; | ||
| 12 | |||
| 13 | vec4 cubic(float v) { | ||
| 14 | vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; | ||
| 15 | vec4 s = n * n * n; | ||
| 16 | float x = s.x; | ||
| 17 | float y = s.y - 4.0 * s.x; | ||
| 18 | float z = s.z - 4.0 * s.y + 6.0 * s.x; | ||
| 19 | float w = 6.0 - x - y - z; | ||
| 20 | return vec4(x, y, z, w) * (1.0 / 6.0); | ||
| 21 | } | ||
| 22 | |||
| 23 | vec4 textureBicubic( sampler2D textureSampler, vec2 texCoords ) { | ||
| 24 | |||
| 25 | vec2 texSize = textureSize(textureSampler, 0); | ||
| 26 | vec2 invTexSize = 1.0 / texSize; | ||
| 27 | |||
| 28 | texCoords = texCoords * texSize - 0.5; | ||
| 29 | |||
| 30 | vec2 fxy = fract(texCoords); | ||
| 31 | texCoords -= fxy; | ||
| 32 | |||
| 33 | vec4 xcubic = cubic(fxy.x); | ||
| 34 | vec4 ycubic = cubic(fxy.y); | ||
| 35 | |||
| 36 | vec4 c = texCoords.xxyy + vec2(-0.5, +1.5).xyxy; | ||
| 37 | |||
| 38 | vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); | ||
| 39 | vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; | ||
| 40 | |||
| 41 | offset *= invTexSize.xxyy; | ||
| 42 | |||
| 43 | vec4 sample0 = texture(textureSampler, offset.xz); | ||
| 44 | vec4 sample1 = texture(textureSampler, offset.yz); | ||
| 45 | vec4 sample2 = texture(textureSampler, offset.xw); | ||
| 46 | vec4 sample3 = texture(textureSampler, offset.yw); | ||
| 47 | |||
| 48 | float sx = s.x / (s.x + s.y); | ||
| 49 | float sy = s.z / (s.z + s.w); | ||
| 50 | |||
| 51 | return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); | ||
| 52 | } | ||
| 53 | |||
| 54 | void main() { | ||
| 55 | color = vec4(textureBicubic(color_texture, frag_tex_coord).rgb, 1.0f); | ||
| 56 | } | ||
diff --git a/src/video_core/host_shaders/opengl_present_scaleforce.frag b/src/video_core/host_shaders/opengl_present_scaleforce.frag new file mode 100644 index 000000000..0153f62c0 --- /dev/null +++ b/src/video_core/host_shaders/opengl_present_scaleforce.frag | |||
| @@ -0,0 +1,135 @@ | |||
| 1 | // from https://github.com/BreadFish64/ScaleFish/tree/master/scale_force | ||
| 2 | |||
| 3 | // MIT License | ||
| 4 | // | ||
| 5 | // Copyright (c) 2020 BreadFish64 | ||
| 6 | // | ||
| 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 8 | // of this software and associated documentation files (the "Software"), to deal | ||
| 9 | // in the Software without restriction, including without limitation the rights | ||
| 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 11 | // copies of the Software, and to permit persons to whom the Software is | ||
| 12 | // furnished to do so, subject to the following conditions: | ||
| 13 | // | ||
| 14 | // The above copyright notice and this permission notice shall be included in all | ||
| 15 | // copies or substantial portions of the Software. | ||
| 16 | // | ||
| 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 23 | // SOFTWARE. | ||
| 24 | |||
| 25 | precision mediump float; | ||
| 26 | |||
| 27 | layout (location = 0) in vec2 tex_coord; | ||
| 28 | |||
| 29 | layout (location = 0) out vec4 frag_color; | ||
| 30 | |||
| 31 | layout (binding = 1) uniform sampler2D input_texture; | ||
| 32 | |||
| 33 | vec2 tex_size; | ||
| 34 | vec2 inv_tex_size; | ||
| 35 | |||
| 36 | vec4 cubic(float v) { | ||
| 37 | vec3 n = vec3(1.0, 2.0, 3.0) - v; | ||
| 38 | vec3 s = n * n * n; | ||
| 39 | float x = s.x; | ||
| 40 | float y = s.y - 4.0 * s.x; | ||
| 41 | float z = s.z - 4.0 * s.y + 6.0 * s.x; | ||
| 42 | float w = 6.0 - x - y - z; | ||
| 43 | return vec4(x, y, z, w) / 6.0; | ||
| 44 | } | ||
| 45 | |||
| 46 | // Bicubic interpolation | ||
| 47 | vec4 textureBicubic(vec2 tex_coords) { | ||
| 48 | tex_coords = tex_coords * tex_size - 0.5; | ||
| 49 | |||
| 50 | vec2 fxy = modf(tex_coords, tex_coords); | ||
| 51 | |||
| 52 | vec4 xcubic = cubic(fxy.x); | ||
| 53 | vec4 ycubic = cubic(fxy.y); | ||
| 54 | |||
| 55 | vec4 c = tex_coords.xxyy + vec2(-0.5, +1.5).xyxy; | ||
| 56 | |||
| 57 | vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); | ||
| 58 | vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; | ||
| 59 | |||
| 60 | offset *= inv_tex_size.xxyy; | ||
| 61 | |||
| 62 | vec4 sample0 = textureLod(input_texture, offset.xz, 0.0); | ||
| 63 | vec4 sample1 = textureLod(input_texture, offset.yz, 0.0); | ||
| 64 | vec4 sample2 = textureLod(input_texture, offset.xw, 0.0); | ||
| 65 | vec4 sample3 = textureLod(input_texture, offset.yw, 0.0); | ||
| 66 | |||
| 67 | float sx = s.x / (s.x + s.y); | ||
| 68 | float sy = s.z / (s.z + s.w); | ||
| 69 | |||
| 70 | return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); | ||
| 71 | } | ||
| 72 | |||
| 73 | mat4x3 center_matrix; | ||
| 74 | vec4 center_alpha; | ||
| 75 | |||
| 76 | // Finds the distance between four colors and cc in YCbCr space | ||
| 77 | vec4 ColorDist(vec4 A, vec4 B, vec4 C, vec4 D) { | ||
| 78 | // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion | ||
| 79 | const vec3 K = vec3(0.2627, 0.6780, 0.0593); | ||
| 80 | const float LUMINANCE_WEIGHT = .6; | ||
| 81 | const mat3 YCBCR_MATRIX = | ||
| 82 | mat3(K * LUMINANCE_WEIGHT, -.5 * K.r / (1.0 - K.b), -.5 * K.g / (1.0 - K.b), .5, .5, | ||
| 83 | -.5 * K.g / (1.0 - K.r), -.5 * K.b / (1.0 - K.r)); | ||
| 84 | |||
| 85 | mat4x3 colors = mat4x3(A.rgb, B.rgb, C.rgb, D.rgb) - center_matrix; | ||
| 86 | mat4x3 YCbCr = YCBCR_MATRIX * colors; | ||
| 87 | vec4 color_dist = vec3(1.0) * YCbCr; | ||
| 88 | color_dist *= color_dist; | ||
| 89 | vec4 alpha = vec4(A.a, B.a, C.a, D.a); | ||
| 90 | |||
| 91 | return sqrt((color_dist + abs(center_alpha - alpha)) * alpha * center_alpha); | ||
| 92 | } | ||
| 93 | |||
| 94 | void main() { | ||
| 95 | vec4 bl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, -1)); | ||
| 96 | vec4 bc = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(0, -1)); | ||
| 97 | vec4 br = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, -1)); | ||
| 98 | vec4 cl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, 0)); | ||
| 99 | vec4 cc = textureLod(input_texture, tex_coord, 0.0); | ||
| 100 | vec4 cr = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, 0)); | ||
| 101 | vec4 tl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, 1)); | ||
| 102 | vec4 tc = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(0, 1)); | ||
| 103 | vec4 tr = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, 1)); | ||
| 104 | |||
| 105 | |||
| 106 | tex_size = vec2(textureSize(input_texture, 0)); | ||
| 107 | inv_tex_size = 1.0 / tex_size; | ||
| 108 | center_matrix = mat4x3(cc.rgb, cc.rgb, cc.rgb, cc.rgb); | ||
| 109 | center_alpha = cc.aaaa; | ||
| 110 | |||
| 111 | vec4 offset_tl = ColorDist(tl, tc, tr, cr); | ||
| 112 | vec4 offset_br = ColorDist(br, bc, bl, cl); | ||
| 113 | |||
| 114 | // Calculate how different cc is from the texels around it | ||
| 115 | float total_dist = dot(offset_tl + offset_br, vec4(1.0)); | ||
| 116 | |||
| 117 | // Add together all the distances with direction taken into account | ||
| 118 | vec4 tmp = offset_tl - offset_br; | ||
| 119 | vec2 total_offset = tmp.wy + tmp.zz + vec2(-tmp.x, tmp.x); | ||
| 120 | |||
| 121 | if (total_dist == 0.0) { | ||
| 122 | // Doing bicubic filtering just past the edges where the offset is 0 causes black floaters | ||
| 123 | // and it doesn't really matter which filter is used when the colors aren't changing. | ||
| 124 | frag_color = vec4(cc.rgb, 1.0f); | ||
| 125 | } else { | ||
| 126 | // When the image has thin points, they tend to split apart. | ||
| 127 | // This is because the texels all around are different | ||
| 128 | // and total_offset reaches into clear areas. | ||
| 129 | // This works pretty well to keep the offset in bounds for these cases. | ||
| 130 | float clamp_val = length(total_offset) / total_dist; | ||
| 131 | vec2 final_offset = clamp(total_offset, -clamp_val, clamp_val) * inv_tex_size; | ||
| 132 | |||
| 133 | frag_color = vec4(textureBicubic(tex_coord - final_offset).rgb, 1.0f); | ||
| 134 | } | ||
| 135 | } | ||
diff --git a/src/video_core/host_shaders/vulkan_present_bicubic.frag b/src/video_core/host_shaders/vulkan_present_bicubic.frag new file mode 100644 index 000000000..17772095a --- /dev/null +++ b/src/video_core/host_shaders/vulkan_present_bicubic.frag | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | // Copyright 2019 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #version 460 core | ||
| 6 | |||
| 7 | layout (location = 0) in vec2 frag_tex_coord; | ||
| 8 | |||
| 9 | layout (location = 0) out vec4 color; | ||
| 10 | |||
| 11 | layout (binding = 1) uniform sampler2D color_texture; | ||
| 12 | |||
| 13 | vec4 cubic(float v) { | ||
| 14 | vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; | ||
| 15 | vec4 s = n * n * n; | ||
| 16 | float x = s.x; | ||
| 17 | float y = s.y - 4.0 * s.x; | ||
| 18 | float z = s.z - 4.0 * s.y + 6.0 * s.x; | ||
| 19 | float w = 6.0 - x - y - z; | ||
| 20 | return vec4(x, y, z, w) * (1.0 / 6.0); | ||
| 21 | } | ||
| 22 | |||
| 23 | vec4 textureBicubic( sampler2D textureSampler, vec2 texCoords ) { | ||
| 24 | |||
| 25 | vec2 texSize = textureSize(textureSampler, 0); | ||
| 26 | vec2 invTexSize = 1.0 / texSize; | ||
| 27 | |||
| 28 | texCoords = texCoords * texSize - 0.5; | ||
| 29 | |||
| 30 | vec2 fxy = fract(texCoords); | ||
| 31 | texCoords -= fxy; | ||
| 32 | |||
| 33 | vec4 xcubic = cubic(fxy.x); | ||
| 34 | vec4 ycubic = cubic(fxy.y); | ||
| 35 | |||
| 36 | vec4 c = texCoords.xxyy + vec2(-0.5, +1.5).xyxy; | ||
| 37 | |||
| 38 | vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); | ||
| 39 | vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; | ||
| 40 | |||
| 41 | offset *= invTexSize.xxyy; | ||
| 42 | |||
| 43 | vec4 sample0 = texture(textureSampler, offset.xz); | ||
| 44 | vec4 sample1 = texture(textureSampler, offset.yz); | ||
| 45 | vec4 sample2 = texture(textureSampler, offset.xw); | ||
| 46 | vec4 sample3 = texture(textureSampler, offset.yw); | ||
| 47 | |||
| 48 | float sx = s.x / (s.x + s.y); | ||
| 49 | float sy = s.z / (s.z + s.w); | ||
| 50 | |||
| 51 | return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); | ||
| 52 | } | ||
| 53 | |||
| 54 | void main() { | ||
| 55 | color = vec4(textureBicubic(color_texture, frag_tex_coord).rgb, 1.0f); | ||
| 56 | } | ||
diff --git a/src/video_core/host_shaders/vulkan_present_scaleforce.frag b/src/video_core/host_shaders/vulkan_present_scaleforce.frag new file mode 100644 index 000000000..801c8eae9 --- /dev/null +++ b/src/video_core/host_shaders/vulkan_present_scaleforce.frag | |||
| @@ -0,0 +1,137 @@ | |||
| 1 | #version 320 es | ||
| 2 | |||
| 3 | // from https://github.com/BreadFish64/ScaleFish/tree/master/scale_force | ||
| 4 | |||
| 5 | // MIT License | ||
| 6 | // | ||
| 7 | // Copyright (c) 2020 BreadFish64 | ||
| 8 | // | ||
| 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 10 | // of this software and associated documentation files (the "Software"), to deal | ||
| 11 | // in the Software without restriction, including without limitation the rights | ||
| 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 13 | // copies of the Software, and to permit persons to whom the Software is | ||
| 14 | // furnished to do so, subject to the following conditions: | ||
| 15 | // | ||
| 16 | // The above copyright notice and this permission notice shall be included in all | ||
| 17 | // copies or substantial portions of the Software. | ||
| 18 | // | ||
| 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 25 | // SOFTWARE. | ||
| 26 | |||
| 27 | precision mediump float; | ||
| 28 | |||
| 29 | layout (location = 0) in vec2 tex_coord; | ||
| 30 | |||
| 31 | layout (location = 0) out vec4 frag_color; | ||
| 32 | |||
| 33 | layout (binding = 1) uniform sampler2D input_texture; | ||
| 34 | |||
| 35 | vec2 tex_size; | ||
| 36 | vec2 inv_tex_size; | ||
| 37 | |||
| 38 | vec4 cubic(float v) { | ||
| 39 | vec3 n = vec3(1.0, 2.0, 3.0) - v; | ||
| 40 | vec3 s = n * n * n; | ||
| 41 | float x = s.x; | ||
| 42 | float y = s.y - 4.0 * s.x; | ||
| 43 | float z = s.z - 4.0 * s.y + 6.0 * s.x; | ||
| 44 | float w = 6.0 - x - y - z; | ||
| 45 | return vec4(x, y, z, w) / 6.0; | ||
| 46 | } | ||
| 47 | |||
| 48 | // Bicubic interpolation | ||
| 49 | vec4 textureBicubic(vec2 tex_coords) { | ||
| 50 | tex_coords = tex_coords * tex_size - 0.5; | ||
| 51 | |||
| 52 | vec2 fxy = modf(tex_coords, tex_coords); | ||
| 53 | |||
| 54 | vec4 xcubic = cubic(fxy.x); | ||
| 55 | vec4 ycubic = cubic(fxy.y); | ||
| 56 | |||
| 57 | vec4 c = tex_coords.xxyy + vec2(-0.5, +1.5).xyxy; | ||
| 58 | |||
| 59 | vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); | ||
| 60 | vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; | ||
| 61 | |||
| 62 | offset *= inv_tex_size.xxyy; | ||
| 63 | |||
| 64 | vec4 sample0 = textureLod(input_texture, offset.xz, 0.0); | ||
| 65 | vec4 sample1 = textureLod(input_texture, offset.yz, 0.0); | ||
| 66 | vec4 sample2 = textureLod(input_texture, offset.xw, 0.0); | ||
| 67 | vec4 sample3 = textureLod(input_texture, offset.yw, 0.0); | ||
| 68 | |||
| 69 | float sx = s.x / (s.x + s.y); | ||
| 70 | float sy = s.z / (s.z + s.w); | ||
| 71 | |||
| 72 | return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); | ||
| 73 | } | ||
| 74 | |||
| 75 | mat4x3 center_matrix; | ||
| 76 | vec4 center_alpha; | ||
| 77 | |||
| 78 | // Finds the distance between four colors and cc in YCbCr space | ||
| 79 | vec4 ColorDist(vec4 A, vec4 B, vec4 C, vec4 D) { | ||
| 80 | // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion | ||
| 81 | const vec3 K = vec3(0.2627, 0.6780, 0.0593); | ||
| 82 | const float LUMINANCE_WEIGHT = .6; | ||
| 83 | const mat3 YCBCR_MATRIX = | ||
| 84 | mat3(K * LUMINANCE_WEIGHT, -.5 * K.r / (1.0 - K.b), -.5 * K.g / (1.0 - K.b), .5, .5, | ||
| 85 | -.5 * K.g / (1.0 - K.r), -.5 * K.b / (1.0 - K.r)); | ||
| 86 | |||
| 87 | mat4x3 colors = mat4x3(A.rgb, B.rgb, C.rgb, D.rgb) - center_matrix; | ||
| 88 | mat4x3 YCbCr = YCBCR_MATRIX * colors; | ||
| 89 | vec4 color_dist = vec3(1.0) * YCbCr; | ||
| 90 | color_dist *= color_dist; | ||
| 91 | vec4 alpha = vec4(A.a, B.a, C.a, D.a); | ||
| 92 | |||
| 93 | return sqrt((color_dist + abs(center_alpha - alpha)) * alpha * center_alpha); | ||
| 94 | } | ||
| 95 | |||
| 96 | void main() { | ||
| 97 | vec4 bl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, -1)); | ||
| 98 | vec4 bc = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(0, -1)); | ||
| 99 | vec4 br = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, -1)); | ||
| 100 | vec4 cl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, 0)); | ||
| 101 | vec4 cc = textureLod(input_texture, tex_coord, 0.0); | ||
| 102 | vec4 cr = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, 0)); | ||
| 103 | vec4 tl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, 1)); | ||
| 104 | vec4 tc = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(0, 1)); | ||
| 105 | vec4 tr = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, 1)); | ||
| 106 | |||
| 107 | |||
| 108 | tex_size = vec2(textureSize(input_texture, 0)); | ||
| 109 | inv_tex_size = 1.0 / tex_size; | ||
| 110 | center_matrix = mat4x3(cc.rgb, cc.rgb, cc.rgb, cc.rgb); | ||
| 111 | center_alpha = cc.aaaa; | ||
| 112 | |||
| 113 | vec4 offset_tl = ColorDist(tl, tc, tr, cr); | ||
| 114 | vec4 offset_br = ColorDist(br, bc, bl, cl); | ||
| 115 | |||
| 116 | // Calculate how different cc is from the texels around it | ||
| 117 | float total_dist = dot(offset_tl + offset_br, vec4(1.0)); | ||
| 118 | |||
| 119 | // Add together all the distances with direction taken into account | ||
| 120 | vec4 tmp = offset_tl - offset_br; | ||
| 121 | vec2 total_offset = tmp.wy + tmp.zz + vec2(-tmp.x, tmp.x); | ||
| 122 | |||
| 123 | if (total_dist == 0.0) { | ||
| 124 | // Doing bicubic filtering just past the edges where the offset is 0 causes black floaters | ||
| 125 | // and it doesn't really matter which filter is used when the colors aren't changing. | ||
| 126 | frag_color = vec4(cc.rgb, 1.0f); | ||
| 127 | } else { | ||
| 128 | // When the image has thin points, they tend to split apart. | ||
| 129 | // This is because the texels all around are different | ||
| 130 | // and total_offset reaches into clear areas. | ||
| 131 | // This works pretty well to keep the offset in bounds for these cases. | ||
| 132 | float clamp_val = length(total_offset) / total_dist; | ||
| 133 | vec2 final_offset = clamp(total_offset, -clamp_val, clamp_val) * inv_tex_size; | ||
| 134 | |||
| 135 | frag_color = vec4(textureBicubic(tex_coord - final_offset).rgb, 1.0f); | ||
| 136 | } | ||
| 137 | } | ||
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 0f7b69c6d..71a5e3adf 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp | |||
| @@ -21,7 +21,9 @@ | |||
| 21 | #include "core/memory.h" | 21 | #include "core/memory.h" |
| 22 | #include "core/perf_stats.h" | 22 | #include "core/perf_stats.h" |
| 23 | #include "core/telemetry_session.h" | 23 | #include "core/telemetry_session.h" |
| 24 | #include "video_core/host_shaders/opengl_present_bicubic_frag.h" | ||
| 24 | #include "video_core/host_shaders/opengl_present_frag.h" | 25 | #include "video_core/host_shaders/opengl_present_frag.h" |
| 26 | #include "video_core/host_shaders/opengl_present_scaleforce_frag.h" | ||
| 25 | #include "video_core/host_shaders/opengl_present_vert.h" | 27 | #include "video_core/host_shaders/opengl_present_vert.h" |
| 26 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 28 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 27 | #include "video_core/renderer_opengl/gl_shader_manager.h" | 29 | #include "video_core/renderer_opengl/gl_shader_manager.h" |
| @@ -252,7 +254,11 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color | |||
| 252 | void RendererOpenGL::InitOpenGLObjects() { | 254 | void RendererOpenGL::InitOpenGLObjects() { |
| 253 | // Create shader programs | 255 | // Create shader programs |
| 254 | present_vertex = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER); | 256 | present_vertex = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER); |
| 255 | present_fragment = CreateProgram(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER); | 257 | present_bilinear_fragment = CreateProgram(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER); |
| 258 | present_bicubic_fragment = | ||
| 259 | CreateProgram(HostShaders::OPENGL_PRESENT_BICUBIC_FRAG, GL_FRAGMENT_SHADER); | ||
| 260 | present_scaleforce_fragment = | ||
| 261 | CreateProgram(HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG, GL_FRAGMENT_SHADER); | ||
| 256 | 262 | ||
| 257 | // Generate presentation sampler | 263 | // Generate presentation sampler |
| 258 | present_sampler.Create(); | 264 | present_sampler.Create(); |
| @@ -337,7 +343,24 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { | |||
| 337 | // Set projection matrix | 343 | // Set projection matrix |
| 338 | const std::array ortho_matrix = | 344 | const std::array ortho_matrix = |
| 339 | MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height)); | 345 | MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height)); |
| 340 | program_manager.BindPresentPrograms(present_vertex.handle, present_fragment.handle); | 346 | |
| 347 | GLuint fragment_handle; | ||
| 348 | const auto filter = Settings::values.scaling_filter.GetValue(); | ||
| 349 | switch (filter) { | ||
| 350 | case Settings::ScalingFilter::Bilinear: | ||
| 351 | fragment_handle = present_bilinear_fragment.handle; | ||
| 352 | break; | ||
| 353 | case Settings::ScalingFilter::Bicubic: | ||
| 354 | fragment_handle = present_bicubic_fragment.handle; | ||
| 355 | break; | ||
| 356 | case Settings::ScalingFilter::ScaleForce: | ||
| 357 | fragment_handle = present_scaleforce_fragment.handle; | ||
| 358 | break; | ||
| 359 | default: | ||
| 360 | fragment_handle = present_bilinear_fragment.handle; | ||
| 361 | break; | ||
| 362 | } | ||
| 363 | program_manager.BindPresentPrograms(present_vertex.handle, fragment_handle); | ||
| 341 | glProgramUniformMatrix3x2fv(present_vertex.handle, ModelViewMatrixLocation, 1, GL_FALSE, | 364 | glProgramUniformMatrix3x2fv(present_vertex.handle, ModelViewMatrixLocation, 1, GL_FALSE, |
| 342 | ortho_matrix.data()); | 365 | ortho_matrix.data()); |
| 343 | 366 | ||
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index d455f572f..bf3d3502c 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h | |||
| @@ -111,7 +111,9 @@ private: | |||
| 111 | OGLSampler present_sampler; | 111 | OGLSampler present_sampler; |
| 112 | OGLBuffer vertex_buffer; | 112 | OGLBuffer vertex_buffer; |
| 113 | OGLProgram present_vertex; | 113 | OGLProgram present_vertex; |
| 114 | OGLProgram present_fragment; | 114 | OGLProgram present_bilinear_fragment; |
| 115 | OGLProgram present_bicubic_fragment; | ||
| 116 | OGLProgram present_scaleforce_fragment; | ||
| 115 | OGLFramebuffer screenshot_framebuffer; | 117 | OGLFramebuffer screenshot_framebuffer; |
| 116 | 118 | ||
| 117 | // GPU address of the vertex buffer | 119 | // GPU address of the vertex buffer |
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 7051e6559..19d91ecfc 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp | |||
| @@ -12,11 +12,14 @@ | |||
| 12 | #include "common/assert.h" | 12 | #include "common/assert.h" |
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "common/math_util.h" | 14 | #include "common/math_util.h" |
| 15 | #include "common/settings.h" | ||
| 15 | #include "core/core.h" | 16 | #include "core/core.h" |
| 16 | #include "core/frontend/emu_window.h" | 17 | #include "core/frontend/emu_window.h" |
| 17 | #include "core/memory.h" | 18 | #include "core/memory.h" |
| 18 | #include "video_core/gpu.h" | 19 | #include "video_core/gpu.h" |
| 20 | #include "video_core/host_shaders/vulkan_present_bicubic_frag_spv.h" | ||
| 19 | #include "video_core/host_shaders/vulkan_present_frag_spv.h" | 21 | #include "video_core/host_shaders/vulkan_present_frag_spv.h" |
| 22 | #include "video_core/host_shaders/vulkan_present_scaleforce_frag_spv.h" | ||
| 20 | #include "video_core/host_shaders/vulkan_present_vert_spv.h" | 23 | #include "video_core/host_shaders/vulkan_present_vert_spv.h" |
| 21 | #include "video_core/renderer_vulkan/renderer_vulkan.h" | 24 | #include "video_core/renderer_vulkan/renderer_vulkan.h" |
| 22 | #include "video_core/renderer_vulkan/vk_blit_screen.h" | 25 | #include "video_core/renderer_vulkan/vk_blit_screen.h" |
| @@ -258,8 +261,22 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, | |||
| 258 | .offset = {0, 0}, | 261 | .offset = {0, 0}, |
| 259 | .extent = size, | 262 | .extent = size, |
| 260 | }; | 263 | }; |
| 264 | const auto filter = Settings::values.scaling_filter.GetValue(); | ||
| 261 | cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); | 265 | cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); |
| 262 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); | 266 | switch (filter) { |
| 267 | case Settings::ScalingFilter::Bilinear: | ||
| 268 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bilinear_pipeline); | ||
| 269 | break; | ||
| 270 | case Settings::ScalingFilter::Bicubic: | ||
| 271 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bicubic_pipeline); | ||
| 272 | break; | ||
| 273 | case Settings::ScalingFilter::ScaleForce: | ||
| 274 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *scaleforce_pipeline); | ||
| 275 | break; | ||
| 276 | default: | ||
| 277 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bilinear_pipeline); | ||
| 278 | break; | ||
| 279 | } | ||
| 263 | cmdbuf.SetViewport(0, viewport); | 280 | cmdbuf.SetViewport(0, viewport); |
| 264 | cmdbuf.SetScissor(0, scissor); | 281 | cmdbuf.SetScissor(0, scissor); |
| 265 | 282 | ||
| @@ -324,7 +341,9 @@ void VKBlitScreen::RefreshResources(const Tegra::FramebufferConfig& framebuffer) | |||
| 324 | 341 | ||
| 325 | void VKBlitScreen::CreateShaders() { | 342 | void VKBlitScreen::CreateShaders() { |
| 326 | vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV); | 343 | vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV); |
| 327 | fragment_shader = BuildShader(device, VULKAN_PRESENT_FRAG_SPV); | 344 | bilinear_fragment_shader = BuildShader(device, VULKAN_PRESENT_FRAG_SPV); |
| 345 | bicubic_fragment_shader = BuildShader(device, VULKAN_PRESENT_BICUBIC_FRAG_SPV); | ||
| 346 | scaleforce_fragment_shader = BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FRAG_SPV); | ||
| 328 | } | 347 | } |
| 329 | 348 | ||
| 330 | void VKBlitScreen::CreateSemaphores() { | 349 | void VKBlitScreen::CreateSemaphores() { |
| @@ -468,7 +487,7 @@ void VKBlitScreen::CreatePipelineLayout() { | |||
| 468 | } | 487 | } |
| 469 | 488 | ||
| 470 | void VKBlitScreen::CreateGraphicsPipeline() { | 489 | void VKBlitScreen::CreateGraphicsPipeline() { |
| 471 | const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{ | 490 | const std::array<VkPipelineShaderStageCreateInfo, 2> bilinear_shader_stages{{ |
| 472 | { | 491 | { |
| 473 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | 492 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| 474 | .pNext = nullptr, | 493 | .pNext = nullptr, |
| @@ -483,7 +502,49 @@ void VKBlitScreen::CreateGraphicsPipeline() { | |||
| 483 | .pNext = nullptr, | 502 | .pNext = nullptr, |
| 484 | .flags = 0, | 503 | .flags = 0, |
| 485 | .stage = VK_SHADER_STAGE_FRAGMENT_BIT, | 504 | .stage = VK_SHADER_STAGE_FRAGMENT_BIT, |
| 486 | .module = *fragment_shader, | 505 | .module = *bilinear_fragment_shader, |
| 506 | .pName = "main", | ||
| 507 | .pSpecializationInfo = nullptr, | ||
| 508 | }, | ||
| 509 | }}; | ||
| 510 | |||
| 511 | const std::array<VkPipelineShaderStageCreateInfo, 2> bicubic_shader_stages{{ | ||
| 512 | { | ||
| 513 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||
| 514 | .pNext = nullptr, | ||
| 515 | .flags = 0, | ||
| 516 | .stage = VK_SHADER_STAGE_VERTEX_BIT, | ||
| 517 | .module = *vertex_shader, | ||
| 518 | .pName = "main", | ||
| 519 | .pSpecializationInfo = nullptr, | ||
| 520 | }, | ||
| 521 | { | ||
| 522 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||
| 523 | .pNext = nullptr, | ||
| 524 | .flags = 0, | ||
| 525 | .stage = VK_SHADER_STAGE_FRAGMENT_BIT, | ||
| 526 | .module = *bicubic_fragment_shader, | ||
| 527 | .pName = "main", | ||
| 528 | .pSpecializationInfo = nullptr, | ||
| 529 | }, | ||
| 530 | }}; | ||
| 531 | |||
| 532 | const std::array<VkPipelineShaderStageCreateInfo, 2> scaleforce_shader_stages{{ | ||
| 533 | { | ||
| 534 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||
| 535 | .pNext = nullptr, | ||
| 536 | .flags = 0, | ||
| 537 | .stage = VK_SHADER_STAGE_VERTEX_BIT, | ||
| 538 | .module = *vertex_shader, | ||
| 539 | .pName = "main", | ||
| 540 | .pSpecializationInfo = nullptr, | ||
| 541 | }, | ||
| 542 | { | ||
| 543 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||
| 544 | .pNext = nullptr, | ||
| 545 | .flags = 0, | ||
| 546 | .stage = VK_SHADER_STAGE_FRAGMENT_BIT, | ||
| 547 | .module = *scaleforce_fragment_shader, | ||
| 487 | .pName = "main", | 548 | .pName = "main", |
| 488 | .pSpecializationInfo = nullptr, | 549 | .pSpecializationInfo = nullptr, |
| 489 | }, | 550 | }, |
| @@ -583,12 +644,56 @@ void VKBlitScreen::CreateGraphicsPipeline() { | |||
| 583 | .pDynamicStates = dynamic_states.data(), | 644 | .pDynamicStates = dynamic_states.data(), |
| 584 | }; | 645 | }; |
| 585 | 646 | ||
| 586 | const VkGraphicsPipelineCreateInfo pipeline_ci{ | 647 | const VkGraphicsPipelineCreateInfo bilinear_pipeline_ci{ |
| 648 | .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | ||
| 649 | .pNext = nullptr, | ||
| 650 | .flags = 0, | ||
| 651 | .stageCount = static_cast<u32>(bilinear_shader_stages.size()), | ||
| 652 | .pStages = bilinear_shader_stages.data(), | ||
| 653 | .pVertexInputState = &vertex_input_ci, | ||
| 654 | .pInputAssemblyState = &input_assembly_ci, | ||
| 655 | .pTessellationState = nullptr, | ||
| 656 | .pViewportState = &viewport_state_ci, | ||
| 657 | .pRasterizationState = &rasterization_ci, | ||
| 658 | .pMultisampleState = &multisampling_ci, | ||
| 659 | .pDepthStencilState = nullptr, | ||
| 660 | .pColorBlendState = &color_blend_ci, | ||
| 661 | .pDynamicState = &dynamic_state_ci, | ||
| 662 | .layout = *pipeline_layout, | ||
| 663 | .renderPass = *renderpass, | ||
| 664 | .subpass = 0, | ||
| 665 | .basePipelineHandle = 0, | ||
| 666 | .basePipelineIndex = 0, | ||
| 667 | }; | ||
| 668 | |||
| 669 | const VkGraphicsPipelineCreateInfo bicubic_pipeline_ci{ | ||
| 670 | .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | ||
| 671 | .pNext = nullptr, | ||
| 672 | .flags = 0, | ||
| 673 | .stageCount = static_cast<u32>(bicubic_shader_stages.size()), | ||
| 674 | .pStages = bicubic_shader_stages.data(), | ||
| 675 | .pVertexInputState = &vertex_input_ci, | ||
| 676 | .pInputAssemblyState = &input_assembly_ci, | ||
| 677 | .pTessellationState = nullptr, | ||
| 678 | .pViewportState = &viewport_state_ci, | ||
| 679 | .pRasterizationState = &rasterization_ci, | ||
| 680 | .pMultisampleState = &multisampling_ci, | ||
| 681 | .pDepthStencilState = nullptr, | ||
| 682 | .pColorBlendState = &color_blend_ci, | ||
| 683 | .pDynamicState = &dynamic_state_ci, | ||
| 684 | .layout = *pipeline_layout, | ||
| 685 | .renderPass = *renderpass, | ||
| 686 | .subpass = 0, | ||
| 687 | .basePipelineHandle = 0, | ||
| 688 | .basePipelineIndex = 0, | ||
| 689 | }; | ||
| 690 | |||
| 691 | const VkGraphicsPipelineCreateInfo scaleforce_pipeline_ci{ | ||
| 587 | .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | 692 | .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, |
| 588 | .pNext = nullptr, | 693 | .pNext = nullptr, |
| 589 | .flags = 0, | 694 | .flags = 0, |
| 590 | .stageCount = static_cast<u32>(shader_stages.size()), | 695 | .stageCount = static_cast<u32>(scaleforce_shader_stages.size()), |
| 591 | .pStages = shader_stages.data(), | 696 | .pStages = scaleforce_shader_stages.data(), |
| 592 | .pVertexInputState = &vertex_input_ci, | 697 | .pVertexInputState = &vertex_input_ci, |
| 593 | .pInputAssemblyState = &input_assembly_ci, | 698 | .pInputAssemblyState = &input_assembly_ci, |
| 594 | .pTessellationState = nullptr, | 699 | .pTessellationState = nullptr, |
| @@ -605,7 +710,9 @@ void VKBlitScreen::CreateGraphicsPipeline() { | |||
| 605 | .basePipelineIndex = 0, | 710 | .basePipelineIndex = 0, |
| 606 | }; | 711 | }; |
| 607 | 712 | ||
| 608 | pipeline = device.GetLogical().CreateGraphicsPipeline(pipeline_ci); | 713 | bilinear_pipeline = device.GetLogical().CreateGraphicsPipeline(bilinear_pipeline_ci); |
| 714 | bicubic_pipeline = device.GetLogical().CreateGraphicsPipeline(bicubic_pipeline_ci); | ||
| 715 | scaleforce_pipeline = device.GetLogical().CreateGraphicsPipeline(scaleforce_pipeline_ci); | ||
| 609 | } | 716 | } |
| 610 | 717 | ||
| 611 | void VKBlitScreen::CreateSampler() { | 718 | void VKBlitScreen::CreateSampler() { |
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h index 430bcfbca..d3a16f0ba 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.h +++ b/src/video_core/renderer_vulkan/vk_blit_screen.h | |||
| @@ -107,11 +107,15 @@ private: | |||
| 107 | const VKScreenInfo& screen_info; | 107 | const VKScreenInfo& screen_info; |
| 108 | 108 | ||
| 109 | vk::ShaderModule vertex_shader; | 109 | vk::ShaderModule vertex_shader; |
| 110 | vk::ShaderModule fragment_shader; | 110 | vk::ShaderModule bilinear_fragment_shader; |
| 111 | vk::ShaderModule bicubic_fragment_shader; | ||
| 112 | vk::ShaderModule scaleforce_fragment_shader; | ||
| 111 | vk::DescriptorPool descriptor_pool; | 113 | vk::DescriptorPool descriptor_pool; |
| 112 | vk::DescriptorSetLayout descriptor_set_layout; | 114 | vk::DescriptorSetLayout descriptor_set_layout; |
| 113 | vk::PipelineLayout pipeline_layout; | 115 | vk::PipelineLayout pipeline_layout; |
| 114 | vk::Pipeline pipeline; | 116 | vk::Pipeline bilinear_pipeline; |
| 117 | vk::Pipeline bicubic_pipeline; | ||
| 118 | vk::Pipeline scaleforce_pipeline; | ||
| 115 | vk::RenderPass renderpass; | 119 | vk::RenderPass renderpass; |
| 116 | std::vector<vk::Framebuffer> framebuffers; | 120 | std::vector<vk::Framebuffer> framebuffers; |
| 117 | vk::DescriptorSets descriptor_sets; | 121 | vk::DescriptorSets descriptor_sets; |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 7ed833203..3803bf501 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -825,6 +825,7 @@ void Config::ReadRendererValues() { | |||
| 825 | ReadGlobalSetting(Settings::values.fullscreen_mode); | 825 | ReadGlobalSetting(Settings::values.fullscreen_mode); |
| 826 | ReadGlobalSetting(Settings::values.aspect_ratio); | 826 | ReadGlobalSetting(Settings::values.aspect_ratio); |
| 827 | ReadGlobalSetting(Settings::values.resolution_setup); | 827 | ReadGlobalSetting(Settings::values.resolution_setup); |
| 828 | ReadGlobalSetting(Settings::values.scaling_filter); | ||
| 828 | ReadGlobalSetting(Settings::values.max_anisotropy); | 829 | ReadGlobalSetting(Settings::values.max_anisotropy); |
| 829 | ReadGlobalSetting(Settings::values.use_speed_limit); | 830 | ReadGlobalSetting(Settings::values.use_speed_limit); |
| 830 | ReadGlobalSetting(Settings::values.speed_limit); | 831 | ReadGlobalSetting(Settings::values.speed_limit); |
| @@ -1371,6 +1372,10 @@ void Config::SaveRendererValues() { | |||
| 1371 | static_cast<u32>(Settings::values.resolution_setup.GetValue(global)), | 1372 | static_cast<u32>(Settings::values.resolution_setup.GetValue(global)), |
| 1372 | static_cast<u32>(Settings::values.resolution_setup.GetDefault()), | 1373 | static_cast<u32>(Settings::values.resolution_setup.GetDefault()), |
| 1373 | Settings::values.resolution_setup.UsingGlobal()); | 1374 | Settings::values.resolution_setup.UsingGlobal()); |
| 1375 | WriteSetting(QString::fromStdString(Settings::values.scaling_filter.GetLabel()), | ||
| 1376 | static_cast<u32>(Settings::values.scaling_filter.GetValue(global)), | ||
| 1377 | static_cast<u32>(Settings::values.scaling_filter.GetDefault()), | ||
| 1378 | Settings::values.scaling_filter.UsingGlobal()); | ||
| 1374 | WriteGlobalSetting(Settings::values.max_anisotropy); | 1379 | WriteGlobalSetting(Settings::values.max_anisotropy); |
| 1375 | WriteGlobalSetting(Settings::values.use_speed_limit); | 1380 | WriteGlobalSetting(Settings::values.use_speed_limit); |
| 1376 | WriteGlobalSetting(Settings::values.speed_limit); | 1381 | WriteGlobalSetting(Settings::values.speed_limit); |
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index fbb91d312..97dc1bb47 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h | |||
| @@ -190,5 +190,6 @@ Q_DECLARE_METATYPE(Settings::GPUAccuracy); | |||
| 190 | Q_DECLARE_METATYPE(Settings::FullscreenMode); | 190 | Q_DECLARE_METATYPE(Settings::FullscreenMode); |
| 191 | Q_DECLARE_METATYPE(Settings::NvdecEmulation); | 191 | Q_DECLARE_METATYPE(Settings::NvdecEmulation); |
| 192 | Q_DECLARE_METATYPE(Settings::ResolutionSetup); | 192 | Q_DECLARE_METATYPE(Settings::ResolutionSetup); |
| 193 | Q_DECLARE_METATYPE(Settings::ScalingFilter); | ||
| 193 | Q_DECLARE_METATYPE(Settings::RendererBackend); | 194 | Q_DECLARE_METATYPE(Settings::RendererBackend); |
| 194 | Q_DECLARE_METATYPE(Settings::ShaderBackend); | 195 | Q_DECLARE_METATYPE(Settings::ShaderBackend); |
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 4f08ae3e0..e01efaeda 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -89,6 +89,7 @@ void ConfigureGraphics::SetConfiguration() { | |||
| 89 | ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); | 89 | ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); |
| 90 | ui->use_disk_shader_cache->setEnabled(runtime_lock); | 90 | ui->use_disk_shader_cache->setEnabled(runtime_lock); |
| 91 | ui->nvdec_emulation_widget->setEnabled(runtime_lock); | 91 | ui->nvdec_emulation_widget->setEnabled(runtime_lock); |
| 92 | ui->resolution_combobox->setEnabled(runtime_lock); | ||
| 92 | ui->accelerate_astc->setEnabled(runtime_lock); | 93 | ui->accelerate_astc->setEnabled(runtime_lock); |
| 93 | ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue()); | 94 | ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue()); |
| 94 | ui->use_asynchronous_gpu_emulation->setChecked( | 95 | ui->use_asynchronous_gpu_emulation->setChecked( |
| @@ -104,6 +105,8 @@ void ConfigureGraphics::SetConfiguration() { | |||
| 104 | ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue()); | 105 | ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue()); |
| 105 | ui->resolution_combobox->setCurrentIndex( | 106 | ui->resolution_combobox->setCurrentIndex( |
| 106 | static_cast<int>(Settings::values.resolution_setup.GetValue())); | 107 | static_cast<int>(Settings::values.resolution_setup.GetValue())); |
| 108 | ui->scaling_filter_combobox->setCurrentIndex( | ||
| 109 | static_cast<int>(Settings::values.scaling_filter.GetValue())); | ||
| 107 | } else { | 110 | } else { |
| 108 | ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend); | 111 | ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend); |
| 109 | ConfigurationShared::SetHighlight(ui->api_widget, | 112 | ConfigurationShared::SetHighlight(ui->api_widget, |
| @@ -129,6 +132,11 @@ void ConfigureGraphics::SetConfiguration() { | |||
| 129 | ConfigurationShared::SetHighlight(ui->resolution_label, | 132 | ConfigurationShared::SetHighlight(ui->resolution_label, |
| 130 | !Settings::values.resolution_setup.UsingGlobal()); | 133 | !Settings::values.resolution_setup.UsingGlobal()); |
| 131 | 134 | ||
| 135 | ConfigurationShared::SetPerGameSetting(ui->scaling_filter_combobox, | ||
| 136 | &Settings::values.scaling_filter); | ||
| 137 | ConfigurationShared::SetHighlight(ui->scaling_filter_label, | ||
| 138 | !Settings::values.scaling_filter.UsingGlobal()); | ||
| 139 | |||
| 132 | ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1); | 140 | ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1); |
| 133 | ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal()); | 141 | ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal()); |
| 134 | ConfigurationShared::SetHighlight(ui->bg_layout, !Settings::values.bg_red.UsingGlobal()); | 142 | ConfigurationShared::SetHighlight(ui->bg_layout, !Settings::values.bg_red.UsingGlobal()); |
| @@ -144,6 +152,10 @@ void ConfigureGraphics::ApplyConfiguration() { | |||
| 144 | ui->resolution_combobox->currentIndex() - | 152 | ui->resolution_combobox->currentIndex() - |
| 145 | ((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET)); | 153 | ((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET)); |
| 146 | 154 | ||
| 155 | const auto scaling_filter = static_cast<Settings::ScalingFilter>( | ||
| 156 | ui->scaling_filter_combobox->currentIndex() - | ||
| 157 | ((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET)); | ||
| 158 | |||
| 147 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.fullscreen_mode, | 159 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.fullscreen_mode, |
| 148 | ui->fullscreen_mode_combobox); | 160 | ui->fullscreen_mode_combobox); |
| 149 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.aspect_ratio, | 161 | ConfigurationShared::ApplyPerGameSetting(&Settings::values.aspect_ratio, |
| @@ -178,6 +190,9 @@ void ConfigureGraphics::ApplyConfiguration() { | |||
| 178 | if (Settings::values.resolution_setup.UsingGlobal()) { | 190 | if (Settings::values.resolution_setup.UsingGlobal()) { |
| 179 | Settings::values.resolution_setup.SetValue(resolution_setup); | 191 | Settings::values.resolution_setup.SetValue(resolution_setup); |
| 180 | } | 192 | } |
| 193 | if (Settings::values.scaling_filter.UsingGlobal()) { | ||
| 194 | Settings::values.scaling_filter.SetValue(scaling_filter); | ||
| 195 | } | ||
| 181 | } else { | 196 | } else { |
| 182 | if (ui->resolution_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { | 197 | if (ui->resolution_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { |
| 183 | Settings::values.resolution_setup.SetGlobal(true); | 198 | Settings::values.resolution_setup.SetGlobal(true); |
| @@ -185,6 +200,12 @@ void ConfigureGraphics::ApplyConfiguration() { | |||
| 185 | Settings::values.resolution_setup.SetGlobal(false); | 200 | Settings::values.resolution_setup.SetGlobal(false); |
| 186 | Settings::values.resolution_setup.SetValue(resolution_setup); | 201 | Settings::values.resolution_setup.SetValue(resolution_setup); |
| 187 | } | 202 | } |
| 203 | if (ui->scaling_filter_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { | ||
| 204 | Settings::values.scaling_filter.SetGlobal(true); | ||
| 205 | } else { | ||
| 206 | Settings::values.scaling_filter.SetGlobal(false); | ||
| 207 | Settings::values.scaling_filter.SetValue(scaling_filter); | ||
| 208 | } | ||
| 188 | if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { | 209 | if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { |
| 189 | Settings::values.renderer_backend.SetGlobal(true); | 210 | Settings::values.renderer_backend.SetGlobal(true); |
| 190 | Settings::values.shader_backend.SetGlobal(true); | 211 | Settings::values.shader_backend.SetGlobal(true); |
| @@ -333,6 +354,7 @@ void ConfigureGraphics::SetupPerGameUI() { | |||
| 333 | ui->fullscreen_mode_combobox->setEnabled(Settings::values.fullscreen_mode.UsingGlobal()); | 354 | ui->fullscreen_mode_combobox->setEnabled(Settings::values.fullscreen_mode.UsingGlobal()); |
| 334 | ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal()); | 355 | ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal()); |
| 335 | ui->resolution_combobox->setEnabled(Settings::values.resolution_setup.UsingGlobal()); | 356 | ui->resolution_combobox->setEnabled(Settings::values.resolution_setup.UsingGlobal()); |
| 357 | ui->scaling_filter_combobox->setEnabled(Settings::values.scaling_filter.UsingGlobal()); | ||
| 336 | ui->use_asynchronous_gpu_emulation->setEnabled( | 358 | ui->use_asynchronous_gpu_emulation->setEnabled( |
| 337 | Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()); | 359 | Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()); |
| 338 | ui->nvdec_emulation->setEnabled(Settings::values.nvdec_emulation.UsingGlobal()); | 360 | ui->nvdec_emulation->setEnabled(Settings::values.nvdec_emulation.UsingGlobal()); |
| @@ -364,6 +386,9 @@ void ConfigureGraphics::SetupPerGameUI() { | |||
| 364 | ConfigurationShared::SetColoredComboBox( | 386 | ConfigurationShared::SetColoredComboBox( |
| 365 | ui->resolution_combobox, ui->resolution_label, | 387 | ui->resolution_combobox, ui->resolution_label, |
| 366 | static_cast<int>(Settings::values.resolution_setup.GetValue(true))); | 388 | static_cast<int>(Settings::values.resolution_setup.GetValue(true))); |
| 389 | ConfigurationShared::SetColoredComboBox( | ||
| 390 | ui->scaling_filter_combobox, ui->scaling_filter_label, | ||
| 391 | static_cast<int>(Settings::values.scaling_filter.GetValue(true))); | ||
| 367 | ConfigurationShared::InsertGlobalItem( | 392 | ConfigurationShared::InsertGlobalItem( |
| 368 | ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); | 393 | ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); |
| 369 | ConfigurationShared::InsertGlobalItem( | 394 | ConfigurationShared::InsertGlobalItem( |
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 1b6ac3cbb..d5e0d4e89 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -340,32 +340,66 @@ | |||
| 340 | </item> | 340 | </item> |
| 341 | <item> | 341 | <item> |
| 342 | <property name="text"> | 342 | <property name="text"> |
| 343 | <string>0.75X (540p/810p)</string> | 343 | <string>1X (720p/1080p)</string> |
| 344 | </property> | 344 | </property> |
| 345 | </item> | 345 | </item> |
| 346 | <item> | 346 | <item> |
| 347 | <property name="text"> | 347 | <property name="text"> |
| 348 | <string>1X (720p/1080p)</string> | 348 | <string>2X (1440p/2160[4K]p)</string> |
| 349 | </property> | 349 | </property> |
| 350 | </item> | 350 | </item> |
| 351 | <item> | 351 | <item> |
| 352 | <property name="text"> | 352 | <property name="text"> |
| 353 | <string>1.5X (1080p/1620p)</string> | 353 | <string>3X (2160p[4K]/3240p[6K])</string> |
| 354 | </property> | 354 | </property> |
| 355 | </item> | 355 | </item> |
| 356 | <item> | 356 | <item> |
| 357 | <property name="text"> | 357 | <property name="text"> |
| 358 | <string>2X (1440p/2160[4K]p)</string> | 358 | <string>4X (2880p/4320p[8K])</string> |
| 359 | </property> | 359 | </property> |
| 360 | </item> | 360 | </item> |
| 361 | </widget> | ||
| 362 | </item> | ||
| 363 | </layout> | ||
| 364 | </widget> | ||
| 365 | </item> | ||
| 366 | <item> | ||
| 367 | <widget class="QWidget" name="scaling_filter_layout" native="true"> | ||
| 368 | <layout class="QHBoxLayout" name="horizontalLayout_5"> | ||
| 369 | <property name="leftMargin"> | ||
| 370 | <number>0</number> | ||
| 371 | </property> | ||
| 372 | <property name="topMargin"> | ||
| 373 | <number>0</number> | ||
| 374 | </property> | ||
| 375 | <property name="rightMargin"> | ||
| 376 | <number>0</number> | ||
| 377 | </property> | ||
| 378 | <property name="bottomMargin"> | ||
| 379 | <number>0</number> | ||
| 380 | </property> | ||
| 381 | <item> | ||
| 382 | <widget class="QLabel" name="scaling_filter_label"> | ||
| 383 | <property name="text"> | ||
| 384 | <string>Window Adapting Filter:</string> | ||
| 385 | </property> | ||
| 386 | </widget> | ||
| 387 | </item> | ||
| 388 | <item> | ||
| 389 | <widget class="QComboBox" name="scaling_filter_combobox"> | ||
| 361 | <item> | 390 | <item> |
| 362 | <property name="text"> | 391 | <property name="text"> |
| 363 | <string>3X (2160p[4K]/3240p[6K])</string> | 392 | <string>Bilinear</string> |
| 364 | </property> | 393 | </property> |
| 365 | </item> | 394 | </item> |
| 366 | <item> | 395 | <item> |
| 367 | <property name="text"> | 396 | <property name="text"> |
| 368 | <string>4X (2880p/4320p[8K])</string> | 397 | <string>Bicubic</string> |
| 398 | </property> | ||
| 399 | </item> | ||
| 400 | <item> | ||
| 401 | <property name="text"> | ||
| 402 | <string>ScaleForce</string> | ||
| 369 | </property> | 403 | </property> |
| 370 | </item> | 404 | </item> |
| 371 | </widget> | 405 | </widget> |