#version 450 /* Scale2xSFX by Sp00kyFox, 2015 Filter: Nearest Scale: 2x Scale2SFX improves upon the original Scale2x (aka EPX) by avoiding the occurence of artifacts. */ layout(push_constant) uniform Push { vec4 SourceSize; vec4 OriginalSize; vec4 OutputSize; uint FrameCount; float YTR; float UTR; float VTR; } params; #pragma parameter YTR "SCALE2xSFX Y Threshold" 48.0 0.0 255.0 1.0 #pragma parameter UTR "SCALE2xSFX U Threshold" 7.0 0.0 255.0 1.0 #pragma parameter VTR "SCALE2xSFX V Threshold" 6.0 0.0 255.0 1.0 #define YTR params.YTR #define UTR params.UTR #define VTR params.VTR layout(std140, set = 0, binding = 0) uniform UBO { mat4 MVP; } global; #define saturate(c) clamp(c, 0.0, 1.0) #define lerp(a,b,c) mix(a,b,c) #define mul(a,b) (b*a) #define fmod(c,d) mod(c,d) #define frac(c) fract(c) #define tex2D(c,d) texture(c,d) #define float2 vec2 #define float3 vec3 #define float4 vec4 #define int2 ivec2 #define int3 ivec3 #define int4 ivec4 #define bool2 bvec2 #define bool3 bvec3 #define bool4 bvec4 #define float2x2 mat2x2 #define float3x3 mat3x3 #define float4x4 mat4x4 #define float4x3 mat4x3 #define decal Source const float3x3 YUV = float3x3(0.299, -0.168736, 0.5, 0.587, -0.331264, -0.418688, 0.114, 0.5, -0.081312); // transponed float3 thresh = float3(YTR, UTR, VTR)/255.0; bool eq(float3 A, float3 B){ return (A==B); } bool neq(float3 A, float3 B){ return (A!=B); } #pragma stage vertex layout(location = 0) in vec4 Position; layout(location = 1) in vec2 TexCoord; layout(location = 0) out vec2 texCoord; layout(location = 1) out vec4 t1; layout(location = 2) out vec4 t2; layout(location = 3) out vec4 t3; layout(location = 4) out vec4 t4; layout(location = 5) out vec4 t5; void main() { gl_Position = global.MVP * Position; texCoord = TexCoord; float2 ps = float2(params.SourceSize.z, params.SourceSize.w); float dx = ps.x; float dy = ps.y; t1 = texCoord.xxxy + float4(-dx, 0, dx,-dy); // A, B, C t2 = texCoord.xxxy + float4(-dx, 0, dx, 0); // D, E, F t3 = texCoord.xxxy + float4(-dx, 0, dx, dy); // G, H, I t4 = texCoord.xyxy + float4( 0,-2*dy,-2*dx, 0); // J, K t5 = texCoord.xyxy + float4( 2*dx, 0, 0, 2*dy); // L, M } #pragma stage fragment layout(location = 0) in vec2 texCoord; layout(location = 1) in vec4 t1; layout(location = 2) in vec4 t2; layout(location = 3) in vec4 t3; layout(location = 4) in vec4 t4; layout(location = 5) in vec4 t5; layout(location = 0) out vec4 FragColor; layout(set = 0, binding = 2) uniform sampler2D Source; void main() { // subpixel determination float2 fp = floor(2.0 * frac(texCoord*params.SourceSize.xy)); /* J A B C E0 E1 K D E F L E2 E3 G H I M */ // reading the texels & colorspace conversion float3 b = tex2D(decal, t1.yw).xyz; float3 d = tex2D(decal, t2.xw).xyz; float3 e = tex2D(decal, t2.yw).xyz; float3 f = tex2D(decal, t2.zw).xyz; float3 h = tex2D(decal, t3.yw).xyz; float4x3 tmp = mul(float4x3(b,d,e,f), YUV); float3 B = tmp[0], D = tmp[1], E = tmp[2], F = tmp[3], H = mul(h, YUV); float3 A = tex2D(decal, t1.xw).xyz; float3 C = tex2D(decal, t1.zw).xyz; float3 G = tex2D(decal, t3.xw).xyz; float3 I = tex2D(decal, t3.zw).xyz; tmp = mul(float4x3(A,C,G,I), YUV); A = tmp[0], C = tmp[1], G = tmp[2], I = tmp[3]; float3 J = tex2D(decal, t4.xy).xyz; float3 K = tex2D(decal, t4.zw).xyz; float3 L = tex2D(decal, t5.xy).xyz; float3 M = tex2D(decal, t5.zw).xyz; tmp = mul(float4x3(J,K,L,M), YUV); J = tmp[0], K = tmp[1], L = tmp[2], M = tmp[3]; // parent condition bool par0 = neq(B,F) && neq(D,H); bool par1 = neq(B,D) && neq(F,H); // equality checks bool AE = eq(A,E), CE = eq(C,E), EG = eq(E,G), EI = eq(E,I); // artifact prevention bool art0 = CE || EG; bool art1 = AE || EI; // rules float3 E0 = eq(B,D) && par0 && (!AE || art0 || eq(A,J) || eq(A,K)) ? 0.5*(b+d) : e; float3 E1 = eq(B,F) && par1 && (!CE || art1 || eq(C,J) || eq(C,L)) ? 0.5*(b+f) : e; float3 E2 = eq(D,H) && par1 && (!EG || art1 || eq(G,K) || eq(G,M)) ? 0.5*(h+d) : e; float3 E3 = eq(F,H) && par0 && (!EI || art0 || eq(I,L) || eq(I,M)) ? 0.5*(h+f) : e; // subpixel output FragColor = vec4(fp.y == 0 ? (fp.x == 0 ? E0 : E1) : (fp.x == 0 ? E2 : E3), 1.0); }