From a6b88e85bfb14c45345f6443b54d15a61e3975d5 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Tue, 17 Aug 2021 00:12:52 +0200 Subject: Renderer: Implement Bicubic and ScaleForce filters. --- src/video_core/host_shaders/CMakeLists.txt | 4 + .../host_shaders/opengl_present_bicubic.frag | 56 +++++++++ .../host_shaders/opengl_present_scaleforce.frag | 135 ++++++++++++++++++++ .../host_shaders/vulkan_present_bicubic.frag | 56 +++++++++ .../host_shaders/vulkan_present_scaleforce.frag | 137 +++++++++++++++++++++ 5 files changed, 388 insertions(+) create mode 100644 src/video_core/host_shaders/opengl_present_bicubic.frag create mode 100644 src/video_core/host_shaders/opengl_present_scaleforce.frag create mode 100644 src/video_core/host_shaders/vulkan_present_bicubic.frag create mode 100644 src/video_core/host_shaders/vulkan_present_scaleforce.frag (limited to 'src/video_core/host_shaders') 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 convert_float_to_depth.frag full_screen_triangle.vert opengl_copy_bc4.comp + opengl_present_scaleforce.frag + opengl_present_bicubic.frag opengl_present.frag opengl_present.vert pitch_unswizzle.comp vulkan_blit_color_float.frag vulkan_blit_depth_stencil.frag + vulkan_present_bicubic.frag + vulkan_present_scaleforce.frag vulkan_present.frag vulkan_present.vert 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 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#version 460 core + +layout (location = 0) in vec2 frag_tex_coord; + +layout (location = 0) out vec4 color; + +layout (binding = 1) uniform sampler2D color_texture; + +vec4 cubic(float v) { + vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; + vec4 s = n * n * n; + float x = s.x; + float y = s.y - 4.0 * s.x; + float z = s.z - 4.0 * s.y + 6.0 * s.x; + float w = 6.0 - x - y - z; + return vec4(x, y, z, w) * (1.0 / 6.0); +} + +vec4 textureBicubic( sampler2D textureSampler, vec2 texCoords ) { + + vec2 texSize = textureSize(textureSampler, 0); + vec2 invTexSize = 1.0 / texSize; + + texCoords = texCoords * texSize - 0.5; + + vec2 fxy = fract(texCoords); + texCoords -= fxy; + + vec4 xcubic = cubic(fxy.x); + vec4 ycubic = cubic(fxy.y); + + vec4 c = texCoords.xxyy + vec2(-0.5, +1.5).xyxy; + + vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); + vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; + + offset *= invTexSize.xxyy; + + vec4 sample0 = texture(textureSampler, offset.xz); + vec4 sample1 = texture(textureSampler, offset.yz); + vec4 sample2 = texture(textureSampler, offset.xw); + vec4 sample3 = texture(textureSampler, offset.yw); + + float sx = s.x / (s.x + s.y); + float sy = s.z / (s.z + s.w); + + return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); +} + +void main() { + color = vec4(textureBicubic(color_texture, frag_tex_coord).rgb, 1.0f); +} 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 @@ +// from https://github.com/BreadFish64/ScaleFish/tree/master/scale_force + +// MIT License +// +// Copyright (c) 2020 BreadFish64 +// +// 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. + +precision mediump float; + +layout (location = 0) in vec2 tex_coord; + +layout (location = 0) out vec4 frag_color; + +layout (binding = 1) uniform sampler2D input_texture; + +vec2 tex_size; +vec2 inv_tex_size; + +vec4 cubic(float v) { + vec3 n = vec3(1.0, 2.0, 3.0) - v; + vec3 s = n * n * n; + float x = s.x; + float y = s.y - 4.0 * s.x; + float z = s.z - 4.0 * s.y + 6.0 * s.x; + float w = 6.0 - x - y - z; + return vec4(x, y, z, w) / 6.0; +} + +// Bicubic interpolation +vec4 textureBicubic(vec2 tex_coords) { + tex_coords = tex_coords * tex_size - 0.5; + + vec2 fxy = modf(tex_coords, tex_coords); + + vec4 xcubic = cubic(fxy.x); + vec4 ycubic = cubic(fxy.y); + + vec4 c = tex_coords.xxyy + vec2(-0.5, +1.5).xyxy; + + vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); + vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; + + offset *= inv_tex_size.xxyy; + + vec4 sample0 = textureLod(input_texture, offset.xz, 0.0); + vec4 sample1 = textureLod(input_texture, offset.yz, 0.0); + vec4 sample2 = textureLod(input_texture, offset.xw, 0.0); + vec4 sample3 = textureLod(input_texture, offset.yw, 0.0); + + float sx = s.x / (s.x + s.y); + float sy = s.z / (s.z + s.w); + + return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); +} + +mat4x3 center_matrix; +vec4 center_alpha; + +// Finds the distance between four colors and cc in YCbCr space +vec4 ColorDist(vec4 A, vec4 B, vec4 C, vec4 D) { + // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion + const vec3 K = vec3(0.2627, 0.6780, 0.0593); + const float LUMINANCE_WEIGHT = .6; + const mat3 YCBCR_MATRIX = + mat3(K * LUMINANCE_WEIGHT, -.5 * K.r / (1.0 - K.b), -.5 * K.g / (1.0 - K.b), .5, .5, + -.5 * K.g / (1.0 - K.r), -.5 * K.b / (1.0 - K.r)); + + mat4x3 colors = mat4x3(A.rgb, B.rgb, C.rgb, D.rgb) - center_matrix; + mat4x3 YCbCr = YCBCR_MATRIX * colors; + vec4 color_dist = vec3(1.0) * YCbCr; + color_dist *= color_dist; + vec4 alpha = vec4(A.a, B.a, C.a, D.a); + + return sqrt((color_dist + abs(center_alpha - alpha)) * alpha * center_alpha); +} + +void main() { + vec4 bl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, -1)); + vec4 bc = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(0, -1)); + vec4 br = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, -1)); + vec4 cl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, 0)); + vec4 cc = textureLod(input_texture, tex_coord, 0.0); + vec4 cr = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, 0)); + vec4 tl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, 1)); + vec4 tc = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(0, 1)); + vec4 tr = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, 1)); + + + tex_size = vec2(textureSize(input_texture, 0)); + inv_tex_size = 1.0 / tex_size; + center_matrix = mat4x3(cc.rgb, cc.rgb, cc.rgb, cc.rgb); + center_alpha = cc.aaaa; + + vec4 offset_tl = ColorDist(tl, tc, tr, cr); + vec4 offset_br = ColorDist(br, bc, bl, cl); + + // Calculate how different cc is from the texels around it + float total_dist = dot(offset_tl + offset_br, vec4(1.0)); + + // Add together all the distances with direction taken into account + vec4 tmp = offset_tl - offset_br; + vec2 total_offset = tmp.wy + tmp.zz + vec2(-tmp.x, tmp.x); + + if (total_dist == 0.0) { + // Doing bicubic filtering just past the edges where the offset is 0 causes black floaters + // and it doesn't really matter which filter is used when the colors aren't changing. + frag_color = vec4(cc.rgb, 1.0f); + } else { + // When the image has thin points, they tend to split apart. + // This is because the texels all around are different + // and total_offset reaches into clear areas. + // This works pretty well to keep the offset in bounds for these cases. + float clamp_val = length(total_offset) / total_dist; + vec2 final_offset = clamp(total_offset, -clamp_val, clamp_val) * inv_tex_size; + + frag_color = vec4(textureBicubic(tex_coord - final_offset).rgb, 1.0f); + } +} 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 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#version 460 core + +layout (location = 0) in vec2 frag_tex_coord; + +layout (location = 0) out vec4 color; + +layout (binding = 1) uniform sampler2D color_texture; + +vec4 cubic(float v) { + vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; + vec4 s = n * n * n; + float x = s.x; + float y = s.y - 4.0 * s.x; + float z = s.z - 4.0 * s.y + 6.0 * s.x; + float w = 6.0 - x - y - z; + return vec4(x, y, z, w) * (1.0 / 6.0); +} + +vec4 textureBicubic( sampler2D textureSampler, vec2 texCoords ) { + + vec2 texSize = textureSize(textureSampler, 0); + vec2 invTexSize = 1.0 / texSize; + + texCoords = texCoords * texSize - 0.5; + + vec2 fxy = fract(texCoords); + texCoords -= fxy; + + vec4 xcubic = cubic(fxy.x); + vec4 ycubic = cubic(fxy.y); + + vec4 c = texCoords.xxyy + vec2(-0.5, +1.5).xyxy; + + vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); + vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; + + offset *= invTexSize.xxyy; + + vec4 sample0 = texture(textureSampler, offset.xz); + vec4 sample1 = texture(textureSampler, offset.yz); + vec4 sample2 = texture(textureSampler, offset.xw); + vec4 sample3 = texture(textureSampler, offset.yw); + + float sx = s.x / (s.x + s.y); + float sy = s.z / (s.z + s.w); + + return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); +} + +void main() { + color = vec4(textureBicubic(color_texture, frag_tex_coord).rgb, 1.0f); +} 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 @@ +#version 320 es + +// from https://github.com/BreadFish64/ScaleFish/tree/master/scale_force + +// MIT License +// +// Copyright (c) 2020 BreadFish64 +// +// 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. + +precision mediump float; + +layout (location = 0) in vec2 tex_coord; + +layout (location = 0) out vec4 frag_color; + +layout (binding = 1) uniform sampler2D input_texture; + +vec2 tex_size; +vec2 inv_tex_size; + +vec4 cubic(float v) { + vec3 n = vec3(1.0, 2.0, 3.0) - v; + vec3 s = n * n * n; + float x = s.x; + float y = s.y - 4.0 * s.x; + float z = s.z - 4.0 * s.y + 6.0 * s.x; + float w = 6.0 - x - y - z; + return vec4(x, y, z, w) / 6.0; +} + +// Bicubic interpolation +vec4 textureBicubic(vec2 tex_coords) { + tex_coords = tex_coords * tex_size - 0.5; + + vec2 fxy = modf(tex_coords, tex_coords); + + vec4 xcubic = cubic(fxy.x); + vec4 ycubic = cubic(fxy.y); + + vec4 c = tex_coords.xxyy + vec2(-0.5, +1.5).xyxy; + + vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); + vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; + + offset *= inv_tex_size.xxyy; + + vec4 sample0 = textureLod(input_texture, offset.xz, 0.0); + vec4 sample1 = textureLod(input_texture, offset.yz, 0.0); + vec4 sample2 = textureLod(input_texture, offset.xw, 0.0); + vec4 sample3 = textureLod(input_texture, offset.yw, 0.0); + + float sx = s.x / (s.x + s.y); + float sy = s.z / (s.z + s.w); + + return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); +} + +mat4x3 center_matrix; +vec4 center_alpha; + +// Finds the distance between four colors and cc in YCbCr space +vec4 ColorDist(vec4 A, vec4 B, vec4 C, vec4 D) { + // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion + const vec3 K = vec3(0.2627, 0.6780, 0.0593); + const float LUMINANCE_WEIGHT = .6; + const mat3 YCBCR_MATRIX = + mat3(K * LUMINANCE_WEIGHT, -.5 * K.r / (1.0 - K.b), -.5 * K.g / (1.0 - K.b), .5, .5, + -.5 * K.g / (1.0 - K.r), -.5 * K.b / (1.0 - K.r)); + + mat4x3 colors = mat4x3(A.rgb, B.rgb, C.rgb, D.rgb) - center_matrix; + mat4x3 YCbCr = YCBCR_MATRIX * colors; + vec4 color_dist = vec3(1.0) * YCbCr; + color_dist *= color_dist; + vec4 alpha = vec4(A.a, B.a, C.a, D.a); + + return sqrt((color_dist + abs(center_alpha - alpha)) * alpha * center_alpha); +} + +void main() { + vec4 bl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, -1)); + vec4 bc = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(0, -1)); + vec4 br = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, -1)); + vec4 cl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, 0)); + vec4 cc = textureLod(input_texture, tex_coord, 0.0); + vec4 cr = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, 0)); + vec4 tl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, 1)); + vec4 tc = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(0, 1)); + vec4 tr = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, 1)); + + + tex_size = vec2(textureSize(input_texture, 0)); + inv_tex_size = 1.0 / tex_size; + center_matrix = mat4x3(cc.rgb, cc.rgb, cc.rgb, cc.rgb); + center_alpha = cc.aaaa; + + vec4 offset_tl = ColorDist(tl, tc, tr, cr); + vec4 offset_br = ColorDist(br, bc, bl, cl); + + // Calculate how different cc is from the texels around it + float total_dist = dot(offset_tl + offset_br, vec4(1.0)); + + // Add together all the distances with direction taken into account + vec4 tmp = offset_tl - offset_br; + vec2 total_offset = tmp.wy + tmp.zz + vec2(-tmp.x, tmp.x); + + if (total_dist == 0.0) { + // Doing bicubic filtering just past the edges where the offset is 0 causes black floaters + // and it doesn't really matter which filter is used when the colors aren't changing. + frag_color = vec4(cc.rgb, 1.0f); + } else { + // When the image has thin points, they tend to split apart. + // This is because the texels all around are different + // and total_offset reaches into clear areas. + // This works pretty well to keep the offset in bounds for these cases. + float clamp_val = length(total_offset) / total_dist; + vec2 final_offset = clamp(total_offset, -clamp_val, clamp_val) * inv_tex_size; + + frag_color = vec4(textureBicubic(tex_coord - final_offset).rgb, 1.0f); + } +} -- cgit v1.2.3 From ae8d19d17eb5448207ece99ae07507c542c6ddae Mon Sep 17 00:00:00 2001 From: ameerj <52414509+ameerj@users.noreply.github.com> Date: Thu, 9 Sep 2021 23:01:39 -0400 Subject: Renderers: Unify post processing filter shaders --- src/video_core/host_shaders/CMakeLists.txt | 6 +- .../host_shaders/opengl_present_bicubic.frag | 56 -------- .../host_shaders/opengl_present_scaleforce.frag | 135 ------------------- src/video_core/host_shaders/present_bicubic.frag | 67 ++++++++++ .../host_shaders/present_scaleforce.frag | 145 +++++++++++++++++++++ .../host_shaders/vulkan_present_bicubic.frag | 56 -------- .../host_shaders/vulkan_present_scaleforce.frag | 137 ------------------- src/video_core/renderer_opengl/renderer_opengl.cpp | 9 +- src/video_core/renderer_vulkan/vk_blit_screen.cpp | 8 +- 9 files changed, 222 insertions(+), 397 deletions(-) delete mode 100644 src/video_core/host_shaders/opengl_present_bicubic.frag delete mode 100644 src/video_core/host_shaders/opengl_present_scaleforce.frag create mode 100644 src/video_core/host_shaders/present_bicubic.frag create mode 100644 src/video_core/host_shaders/present_scaleforce.frag delete mode 100644 src/video_core/host_shaders/vulkan_present_bicubic.frag delete mode 100644 src/video_core/host_shaders/vulkan_present_scaleforce.frag (limited to 'src/video_core/host_shaders') diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 835b37944..664d6ce5d 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -6,15 +6,13 @@ set(SHADER_FILES convert_float_to_depth.frag full_screen_triangle.vert opengl_copy_bc4.comp - opengl_present_scaleforce.frag - opengl_present_bicubic.frag opengl_present.frag opengl_present.vert pitch_unswizzle.comp + present_scaleforce.frag + present_bicubic.frag vulkan_blit_color_float.frag vulkan_blit_depth_stencil.frag - vulkan_present_bicubic.frag - vulkan_present_scaleforce.frag vulkan_present.frag vulkan_present.vert 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 deleted file mode 100644 index 17772095a..000000000 --- a/src/video_core/host_shaders/opengl_present_bicubic.frag +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#version 460 core - -layout (location = 0) in vec2 frag_tex_coord; - -layout (location = 0) out vec4 color; - -layout (binding = 1) uniform sampler2D color_texture; - -vec4 cubic(float v) { - vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; - vec4 s = n * n * n; - float x = s.x; - float y = s.y - 4.0 * s.x; - float z = s.z - 4.0 * s.y + 6.0 * s.x; - float w = 6.0 - x - y - z; - return vec4(x, y, z, w) * (1.0 / 6.0); -} - -vec4 textureBicubic( sampler2D textureSampler, vec2 texCoords ) { - - vec2 texSize = textureSize(textureSampler, 0); - vec2 invTexSize = 1.0 / texSize; - - texCoords = texCoords * texSize - 0.5; - - vec2 fxy = fract(texCoords); - texCoords -= fxy; - - vec4 xcubic = cubic(fxy.x); - vec4 ycubic = cubic(fxy.y); - - vec4 c = texCoords.xxyy + vec2(-0.5, +1.5).xyxy; - - vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); - vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; - - offset *= invTexSize.xxyy; - - vec4 sample0 = texture(textureSampler, offset.xz); - vec4 sample1 = texture(textureSampler, offset.yz); - vec4 sample2 = texture(textureSampler, offset.xw); - vec4 sample3 = texture(textureSampler, offset.yw); - - float sx = s.x / (s.x + s.y); - float sy = s.z / (s.z + s.w); - - return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); -} - -void main() { - color = vec4(textureBicubic(color_texture, frag_tex_coord).rgb, 1.0f); -} diff --git a/src/video_core/host_shaders/opengl_present_scaleforce.frag b/src/video_core/host_shaders/opengl_present_scaleforce.frag deleted file mode 100644 index 0153f62c0..000000000 --- a/src/video_core/host_shaders/opengl_present_scaleforce.frag +++ /dev/null @@ -1,135 +0,0 @@ -// from https://github.com/BreadFish64/ScaleFish/tree/master/scale_force - -// MIT License -// -// Copyright (c) 2020 BreadFish64 -// -// 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. - -precision mediump float; - -layout (location = 0) in vec2 tex_coord; - -layout (location = 0) out vec4 frag_color; - -layout (binding = 1) uniform sampler2D input_texture; - -vec2 tex_size; -vec2 inv_tex_size; - -vec4 cubic(float v) { - vec3 n = vec3(1.0, 2.0, 3.0) - v; - vec3 s = n * n * n; - float x = s.x; - float y = s.y - 4.0 * s.x; - float z = s.z - 4.0 * s.y + 6.0 * s.x; - float w = 6.0 - x - y - z; - return vec4(x, y, z, w) / 6.0; -} - -// Bicubic interpolation -vec4 textureBicubic(vec2 tex_coords) { - tex_coords = tex_coords * tex_size - 0.5; - - vec2 fxy = modf(tex_coords, tex_coords); - - vec4 xcubic = cubic(fxy.x); - vec4 ycubic = cubic(fxy.y); - - vec4 c = tex_coords.xxyy + vec2(-0.5, +1.5).xyxy; - - vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); - vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; - - offset *= inv_tex_size.xxyy; - - vec4 sample0 = textureLod(input_texture, offset.xz, 0.0); - vec4 sample1 = textureLod(input_texture, offset.yz, 0.0); - vec4 sample2 = textureLod(input_texture, offset.xw, 0.0); - vec4 sample3 = textureLod(input_texture, offset.yw, 0.0); - - float sx = s.x / (s.x + s.y); - float sy = s.z / (s.z + s.w); - - return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); -} - -mat4x3 center_matrix; -vec4 center_alpha; - -// Finds the distance between four colors and cc in YCbCr space -vec4 ColorDist(vec4 A, vec4 B, vec4 C, vec4 D) { - // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion - const vec3 K = vec3(0.2627, 0.6780, 0.0593); - const float LUMINANCE_WEIGHT = .6; - const mat3 YCBCR_MATRIX = - mat3(K * LUMINANCE_WEIGHT, -.5 * K.r / (1.0 - K.b), -.5 * K.g / (1.0 - K.b), .5, .5, - -.5 * K.g / (1.0 - K.r), -.5 * K.b / (1.0 - K.r)); - - mat4x3 colors = mat4x3(A.rgb, B.rgb, C.rgb, D.rgb) - center_matrix; - mat4x3 YCbCr = YCBCR_MATRIX * colors; - vec4 color_dist = vec3(1.0) * YCbCr; - color_dist *= color_dist; - vec4 alpha = vec4(A.a, B.a, C.a, D.a); - - return sqrt((color_dist + abs(center_alpha - alpha)) * alpha * center_alpha); -} - -void main() { - vec4 bl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, -1)); - vec4 bc = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(0, -1)); - vec4 br = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, -1)); - vec4 cl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, 0)); - vec4 cc = textureLod(input_texture, tex_coord, 0.0); - vec4 cr = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, 0)); - vec4 tl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, 1)); - vec4 tc = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(0, 1)); - vec4 tr = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, 1)); - - - tex_size = vec2(textureSize(input_texture, 0)); - inv_tex_size = 1.0 / tex_size; - center_matrix = mat4x3(cc.rgb, cc.rgb, cc.rgb, cc.rgb); - center_alpha = cc.aaaa; - - vec4 offset_tl = ColorDist(tl, tc, tr, cr); - vec4 offset_br = ColorDist(br, bc, bl, cl); - - // Calculate how different cc is from the texels around it - float total_dist = dot(offset_tl + offset_br, vec4(1.0)); - - // Add together all the distances with direction taken into account - vec4 tmp = offset_tl - offset_br; - vec2 total_offset = tmp.wy + tmp.zz + vec2(-tmp.x, tmp.x); - - if (total_dist == 0.0) { - // Doing bicubic filtering just past the edges where the offset is 0 causes black floaters - // and it doesn't really matter which filter is used when the colors aren't changing. - frag_color = vec4(cc.rgb, 1.0f); - } else { - // When the image has thin points, they tend to split apart. - // This is because the texels all around are different - // and total_offset reaches into clear areas. - // This works pretty well to keep the offset in bounds for these cases. - float clamp_val = length(total_offset) / total_dist; - vec2 final_offset = clamp(total_offset, -clamp_val, clamp_val) * inv_tex_size; - - frag_color = vec4(textureBicubic(tex_coord - final_offset).rgb, 1.0f); - } -} diff --git a/src/video_core/host_shaders/present_bicubic.frag b/src/video_core/host_shaders/present_bicubic.frag new file mode 100644 index 000000000..f3e5410e7 --- /dev/null +++ b/src/video_core/host_shaders/present_bicubic.frag @@ -0,0 +1,67 @@ +// 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; + +vec4 cubic(float v) { + vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; + vec4 s = n * n * n; + float x = s.x; + float y = s.y - 4.0 * s.x; + float z = s.z - 4.0 * s.y + 6.0 * s.x; + float w = 6.0 - x - y - z; + return vec4(x, y, z, w) * (1.0 / 6.0); +} + +vec4 textureBicubic( sampler2D textureSampler, vec2 texCoords ) { + + vec2 texSize = textureSize(textureSampler, 0); + vec2 invTexSize = 1.0 / texSize; + + texCoords = texCoords * texSize - 0.5; + + vec2 fxy = fract(texCoords); + texCoords -= fxy; + + vec4 xcubic = cubic(fxy.x); + vec4 ycubic = cubic(fxy.y); + + vec4 c = texCoords.xxyy + vec2(-0.5, +1.5).xyxy; + + vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); + vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; + + offset *= invTexSize.xxyy; + + vec4 sample0 = texture(textureSampler, offset.xz); + vec4 sample1 = texture(textureSampler, offset.yz); + vec4 sample2 = texture(textureSampler, offset.xw); + vec4 sample3 = texture(textureSampler, offset.yw); + + float sx = s.x / (s.x + s.y); + float sy = s.z / (s.z + s.w); + + return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); +} + +void main() { + color = vec4(textureBicubic(color_texture, frag_tex_coord).rgb, 1.0f); +} diff --git a/src/video_core/host_shaders/present_scaleforce.frag b/src/video_core/host_shaders/present_scaleforce.frag new file mode 100644 index 000000000..1829a9be8 --- /dev/null +++ b/src/video_core/host_shaders/present_scaleforce.frag @@ -0,0 +1,145 @@ +// MIT License +// +// Copyright (c) 2020 BreadFish64 +// +// 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. + +// Adapted from https://github.com/BreadFish64/ScaleFish/tree/master/scaleforce + +#version 460 + +#ifdef VULKAN + +#define BINDING_COLOR_TEXTURE 1 + +#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv + +#define BINDING_COLOR_TEXTURE 0 + +#endif + +layout (location = 0) in vec2 tex_coord; + +layout (location = 0) out vec4 frag_color; + +layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D input_texture; + +vec2 tex_size; +vec2 inv_tex_size; + +vec4 cubic(float v) { + vec3 n = vec3(1.0, 2.0, 3.0) - v; + vec3 s = n * n * n; + float x = s.x; + float y = s.y - 4.0 * s.x; + float z = s.z - 4.0 * s.y + 6.0 * s.x; + float w = 6.0 - x - y - z; + return vec4(x, y, z, w) / 6.0; +} + +// Bicubic interpolation +vec4 textureBicubic(vec2 tex_coords) { + tex_coords = tex_coords * tex_size - 0.5; + + vec2 fxy = modf(tex_coords, tex_coords); + + vec4 xcubic = cubic(fxy.x); + vec4 ycubic = cubic(fxy.y); + + vec4 c = tex_coords.xxyy + vec2(-0.5, +1.5).xyxy; + + vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); + vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; + + offset *= inv_tex_size.xxyy; + + vec4 sample0 = textureLod(input_texture, offset.xz, 0.0); + vec4 sample1 = textureLod(input_texture, offset.yz, 0.0); + vec4 sample2 = textureLod(input_texture, offset.xw, 0.0); + vec4 sample3 = textureLod(input_texture, offset.yw, 0.0); + + float sx = s.x / (s.x + s.y); + float sy = s.z / (s.z + s.w); + + return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); +} + +mat4x3 center_matrix; +vec4 center_alpha; + +// Finds the distance between four colors and cc in YCbCr space +vec4 ColorDist(vec4 A, vec4 B, vec4 C, vec4 D) { + // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion + const vec3 K = vec3(0.2627, 0.6780, 0.0593); + const float LUMINANCE_WEIGHT = .6; + const mat3 YCBCR_MATRIX = + mat3(K * LUMINANCE_WEIGHT, -.5 * K.r / (1.0 - K.b), -.5 * K.g / (1.0 - K.b), .5, .5, + -.5 * K.g / (1.0 - K.r), -.5 * K.b / (1.0 - K.r)); + + mat4x3 colors = mat4x3(A.rgb, B.rgb, C.rgb, D.rgb) - center_matrix; + mat4x3 YCbCr = YCBCR_MATRIX * colors; + vec4 color_dist = vec3(1.0) * YCbCr; + color_dist *= color_dist; + vec4 alpha = vec4(A.a, B.a, C.a, D.a); + + return sqrt((color_dist + abs(center_alpha - alpha)) * alpha * center_alpha); +} + +void main() { + vec4 bl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, -1)); + vec4 bc = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(0, -1)); + vec4 br = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, -1)); + vec4 cl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, 0)); + vec4 cc = textureLod(input_texture, tex_coord, 0.0); + vec4 cr = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, 0)); + vec4 tl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, 1)); + vec4 tc = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(0, 1)); + vec4 tr = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, 1)); + + + tex_size = vec2(textureSize(input_texture, 0)); + inv_tex_size = 1.0 / tex_size; + center_matrix = mat4x3(cc.rgb, cc.rgb, cc.rgb, cc.rgb); + center_alpha = cc.aaaa; + + vec4 offset_tl = ColorDist(tl, tc, tr, cr); + vec4 offset_br = ColorDist(br, bc, bl, cl); + + // Calculate how different cc is from the texels around it + float total_dist = dot(offset_tl + offset_br, vec4(1.0)); + + // Add together all the distances with direction taken into account + vec4 tmp = offset_tl - offset_br; + vec2 total_offset = tmp.wy + tmp.zz + vec2(-tmp.x, tmp.x); + + if (total_dist == 0.0) { + // Doing bicubic filtering just past the edges where the offset is 0 causes black floaters + // and it doesn't really matter which filter is used when the colors aren't changing. + frag_color = vec4(cc.rgb, 1.0f); + } else { + // When the image has thin points, they tend to split apart. + // This is because the texels all around are different + // and total_offset reaches into clear areas. + // This works pretty well to keep the offset in bounds for these cases. + float clamp_val = length(total_offset) / total_dist; + vec2 final_offset = clamp(total_offset, -clamp_val, clamp_val) * inv_tex_size; + + frag_color = vec4(textureBicubic(tex_coord - final_offset).rgb, 1.0f); + } +} diff --git a/src/video_core/host_shaders/vulkan_present_bicubic.frag b/src/video_core/host_shaders/vulkan_present_bicubic.frag deleted file mode 100644 index 17772095a..000000000 --- a/src/video_core/host_shaders/vulkan_present_bicubic.frag +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#version 460 core - -layout (location = 0) in vec2 frag_tex_coord; - -layout (location = 0) out vec4 color; - -layout (binding = 1) uniform sampler2D color_texture; - -vec4 cubic(float v) { - vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; - vec4 s = n * n * n; - float x = s.x; - float y = s.y - 4.0 * s.x; - float z = s.z - 4.0 * s.y + 6.0 * s.x; - float w = 6.0 - x - y - z; - return vec4(x, y, z, w) * (1.0 / 6.0); -} - -vec4 textureBicubic( sampler2D textureSampler, vec2 texCoords ) { - - vec2 texSize = textureSize(textureSampler, 0); - vec2 invTexSize = 1.0 / texSize; - - texCoords = texCoords * texSize - 0.5; - - vec2 fxy = fract(texCoords); - texCoords -= fxy; - - vec4 xcubic = cubic(fxy.x); - vec4 ycubic = cubic(fxy.y); - - vec4 c = texCoords.xxyy + vec2(-0.5, +1.5).xyxy; - - vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); - vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; - - offset *= invTexSize.xxyy; - - vec4 sample0 = texture(textureSampler, offset.xz); - vec4 sample1 = texture(textureSampler, offset.yz); - vec4 sample2 = texture(textureSampler, offset.xw); - vec4 sample3 = texture(textureSampler, offset.yw); - - float sx = s.x / (s.x + s.y); - float sy = s.z / (s.z + s.w); - - return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); -} - -void main() { - color = vec4(textureBicubic(color_texture, frag_tex_coord).rgb, 1.0f); -} diff --git a/src/video_core/host_shaders/vulkan_present_scaleforce.frag b/src/video_core/host_shaders/vulkan_present_scaleforce.frag deleted file mode 100644 index 801c8eae9..000000000 --- a/src/video_core/host_shaders/vulkan_present_scaleforce.frag +++ /dev/null @@ -1,137 +0,0 @@ -#version 320 es - -// from https://github.com/BreadFish64/ScaleFish/tree/master/scale_force - -// MIT License -// -// Copyright (c) 2020 BreadFish64 -// -// 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. - -precision mediump float; - -layout (location = 0) in vec2 tex_coord; - -layout (location = 0) out vec4 frag_color; - -layout (binding = 1) uniform sampler2D input_texture; - -vec2 tex_size; -vec2 inv_tex_size; - -vec4 cubic(float v) { - vec3 n = vec3(1.0, 2.0, 3.0) - v; - vec3 s = n * n * n; - float x = s.x; - float y = s.y - 4.0 * s.x; - float z = s.z - 4.0 * s.y + 6.0 * s.x; - float w = 6.0 - x - y - z; - return vec4(x, y, z, w) / 6.0; -} - -// Bicubic interpolation -vec4 textureBicubic(vec2 tex_coords) { - tex_coords = tex_coords * tex_size - 0.5; - - vec2 fxy = modf(tex_coords, tex_coords); - - vec4 xcubic = cubic(fxy.x); - vec4 ycubic = cubic(fxy.y); - - vec4 c = tex_coords.xxyy + vec2(-0.5, +1.5).xyxy; - - vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); - vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; - - offset *= inv_tex_size.xxyy; - - vec4 sample0 = textureLod(input_texture, offset.xz, 0.0); - vec4 sample1 = textureLod(input_texture, offset.yz, 0.0); - vec4 sample2 = textureLod(input_texture, offset.xw, 0.0); - vec4 sample3 = textureLod(input_texture, offset.yw, 0.0); - - float sx = s.x / (s.x + s.y); - float sy = s.z / (s.z + s.w); - - return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); -} - -mat4x3 center_matrix; -vec4 center_alpha; - -// Finds the distance between four colors and cc in YCbCr space -vec4 ColorDist(vec4 A, vec4 B, vec4 C, vec4 D) { - // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion - const vec3 K = vec3(0.2627, 0.6780, 0.0593); - const float LUMINANCE_WEIGHT = .6; - const mat3 YCBCR_MATRIX = - mat3(K * LUMINANCE_WEIGHT, -.5 * K.r / (1.0 - K.b), -.5 * K.g / (1.0 - K.b), .5, .5, - -.5 * K.g / (1.0 - K.r), -.5 * K.b / (1.0 - K.r)); - - mat4x3 colors = mat4x3(A.rgb, B.rgb, C.rgb, D.rgb) - center_matrix; - mat4x3 YCbCr = YCBCR_MATRIX * colors; - vec4 color_dist = vec3(1.0) * YCbCr; - color_dist *= color_dist; - vec4 alpha = vec4(A.a, B.a, C.a, D.a); - - return sqrt((color_dist + abs(center_alpha - alpha)) * alpha * center_alpha); -} - -void main() { - vec4 bl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, -1)); - vec4 bc = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(0, -1)); - vec4 br = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, -1)); - vec4 cl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, 0)); - vec4 cc = textureLod(input_texture, tex_coord, 0.0); - vec4 cr = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, 0)); - vec4 tl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, 1)); - vec4 tc = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(0, 1)); - vec4 tr = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, 1)); - - - tex_size = vec2(textureSize(input_texture, 0)); - inv_tex_size = 1.0 / tex_size; - center_matrix = mat4x3(cc.rgb, cc.rgb, cc.rgb, cc.rgb); - center_alpha = cc.aaaa; - - vec4 offset_tl = ColorDist(tl, tc, tr, cr); - vec4 offset_br = ColorDist(br, bc, bl, cl); - - // Calculate how different cc is from the texels around it - float total_dist = dot(offset_tl + offset_br, vec4(1.0)); - - // Add together all the distances with direction taken into account - vec4 tmp = offset_tl - offset_br; - vec2 total_offset = tmp.wy + tmp.zz + vec2(-tmp.x, tmp.x); - - if (total_dist == 0.0) { - // Doing bicubic filtering just past the edges where the offset is 0 causes black floaters - // and it doesn't really matter which filter is used when the colors aren't changing. - frag_color = vec4(cc.rgb, 1.0f); - } else { - // When the image has thin points, they tend to split apart. - // This is because the texels all around are different - // and total_offset reaches into clear areas. - // This works pretty well to keep the offset in bounds for these cases. - float clamp_val = length(total_offset) / total_dist; - vec2 final_offset = clamp(total_offset, -clamp_val, clamp_val) * inv_tex_size; - - frag_color = vec4(textureBicubic(tex_coord - final_offset).rgb, 1.0f); - } -} diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 71a5e3adf..955dbc744 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -21,10 +21,10 @@ #include "core/memory.h" #include "core/perf_stats.h" #include "core/telemetry_session.h" -#include "video_core/host_shaders/opengl_present_bicubic_frag.h" #include "video_core/host_shaders/opengl_present_frag.h" -#include "video_core/host_shaders/opengl_present_scaleforce_frag.h" #include "video_core/host_shaders/opengl_present_vert.h" +#include "video_core/host_shaders/present_bicubic_frag.h" +#include "video_core/host_shaders/present_scaleforce_frag.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_manager.h" #include "video_core/renderer_opengl/gl_shader_util.h" @@ -255,10 +255,9 @@ void RendererOpenGL::InitOpenGLObjects() { // Create shader programs 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::OPENGL_PRESENT_BICUBIC_FRAG, GL_FRAGMENT_SHADER); + present_bicubic_fragment = CreateProgram(HostShaders::PRESENT_BICUBIC_FRAG, GL_FRAGMENT_SHADER); present_scaleforce_fragment = - CreateProgram(HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG, GL_FRAGMENT_SHADER); + CreateProgram(HostShaders::PRESENT_SCALEFORCE_FRAG, GL_FRAGMENT_SHADER); // Generate presentation sampler present_sampler.Create(); diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 19d91ecfc..c91b24e3a 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -17,9 +17,9 @@ #include "core/frontend/emu_window.h" #include "core/memory.h" #include "video_core/gpu.h" -#include "video_core/host_shaders/vulkan_present_bicubic_frag_spv.h" +#include "video_core/host_shaders/present_bicubic_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_scaleforce_frag_spv.h" #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" @@ -342,8 +342,8 @@ void VKBlitScreen::RefreshResources(const Tegra::FramebufferConfig& framebuffer) 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, VULKAN_PRESENT_BICUBIC_FRAG_SPV); - scaleforce_fragment_shader = BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FRAG_SPV); + bicubic_fragment_shader = BuildShader(device, PRESENT_BICUBIC_FRAG_SPV); + scaleforce_fragment_shader = BuildShader(device, PRESENT_SCALEFORCE_FRAG_SPV); } void VKBlitScreen::CreateSemaphores() { -- cgit v1.2.3 From 37cb0377ae30e2139f6fa381d04124e51fcccded Mon Sep 17 00:00:00 2001 From: Marshall Mohror Date: Sat, 16 Oct 2021 20:33:58 -0500 Subject: vulkan: Implement FidelityFX Super Resolution --- src/common/settings.h | 1 + src/video_core/CMakeLists.txt | 2 + src/video_core/host_shaders/CMakeLists.txt | 17 +- src/video_core/host_shaders/fidelityfx_fsr.comp | 114 +++++++ .../host_shaders/vulkan_fidelityfx_fsr_easu.comp | 13 + .../host_shaders/vulkan_fidelityfx_fsr_rcas.comp | 13 + src/video_core/renderer_vulkan/vk_blit_screen.cpp | 62 +++- src/video_core/renderer_vulkan/vk_blit_screen.h | 4 + src/video_core/renderer_vulkan/vk_fsr.cpp | 375 +++++++++++++++++++++ src/video_core/renderer_vulkan/vk_fsr.h | 54 +++ src/yuzu/configuration/configure_graphics.ui | 5 + 11 files changed, 643 insertions(+), 17 deletions(-) create mode 100644 src/video_core/host_shaders/fidelityfx_fsr.comp create mode 100644 src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu.comp create mode 100644 src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas.comp create mode 100644 src/video_core/renderer_vulkan/vk_fsr.cpp create mode 100644 src/video_core/renderer_vulkan/vk_fsr.h (limited to 'src/video_core/host_shaders') diff --git a/src/common/settings.h b/src/common/settings.h index a09db0822..9da447ce0 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -64,6 +64,7 @@ enum class ScalingFilter : u32 { Bilinear = 0, Bicubic = 1, ScaleForce = 2, + Fsr = 3, }; struct ResolutionScalingInfo { diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 6aac7f305..91a30fef7 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -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 diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 664d6ce5d..32e2ab500 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -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} ) diff --git a/src/video_core/host_shaders/fidelityfx_fsr.comp b/src/video_core/host_shaders/fidelityfx_fsr.comp new file mode 100644 index 000000000..cbb601580 --- /dev/null +++ b/src/video_core/host_shaders/fidelityfx_fsr.comp @@ -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); +} diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu.comp new file mode 100644 index 000000000..6525eeeb5 --- /dev/null +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu.comp @@ -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" diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas.comp new file mode 100644 index 000000000..9463ed842 --- /dev/null +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas.comp @@ -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" diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index c91b24e3a..8ce60e874 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -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( + 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(framebuffer_crop_rect.GetWidth()) / - static_cast(screen_info.width); - } - if (framebuffer_crop_rect.GetHeight() > 0) { - scale_v = static_cast(framebuffer_crop_rect.GetHeight()) / - static_cast(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(framebuffer_crop_rect.GetWidth()) / + static_cast(screen_info.width); + } + if (framebuffer_crop_rect.GetHeight() > 0) { + scale_v = static_cast(framebuffer_crop_rect.GetHeight()) / + static_cast(screen_info.height); + } } const auto& screen = layout.screen; diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h index d3a16f0ba..337931468 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.h +++ b/src/video_core/renderer_vulkan/vk_blit_screen.h @@ -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 raw_buffer_commits; u32 raw_width = 0; u32 raw_height = 0; + + std::unique_ptr fsr; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_fsr.cpp b/src/video_core/renderer_vulkan/vk_fsr.cpp new file mode 100644 index 000000000..fd0a4aa42 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_fsr.cpp @@ -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& 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(crop_rect.GetWidth()), + .height = static_cast(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 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 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 pool_sizes{{ + { + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = static_cast(image_count * 2), + }, + { + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .descriptorCount = static_cast(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(image_count * 2), + .poolSizeCount = static_cast(pool_sizes.size()), + .pPoolSizes = pool_sizes.data(), + }; + descriptor_pool = device.GetLogical().CreateDescriptorPool(ci); +} + +void FSR::CreateDescriptorSetLayout() { + const std::array 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(layout_bindings.size()), + .pBindings = layout_bindings.data(), + }; + + descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci); +} + +void FSR::CreateDescriptorSets() { + const u32 sets = static_cast(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), + }; + 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 diff --git a/src/video_core/renderer_vulkan/vk_fsr.h b/src/video_core/renderer_vulkan/vk_fsr.h new file mode 100644 index 000000000..8391e2e58 --- /dev/null +++ b/src/video_core/renderer_vulkan/vk_fsr.h @@ -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& 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 images; + std::vector image_views; + std::vector buffer_commits; +}; + +} // namespace Vulkan diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index d5e0d4e89..014ca6683 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui @@ -402,6 +402,11 @@ ScaleForce + + + FidelityFX Super Resolution + + -- cgit v1.2.3 From 916b882ea8870e695d50e8ca8c8e4c35fb1895d5 Mon Sep 17 00:00:00 2001 From: Marshall Mohror Date: Wed, 20 Oct 2021 13:35:59 -0500 Subject: Update scaleforce to use FP16 --- .../host_shaders/present_scaleforce.frag | 143 ++++++++------------- 1 file changed, 55 insertions(+), 88 deletions(-) (limited to 'src/video_core/host_shaders') diff --git a/src/video_core/host_shaders/present_scaleforce.frag b/src/video_core/host_shaders/present_scaleforce.frag index 1829a9be8..ebc0d9b90 100644 --- a/src/video_core/host_shaders/present_scaleforce.frag +++ b/src/video_core/host_shaders/present_scaleforce.frag @@ -24,6 +24,9 @@ #version 460 +#extension GL_AMD_gpu_shader_half_float : enable +#extension GL_NV_gpu_shader5 : enable + #ifdef VULKAN #define BINDING_COLOR_TEXTURE 1 @@ -40,106 +43,70 @@ layout (location = 0) out vec4 frag_color; layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D input_texture; -vec2 tex_size; -vec2 inv_tex_size; - -vec4 cubic(float v) { - vec3 n = vec3(1.0, 2.0, 3.0) - v; - vec3 s = n * n * n; - float x = s.x; - float y = s.y - 4.0 * s.x; - float z = s.z - 4.0 * s.y + 6.0 * s.x; - float w = 6.0 - x - y - z; - return vec4(x, y, z, w) / 6.0; -} - -// Bicubic interpolation -vec4 textureBicubic(vec2 tex_coords) { - tex_coords = tex_coords * tex_size - 0.5; - - vec2 fxy = modf(tex_coords, tex_coords); - - vec4 xcubic = cubic(fxy.x); - vec4 ycubic = cubic(fxy.y); - - vec4 c = tex_coords.xxyy + vec2(-0.5, +1.5).xyxy; +const bool ignore_alpha = true; - vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); - vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; - - offset *= inv_tex_size.xxyy; - - vec4 sample0 = textureLod(input_texture, offset.xz, 0.0); - vec4 sample1 = textureLod(input_texture, offset.yz, 0.0); - vec4 sample2 = textureLod(input_texture, offset.xw, 0.0); - vec4 sample3 = textureLod(input_texture, offset.yw, 0.0); - - float sx = s.x / (s.x + s.y); - float sy = s.z / (s.z + s.w); - - return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy); -} - -mat4x3 center_matrix; -vec4 center_alpha; - -// Finds the distance between four colors and cc in YCbCr space -vec4 ColorDist(vec4 A, vec4 B, vec4 C, vec4 D) { +float16_t ColorDist1(f16vec4 a, f16vec4 b) { // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion - const vec3 K = vec3(0.2627, 0.6780, 0.0593); - const float LUMINANCE_WEIGHT = .6; - const mat3 YCBCR_MATRIX = - mat3(K * LUMINANCE_WEIGHT, -.5 * K.r / (1.0 - K.b), -.5 * K.g / (1.0 - K.b), .5, .5, - -.5 * K.g / (1.0 - K.r), -.5 * K.b / (1.0 - K.r)); - - mat4x3 colors = mat4x3(A.rgb, B.rgb, C.rgb, D.rgb) - center_matrix; - mat4x3 YCbCr = YCBCR_MATRIX * colors; - vec4 color_dist = vec3(1.0) * YCbCr; - color_dist *= color_dist; - vec4 alpha = vec4(A.a, B.a, C.a, D.a); - - return sqrt((color_dist + abs(center_alpha - alpha)) * alpha * center_alpha); + const f16vec3 K = f16vec3(0.2627, 0.6780, 0.0593); + const float16_t scaleB = float16_t(0.5) / (float16_t(1.0) - K.b); + const float16_t scaleR = float16_t(0.5) / (float16_t(1.0) - K.r); + f16vec4 diff = a - b; + float16_t Y = dot(diff.rgb, K); + float16_t Cb = scaleB * (diff.b - Y); + float16_t Cr = scaleR * (diff.r - Y); + f16vec3 YCbCr = f16vec3(Y, Cb, Cr); + float16_t d = length(YCbCr); + if (ignore_alpha) { + return d; + } + return sqrt(a.a * b.a * d * d + diff.a * diff.a); } -void main() { - vec4 bl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, -1)); - vec4 bc = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(0, -1)); - vec4 br = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, -1)); - vec4 cl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, 0)); - vec4 cc = textureLod(input_texture, tex_coord, 0.0); - vec4 cr = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, 0)); - vec4 tl = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(-1, 1)); - vec4 tc = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(0, 1)); - vec4 tr = textureLodOffset(input_texture, tex_coord, 0.0, ivec2(1, 1)); - +f16vec4 ColorDist(f16vec4 ref, f16vec4 A, f16vec4 B, f16vec4 C, f16vec4 D) { + return f16vec4( + ColorDist1(ref, A), + ColorDist1(ref, B), + ColorDist1(ref, C), + ColorDist1(ref, D) + ); +} - tex_size = vec2(textureSize(input_texture, 0)); - inv_tex_size = 1.0 / tex_size; - center_matrix = mat4x3(cc.rgb, cc.rgb, cc.rgb, cc.rgb); - center_alpha = cc.aaaa; +vec4 Scaleforce(sampler2D tex, vec2 tex_coord) { + f16vec4 bl = f16vec4(textureOffset(tex, tex_coord, ivec2(-1, -1))); + f16vec4 bc = f16vec4(textureOffset(tex, tex_coord, ivec2(0, -1))); + f16vec4 br = f16vec4(textureOffset(tex, tex_coord, ivec2(1, -1))); + f16vec4 cl = f16vec4(textureOffset(tex, tex_coord, ivec2(-1, 0))); + f16vec4 cc = f16vec4(texture(tex, tex_coord)); + f16vec4 cr = f16vec4(textureOffset(tex, tex_coord, ivec2(1, 0))); + f16vec4 tl = f16vec4(textureOffset(tex, tex_coord, ivec2(-1, 1))); + f16vec4 tc = f16vec4(textureOffset(tex, tex_coord, ivec2(0, 1))); + f16vec4 tr = f16vec4(textureOffset(tex, tex_coord, ivec2(1, 1))); - vec4 offset_tl = ColorDist(tl, tc, tr, cr); - vec4 offset_br = ColorDist(br, bc, bl, cl); + f16vec4 offset_tl = ColorDist(cc, tl, tc, tr, cr); + f16vec4 offset_br = ColorDist(cc, br, bc, bl, cl); // Calculate how different cc is from the texels around it - float total_dist = dot(offset_tl + offset_br, vec4(1.0)); - - // Add together all the distances with direction taken into account - vec4 tmp = offset_tl - offset_br; - vec2 total_offset = tmp.wy + tmp.zz + vec2(-tmp.x, tmp.x); + const float16_t plus_weight = float16_t(1.5); + const float16_t cross_weight = float16_t(1.5); + float16_t total_dist = dot(offset_tl + offset_br, f16vec4(cross_weight, plus_weight, cross_weight, plus_weight)); - if (total_dist == 0.0) { - // Doing bicubic filtering just past the edges where the offset is 0 causes black floaters - // and it doesn't really matter which filter is used when the colors aren't changing. - frag_color = vec4(cc.rgb, 1.0f); + if (total_dist == float16_t(0.0)) { + return cc; } else { + // Add together all the distances with direction taken into account + f16vec4 tmp = offset_tl - offset_br; + f16vec2 total_offset = tmp.wy * plus_weight + (tmp.zz + f16vec2(-tmp.x, tmp.x)) * cross_weight; + // When the image has thin points, they tend to split apart. - // This is because the texels all around are different - // and total_offset reaches into clear areas. + // This is because the texels all around are different and total_offset reaches into clear areas. // This works pretty well to keep the offset in bounds for these cases. - float clamp_val = length(total_offset) / total_dist; - vec2 final_offset = clamp(total_offset, -clamp_val, clamp_val) * inv_tex_size; + float16_t clamp_val = length(total_offset) / total_dist; + f16vec2 final_offset = clamp(total_offset, -clamp_val, clamp_val) / f16vec2(textureSize(tex, 0)); - frag_color = vec4(textureBicubic(tex_coord - final_offset).rgb, 1.0f); + return texture(tex, tex_coord - final_offset); } } + +void main() { + frag_color = Scaleforce(input_texture, tex_coord); +} \ No newline at end of file -- cgit v1.2.3 From 9e065b9c7d3b25ddfe20afa4a945cca6e9767fa9 Mon Sep 17 00:00:00 2001 From: FernandoS27 Date: Thu, 21 Oct 2021 01:27:54 +0200 Subject: VideoCore: Add gaussian filtering. --- src/common/settings.h | 5 +- src/video_core/host_shaders/CMakeLists.txt | 1 + src/video_core/host_shaders/present_gaussian.frag | 74 ++++++++++++++++++++++ src/video_core/renderer_opengl/renderer_opengl.cpp | 5 ++ src/video_core/renderer_opengl/renderer_opengl.h | 1 + src/video_core/renderer_vulkan/vk_blit_screen.cpp | 49 ++++++++++++++ src/video_core/renderer_vulkan/vk_blit_screen.h | 2 + src/yuzu/configuration/configure_graphics.ui | 5 ++ 8 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 src/video_core/host_shaders/present_gaussian.frag (limited to 'src/video_core/host_shaders') 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 -- cgit v1.2.3 From 48cf3764626e1ed30450d15e00befb75a4eae329 Mon Sep 17 00:00:00 2001 From: Marshall Mohror Date: Wed, 20 Oct 2021 18:36:06 -0500 Subject: OpenGL: Implement FXAA --- src/video_core/host_shaders/CMakeLists.txt | 2 + src/video_core/host_shaders/fxaa.frag | 72 ++++++++++++++ src/video_core/host_shaders/fxaa.vert | 40 ++++++++ .../renderer_opengl/gl_resource_manager.cpp | 2 +- src/video_core/renderer_opengl/renderer_opengl.cpp | 109 ++++++++++++++------- src/video_core/renderer_opengl/renderer_opengl.h | 4 + 6 files changed, 194 insertions(+), 35 deletions(-) create mode 100644 src/video_core/host_shaders/fxaa.frag create mode 100644 src/video_core/host_shaders/fxaa.vert (limited to 'src/video_core/host_shaders') diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index b0e15773c..6b5ea649a 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -13,6 +13,8 @@ set(SHADER_FILES convert_depth_to_float.frag convert_float_to_depth.frag full_screen_triangle.vert + fxaa.frag + fxaa.vert opengl_copy_bc4.comp opengl_present.frag opengl_present.vert diff --git a/src/video_core/host_shaders/fxaa.frag b/src/video_core/host_shaders/fxaa.frag new file mode 100644 index 000000000..23f910d4c --- /dev/null +++ b/src/video_core/host_shaders/fxaa.frag @@ -0,0 +1,72 @@ +// Adapted from +// https://www.geeks3d.com/20110405/fxaa-fast-approximate-anti-aliasing-demo-glsl-opengl-test-radeon-geforce/3/ + +#version 460 + +#ifdef VULKAN + +#define BINDING_COLOR_TEXTURE 1 + +#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv + +#define BINDING_COLOR_TEXTURE 0 + +#endif + +layout (location = 0) in vec4 posPos; + +layout (location = 0) out vec4 frag_color; + +layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D input_texture; + +const float FXAA_SPAN_MAX = 8.0; +const float FXAA_REDUCE_MUL = 1.0 / 8.0; +const float FXAA_REDUCE_MIN = 1.0 / 128.0; + +#define FxaaTexLod0(t, p) textureLod(t, p, 0.0) +#define FxaaTexOff(t, p, o) textureLodOffset(t, p, 0.0, o) + +vec3 FxaaPixelShader(vec4 posPos, sampler2D tex) { + + vec3 rgbNW = FxaaTexLod0(tex, posPos.zw).xyz; + vec3 rgbNE = FxaaTexOff(tex, posPos.zw, ivec2(1,0)).xyz; + vec3 rgbSW = FxaaTexOff(tex, posPos.zw, ivec2(0,1)).xyz; + vec3 rgbSE = FxaaTexOff(tex, posPos.zw, ivec2(1,1)).xyz; + vec3 rgbM = FxaaTexLod0(tex, posPos.xy).xyz; +/*---------------------------------------------------------*/ + vec3 luma = vec3(0.299, 0.587, 0.114); + float lumaNW = dot(rgbNW, luma); + float lumaNE = dot(rgbNE, luma); + float lumaSW = dot(rgbSW, luma); + float lumaSE = dot(rgbSE, luma); + float lumaM = dot(rgbM, luma); +/*---------------------------------------------------------*/ + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); +/*---------------------------------------------------------*/ + vec2 dir; + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); +/*---------------------------------------------------------*/ + float dirReduce = max( + (lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), + FXAA_REDUCE_MIN); + float rcpDirMin = 1.0/(min(abs(dir.x), abs(dir.y)) + dirReduce); + dir = min(vec2( FXAA_SPAN_MAX, FXAA_SPAN_MAX), + max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), + dir * rcpDirMin)) / textureSize(tex, 0); +/*--------------------------------------------------------*/ + vec3 rgbA = (1.0 / 2.0) * ( + FxaaTexLod0(tex, posPos.xy + dir * (1.0 / 3.0 - 0.5)).xyz + + FxaaTexLod0(tex, posPos.xy + dir * (2.0 / 3.0 - 0.5)).xyz); + vec3 rgbB = rgbA * (1.0 / 2.0) + (1.0 / 4.0) * ( + FxaaTexLod0(tex, posPos.xy + dir * (0.0 / 3.0 - 0.5)).xyz + + FxaaTexLod0(tex, posPos.xy + dir * (3.0 / 3.0 - 0.5)).xyz); + float lumaB = dot(rgbB, luma); + if((lumaB < lumaMin) || (lumaB > lumaMax)) return rgbA; + return rgbB; +} + +void main() { + frag_color = vec4(FxaaPixelShader(posPos, input_texture), 1.0); +} diff --git a/src/video_core/host_shaders/fxaa.vert b/src/video_core/host_shaders/fxaa.vert new file mode 100644 index 000000000..715fce462 --- /dev/null +++ b/src/video_core/host_shaders/fxaa.vert @@ -0,0 +1,40 @@ +// Copyright 2019 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#version 460 + +out gl_PerVertex { + vec4 gl_Position; +}; + +const vec2 vertices[4] = + vec2[4](vec2(-1.0, 1.0), vec2(1.0, 1.0), vec2(-1.0, -1.0), vec2(1.0, -1.0)); + +layout (location = 0) out vec4 posPos; + +#ifdef VULKAN + +#define BINDING_COLOR_TEXTURE 1 + +#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv + +#define BINDING_COLOR_TEXTURE 0 + +#endif + +layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D input_texture; + +const float FXAA_SUBPIX_SHIFT = 0; + +void main() { +#ifdef VULKAN + vec2 vertex = vertices[gl_VertexIndex]; +#else + vec2 vertex = vertices[gl_VertexID]; +#endif + gl_Position = vec4(vertex, 0.0, 1.0); + vec2 vert_tex_coord = (vertex + 1.0) / 2.0; + posPos.xy = vert_tex_coord; + posPos.zw = vert_tex_coord - (0.5 + FXAA_SUBPIX_SHIFT) / textureSize(input_texture, 0); +} diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp index 8695c29e3..70947838c 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.cpp +++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp @@ -166,7 +166,7 @@ void OGLFramebuffer::Create() { return; MICROPROFILE_SCOPE(OpenGL_ResourceCreation); - glGenFramebuffers(1, &handle); + glCreateFramebuffers(1, &handle); } void OGLFramebuffer::Release() { diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 227697c4f..dbe66a1b6 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -21,6 +21,8 @@ #include "core/memory.h" #include "core/perf_stats.h" #include "core/telemetry_session.h" +#include "video_core/host_shaders/fxaa_frag.h" +#include "video_core/host_shaders/fxaa_vert.h" #include "video_core/host_shaders/opengl_present_frag.h" #include "video_core/host_shaders/opengl_present_vert.h" #include "video_core/host_shaders/present_bicubic_frag.h" @@ -254,6 +256,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color void RendererOpenGL::InitOpenGLObjects() { // Create shader programs + fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER); + fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER); 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); @@ -287,6 +291,8 @@ void RendererOpenGL::InitOpenGLObjects() { // Clear screen to black LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture); + + fxaa_framebuffer.Create(); } void RendererOpenGL::AddTelemetryFields() { @@ -338,14 +344,83 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, texture.resource.Release(); texture.resource.Create(GL_TEXTURE_2D); glTextureStorage2D(texture.resource.handle, 1, internal_format, texture.width, texture.height); + fxaa_texture.Release(); + fxaa_texture.Create(GL_TEXTURE_2D); + glTextureStorage2D(fxaa_texture.handle, 1, GL_RGBA16F, texture.width, texture.height); + glNamedFramebufferTexture(fxaa_framebuffer.handle, GL_COLOR_ATTACHMENT0, fxaa_texture.handle, + 0); } void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { + // TODO: Signal state tracker about these changes + state_tracker.NotifyScreenDrawVertexArray(); + state_tracker.NotifyPolygonModes(); + state_tracker.NotifyViewport0(); + state_tracker.NotifyScissor0(); + state_tracker.NotifyColorMask(0); + state_tracker.NotifyBlend0(); + state_tracker.NotifyFramebuffer(); + state_tracker.NotifyFrontFace(); + state_tracker.NotifyCullTest(); + state_tracker.NotifyDepthTest(); + state_tracker.NotifyStencilTest(); + state_tracker.NotifyPolygonOffset(); + state_tracker.NotifyRasterizeEnable(); + state_tracker.NotifyFramebufferSRGB(); + state_tracker.NotifyLogicOp(); + state_tracker.NotifyClipControl(); + state_tracker.NotifyAlphaTest(); + + state_tracker.ClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); + // Update background color before drawing glClearColor(Settings::values.bg_red.GetValue() / 255.0f, Settings::values.bg_green.GetValue() / 255.0f, Settings::values.bg_blue.GetValue() / 255.0f, 1.0f); + glEnable(GL_CULL_FACE); + glDisable(GL_COLOR_LOGIC_OP); + glDisable(GL_DEPTH_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_RASTERIZER_DISCARD); + glDisable(GL_ALPHA_TEST); + glDisablei(GL_BLEND, 0); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glCullFace(GL_BACK); + glFrontFace(GL_CW); + glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + glBindTextureUnit(0, screen_info.display_texture); + + if (Settings::values.anti_aliasing.GetValue() == Settings::AntiAliasing::Fxaa) { + program_manager.BindPresentPrograms(fxaa_vertex.handle, fxaa_fragment.handle); + + glEnablei(GL_SCISSOR_TEST, 0); + glScissorIndexed(0, 0, 0, + framebuffer_crop_rect.GetWidth() != 0 ? framebuffer_crop_rect.GetWidth() + : screen_info.texture.width, + framebuffer_crop_rect.GetHeight() != 0 ? framebuffer_crop_rect.GetHeight() + : screen_info.texture.height); + glViewportIndexedf(0, 0.0f, 0.0f, static_cast(screen_info.texture.width), + static_cast(screen_info.texture.height)); + glDepthRangeIndexed(0, 0.0, 0.0); + + glBindSampler(0, present_sampler.handle); + GLint old_read_fb; + GLint old_draw_fb; + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fxaa_framebuffer.handle); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); + + glBindTextureUnit(0, fxaa_texture.handle); + } + // Set projection matrix const std::array ortho_matrix = MakeOrthographicMatrix(static_cast(layout.width), static_cast(layout.height)); @@ -422,47 +497,14 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { }; glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices)); - // TODO: Signal state tracker about these changes - state_tracker.NotifyScreenDrawVertexArray(); - state_tracker.NotifyPolygonModes(); - state_tracker.NotifyViewport0(); - state_tracker.NotifyScissor0(); - state_tracker.NotifyColorMask(0); - state_tracker.NotifyBlend0(); - state_tracker.NotifyFramebuffer(); - state_tracker.NotifyFrontFace(); - state_tracker.NotifyCullTest(); - state_tracker.NotifyDepthTest(); - state_tracker.NotifyStencilTest(); - state_tracker.NotifyPolygonOffset(); - state_tracker.NotifyRasterizeEnable(); - state_tracker.NotifyFramebufferSRGB(); - state_tracker.NotifyLogicOp(); - state_tracker.NotifyClipControl(); - state_tracker.NotifyAlphaTest(); - - state_tracker.ClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); - glEnable(GL_CULL_FACE); if (screen_info.display_srgb) { glEnable(GL_FRAMEBUFFER_SRGB); } else { glDisable(GL_FRAMEBUFFER_SRGB); } - glDisable(GL_COLOR_LOGIC_OP); - glDisable(GL_DEPTH_TEST); - glDisable(GL_STENCIL_TEST); - glDisable(GL_POLYGON_OFFSET_FILL); - glDisable(GL_RASTERIZER_DISCARD); - glDisable(GL_ALPHA_TEST); - glDisablei(GL_BLEND, 0); glDisablei(GL_SCISSOR_TEST, 0); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glCullFace(GL_BACK); - glFrontFace(GL_CW); - glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glViewportIndexedf(0, 0.0f, 0.0f, static_cast(layout.width), static_cast(layout.height)); - glDepthRangeIndexed(0, 0.0, 0.0); glEnableVertexAttribArray(PositionLocation); glEnableVertexAttribArray(TexCoordLocation); @@ -482,7 +524,6 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); } - glBindTextureUnit(0, screen_info.display_texture); if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) { glBindSampler(0, present_sampler.handle); } else { diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 62a746e41..f6c66f804 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -111,6 +111,8 @@ private: OGLSampler present_sampler; OGLSampler present_sampler_nn; OGLBuffer vertex_buffer; + OGLProgram fxaa_vertex; + OGLProgram fxaa_fragment; OGLProgram present_vertex; OGLProgram present_bilinear_fragment; OGLProgram present_bicubic_fragment; @@ -123,6 +125,8 @@ private: /// Display information for Switch screen ScreenInfo screen_info; + OGLTexture fxaa_texture; + OGLFramebuffer fxaa_framebuffer; /// OpenGL framebuffer data std::vector gl_framebuffer_data; -- cgit v1.2.3 From e6f1ed08fb1f11d86bb4cb7c03d83a9f443d6c12 Mon Sep 17 00:00:00 2001 From: FernandoS27 Date: Fri, 22 Oct 2021 19:22:34 +0200 Subject: Vulkan: Implement FXAA --- src/video_core/host_shaders/fxaa.vert | 2 +- src/video_core/renderer_vulkan/vk_blit_screen.cpp | 390 ++++++++++++++++++++-- src/video_core/renderer_vulkan/vk_blit_screen.h | 17 + 3 files changed, 387 insertions(+), 22 deletions(-) (limited to 'src/video_core/host_shaders') diff --git a/src/video_core/host_shaders/fxaa.vert b/src/video_core/host_shaders/fxaa.vert index 715fce462..01d5ff4df 100644 --- a/src/video_core/host_shaders/fxaa.vert +++ b/src/video_core/host_shaders/fxaa.vert @@ -15,7 +15,7 @@ layout (location = 0) out vec4 posPos; #ifdef VULKAN -#define BINDING_COLOR_TEXTURE 1 +#define BINDING_COLOR_TEXTURE 0 #else // ^^^ Vulkan ^^^ // vvv OpenGL vvv diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 0d6bce214..c0abcc17a 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -17,6 +17,8 @@ #include "core/frontend/emu_window.h" #include "core/memory.h" #include "video_core/gpu.h" +#include "video_core/host_shaders/fxaa_frag_spv.h" +#include "video_core/host_shaders/fxaa_vert_spv.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" @@ -149,15 +151,9 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, scheduler.Wait(resource_ticks[image_index]); resource_ticks[image_index] = scheduler.CurrentTick(); - const VkImageView source_image_view = + VkImageView source_image_view = use_accelerated ? screen_info.image_view : *raw_image_views[image_index]; - if (!fsr) { - const bool is_nn = - Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::NearestNeighbor; - UpdateDescriptorSet(image_index, source_image_view, is_nn); - } - BufferData data; SetUniformData(data, layout); SetVertexData(data, framebuffer, layout); @@ -239,6 +235,68 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, }); } + const auto anti_alias_pass = Settings::values.anti_aliasing.GetValue(); + if (use_accelerated && anti_alias_pass != Settings::AntiAliasing::None) { + UpdateAADescriptorSet(image_index, source_image_view, false); + const u32 up_scale = Settings::values.resolution_info.up_scale; + const u32 down_shift = Settings::values.resolution_info.down_shift; + VkExtent2D size{ + .width = (up_scale * framebuffer.width) >> down_shift, + .height = (up_scale * framebuffer.height) >> down_shift, + }; + source_image_view = *aa_image_view; + scheduler.Record([this, image_index, size, anti_alias_pass](vk::CommandBuffer cmdbuf) { + const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; + const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; + const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; + const VkClearValue clear_color{ + .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, + }; + const VkRenderPassBeginInfo renderpass_bi{ + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .pNext = nullptr, + .renderPass = *aa_renderpass, + .framebuffer = *aa_framebuffer, + .renderArea = + { + .offset = {0, 0}, + .extent = size, + }, + .clearValueCount = 1, + .pClearValues = &clear_color, + }; + const VkViewport viewport{ + .x = 0.0f, + .y = 0.0f, + .width = static_cast(size.width), + .height = static_cast(size.height), + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + const VkRect2D scissor{ + .offset = {0, 0}, + .extent = size, + }; + cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); + switch (anti_alias_pass) { + case Settings::AntiAliasing::Fxaa: + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline); + break; + default: + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline); + break; + } + cmdbuf.SetViewport(0, viewport); + cmdbuf.SetScissor(0, scissor); + + cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline_layout, 0, + aa_descriptor_sets[image_index], {}); + cmdbuf.Draw(4, 1, 0, 0); + cmdbuf.EndRenderPass(); + }); + } + if (fsr) { auto crop_rect = framebuffer.crop_rect; if (crop_rect.GetWidth() == 0) { @@ -251,6 +309,10 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, VkImageView fsr_image_view = fsr->Draw(scheduler, image_index, source_image_view, crop_rect); UpdateDescriptorSet(image_index, fsr_image_view, true); + } else { + const bool is_nn = + Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::NearestNeighbor; + UpdateDescriptorSet(image_index, source_image_view, is_nn); } scheduler.Record( @@ -329,11 +391,16 @@ VkSemaphore VKBlitScreen::DrawToSwapchain(const Tegra::FramebufferConfig& frameb } vk::Framebuffer VKBlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent) { + return CreateFramebuffer(image_view, extent, renderpass); +} + +vk::Framebuffer VKBlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent, + vk::RenderPass& rd) { return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .pNext = nullptr, .flags = 0, - .renderPass = *renderpass, + .renderPass = *rd, .attachmentCount = 1, .pAttachments = &image_view, .width = extent.width, @@ -390,6 +457,8 @@ void VKBlitScreen::RefreshResources(const Tegra::FramebufferConfig& framebuffer) void VKBlitScreen::CreateShaders() { vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV); + fxaa_vertex_shader = BuildShader(device, FXAA_VERT_SPV); + fxaa_fragment_shader = BuildShader(device, FXAA_FRAG_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); @@ -413,6 +482,13 @@ void VKBlitScreen::CreateDescriptorPool() { }, }}; + const std::array pool_sizes_aa{{ + { + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = static_cast(2 * image_count), + }, + }}; + const VkDescriptorPoolCreateInfo ci{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, .pNext = nullptr, @@ -422,19 +498,33 @@ void VKBlitScreen::CreateDescriptorPool() { .pPoolSizes = pool_sizes.data(), }; descriptor_pool = device.GetLogical().CreateDescriptorPool(ci); + + const VkDescriptorPoolCreateInfo ci_aa{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .pNext = nullptr, + .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, + .maxSets = static_cast(image_count), + .poolSizeCount = static_cast(pool_sizes_aa.size()), + .pPoolSizes = pool_sizes_aa.data(), + }; + aa_descriptor_pool = device.GetLogical().CreateDescriptorPool(ci_aa); } void VKBlitScreen::CreateRenderPass() { + renderpass = CreateRenderPassImpl(swapchain.GetImageViewFormat()); +} + +vk::RenderPass VKBlitScreen::CreateRenderPassImpl(VkFormat format, bool is_present) { const VkAttachmentDescription color_attachment{ .flags = 0, - .format = swapchain.GetImageViewFormat(), + .format = format, .samples = VK_SAMPLE_COUNT_1_BIT, .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + .finalLayout = is_present ? VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_GENERAL, }; const VkAttachmentReference color_attachment_ref{ @@ -477,7 +567,7 @@ void VKBlitScreen::CreateRenderPass() { .pDependencies = &dependency, }; - renderpass = device.GetLogical().CreateRenderPass(renderpass_ci); + return device.GetLogical().CreateRenderPass(renderpass_ci); } void VKBlitScreen::CreateDescriptorSetLayout() { @@ -498,6 +588,23 @@ void VKBlitScreen::CreateDescriptorSetLayout() { }, }}; + const std::array layout_bindings_aa{{ + { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + .pImmutableSamplers = nullptr, + }, + { + .binding = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + .pImmutableSamplers = nullptr, + }, + }}; + const VkDescriptorSetLayoutCreateInfo ci{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .pNext = nullptr, @@ -506,11 +613,21 @@ void VKBlitScreen::CreateDescriptorSetLayout() { .pBindings = layout_bindings.data(), }; + const VkDescriptorSetLayoutCreateInfo ci_aa{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .bindingCount = static_cast(layout_bindings_aa.size()), + .pBindings = layout_bindings_aa.data(), + }; + descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci); + aa_descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci_aa); } void VKBlitScreen::CreateDescriptorSets() { const std::vector layouts(image_count, *descriptor_set_layout); + const std::vector layouts_aa(image_count, *aa_descriptor_set_layout); const VkDescriptorSetAllocateInfo ai{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, @@ -520,7 +637,16 @@ void VKBlitScreen::CreateDescriptorSets() { .pSetLayouts = layouts.data(), }; + const VkDescriptorSetAllocateInfo ai_aa{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .pNext = nullptr, + .descriptorPool = *aa_descriptor_pool, + .descriptorSetCount = static_cast(image_count), + .pSetLayouts = layouts_aa.data(), + }; + descriptor_sets = descriptor_pool.Allocate(ai); + aa_descriptor_sets = aa_descriptor_pool.Allocate(ai_aa); } void VKBlitScreen::CreatePipelineLayout() { @@ -533,7 +659,17 @@ void VKBlitScreen::CreatePipelineLayout() { .pushConstantRangeCount = 0, .pPushConstantRanges = nullptr, }; + const VkPipelineLayoutCreateInfo ci_aa{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .setLayoutCount = 1, + .pSetLayouts = aa_descriptor_set_layout.address(), + .pushConstantRangeCount = 0, + .pPushConstantRanges = nullptr, + }; pipeline_layout = device.GetLogical().CreatePipelineLayout(ci); + aa_pipeline_layout = device.GetLogical().CreatePipelineLayout(ci_aa); } void VKBlitScreen::CreateGraphicsPipeline() { @@ -862,7 +998,7 @@ void VKBlitScreen::CreateFramebuffers() { for (std::size_t i = 0; i < image_count; ++i) { const VkImageView image_view{swapchain.GetImageViewIndex(i)}; - framebuffers[i] = CreateFramebuffer(image_view, size); + framebuffers[i] = CreateFramebuffer(image_view, size, renderpass); } } @@ -872,6 +1008,11 @@ void VKBlitScreen::ReleaseRawImages() { } raw_images.clear(); raw_buffer_commits.clear(); + + aa_image_view.reset(); + aa_image.reset(); + aa_commit = MemoryCommit{}; + buffer.reset(); buffer_commit = MemoryCommit{}; } @@ -898,8 +1039,11 @@ void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) raw_image_views.resize(image_count); raw_buffer_commits.resize(image_count); - for (size_t i = 0; i < image_count; ++i) { - raw_images[i] = device.GetLogical().CreateImage(VkImageCreateInfo{ + const auto create_image = [&](bool used_on_framebuffer = false, u32 up_scale = 1, + u32 down_shift = 0) { + u32 extra_usages = used_on_framebuffer ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT + : VK_IMAGE_USAGE_TRANSFER_DST_BIT; + return device.GetLogical().CreateImage(VkImageCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, @@ -907,26 +1051,30 @@ void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) .format = GetFormat(framebuffer), .extent = { - .width = framebuffer.width, - .height = framebuffer.height, + .width = (up_scale * framebuffer.width) >> down_shift, + .height = (up_scale * framebuffer.height) >> down_shift, .depth = 1, }, .mipLevels = 1, .arrayLayers = 1, .samples = VK_SAMPLE_COUNT_1_BIT, - .tiling = VK_IMAGE_TILING_LINEAR, - .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + .tiling = used_on_framebuffer ? VK_IMAGE_TILING_OPTIMAL : VK_IMAGE_TILING_LINEAR, + .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | extra_usages, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, }); - raw_buffer_commits[i] = memory_allocator.Commit(raw_images[i], MemoryUsage::DeviceLocal); - raw_image_views[i] = device.GetLogical().CreateImageView(VkImageViewCreateInfo{ + }; + const auto create_commit = [&](vk::Image& image) { + return memory_allocator.Commit(image, MemoryUsage::DeviceLocal); + }; + const auto create_image_view = [&](vk::Image& image) { + return device.GetLogical().CreateImageView(VkImageViewCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .pNext = nullptr, .flags = 0, - .image = *raw_images[i], + .image = *image, .viewType = VK_IMAGE_VIEW_TYPE_2D, .format = GetFormat(framebuffer), .components = @@ -945,7 +1093,207 @@ void VKBlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) .layerCount = 1, }, }); + }; + + for (size_t i = 0; i < image_count; ++i) { + raw_images[i] = create_image(); + raw_buffer_commits[i] = create_commit(raw_images[i]); + raw_image_views[i] = create_image_view(raw_images[i]); } + + // AA Resources + const u32 up_scale = Settings::values.resolution_info.up_scale; + const u32 down_shift = Settings::values.resolution_info.down_shift; + aa_image = create_image(true, up_scale, down_shift); + aa_commit = create_commit(aa_image); + aa_image_view = create_image_view(aa_image); + VkExtent2D size{ + .width = (up_scale * framebuffer.width) >> down_shift, + .height = (up_scale * framebuffer.height) >> down_shift, + }; + if (aa_renderpass) { + aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass); + return; + } + aa_renderpass = CreateRenderPassImpl(GetFormat(framebuffer), false); + aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass); + + const std::array fxaa_shader_stages{{ + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stage = VK_SHADER_STAGE_VERTEX_BIT, + .module = *fxaa_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 = *fxaa_fragment_shader, + .pName = "main", + .pSpecializationInfo = nullptr, + }, + }}; + + const auto vertex_binding_description = ScreenRectVertex::GetDescription(); + const auto vertex_attrs_description = ScreenRectVertex::GetAttributes(); + + const VkPipelineVertexInputStateCreateInfo vertex_input_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .vertexBindingDescriptionCount = 1, + .pVertexBindingDescriptions = &vertex_binding_description, + .vertexAttributeDescriptionCount = u32{vertex_attrs_description.size()}, + .pVertexAttributeDescriptions = vertex_attrs_description.data(), + }; + + const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, + .primitiveRestartEnable = VK_FALSE, + }; + + const VkPipelineViewportStateCreateInfo viewport_state_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .viewportCount = 1, + .pViewports = nullptr, + .scissorCount = 1, + .pScissors = nullptr, + }; + + const VkPipelineRasterizationStateCreateInfo rasterization_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .depthClampEnable = VK_FALSE, + .rasterizerDiscardEnable = VK_FALSE, + .polygonMode = VK_POLYGON_MODE_FILL, + .cullMode = VK_CULL_MODE_NONE, + .frontFace = VK_FRONT_FACE_CLOCKWISE, + .depthBiasEnable = VK_FALSE, + .depthBiasConstantFactor = 0.0f, + .depthBiasClamp = 0.0f, + .depthBiasSlopeFactor = 0.0f, + .lineWidth = 1.0f, + }; + + const VkPipelineMultisampleStateCreateInfo multisampling_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, + .sampleShadingEnable = VK_FALSE, + .minSampleShading = 0.0f, + .pSampleMask = nullptr, + .alphaToCoverageEnable = VK_FALSE, + .alphaToOneEnable = VK_FALSE, + }; + + const VkPipelineColorBlendAttachmentState color_blend_attachment{ + .blendEnable = VK_FALSE, + .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, + .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, + .colorBlendOp = VK_BLEND_OP_ADD, + .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, + .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, + .alphaBlendOp = VK_BLEND_OP_ADD, + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, + }; + + const VkPipelineColorBlendStateCreateInfo color_blend_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .logicOpEnable = VK_FALSE, + .logicOp = VK_LOGIC_OP_COPY, + .attachmentCount = 1, + .pAttachments = &color_blend_attachment, + .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, + }; + + static constexpr std::array dynamic_states{ + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + }; + const VkPipelineDynamicStateCreateInfo dynamic_state_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .dynamicStateCount = static_cast(dynamic_states.size()), + .pDynamicStates = dynamic_states.data(), + }; + + const VkGraphicsPipelineCreateInfo fxaa_pipeline_ci{ + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stageCount = static_cast(fxaa_shader_stages.size()), + .pStages = fxaa_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 = *aa_pipeline_layout, + .renderPass = *aa_renderpass, + .subpass = 0, + .basePipelineHandle = 0, + .basePipelineIndex = 0, + }; + + // AA + aa_pipeline = device.GetLogical().CreateGraphicsPipeline(fxaa_pipeline_ci); +} + +void VKBlitScreen::UpdateAADescriptorSet(std::size_t image_index, VkImageView image_view, + bool nn) const { + const VkDescriptorImageInfo image_info{ + .sampler = nn ? *nn_sampler : *sampler, + .imageView = image_view, + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + }; + + const VkWriteDescriptorSet sampler_write{ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext = nullptr, + .dstSet = aa_descriptor_sets[image_index], + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImageInfo = &image_info, + .pBufferInfo = nullptr, + .pTexelBufferView = nullptr, + }; + + const VkWriteDescriptorSet sampler_write_2{ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext = nullptr, + .dstSet = aa_descriptor_sets[image_index], + .dstBinding = 1, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImageInfo = &image_info, + .pBufferInfo = nullptr, + .pTexelBufferView = nullptr, + }; + + device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, sampler_write_2}, {}); } void VKBlitScreen::UpdateDescriptorSet(std::size_t image_index, VkImageView image_view, diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h index 96a5598ad..e8737537e 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.h +++ b/src/video_core/renderer_vulkan/vk_blit_screen.h @@ -68,6 +68,9 @@ public: [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent); + [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, + VkExtent2D extent, vk::RenderPass& rd); + private: struct BufferData; @@ -76,6 +79,7 @@ private: void CreateSemaphores(); void CreateDescriptorPool(); void CreateRenderPass(); + vk::RenderPass CreateRenderPassImpl(VkFormat, bool is_present = true); void CreateDescriptorSetLayout(); void CreateDescriptorSets(); void CreatePipelineLayout(); @@ -91,6 +95,7 @@ private: void CreateRawImages(const Tegra::FramebufferConfig& framebuffer); void UpdateDescriptorSet(std::size_t image_index, VkImageView image_view, bool nn) const; + void UpdateAADescriptorSet(std::size_t image_index, VkImageView image_view, bool nn) const; void SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const; void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer, const Layout::FramebufferLayout layout) const; @@ -109,6 +114,8 @@ private: const VKScreenInfo& screen_info; vk::ShaderModule vertex_shader; + vk::ShaderModule fxaa_vertex_shader; + vk::ShaderModule fxaa_fragment_shader; vk::ShaderModule bilinear_fragment_shader; vk::ShaderModule bicubic_fragment_shader; vk::ShaderModule gaussian_fragment_shader; @@ -116,6 +123,7 @@ private: vk::DescriptorPool descriptor_pool; vk::DescriptorSetLayout descriptor_set_layout; vk::PipelineLayout pipeline_layout; + vk::Pipeline aa_pipeline; vk::Pipeline nearest_neightbor_pipeline; vk::Pipeline bilinear_pipeline; vk::Pipeline bicubic_pipeline; @@ -136,6 +144,15 @@ private: std::vector raw_images; std::vector raw_image_views; std::vector raw_buffer_commits; + vk::Image aa_image; + vk::ImageView aa_image_view; + MemoryCommit aa_commit; + vk::Framebuffer aa_framebuffer; + vk::RenderPass aa_renderpass; + vk::DescriptorSets aa_descriptor_sets; + vk::DescriptorPool aa_descriptor_pool; + vk::DescriptorSetLayout aa_descriptor_set_layout; + vk::PipelineLayout aa_pipeline_layout; u32 raw_width = 0; u32 raw_height = 0; -- cgit v1.2.3 From 99547d2656ee8e84b684794fa8e013b146f15284 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sat, 23 Oct 2021 00:23:50 +0200 Subject: HostShader: Fix gaussian and add attribution. --- src/video_core/host_shaders/present_gaussian.frag | 42 ++++++++++------------- 1 file changed, 19 insertions(+), 23 deletions(-) (limited to 'src/video_core/host_shaders') diff --git a/src/video_core/host_shaders/present_gaussian.frag b/src/video_core/host_shaders/present_gaussian.frag index d5e2b1781..a9558548f 100644 --- a/src/video_core/host_shaders/present_gaussian.frag +++ b/src/video_core/host_shaders/present_gaussian.frag @@ -2,6 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +// Code obtained from this 2 sources: +// - https://learnopengl.com/Advanced-Lighting/Bloom +// - https://www.rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/ + #version 460 core #ifdef VULKAN @@ -14,50 +18,40 @@ #endif -layout (location = 0) in vec2 frag_tex_coord; +layout(location = 0) in vec2 frag_tex_coord; -layout (location = 0) out vec4 color; +layout(location = 0) out vec4 color; -layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D color_texture; +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]; + 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]; + 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++) { + for (int i = 1; i < 3; i++) { result += - texture(textureSampler, vec2(coord) + (vec2(offset[i], offset[i]) * norm)) - * weight[i]; + 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]; + texture(textureSampler, vec2(coord) - (vec2(offset[i], offset[i]) * norm)) * weight[i]; } return result; } @@ -65,10 +59,12 @@ vec4 blurDiagonal(sampler2D textureSampler, vec2 coord, vec2 norm) { void main() { vec3 base = texture(color_texture, vec2(frag_tex_coord)).rgb * weight[0]; vec2 tex_offset = 1.0f / textureSize(color_texture, 0); + + // TODO(Blinkhawk): This code can be optimized through shader group instructions. 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 diagonalB = blurVertical(color_texture, frag_tex_coord, tex_offset * vec2(1.0, -1.0)).rgb; vec3 combination = mix(mix(horizontal, vertical, 0.5f), mix(diagonalA, diagonalB, 0.5f), 0.5f); color = vec4(combination + base, 1.0f); } -- cgit v1.2.3 From dcc5b4f6b005a2c89bb4e77bca4cfe8705734021 Mon Sep 17 00:00:00 2001 From: Marshall Mohror Date: Fri, 22 Oct 2021 23:09:29 -0500 Subject: Presentation: Only use FP16 in scaling shaders on supported devices in Vulkan --- externals/CMakeLists.txt | 3 + src/video_core/CMakeLists.txt | 1 + src/video_core/host_shaders/CMakeLists.txt | 10 +- src/video_core/host_shaders/fidelityfx_fsr.comp | 106 ++++++++--------- .../host_shaders/opengl_present_scaleforce.frag | 130 +++++++++++++++++++++ .../host_shaders/present_scaleforce.frag | 112 ------------------ .../host_shaders/vulkan_fidelityfx_fsr_easu.comp | 13 --- .../vulkan_fidelityfx_fsr_easu_fp16.comp | 11 ++ .../vulkan_fidelityfx_fsr_easu_fp32.comp | 10 ++ .../host_shaders/vulkan_fidelityfx_fsr_rcas.comp | 13 --- .../vulkan_fidelityfx_fsr_rcas_fp16.comp | 11 ++ .../vulkan_fidelityfx_fsr_rcas_fp32.comp | 10 ++ .../vulkan_present_scaleforce_fp16.frag | 7 ++ .../vulkan_present_scaleforce_fp32.frag | 5 + src/video_core/renderer_opengl/renderer_opengl.cpp | 5 +- src/video_core/renderer_vulkan/vk_blit_screen.cpp | 17 ++- src/video_core/renderer_vulkan/vk_fsr.cpp | 46 +++++--- src/video_core/renderer_vulkan/vk_fsr.h | 2 +- 18 files changed, 298 insertions(+), 214 deletions(-) create mode 100644 src/video_core/host_shaders/opengl_present_scaleforce.frag delete mode 100644 src/video_core/host_shaders/present_scaleforce.frag delete mode 100644 src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu.comp create mode 100644 src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.comp create mode 100644 src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.comp delete mode 100644 src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas.comp create mode 100644 src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.comp create mode 100644 src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.comp create mode 100644 src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag create mode 100644 src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag (limited to 'src/video_core/host_shaders') diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index a76a3d800..7ff2ccc24 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -25,6 +25,9 @@ if (ARCHITECTURE_x86_64) add_subdirectory(dynarmic) endif() +add_library(ffx-fsr INTERFACE) +target_include_directories(ffx-fsr INTERFACE FidelityFX-FSR/ffx-fsr) + # getopt if (MSVC) add_subdirectory(getopt) diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 91a30fef7..07b94dcc8 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -237,6 +237,7 @@ target_include_directories(video_core PRIVATE ${FFmpeg_INCLUDE_DIR}) target_link_libraries(video_core PRIVATE ${FFmpeg_LIBRARIES}) target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS}) +target_link_libraries(video_core PRIVATE ffx-fsr) add_dependencies(video_core host_shaders) target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE}) target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 6b5ea649a..d779a967a 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -18,16 +18,20 @@ set(SHADER_FILES opengl_copy_bc4.comp opengl_present.frag opengl_present.vert + opengl_present_scaleforce.frag 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 - vulkan_fidelityfx_fsr_rcas.comp + vulkan_fidelityfx_fsr_easu_fp16.comp + vulkan_fidelityfx_fsr_easu_fp32.comp + vulkan_fidelityfx_fsr_rcas_fp16.comp + vulkan_fidelityfx_fsr_rcas_fp32.comp vulkan_present.frag vulkan_present.vert + vulkan_present_scaleforce_fp16.frag + vulkan_present_scaleforce_fp32.frag vulkan_quad_indexed.comp vulkan_uint8.comp ) diff --git a/src/video_core/host_shaders/fidelityfx_fsr.comp b/src/video_core/host_shaders/fidelityfx_fsr.comp index cbb601580..6b97f789d 100644 --- a/src/video_core/host_shaders/fidelityfx_fsr.comp +++ b/src/video_core/host_shaders/fidelityfx_fsr.comp @@ -28,80 +28,82 @@ // THE SOFTWARE. layout( push_constant ) uniform constants { - u32vec2 input_size; + uvec4 Const0; + uvec4 Const1; + uvec4 Const2; + uvec4 Const3; }; -uvec4 Const0; -uvec4 Const1; -uvec4 Const2; -uvec4 Const3; +layout(set=0,binding=0) uniform sampler2D InputTexture; +layout(set=0,binding=1,rgba16f) uniform image2D OutputTexture; #define A_GPU 1 #define A_GLSL 1 -#define A_HALF -#include "ffx_a.h" +#ifndef YUZU_USE_FP16 + #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_F 1 + AF4 FsrEasuRF(AF2 p) { AF4 res = textureGather(InputTexture, p, 0); return res; } + AF4 FsrEasuGF(AF2 p) { AF4 res = textureGather(InputTexture, p, 1); return res; } + AF4 FsrEasuBF(AF2 p) { AF4 res = textureGather(InputTexture, p, 2); return res; } + #endif + #if USE_RCAS + #define FSR_RCAS_F 1 + AF4 FsrRcasLoadF(ASU2 p) { return texelFetch(InputTexture, ASU2(p), 0); } + void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {} + #endif +#else + #define A_HALF + #include "ffx_a.h" -#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) {} + #if USE_EASU + #define FSR_EASU_H 1 + AH4 FsrEasuRH(AF2 p) { AH4 res = AH4(textureGather(InputTexture, p, 0)); return res; } + AH4 FsrEasuGH(AF2 p) { AH4 res = AH4(textureGather(InputTexture, p, 1)); return res; } + AH4 FsrEasuBH(AF2 p) { AH4 res = AH4(textureGather(InputTexture, p, 2)); return res; } + #endif + #if USE_RCAS + #define FSR_RCAS_H 1 + AH4 FsrRcasLoadH(ASW2 p) { return AH4(texelFetch(InputTexture, ASU2(p), 0)); } + void FsrRcasInputH(inout AH1 r,inout AH1 g,inout AH1 b){} + #endif #endif #include "ffx_fsr1.h" -void CurrFilter(u32vec2 pos) { - // For debugging +void CurrFilter(AU2 pos) { #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)); + AF2 pp = (AF2(pos) * AF2_AU2(Const0.xy) + AF2_AU2(Const0.zw)) * AF2_AU2(Const1.xy) + AF2(0.5, -0.5) * AF2_AU2(Const1.zw); + imageStore(OutputTexture, ASU2(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)); + #ifndef YUZU_USE_FP16 + AF3 c; + FsrEasuF(c, pos, Const0, Const1, Const2, Const3); + imageStore(OutputTexture, ASU2(pos), AF4(c, 1)); + #else + AH3 c; + FsrEasuH(c, pos, Const0, Const1, Const2, Const3); + imageStore(OutputTexture, ASU2(pos), AH4(c, 1)); + #endif #endif #if USE_RCAS - f16vec3 c; - FsrRcasH(c.r, c.g, c.b, pos, Const0); - imageStore(OutputTexture, ivec2(pos), f16vec4(c, 1)); + #ifndef YUZU_USE_FP16 + AF3 c; + FsrRcasF(c.r, c.g, c.b, pos, Const0); + imageStore(OutputTexture, ASU2(pos), AF4(c, 1)); + #else + AH3 c; + FsrRcasH(c.r, c.g, c.b, pos, Const0); + imageStore(OutputTexture, ASU2(pos), AH4(c, 1)); + #endif #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); 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..71ff9e1e3 --- /dev/null +++ b/src/video_core/host_shaders/opengl_present_scaleforce.frag @@ -0,0 +1,130 @@ +// MIT License +// +// Copyright (c) 2020 BreadFish64 +// +// 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. + +// Adapted from https://github.com/BreadFish64/ScaleFish/tree/master/scaleforce + +//! #version 460 + +#extension GL_ARB_separate_shader_objects : enable + +#ifdef YUZU_USE_FP16 + +#extension GL_AMD_gpu_shader_half_float : enable +#extension GL_NV_gpu_shader5 : enable + +#define lfloat float16_t +#define lvec2 f16vec2 +#define lvec3 f16vec3 +#define lvec4 f16vec4 + +#else + +#define lfloat float +#define lvec2 vec2 +#define lvec3 vec3 +#define lvec4 vec4 + +#endif + +#ifdef VULKAN + +#define BINDING_COLOR_TEXTURE 1 + +#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv + +#define BINDING_COLOR_TEXTURE 0 + +#endif + +layout (location = 0) in vec2 tex_coord; + +layout (location = 0) out vec4 frag_color; + +layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D input_texture; + +const bool ignore_alpha = true; + +lfloat ColorDist1(lvec4 a, lvec4 b) { + // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion + const lvec3 K = lvec3(0.2627, 0.6780, 0.0593); + const lfloat scaleB = lfloat(0.5) / (lfloat(1.0) - K.b); + const lfloat scaleR = lfloat(0.5) / (lfloat(1.0) - K.r); + lvec4 diff = a - b; + lfloat Y = dot(diff.rgb, K); + lfloat Cb = scaleB * (diff.b - Y); + lfloat Cr = scaleR * (diff.r - Y); + lvec3 YCbCr = lvec3(Y, Cb, Cr); + lfloat d = length(YCbCr); + if (ignore_alpha) { + return d; + } + return sqrt(a.a * b.a * d * d + diff.a * diff.a); +} + +lvec4 ColorDist(lvec4 ref, lvec4 A, lvec4 B, lvec4 C, lvec4 D) { + return lvec4( + ColorDist1(ref, A), + ColorDist1(ref, B), + ColorDist1(ref, C), + ColorDist1(ref, D) + ); +} + +vec4 Scaleforce(sampler2D tex, vec2 tex_coord) { + lvec4 bl = lvec4(textureOffset(tex, tex_coord, ivec2(-1, -1))); + lvec4 bc = lvec4(textureOffset(tex, tex_coord, ivec2(0, -1))); + lvec4 br = lvec4(textureOffset(tex, tex_coord, ivec2(1, -1))); + lvec4 cl = lvec4(textureOffset(tex, tex_coord, ivec2(-1, 0))); + lvec4 cc = lvec4(texture(tex, tex_coord)); + lvec4 cr = lvec4(textureOffset(tex, tex_coord, ivec2(1, 0))); + lvec4 tl = lvec4(textureOffset(tex, tex_coord, ivec2(-1, 1))); + lvec4 tc = lvec4(textureOffset(tex, tex_coord, ivec2(0, 1))); + lvec4 tr = lvec4(textureOffset(tex, tex_coord, ivec2(1, 1))); + + lvec4 offset_tl = ColorDist(cc, tl, tc, tr, cr); + lvec4 offset_br = ColorDist(cc, br, bc, bl, cl); + + // Calculate how different cc is from the texels around it + const lfloat plus_weight = lfloat(1.5); + const lfloat cross_weight = lfloat(1.5); + lfloat total_dist = dot(offset_tl + offset_br, lvec4(cross_weight, plus_weight, cross_weight, plus_weight)); + + if (total_dist == lfloat(0.0)) { + return cc; + } else { + // Add together all the distances with direction taken into account + lvec4 tmp = offset_tl - offset_br; + lvec2 total_offset = tmp.wy * plus_weight + (tmp.zz + lvec2(-tmp.x, tmp.x)) * cross_weight; + + // When the image has thin points, they tend to split apart. + // This is because the texels all around are different and total_offset reaches into clear areas. + // This works pretty well to keep the offset in bounds for these cases. + lfloat clamp_val = length(total_offset) / total_dist; + vec2 final_offset = vec2(clamp(total_offset, -clamp_val, clamp_val)) / textureSize(tex, 0); + + return texture(tex, tex_coord - final_offset); + } +} + +void main() { + frag_color = Scaleforce(input_texture, tex_coord); +} diff --git a/src/video_core/host_shaders/present_scaleforce.frag b/src/video_core/host_shaders/present_scaleforce.frag deleted file mode 100644 index ebc0d9b90..000000000 --- a/src/video_core/host_shaders/present_scaleforce.frag +++ /dev/null @@ -1,112 +0,0 @@ -// MIT License -// -// Copyright (c) 2020 BreadFish64 -// -// 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. - -// Adapted from https://github.com/BreadFish64/ScaleFish/tree/master/scaleforce - -#version 460 - -#extension GL_AMD_gpu_shader_half_float : enable -#extension GL_NV_gpu_shader5 : enable - -#ifdef VULKAN - -#define BINDING_COLOR_TEXTURE 1 - -#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv - -#define BINDING_COLOR_TEXTURE 0 - -#endif - -layout (location = 0) in vec2 tex_coord; - -layout (location = 0) out vec4 frag_color; - -layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D input_texture; - -const bool ignore_alpha = true; - -float16_t ColorDist1(f16vec4 a, f16vec4 b) { - // https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion - const f16vec3 K = f16vec3(0.2627, 0.6780, 0.0593); - const float16_t scaleB = float16_t(0.5) / (float16_t(1.0) - K.b); - const float16_t scaleR = float16_t(0.5) / (float16_t(1.0) - K.r); - f16vec4 diff = a - b; - float16_t Y = dot(diff.rgb, K); - float16_t Cb = scaleB * (diff.b - Y); - float16_t Cr = scaleR * (diff.r - Y); - f16vec3 YCbCr = f16vec3(Y, Cb, Cr); - float16_t d = length(YCbCr); - if (ignore_alpha) { - return d; - } - return sqrt(a.a * b.a * d * d + diff.a * diff.a); -} - -f16vec4 ColorDist(f16vec4 ref, f16vec4 A, f16vec4 B, f16vec4 C, f16vec4 D) { - return f16vec4( - ColorDist1(ref, A), - ColorDist1(ref, B), - ColorDist1(ref, C), - ColorDist1(ref, D) - ); -} - -vec4 Scaleforce(sampler2D tex, vec2 tex_coord) { - f16vec4 bl = f16vec4(textureOffset(tex, tex_coord, ivec2(-1, -1))); - f16vec4 bc = f16vec4(textureOffset(tex, tex_coord, ivec2(0, -1))); - f16vec4 br = f16vec4(textureOffset(tex, tex_coord, ivec2(1, -1))); - f16vec4 cl = f16vec4(textureOffset(tex, tex_coord, ivec2(-1, 0))); - f16vec4 cc = f16vec4(texture(tex, tex_coord)); - f16vec4 cr = f16vec4(textureOffset(tex, tex_coord, ivec2(1, 0))); - f16vec4 tl = f16vec4(textureOffset(tex, tex_coord, ivec2(-1, 1))); - f16vec4 tc = f16vec4(textureOffset(tex, tex_coord, ivec2(0, 1))); - f16vec4 tr = f16vec4(textureOffset(tex, tex_coord, ivec2(1, 1))); - - f16vec4 offset_tl = ColorDist(cc, tl, tc, tr, cr); - f16vec4 offset_br = ColorDist(cc, br, bc, bl, cl); - - // Calculate how different cc is from the texels around it - const float16_t plus_weight = float16_t(1.5); - const float16_t cross_weight = float16_t(1.5); - float16_t total_dist = dot(offset_tl + offset_br, f16vec4(cross_weight, plus_weight, cross_weight, plus_weight)); - - if (total_dist == float16_t(0.0)) { - return cc; - } else { - // Add together all the distances with direction taken into account - f16vec4 tmp = offset_tl - offset_br; - f16vec2 total_offset = tmp.wy * plus_weight + (tmp.zz + f16vec2(-tmp.x, tmp.x)) * cross_weight; - - // When the image has thin points, they tend to split apart. - // This is because the texels all around are different and total_offset reaches into clear areas. - // This works pretty well to keep the offset in bounds for these cases. - float16_t clamp_val = length(total_offset) / total_dist; - f16vec2 final_offset = clamp(total_offset, -clamp_val, clamp_val) / f16vec2(textureSize(tex, 0)); - - return texture(tex, tex_coord - final_offset); - } -} - -void main() { - frag_color = Scaleforce(input_texture, tex_coord); -} \ No newline at end of file diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu.comp deleted file mode 100644 index 6525eeeb5..000000000 --- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu.comp +++ /dev/null @@ -1,13 +0,0 @@ -// 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" diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.comp new file mode 100644 index 000000000..1c96a7905 --- /dev/null +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.comp @@ -0,0 +1,11 @@ +// 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 + +#define YUZU_USE_FP16 +#define USE_EASU 1 + +#include "fidelityfx_fsr.comp" diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.comp new file mode 100644 index 000000000..f4daff739 --- /dev/null +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.comp @@ -0,0 +1,10 @@ +// 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 + +#define USE_EASU 1 + +#include "fidelityfx_fsr.comp" diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas.comp deleted file mode 100644 index 9463ed842..000000000 --- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas.comp +++ /dev/null @@ -1,13 +0,0 @@ -// 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" diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.comp new file mode 100644 index 000000000..6b6796dd1 --- /dev/null +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.comp @@ -0,0 +1,11 @@ +// 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 + +#define YUZU_USE_FP16 +#define USE_RCAS 1 + +#include "fidelityfx_fsr.comp" diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.comp new file mode 100644 index 000000000..f785eebf3 --- /dev/null +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.comp @@ -0,0 +1,10 @@ +// 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 + +#define USE_RCAS 1 + +#include "fidelityfx_fsr.comp" diff --git a/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag b/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag new file mode 100644 index 000000000..924c03060 --- /dev/null +++ b/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag @@ -0,0 +1,7 @@ +#version 460 + +#extension GL_GOOGLE_include_directive : enable + +#define YUZU_USE_FP16 + +#include "opengl_present_scaleforce.frag" diff --git a/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag b/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag new file mode 100644 index 000000000..a594b83ca --- /dev/null +++ b/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag @@ -0,0 +1,5 @@ +#version 460 + +#extension GL_GOOGLE_include_directive : enable + +#include "opengl_present_scaleforce.frag" diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index e63f0bdd8..28daacd82 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -24,10 +24,10 @@ #include "video_core/host_shaders/fxaa_frag.h" #include "video_core/host_shaders/fxaa_vert.h" #include "video_core/host_shaders/opengl_present_frag.h" +#include "video_core/host_shaders/opengl_present_scaleforce_frag.h" #include "video_core/host_shaders/opengl_present_vert.h" #include "video_core/host_shaders/present_bicubic_frag.h" #include "video_core/host_shaders/present_gaussian_frag.h" -#include "video_core/host_shaders/present_scaleforce_frag.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_manager.h" #include "video_core/renderer_opengl/gl_shader_util.h" @@ -266,7 +266,8 @@ void RendererOpenGL::InitOpenGLObjects() { present_gaussian_fragment = CreateProgram(HostShaders::PRESENT_GAUSSIAN_FRAG, GL_FRAGMENT_SHADER); present_scaleforce_fragment = - CreateProgram(HostShaders::PRESENT_SCALEFORCE_FRAG, GL_FRAGMENT_SHADER); + CreateProgram(fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG), + GL_FRAGMENT_SHADER); // Generate presentation sampler present_sampler.Create(); diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 2bed4f3c5..9dfc508bc 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -21,8 +21,9 @@ #include "video_core/host_shaders/fxaa_vert_spv.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_scaleforce_fp16_frag_spv.h" +#include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h" #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" @@ -328,7 +329,7 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, blit_read_barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; blit_read_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT , + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, blit_read_barrier); } }); @@ -344,8 +345,12 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, crop_rect.bottom = framebuffer.height; } crop_rect = crop_rect.Scale(Settings::values.resolution_info.up_factor); + VkExtent2D fsr_input_size{ + .width = Settings::values.resolution_info.ScaleUp(framebuffer.width), + .height = Settings::values.resolution_info.ScaleUp(framebuffer.height), + }; VkImageView fsr_image_view = - fsr->Draw(scheduler, image_index, source_image_view, crop_rect); + fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect); UpdateDescriptorSet(image_index, fsr_image_view, true); } else { const bool is_nn = @@ -500,7 +505,11 @@ void VKBlitScreen::CreateShaders() { 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); + if (device.IsFloat16Supported()) { + scaleforce_fragment_shader = BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP16_FRAG_SPV); + } else { + scaleforce_fragment_shader = BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP32_FRAG_SPV); + } } void VKBlitScreen::CreateSemaphores() { diff --git a/src/video_core/renderer_vulkan/vk_fsr.cpp b/src/video_core/renderer_vulkan/vk_fsr.cpp index 1f60974be..9288aa7c2 100644 --- a/src/video_core/renderer_vulkan/vk_fsr.cpp +++ b/src/video_core/renderer_vulkan/vk_fsr.cpp @@ -4,13 +4,19 @@ #include "common/common_types.h" #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/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_comp_spv.h" +#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_comp_spv.h" +#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_comp_spv.h" +#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32_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" +#define A_CPU +#include +#include + namespace Vulkan { FSR::FSR(const Device& device_, MemoryAllocator& memory_allocator_, size_t image_count_, @@ -29,11 +35,11 @@ FSR::FSR(const Device& device_, MemoryAllocator& memory_allocator_, size_t image } VkImageView FSR::Draw(VKScheduler& scheduler, size_t image_index, VkImageView image_view, - const Common::Rectangle& crop_rect) { + VkExtent2D input_image_extent, const Common::Rectangle& crop_rect) { UpdateDescriptorSet(image_index, image_view); - scheduler.Record([this, image_index, crop_rect](vk::CommandBuffer cmdbuf) { + scheduler.Record([this, image_index, input_image_extent, crop_rect](vk::CommandBuffer cmdbuf) { const VkImageMemoryBarrier base_barrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, @@ -54,13 +60,18 @@ VkImageView FSR::Draw(VKScheduler& scheduler, size_t image_index, VkImageView im }, }; - // 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(crop_rect.GetWidth()), - .height = static_cast(crop_rect.GetHeight()), - }); + + std::array push_constants; + FsrEasuConOffset( + push_constants.data() + 0, push_constants.data() + 4, push_constants.data() + 8, + push_constants.data() + 12, + + static_cast(crop_rect.GetWidth()), static_cast(crop_rect.GetHeight()), + static_cast(input_image_extent.width), static_cast(input_image_extent.height), + static_cast(output_size.width), static_cast(output_size.height), + static_cast(crop_rect.left), static_cast(crop_rect.top)); + cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants); { VkImageMemoryBarrier fsr_write_barrier = base_barrier; @@ -77,7 +88,9 @@ VkImageView FSR::Draw(VKScheduler& scheduler, size_t image_index, VkImageView im 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); + + FsrRcasCon(push_constants.data(), 0.25f); + cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants); { std::array barriers; @@ -247,7 +260,7 @@ void FSR::CreatePipelineLayout() { VkPushConstantRange push_const{ .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, .offset = 0, - .size = sizeof(std::array), + .size = sizeof(std::array), }; VkPipelineLayoutCreateInfo ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, @@ -344,8 +357,13 @@ void FSR::CreateSampler() { } void FSR::CreateShaders() { - easu_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_EASU_COMP_SPV); - rcas_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_RCAS_COMP_SPV); + if (device.IsFloat16Supported()) { + easu_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_EASU_FP16_COMP_SPV); + rcas_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_RCAS_FP16_COMP_SPV); + } else { + easu_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_EASU_FP32_COMP_SPV); + rcas_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_RCAS_FP32_COMP_SPV); + } } void FSR::CreatePipeline() { diff --git a/src/video_core/renderer_vulkan/vk_fsr.h b/src/video_core/renderer_vulkan/vk_fsr.h index 8391e2e58..6bbec3d36 100644 --- a/src/video_core/renderer_vulkan/vk_fsr.h +++ b/src/video_core/renderer_vulkan/vk_fsr.h @@ -18,7 +18,7 @@ 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& crop_rect); + VkExtent2D input_image_extent, const Common::Rectangle& crop_rect); private: void CreateDescriptorPool(); -- cgit v1.2.3 From 87abab71fff2189c549ae247eb6c281c8f618acd Mon Sep 17 00:00:00 2001 From: ameerj <52414509+ameerj@users.noreply.github.com> Date: Sat, 23 Oct 2021 02:40:02 -0400 Subject: host_shaders: Misc copyright/style changes --- src/video_core/host_shaders/fxaa.frag | 6 +++++- src/video_core/host_shaders/fxaa.vert | 10 ++++------ src/video_core/host_shaders/present_bicubic.frag | 2 +- src/video_core/host_shaders/present_gaussian.frag | 4 ++-- 4 files changed, 12 insertions(+), 10 deletions(-) (limited to 'src/video_core/host_shaders') diff --git a/src/video_core/host_shaders/fxaa.frag b/src/video_core/host_shaders/fxaa.frag index 23f910d4c..02f4068d1 100644 --- a/src/video_core/host_shaders/fxaa.frag +++ b/src/video_core/host_shaders/fxaa.frag @@ -1,4 +1,8 @@ -// Adapted from +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +// Source code is adapted from // https://www.geeks3d.com/20110405/fxaa-fast-approximate-anti-aliasing-demo-glsl-opengl-test-radeon-geforce/3/ #version 460 diff --git a/src/video_core/host_shaders/fxaa.vert b/src/video_core/host_shaders/fxaa.vert index 01d5ff4df..ac20c04e9 100644 --- a/src/video_core/host_shaders/fxaa.vert +++ b/src/video_core/host_shaders/fxaa.vert @@ -1,4 +1,4 @@ -// Copyright 2019 yuzu Emulator Project +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -16,10 +16,12 @@ layout (location = 0) out vec4 posPos; #ifdef VULKAN #define BINDING_COLOR_TEXTURE 0 +#define VERTEX_ID gl_VertexIndex #else // ^^^ Vulkan ^^^ // vvv OpenGL vvv #define BINDING_COLOR_TEXTURE 0 +#define VERTEX_ID gl_VertexID #endif @@ -28,11 +30,7 @@ layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D input_texture; const float FXAA_SUBPIX_SHIFT = 0; void main() { -#ifdef VULKAN - vec2 vertex = vertices[gl_VertexIndex]; -#else - vec2 vertex = vertices[gl_VertexID]; -#endif + vec2 vertex = vertices[VERTEX_ID]; gl_Position = vec4(vertex, 0.0, 1.0); vec2 vert_tex_coord = (vertex + 1.0) / 2.0; posPos.xy = vert_tex_coord; diff --git a/src/video_core/host_shaders/present_bicubic.frag b/src/video_core/host_shaders/present_bicubic.frag index f3e5410e7..902b70c2b 100644 --- a/src/video_core/host_shaders/present_bicubic.frag +++ b/src/video_core/host_shaders/present_bicubic.frag @@ -1,4 +1,4 @@ -// Copyright 2019 yuzu Emulator Project +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/video_core/host_shaders/present_gaussian.frag b/src/video_core/host_shaders/present_gaussian.frag index a9558548f..72a300dac 100644 --- a/src/video_core/host_shaders/present_gaussian.frag +++ b/src/video_core/host_shaders/present_gaussian.frag @@ -1,8 +1,8 @@ -// Copyright 2019 yuzu Emulator Project +// Copyright 2021 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -// Code obtained from this 2 sources: +// Code adapted from the following sources: // - https://learnopengl.com/Advanced-Lighting/Bloom // - https://www.rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/ -- cgit v1.2.3 From d46a71e786b42ecfe702ae00e01e6dcdf9121875 Mon Sep 17 00:00:00 2001 From: FernandoS27 Date: Fri, 29 Oct 2021 17:45:02 +0200 Subject: HostShader: fix Gaussian filter. --- src/video_core/host_shaders/present_gaussian.frag | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/video_core/host_shaders') diff --git a/src/video_core/host_shaders/present_gaussian.frag b/src/video_core/host_shaders/present_gaussian.frag index 72a300dac..66fed3238 100644 --- a/src/video_core/host_shaders/present_gaussian.frag +++ b/src/video_core/host_shaders/present_gaussian.frag @@ -63,8 +63,8 @@ void main() { // TODO(Blinkhawk): This code can be optimized through shader group instructions. 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 * vec2(1.0, -1.0)).rgb; + vec3 diagonalA = blurDiagonal(color_texture, frag_tex_coord, tex_offset).rgb; + vec3 diagonalB = blurDiagonal(color_texture, frag_tex_coord, tex_offset * vec2(1.0, -1.0)).rgb; vec3 combination = mix(mix(horizontal, vertical, 0.5f), mix(diagonalA, diagonalB, 0.5f), 0.5f); color = vec4(combination + base, 1.0f); } -- cgit v1.2.3