forked from ShuriZma/suyu
Renderer: Implement Bicubic and ScaleForce filters.
This commit is contained in:
parent
c5bbbf3902
commit
a6b88e85bf
|
@ -48,6 +48,7 @@ void LogSettings() {
|
|||
log_setting("Core_UseMultiCore", values.use_multi_core.GetValue());
|
||||
log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue());
|
||||
log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue());
|
||||
log_setting("Renderer_ScalingFilter", values.scaling_filter.GetValue());
|
||||
log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue());
|
||||
log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue());
|
||||
log_setting("Renderer_UseDiskShaderCache", values.use_disk_shader_cache.GetValue());
|
||||
|
@ -113,19 +114,10 @@ void UpdateRescalingInfo() {
|
|||
info.up_scale = 1;
|
||||
info.down_shift = 1;
|
||||
break;
|
||||
case ResolutionSetup::Res3_4X:
|
||||
info.up_scale = 3;
|
||||
info.down_shift = 2;
|
||||
break;
|
||||
case ResolutionSetup::Res1X:
|
||||
info.up_scale = 1;
|
||||
info.down_shift = 0;
|
||||
break;
|
||||
case ResolutionSetup::Res3_2X: {
|
||||
info.up_scale = 3;
|
||||
info.down_shift = 1;
|
||||
break;
|
||||
}
|
||||
case ResolutionSetup::Res2X:
|
||||
info.up_scale = 2;
|
||||
info.down_shift = 0;
|
||||
|
|
|
@ -54,12 +54,16 @@ enum class NvdecEmulation : u32 {
|
|||
|
||||
enum class ResolutionSetup : u32 {
|
||||
Res1_2X = 0,
|
||||
Res3_4X = 1,
|
||||
Res1X = 2,
|
||||
Res3_2X = 3,
|
||||
Res2X = 4,
|
||||
Res3X = 5,
|
||||
Res4X = 6,
|
||||
Res1X = 1,
|
||||
Res2X = 2,
|
||||
Res3X = 3,
|
||||
Res4X = 4,
|
||||
};
|
||||
|
||||
enum class ScalingFilter : u32 {
|
||||
Bilinear = 0,
|
||||
Bicubic = 1,
|
||||
ScaleForce = 2,
|
||||
};
|
||||
|
||||
struct ResolutionScalingInfo {
|
||||
|
@ -471,6 +475,7 @@ struct Values {
|
|||
|
||||
ResolutionScalingInfo resolution_info{};
|
||||
Setting<ResolutionSetup> resolution_setup{ResolutionSetup::Res1X, "resolution_setup"};
|
||||
Setting<ScalingFilter> scaling_filter{ScalingFilter::Bilinear, "scaling_filter"};
|
||||
// *nix platforms may have issues with the borderless windowed fullscreen mode.
|
||||
// Default to exclusive fullscreen on these platforms for now.
|
||||
RangedSetting<FullscreenMode> fullscreen_mode{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,12 @@ 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>(shader_stages.size()),
|
||||
.pStages = shader_stages.data(),
|
||||
.stageCount = static_cast<u32>(bilinear_shader_stages.size()),
|
||||
.pStages = bilinear_shader_stages.data(),
|
||||
.pVertexInputState = &vertex_input_ci,
|
||||
.pInputAssemblyState = &input_assembly_ci,
|
||||
.pTessellationState = nullptr,
|
||||
|
@ -605,7 +666,53 @@ void VKBlitScreen::CreateGraphicsPipeline() {
|
|||
.basePipelineIndex = 0,
|
||||
};
|
||||
|
||||
pipeline = device.GetLogical().CreateGraphicsPipeline(pipeline_ci);
|
||||
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>(scaleforce_shader_stages.size()),
|
||||
.pStages = scaleforce_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,
|
||||
};
|
||||
|
||||
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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -825,6 +825,7 @@ void Config::ReadRendererValues() {
|
|||
ReadGlobalSetting(Settings::values.fullscreen_mode);
|
||||
ReadGlobalSetting(Settings::values.aspect_ratio);
|
||||
ReadGlobalSetting(Settings::values.resolution_setup);
|
||||
ReadGlobalSetting(Settings::values.scaling_filter);
|
||||
ReadGlobalSetting(Settings::values.max_anisotropy);
|
||||
ReadGlobalSetting(Settings::values.use_speed_limit);
|
||||
ReadGlobalSetting(Settings::values.speed_limit);
|
||||
|
@ -1371,6 +1372,10 @@ void Config::SaveRendererValues() {
|
|||
static_cast<u32>(Settings::values.resolution_setup.GetValue(global)),
|
||||
static_cast<u32>(Settings::values.resolution_setup.GetDefault()),
|
||||
Settings::values.resolution_setup.UsingGlobal());
|
||||
WriteSetting(QString::fromStdString(Settings::values.scaling_filter.GetLabel()),
|
||||
static_cast<u32>(Settings::values.scaling_filter.GetValue(global)),
|
||||
static_cast<u32>(Settings::values.scaling_filter.GetDefault()),
|
||||
Settings::values.scaling_filter.UsingGlobal());
|
||||
WriteGlobalSetting(Settings::values.max_anisotropy);
|
||||
WriteGlobalSetting(Settings::values.use_speed_limit);
|
||||
WriteGlobalSetting(Settings::values.speed_limit);
|
||||
|
|
|
@ -190,5 +190,6 @@ Q_DECLARE_METATYPE(Settings::GPUAccuracy);
|
|||
Q_DECLARE_METATYPE(Settings::FullscreenMode);
|
||||
Q_DECLARE_METATYPE(Settings::NvdecEmulation);
|
||||
Q_DECLARE_METATYPE(Settings::ResolutionSetup);
|
||||
Q_DECLARE_METATYPE(Settings::ScalingFilter);
|
||||
Q_DECLARE_METATYPE(Settings::RendererBackend);
|
||||
Q_DECLARE_METATYPE(Settings::ShaderBackend);
|
||||
|
|
|
@ -89,6 +89,7 @@ void ConfigureGraphics::SetConfiguration() {
|
|||
ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock);
|
||||
ui->use_disk_shader_cache->setEnabled(runtime_lock);
|
||||
ui->nvdec_emulation_widget->setEnabled(runtime_lock);
|
||||
ui->resolution_combobox->setEnabled(runtime_lock);
|
||||
ui->accelerate_astc->setEnabled(runtime_lock);
|
||||
ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
|
||||
ui->use_asynchronous_gpu_emulation->setChecked(
|
||||
|
@ -104,6 +105,8 @@ void ConfigureGraphics::SetConfiguration() {
|
|||
ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
|
||||
ui->resolution_combobox->setCurrentIndex(
|
||||
static_cast<int>(Settings::values.resolution_setup.GetValue()));
|
||||
ui->scaling_filter_combobox->setCurrentIndex(
|
||||
static_cast<int>(Settings::values.scaling_filter.GetValue()));
|
||||
} else {
|
||||
ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend);
|
||||
ConfigurationShared::SetHighlight(ui->api_widget,
|
||||
|
@ -129,6 +132,11 @@ void ConfigureGraphics::SetConfiguration() {
|
|||
ConfigurationShared::SetHighlight(ui->resolution_label,
|
||||
!Settings::values.resolution_setup.UsingGlobal());
|
||||
|
||||
ConfigurationShared::SetPerGameSetting(ui->scaling_filter_combobox,
|
||||
&Settings::values.scaling_filter);
|
||||
ConfigurationShared::SetHighlight(ui->scaling_filter_label,
|
||||
!Settings::values.scaling_filter.UsingGlobal());
|
||||
|
||||
ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1);
|
||||
ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal());
|
||||
ConfigurationShared::SetHighlight(ui->bg_layout, !Settings::values.bg_red.UsingGlobal());
|
||||
|
@ -144,6 +152,10 @@ void ConfigureGraphics::ApplyConfiguration() {
|
|||
ui->resolution_combobox->currentIndex() -
|
||||
((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
|
||||
|
||||
const auto scaling_filter = static_cast<Settings::ScalingFilter>(
|
||||
ui->scaling_filter_combobox->currentIndex() -
|
||||
((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
|
||||
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.fullscreen_mode,
|
||||
ui->fullscreen_mode_combobox);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.aspect_ratio,
|
||||
|
@ -178,6 +190,9 @@ void ConfigureGraphics::ApplyConfiguration() {
|
|||
if (Settings::values.resolution_setup.UsingGlobal()) {
|
||||
Settings::values.resolution_setup.SetValue(resolution_setup);
|
||||
}
|
||||
if (Settings::values.scaling_filter.UsingGlobal()) {
|
||||
Settings::values.scaling_filter.SetValue(scaling_filter);
|
||||
}
|
||||
} else {
|
||||
if (ui->resolution_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
Settings::values.resolution_setup.SetGlobal(true);
|
||||
|
@ -185,6 +200,12 @@ void ConfigureGraphics::ApplyConfiguration() {
|
|||
Settings::values.resolution_setup.SetGlobal(false);
|
||||
Settings::values.resolution_setup.SetValue(resolution_setup);
|
||||
}
|
||||
if (ui->scaling_filter_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
Settings::values.scaling_filter.SetGlobal(true);
|
||||
} else {
|
||||
Settings::values.scaling_filter.SetGlobal(false);
|
||||
Settings::values.scaling_filter.SetValue(scaling_filter);
|
||||
}
|
||||
if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
|
||||
Settings::values.renderer_backend.SetGlobal(true);
|
||||
Settings::values.shader_backend.SetGlobal(true);
|
||||
|
@ -333,6 +354,7 @@ void ConfigureGraphics::SetupPerGameUI() {
|
|||
ui->fullscreen_mode_combobox->setEnabled(Settings::values.fullscreen_mode.UsingGlobal());
|
||||
ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal());
|
||||
ui->resolution_combobox->setEnabled(Settings::values.resolution_setup.UsingGlobal());
|
||||
ui->scaling_filter_combobox->setEnabled(Settings::values.scaling_filter.UsingGlobal());
|
||||
ui->use_asynchronous_gpu_emulation->setEnabled(
|
||||
Settings::values.use_asynchronous_gpu_emulation.UsingGlobal());
|
||||
ui->nvdec_emulation->setEnabled(Settings::values.nvdec_emulation.UsingGlobal());
|
||||
|
@ -364,6 +386,9 @@ void ConfigureGraphics::SetupPerGameUI() {
|
|||
ConfigurationShared::SetColoredComboBox(
|
||||
ui->resolution_combobox, ui->resolution_label,
|
||||
static_cast<int>(Settings::values.resolution_setup.GetValue(true)));
|
||||
ConfigurationShared::SetColoredComboBox(
|
||||
ui->scaling_filter_combobox, ui->scaling_filter_label,
|
||||
static_cast<int>(Settings::values.scaling_filter.GetValue(true)));
|
||||
ConfigurationShared::InsertGlobalItem(
|
||||
ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
|
||||
ConfigurationShared::InsertGlobalItem(
|
||||
|
|
|
@ -338,21 +338,11 @@
|
|||
<string>0.5X (360p/540p)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>0.75X (540p/810p)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>1X (720p/1080p)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>1.5X (1080p/1620p)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>2X (1440p/2160[4K]p)</string>
|
||||
|
@ -373,6 +363,50 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="scaling_filter_layout" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="scaling_filter_label">
|
||||
<property name="text">
|
||||
<string>Window Adapting Filter:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="scaling_filter_combobox">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Bilinear</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Bicubic</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>ScaleForce</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="bg_layout" native="true">
|
||||
<property name="sizePolicy">
|
||||
|
|
Loading…
Reference in New Issue