Skip to content

Commit 28df942

Browse files
committed
GS:HW: Shader based sprite round/clamp/align for upscaling.
1 parent 600b038 commit 28df942

26 files changed

Lines changed: 1682 additions & 390 deletions

bin/resources/shaders/dx11/tfx.fx

Lines changed: 382 additions & 52 deletions
Large diffs are not rendered by default.

bin/resources/shaders/opengl/tfx_fs.glsl

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ layout(std140, binding = 0) uniform cb21
9999

100100
float ScaledScaleFactor;
101101
float RcpScaleFactor;
102-
float _pad0_cb1;
103-
float _pad1_cb1;
102+
float ScaleRT_PS;
103+
float ScaleTex_PS;
104104

105105
float LineCovScale;
106106
float _pad2_cb1;
@@ -122,9 +122,15 @@ in SHADER
122122
float inv_cov; // We use the inverse to make it simpler to interpolate.
123123
flat uint interior; // 1 for triangle interior; 0 for edge;
124124

125-
#if PS_ROUND_UV != 0
125+
#if PS_ROUND_UV || PS_CLAMP_UV || PS_ALIGN_UV
126126
flat uvec4 rounduv;
127127
#endif
128+
#if PS_ROUND_UV
129+
flat vec4 scaleuv;
130+
#endif
131+
#if PS_CLAMP_UV
132+
flat vec4 clampuv;
133+
#endif
128134
} PSin;
129135

130136
#define TARGET_0_QUALIFIER out
@@ -469,41 +475,73 @@ vec4 clamp_wrap_uv(vec4 uv)
469475
return uv_out;
470476
}
471477

