From c7bde720600a409f0d88f6572be81e26fa4cdf83 Mon Sep 17 00:00:00 2001 From: Isaac Date: Tue, 27 Jun 2023 21:26:04 +0200 Subject: [PATCH] Add pixel AA to fill shaders --- border/shaders/average_fill/compose.slang | 53 +++++++++-- border/shaders/average_fill/parameters.slang | 2 +- border/shaders/blur_fill/compose.slang | 51 +++++++++-- border/shaders/blur_fill/parameters.slang | 2 +- interpolation/pixel_aa.slangp | 2 +- interpolation/shaders/pixel_aa/pixel_aa.slang | 87 +++++++++++++++++++ .../{pixel_aa.slang => pixel_aa/shared.slang} | 87 +------------------ 7 files changed, 183 insertions(+), 101 deletions(-) create mode 100644 interpolation/shaders/pixel_aa/pixel_aa.slang rename interpolation/shaders/{pixel_aa.slang => pixel_aa/shared.slang} (51%) diff --git a/border/shaders/average_fill/compose.slang b/border/shaders/average_fill/compose.slang index 27d06a9..9dbe42b 100644 --- a/border/shaders/average_fill/compose.slang +++ b/border/shaders/average_fill/compose.slang @@ -1,7 +1,7 @@ #version 450 /* - Average fill v1.3 by fishku + Average fill v1.4 by fishku Copyright (C) 2023 Public domain license (CC0) @@ -27,6 +27,7 @@ 3 = Smooth angle-based blending Changelog: + v1.4: Add anti-aliased interpolation for non-integer scaling. v1.3: Fix scaling bugs. v1.2: Fix scaling bugs. v1.1: Add extension modes from blur fill; Add average gamma adjustment. @@ -52,6 +53,10 @@ layout(push_constant) uniform Push { float ASPECT_V; float FORCE_INTEGER_SCALING; float FILL_GAMMA; + // From pixel AA + float PIX_AA_SHARP; + float PIX_AA_SUBPX; + float PIX_AA_SUBPX_BGR; } param; @@ -117,8 +122,13 @@ vec3 blend_corner(vec3 a, // The first color to blend } } +#include "../../../interpolation/shaders/pixel_aa/shared.slang" + void main() { - const vec2 pixel_coord = o2i(vTexCoord); + const vec2 scale_o2i = scale_o2i(); + const vec2 pixel_coord = + (vTexCoord - 0.49999) * scale_o2i + get_input_center(); + if (pixel_coord.x < param.OS_CROP_LEFT) { if (param.EXTEND_H < 0.5) { FragColor = vec4(0.0, 0.0, 0.0, 1.0); @@ -180,11 +190,40 @@ void main() { FragColor.rgb = pow(FragColor.rgb, vec3(param.FILL_GAMMA)); } else if (pixel_coord.y < param.InputSize.y - param.OS_CROP_BOTTOM) { // Uncropped - // Do a sharp (nearest neighbor) resampling. - FragColor = vec4( - texture(Input, (floor(pixel_coord) + 0.5) * param.InputSize.zw) - .rgb, - 1.0); + if (param.FORCE_INTEGER_SCALING > 0.5) { + // Do a perfectly sharp (nearest neighbor) sampling. + FragColor = vec4(texture(Input, (floor(pixel_coord) + 0.5) * + param.InputSize.zw) + .rgb, + 1.0); + } else { + // Do a sharp anti-aliased interpolation. + // Do not correct for gamma additionally because the input is + // already in linear color space. + if (param.PIX_AA_SUBPX < 0.5) { + const vec2 tx_size = get_texel_size(pixel_coord) * + scale_o2i * param.InputSize.zw; + FragColor = + vec4(sample_aa(Input, pixel_coord, param.InputSize.zw, + false, param.PIX_AA_SHARP, tx_size), + 1.0); + } else { + for (int i = -1; i < 2; ++i) { + const vec2 subpix_coord = + pixel_coord + + vec2((param.PIX_AA_SUBPX_BGR < 0.5 ? i : -i) / 3.0, + 0.0) * + param.FinalViewportSize.zw * param.InputSize.xy; + const vec2 tx_size = get_texel_size(subpix_coord) * + scale_o2i * param.InputSize.zw / + vec2(3.0, 1.0); + FragColor[i + 1] = sample_aa( + Input, subpix_coord, param.InputSize.zw, false, + param.PIX_AA_SHARP, tx_size)[i + 1]; + } + FragColor[3] = 1.0; + } + } } else { if (param.EXTEND_V < 0.5) { FragColor = vec4(0.0, 0.0, 0.0, 1.0); diff --git a/border/shaders/average_fill/parameters.slang b/border/shaders/average_fill/parameters.slang index f937f0e..132b399 100644 --- a/border/shaders/average_fill/parameters.slang +++ b/border/shaders/average_fill/parameters.slang @@ -1,7 +1,7 @@ // See compose.slang for copyright and other information. // clang-format off -#pragma parameter AVERAGE_FILL_SETTINGS "=== Average fill v1.3 settings ===" 0.0 0.0 1.0 1.0 +#pragma parameter AVERAGE_FILL_SETTINGS "=== Average fill v1.4 settings ===" 0.0 0.0 1.0 1.0 #pragma parameter OS_CROP_TOP "Overscan crop top" 0.0 0.0 1024.0 1.0 #pragma parameter OS_CROP_BOTTOM "Overscan crop bottom" 0.0 0.0 1024.0 1.0 #pragma parameter OS_CROP_LEFT "Overscan crop left" 0.0 0.0 1024.0 1.0 diff --git a/border/shaders/blur_fill/compose.slang b/border/shaders/blur_fill/compose.slang index 0c3959d..a02de76 100644 --- a/border/shaders/blur_fill/compose.slang +++ b/border/shaders/blur_fill/compose.slang @@ -1,7 +1,7 @@ #version 450 /* - Blur fill v1.4 by fishku + Blur fill v1.5 by fishku Copyright (C) 2023 Public domain license (CC0) @@ -27,6 +27,7 @@ strength of the blur. Changelog: + v1.5: Add anti-aliased interpolation for non-integer scaling. v1.4: Fix scaling bugs. v1.3: Reduce shimmering artifacts. v1.2: Fix scaling bugs. @@ -54,6 +55,10 @@ layout(push_constant) uniform Push { float FILL_GAMMA; // From dual filter blur float BLUR_RADIUS; + // From pixel AA + float PIX_AA_SHARP; + float PIX_AA_SUBPX; + float PIX_AA_SUBPX_BGR; } param; @@ -79,8 +84,12 @@ layout(set = 0, binding = 2) uniform sampler2D Input; layout(set = 0, binding = 3) uniform sampler2D Tiled; layout(set = 0, binding = 4) uniform sampler2D Blurred; +#include "../../../interpolation/shaders/pixel_aa/shared.slang" + void main() { - const vec2 pixel_coord = o2i(vTexCoord); + const vec2 scale_o2i = scale_o2i(); + const vec2 pixel_coord = + (vTexCoord - 0.49999) * scale_o2i + get_input_center(); const vec4 input_extrema = vec4(param.OS_CROP_LEFT, param.OS_CROP_TOP, param.InputSize.x - param.OS_CROP_RIGHT, @@ -105,9 +114,39 @@ void main() { } } else { // Sample original. - // Do a sharp (nearest neighbor) resampling. - FragColor = vec4( - texture(Input, (floor(pixel_coord) + 0.5) * param.InputSize.zw).rgb, - 1.0); + if (param.FORCE_INTEGER_SCALING > 0.5) { + // Do a perfectly sharp (nearest neighbor) sampling. + FragColor = vec4( + texture(Input, (floor(pixel_coord) + 0.5) * param.InputSize.zw) + .rgb, + 1.0); + } else { + // Do a sharp anti-aliased interpolation. + // Do not correct for gamma additionally because the input is + // already in linear color space. + if (param.PIX_AA_SUBPX < 0.5) { + const vec2 tx_size = get_texel_size(pixel_coord) * scale_o2i * + param.InputSize.zw; + FragColor = + vec4(sample_aa(Input, pixel_coord, param.InputSize.zw, + false, param.PIX_AA_SHARP, tx_size), + 1.0); + } else { + for (int i = -1; i < 2; ++i) { + const vec2 subpix_coord = + pixel_coord + + vec2((param.PIX_AA_SUBPX_BGR < 0.5 ? i : -i) / 3.0, + 0.0) * + param.FinalViewportSize.zw * param.InputSize.xy; + const vec2 tx_size = get_texel_size(subpix_coord) * + scale_o2i * param.InputSize.zw / + vec2(3.0, 1.0); + FragColor[i + 1] = + sample_aa(Input, subpix_coord, param.InputSize.zw, + false, param.PIX_AA_SHARP, tx_size)[i + 1]; + } + FragColor[3] = 1.0; + } + } } } diff --git a/border/shaders/blur_fill/parameters.slang b/border/shaders/blur_fill/parameters.slang index ee49f16..b48b5a8 100644 --- a/border/shaders/blur_fill/parameters.slang +++ b/border/shaders/blur_fill/parameters.slang @@ -1,7 +1,7 @@ // See compose.slang for copyright and other information. // clang-format off -#pragma parameter BLUR_FILL_SETTINGS "=== Blur fill v1.4 settings ===" 0.0 0.0 1.0 1.0 +#pragma parameter BLUR_FILL_SETTINGS "=== Blur fill v1.5 settings ===" 0.0 0.0 1.0 1.0 #pragma parameter OS_CROP_TOP "Overscan crop top" 0.0 0.0 1024.0 1.0 #pragma parameter OS_CROP_BOTTOM "Overscan crop bottom" 0.0 0.0 1024.0 1.0 #pragma parameter OS_CROP_LEFT "Overscan crop left" 0.0 0.0 1024.0 1.0 diff --git a/interpolation/pixel_aa.slangp b/interpolation/pixel_aa.slangp index dab74f2..46e870a 100644 --- a/interpolation/pixel_aa.slangp +++ b/interpolation/pixel_aa.slangp @@ -1,5 +1,5 @@ shaders = 1 -shader0 = shaders/pixel_aa.slang +shader0 = shaders/pixel_aa/pixel_aa.slang filter_linear0 = true scale_type0 = viewport diff --git a/interpolation/shaders/pixel_aa/pixel_aa.slang b/interpolation/shaders/pixel_aa/pixel_aa.slang new file mode 100644 index 0000000..d04a326 --- /dev/null +++ b/interpolation/shaders/pixel_aa/pixel_aa.slang @@ -0,0 +1,87 @@ +#version 450 + +/* + Pixel AA v1.1 by fishku + Copyright (C) 2023 + Public domain license (CC0) + + Features: + - Sharp upscaling with anti-aliasing + - Subpixel upscaling + - Sharpness can be controlled + - Gamma correct blending + - Integer scales result in pixel-perfect scaling + - Can use bilinear filtering for max. performance + + Inspired by: + https://www.shadertoy.com/view/MlB3D3 + by d7samurai + and: + https://www.youtube.com/watch?v=d6tp43wZqps + by t3ssel8r + + With sharpness = 1.0, using the same gamma-correct blending, and disabling + subpixel anti-aliasing, results are identical to the "pixellate" shader. + + Changelog: + v1.1: Better subpixel sampling. + v1.0: Initial release. +*/ + +layout(push_constant) uniform Push { + vec4 SourceSize; + vec4 OutputSize; + float PIX_AA_SHARP; + float PIX_AA_GAMMA; + float PIX_AA_SUBPX; + float PIX_AA_SUBPX_BGR; +} +param; + +layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; } +global; + +#pragma stage vertex +layout(location = 0) in vec4 Position; +layout(location = 1) in vec2 TexCoord; +layout(location = 0) out vec2 vTexCoord; +layout(location = 1) out vec2 pix_coord; + +void main() { + gl_Position = global.MVP * Position; + vTexCoord = TexCoord; + pix_coord = vTexCoord * param.SourceSize.xy; +} + +#pragma stage fragment +layout(location = 0) in vec2 vTexCoord; +layout(location = 1) in vec2 pix_coord; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 2) uniform sampler2D Source; + +#include "shared.slang" + +void main() { + if (param.PIX_AA_SUBPX < 0.5) { + FragColor = + vec4(sample_aa(Source, pix_coord, param.SourceSize.zw, + param.PIX_AA_GAMMA > 0.5, param.PIX_AA_SHARP), + 1.0); + } else { + // Subpixel sampling: Shift the sampling by 1/3rd of an output pixel, + // assuming that the output size is at monitor resolution. + for (int i = -1; i < 2; ++i) { + const vec2 subpix_coord = + pix_coord + + vec2((param.PIX_AA_SUBPX_BGR < 0.5 ? i : -i) / 3.0, 0.0) * + param.OutputSize.zw * param.SourceSize.xy; + // With subpixel sampling, the sampling area is effectively reduced + // to a third. + const vec2 tx_size = get_texel_size(subpix_coord) / vec2(3.0, 1.0); + FragColor[i + 1] = sample_aa( + Source, subpix_coord, param.SourceSize.zw, + param.PIX_AA_GAMMA > 0.5, param.PIX_AA_SHARP, tx_size)[i + 1]; + } + FragColor[3] = 1.0; + } +} diff --git a/interpolation/shaders/pixel_aa.slang b/interpolation/shaders/pixel_aa/shared.slang similarity index 51% rename from interpolation/shaders/pixel_aa.slang rename to interpolation/shaders/pixel_aa/shared.slang index 257d565..4074c04 100644 --- a/interpolation/shaders/pixel_aa.slang +++ b/interpolation/shaders/pixel_aa/shared.slang @@ -1,32 +1,5 @@ -#version 450 - -/* - Pixel AA v1.1 by fishku - Copyright (C) 2023 - Public domain license (CC0) - - Features: - - Sharp upscaling with anti-aliasing - - Subpixel upscaling - - Sharpness can be controlled - - Gamma correct blending - - Integer scales result in pixel-perfect scaling - - Can use bilinear filtering for max. performance - - Inspired by: - https://www.shadertoy.com/view/MlB3D3 - by d7samurai - and: - https://www.youtube.com/watch?v=d6tp43wZqps - by t3ssel8r - - With sharpness = 1.0, using the same gamma-correct blending, and disabling - subpixel anti-aliasing, results are identical to the "pixellate" shader. - - Changelog: - v1.1: Better subpixel sampling. - v1.0: Initial release. -*/ +// This file has to be included at the frag shader stage so that fwidth() is +// defined. // clang-format off #pragma parameter PIX_AA_SETTINGS "=== Pixel AA v1.1 settings ===" 0.0 0.0 1.0 1.0 @@ -36,37 +9,6 @@ #pragma parameter PIX_AA_SUBPX_BGR "Use BGR subpx. instead of RGB" 0.0 0.0 1.0 1.0 // clang-format on -layout(push_constant) uniform Push { - vec4 SourceSize; - vec4 OutputSize; - float PIX_AA_SHARP; - float PIX_AA_GAMMA; - float PIX_AA_SUBPX; - float PIX_AA_SUBPX_BGR; -} -param; - -layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; } -global; - -#pragma stage vertex -layout(location = 0) in vec4 Position; -layout(location = 1) in vec2 TexCoord; -layout(location = 0) out vec2 vTexCoord; -layout(location = 1) out vec2 pix_coord; - -void main() { - gl_Position = global.MVP * Position; - vTexCoord = TexCoord; - pix_coord = vTexCoord * param.SourceSize.xy; -} - -#pragma stage fragment -layout(location = 0) in vec2 vTexCoord; -layout(location = 1) in vec2 pix_coord; -layout(location = 0) out vec4 FragColor; -layout(set = 0, binding = 2) uniform sampler2D Source; - // Similar to smoothstep, but has a configurable slope at x = 0.5. // Original smoothstep has a slope of 1.5 at x = 0.5 #define INSTANTIATE_SLOPESTEP(T) \ @@ -120,28 +62,3 @@ vec3 sample_aa(sampler2D tex, vec2 pix_coord, vec2 px_size_uv, return sample_aa(tex, pix_coord, px_size_uv, gamma_correct, sharpness, tx_size); } - -void main() { - if (param.PIX_AA_SUBPX < 0.5) { - FragColor = - vec4(sample_aa(Source, pix_coord, param.SourceSize.zw, - param.PIX_AA_GAMMA > 0.5, param.PIX_AA_SHARP), - 1.0); - } else { - // Subpixel sampling: Shift the sampling by 1/3rd of an output pixel, - // assuming that the output size is at monitor resolution. - for (int i = -1; i < 2; ++i) { - const vec2 subpix_coord = - pix_coord + - vec2((param.PIX_AA_SUBPX_BGR < 0.5 ? i : -i) / 3.0, 0.0) * - param.OutputSize.zw * param.SourceSize.xy; - // With subpixel sampling, the sampling area is effectively reduced - // to a third. - const vec2 tx_size = get_texel_size(subpix_coord) / vec2(3.0, 1.0); - FragColor[i + 1] = sample_aa( - Source, subpix_coord, param.SourceSize.zw, - param.PIX_AA_GAMMA > 0.5, param.PIX_AA_SHARP, tx_size)[i + 1]; - } - FragColor[3] = 1.0; - } -}