@@ -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
509547mat4 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