472-
vec4 round_uv()
478+
vec4 round_and_clamp_uv()
473479
{
474480
#if PS_ROUND_UV
475481
// Check if we're at the prim top or left.
476-
ivec2 topleft = ivec2(equal(ivec2(gl_FragCoord.xy), ivec2(PSin.rounduv.xy)));
482+
ivec2 native_xy_i = ivec2(gl_FragCoord.xy) / int(ScaleRT_PS);
483+
ivec2 topleft = ivec2(equal(native_xy_i, ivec2(PSin.rounduv.xy)));
477484

478-
// Get flags for whether to round U, V.
479-
ivec2 round_flags = ivec2(PSin.rounduv.zw);
485+
// Extract flags for whether to round U, V.
486+
ivec2 round_per_pixel = ivec2(PSin.rounduv.zw) & ivec2(ROUND_UV_PER_PIXEL);
487+
ivec2 round_flags = ivec2(PSin.rounduv.zw) & ivec2(ROUND_UV_UP | ROUND_UV_DOWN);
488+
round_flags = ivec2(mix(ivec2(0), round_flags, bvec2(round_per_pixel)));
480489

481490
// Being on the top or left pixels converts round down to round up.
482491
ivec2 round_down = ivec2(bvec2(round_flags & ivec2(ROUND_UV_DOWN))) & ~topleft;
483-
ivec2 round_up = ivec2(bvec2(round_flags & ivec2(ROUND_UV_UP))) |
484-
(ivec2(bvec2(round_flags & ivec2(ROUND_UV_DOWN))) & topleft);
492+
ivec2 round_down_tl = ivec2(bvec2(round_flags & ivec2(ROUND_UV_DOWN))) & topleft;
493+
ivec2 round_up = ivec2(bvec2(round_flags & ivec2(ROUND_UV_DOWN)));
494+
#endif
485495

486496
vec2 uv = PSin.t_int.zw; // Unnormalized UVs.
487-
vec2 uvi = round(PSin.t_int.zw / 8.0f) * 8.0f; // Nearest half texel.
497+
498+
#if PS_ROUND_UV == PS_ROUND_UV_NEAREST
499+
// Find the equivalent native UV.
500+
vec2 native_xy = vec2(native_xy_i) + 0.5f;
501+
vec2 upscale_xy = gl_FragCoord.xy / ScaleRT_PS;
502+
vec2 upscale_offset = upscale_xy - native_xy;
503+
vec2 native_uv = uv - 16.0f * PSin.scaleuv.xy * upscale_offset;
504+
uv = native_uv;
505+
#endif
506+
507+
#if PS_ROUND_UV
508+
vec2 uvi = round(uv / 16.0f) * 16.0f; // Nearest texel.
509+
#endif
488510

489-
// Round only if close to a half texel.
511+
#if PS_ROUND_UV == PS_ROUND_UV_NEAREST
512+
// Round only if close to a texel boundary.
490513
ivec2 close = ivec2(lessThanEqual(abs(uv - uvi), vec2(ROUND_UV_THRESHOLD)));
491514
round_down &= close;
515+
round_down_tl &= close;
492516
round_up &= close;
517+
#endif
493518

494-
uv = mix(uv, uvi - vec2(ROUND_UV_THRESHOLD), bvec2(round_down));
495-
uv = mix(uv, uvi + vec2(ROUND_UV_THRESHOLD), bvec2(round_up));
519+
#if PS_ROUND_UV == PS_ROUND_UV_LINEAR
520+
// Bilinear: round to 1/16 texel.
521+
uv = mix(uv, uv - vec2(ROUND_UV_THRESHOLD), bvec2(round_down));
522+
uv = mix(uv, uv + vec2(ROUND_UV_THRESHOLD), bvec2(round_up));
523+
uv = floor(uv);
524+
#elif PS_ROUND_UV == PS_ROUND_UV_NEAREST
525+
// Nearest: get the center of the texel we would sample from at native.
526+
uv = mix(uv, uvi - vec2(8.0f), bvec2(round_down));
527+
uv = mix(uv, uvi + vec2(8.0f), bvec2(round_up));
528+
uv = mix(uv, floor(uv / 16.0f) * 16.0f + 8.0f, bvec2(1 & ~(round_down | round_up)));
496529

497-
uv = mix(uv, uv.yx, bvec2(round_flags & ivec2(ROUND_UV_SWAP)));
530+
// Then offset UV based on the XY offset from native texel center.
531+
uv += 16.0f * sign(PSin.scaleuv.xy) * upscale_offset;
532+
uv = mix(uv, uvi + vec2(8.0f / ScaleTex_PS), bvec2(round_down_tl));
533+
uv += vec2(ROUND_UV_THRESHOLD);
534+
#endif
498535

499-
#if PS_ROUND_UV == PS_ROUND_UV_LINEAR
500-
uv = trunc(uv); // Truncate to closest subtexel for bilinear.
536+
#if PS_CLAMP_UV
537+
uv = clamp(uv, PSin.clampuv.xy, PSin.clampuv.zw);
501538
#endif
502539

503-
return vec4(uv / 16.0f / WH.xy, uv); // Return normalized and unnormalized coords.
504-
#else
505-
return vec4(0.0f);
540+
#if PS_ROUND_UV || PS_CLAMP_UV || PS_ALIGN_UV
541+
uv = mix(uv, uv.yx, bvec2(PSin.rounduv.zw & uvec2(ROUND_UV_SWAP)));
506542
#endif
543+
544+
return vec4(uv / 16.0f / WH.xy, uv); // Return normalized and unnormalized coords.
507545
}
508546

509547
mat4 sample_4c(vec4 uv)
@@ -924,8 +962,8 @@ vec4 ps_color()
924962
#if (PS_FST == 0)
925963
vec2 st = PSin.t_float.xy / vec2(PSin.t_float.w);
926964
vec2 st_int = PSin.t_int.zw / vec2(PSin.t_float.w);
927-
#elif PS_ROUND_UV != 0
928-
vec4 t_int_rounded = round_uv();
965+
#elif PS_ROUND_UV || PS_CLAMP_UV || PS_ALIGN_UV
966+
vec4 t_int_rounded = round_and_clamp_uv();
929967
vec2 st = t_int_rounded.xy;
930968
vec2 st_int = t_int_rounded.zw;
931969
#else

0 commit comments

Comments
 (0)