summaryrefslogtreecommitdiffstats
path: root/src/video_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core')
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt4
-rw-r--r--src/video_core/host_shaders/opengl_present_bicubic.frag56
-rw-r--r--src/video_core/host_shaders/opengl_present_scaleforce.frag135
-rw-r--r--src/video_core/host_shaders/vulkan_present_bicubic.frag56
-rw-r--r--src/video_core/host_shaders/vulkan_present_scaleforce.frag137
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp27
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp123
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h8
9 files changed, 537 insertions, 13 deletions
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);
+ }
+}
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 0f7b69c6d..71a5e3adf 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -21,7 +21,9 @@
#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/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -252,7 +254,11 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
void RendererOpenGL::InitOpenGLObjects() {
// Create shader programs
present_vertex = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER);
- present_fragment = CreateProgram(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_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_scaleforce_fragment =
+ CreateProgram(HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG, GL_FRAGMENT_SHADER);
// Generate presentation sampler
present_sampler.Create();
@@ -337,7 +343,24 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
// Set projection matrix
const std::array ortho_matrix =
MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
- program_manager.BindPresentPrograms(present_vertex.handle, present_fragment.handle);
+
+ GLuint fragment_handle;
+ const auto filter = Settings::values.scaling_filter.GetValue();
+ switch (filter) {
+ case Settings::ScalingFilter::Bilinear:
+ fragment_handle = present_bilinear_fragment.handle;
+ break;
+ case Settings::ScalingFilter::Bicubic:
+ fragment_handle = present_bicubic_fragment.handle;
+ break;
+ case Settings::ScalingFilter::ScaleForce:
+ fragment_handle = present_scaleforce_fragment.handle;
+ break;
+ default:
+ fragment_handle = present_bilinear_fragment.handle;
+ break;
+ }
+ program_manager.BindPresentPrograms(present_vertex.handle, fragment_handle);
glProgramUniformMatrix3x2fv(present_vertex.handle, ModelViewMatrixLocation, 1, GL_FALSE,
ortho_matrix.data());
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index d455f572f..bf3d3502c 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -111,7 +111,9 @@ private:
OGLSampler present_sampler;
OGLBuffer vertex_buffer;
OGLProgram present_vertex;
- OGLProgram present_fragment;
+ OGLProgram present_bilinear_fragment;
+ OGLProgram present_bicubic_fragment;
+ OGLProgram present_scaleforce_fragment;
OGLFramebuffer screenshot_framebuffer;
// GPU address of the vertex buffer
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 7051e6559..19d91ecfc 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -12,11 +12,14 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/math_util.h"
+#include "common/settings.h"
#include "core/core.h"
#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/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"
@@ -258,8 +261,22 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
.offset = {0, 0},
.extent = size,
};
+ const auto filter = Settings::values.scaling_filter.GetValue();
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
- cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+ switch (filter) {
+ case Settings::ScalingFilter::Bilinear:
+ cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bilinear_pipeline);
+ break;
+ case Settings::ScalingFilter::Bicubic:
+ cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bicubic_pipeline);
+ break;
+ case Settings::ScalingFilter::ScaleForce:
+ cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *scaleforce_pipeline);
+ break;
+ default:
+ cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *bilinear_pipeline);
+ break;
+ }
cmdbuf.SetViewport(0, viewport);
cmdbuf.SetScissor(0, scissor);
@@ -324,7 +341,9 @@ void VKBlitScreen::RefreshResources(const Tegra::FramebufferConfig& framebuffer)
void VKBlitScreen::CreateShaders() {
vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV);
- fragment_shader = BuildShader(device, VULKAN_PRESENT_FRAG_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);
}
void VKBlitScreen::CreateSemaphores() {
@@ -468,7 +487,7 @@ void VKBlitScreen::CreatePipelineLayout() {
}
void VKBlitScreen::CreateGraphicsPipeline() {
- const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{
+ const std::array<VkPipelineShaderStageCreateInfo, 2> bilinear_shader_stages{{
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = nullptr,
@@ -483,7 +502,49 @@ void VKBlitScreen::CreateGraphicsPipeline() {
.pNext = nullptr,
.flags = 0,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
- .module = *fragment_shader,
+ .module = *bilinear_fragment_shader,
+ .pName = "main",
+ .pSpecializationInfo = nullptr,
+ },
+ }};
+
+ const std::array<VkPipelineShaderStageCreateInfo, 2> bicubic_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 = *bicubic_fragment_shader,
+ .pName = "main",
+ .pSpecializationInfo = nullptr,
+ },
+ }};
+
+ const std::array<VkPipelineShaderStageCreateInfo, 2> scaleforce_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 = *scaleforce_fragment_shader,
.pName = "main",
.pSpecializationInfo = nullptr,
},
@@ -583,12 +644,56 @@ void VKBlitScreen::CreateGraphicsPipeline() {
.pDynamicStates = dynamic_states.data(),
};
- const VkGraphicsPipelineCreateInfo pipeline_ci{
+ const VkGraphicsPipelineCreateInfo bilinear_pipeline_ci{
+ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .stageCount = static_cast<u32>(bilinear_shader_stages.size()),
+ .pStages = bilinear_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 bicubic_pipeline_ci{
+ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+ .pNext = nullptr,
+ .flags = 0,
+ .stageCount = static_cast<u32>(bicubic_shader_stages.size()),
+ .pStages = bicubic_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,
.flags = 0,
- .stageCount = static_cast<u32>(shader_stages.size()),
- .pStages = shader_stages.data(),
+ .stageCount = static_cast<u32>(scaleforce_shader_stages.size()),
+ .pStages = scaleforce_shader_stages.data(),
.pVertexInputState = &vertex_input_ci,
.pInputAssemblyState = &input_assembly_ci,
.pTessellationState = nullptr,
@@ -605,7 +710,9 @@ void VKBlitScreen::CreateGraphicsPipeline() {
.basePipelineIndex = 0,
};
- pipeline = device.GetLogical().CreateGraphicsPipeline(pipeline_ci);
+ bilinear_pipeline = device.GetLogical().CreateGraphicsPipeline(bilinear_pipeline_ci);
+ bicubic_pipeline = device.GetLogical().CreateGraphicsPipeline(bicubic_pipeline_ci);
+ scaleforce_pipeline = device.GetLogical().CreateGraphicsPipeline(scaleforce_pipeline_ci);
}
void VKBlitScreen::CreateSampler() {
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index 430bcfbca..d3a16f0ba 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -107,11 +107,15 @@ private:
const VKScreenInfo& screen_info;
vk::ShaderModule vertex_shader;
- vk::ShaderModule fragment_shader;
+ vk::ShaderModule bilinear_fragment_shader;
+ vk::ShaderModule bicubic_fragment_shader;
+ vk::ShaderModule scaleforce_fragment_shader;
vk::DescriptorPool descriptor_pool;
vk::DescriptorSetLayout descriptor_set_layout;
vk::PipelineLayout pipeline_layout;
- vk::Pipeline pipeline;
+ vk::Pipeline bilinear_pipeline;
+ vk::Pipeline bicubic_pipeline;
+ vk::Pipeline scaleforce_pipeline;
vk::RenderPass renderpass;
std::vector<vk::Framebuffer> framebuffers;
vk::DescriptorSets descriptor_sets;