@@ -28,8 +28,22 @@ layout(location = 0) out VSOutput
2828 #else
2929 flat vec4 c;
3030 #endif
31+ #if VS_ROUND_UV
32+ flat uvec4 rounduv;
33+ #endif
3134} vsOut;
3235
36+ uvec4 extract_round_uv_bits(float q)
37+ {
38+ uint qi = floatBitsToUint(q);
39+ return uvec4 (
40+ (qi >> 0 ) & 0xFFF, // Prim left
41+ (qi >> 12 ) & 0xFFF, // Prim top
42+ (qi >> 24 ) & 0xF, // Round U flags
43+ (qi >> 28 ) & 0xF // Round V flags
44+ );
45+ }
46+
3347#if VS_EXPAND == 0
3448
3549layout (location = 0 ) in vec2 a_st;
@@ -84,6 +98,10 @@ void main()
8498
8599 vsOut.c = vec4 (a_c);
86100 vsOut.t.z = a_f.r;
101+
102+ #if VS_ROUND_UV
103+ vsOut.rounduv = extract_round_uv_bits(a_q);
104+ #endif
87105}
88106
89107#else // VS_EXPAND
@@ -109,6 +127,9 @@ struct ProcessedVertex
109127 vec4 t;
110128 vec4 ti;
111129 vec4 c;
130+ #if VS_ROUND_UV
131+ uvec4 rounduv;
132+ #endif
112133};
113134
114135ProcessedVertex load_vertex(uint index)
@@ -153,6 +174,10 @@ ProcessedVertex load_vertex(uint index)
153174 vtx.c = a_c;
154175 vtx.t.z = a_f.r;
155176
177+ #if VS_ROUND_UV
178+ vtx.rounduv = extract_round_uv_bits(a_q);
179+ #endif
180+
156181 return vtx;
157182}
158183
@@ -217,6 +242,9 @@ void main()
217242 vsOut.t = vtx.t;
218243 vsOut.ti = vtx.ti;
219244 vsOut.c = vtx.c;
245+ #if VS_ROUND_UV
246+ vsOut.rounduv = vtx.rounduv;
247+ #endif
220248}
221249
222250#endif // VS_EXPAND
@@ -291,6 +319,7 @@ void main()
291319#define PS_ZFLOOR 0
292320#define PS_FEEDBACK_LOOP 0
293321#define PS_TEX_IS_FB 0
322+ #define PS_ROUND_UV 0
294323#endif
295324
296325#define SW_BLEND (PS_BLEND_A || PS_BLEND_B || PS_BLEND_D)
@@ -333,6 +362,9 @@ layout(location = 0) in VSOutput
333362 #else
334363 flat vec4 c;
335364 #endif
365+ #if PS_ROUND_UV
366+ flat uvec4 rounduv;
367+ #endif
336368} vsIn;
337369
338370#if ! PS_NO_COLOR && ! PS_NO_COLOR1
@@ -503,6 +535,35 @@ vec4 clamp_wrap_uv(vec4 uv)
503535 return uv;
504536}
505537
538+ vec4 round_uv()
539+ {
540+ #if PS_ROUND_UV
541+ // Whether we are at the top or left of the prim.
542+ ivec2 topleft = ivec2 (equal (ivec2 (gl_FragCoord .xy), ivec2 (vsIn.rounduv.xy)));
543+
544+ // Extract flags for whether to round U, V.
545+ ivec2 round_flags = ivec2 (vsIn.rounduv.zw);
546+
547+ // Being on the top or left pixels converts round down to round up.
548+ ivec2 round_down = ivec2 (equal (round_flags, ivec2 (PS_ROUND_UV_DOWN))) & ~ topleft;
549+ ivec2 round_up = ivec2 (equal (round_flags, ivec2 (PS_ROUND_UV_UP))) |
550+ (ivec2 (equal (round_flags, ivec2 (PS_ROUND_UV_DOWN))) & topleft);
551+
552+ vec2 uv = vsIn.ti.zw; // Unnormalized UVs.
553+ vec2 uvi = round(vsIn.ti.zw / 8 .0f) * 8 .0f; // Nearest half texel.
554+
555+ ivec2 close = ivec2 (lessThan (abs (uv - uvi), vec2 (PS_ROUND_UV_THRESHOLD)));
556+
557+ // Round only if close to a half texel.
558+ uv = mix (uv, uvi - PS_ROUND_UV_THRESHOLD, bvec2 (close & round_down));
559+ uv = mix (uv, uvi + PS_ROUND_UV_THRESHOLD, bvec2 (close & round_up));
560+
561+ return vec4 (uv / 16 .0f / WH.xy, uv); // Return normalized and unnormalized coords.
562+ #else
563+ return vec4 (0 .0f);
564+ #endif
565+ }
566+
506567mat4 sample_4c(vec4 uv)
507568{
508569 mat4 c;
@@ -923,6 +984,10 @@ vec4 ps_color()
923984#if PS_FST == 0
924985 vec2 st = vsIn.t.xy / vsIn.t.w;
925986 vec2 st_int = vsIn.ti.zw / vsIn.t.w;
987+ #elif PS_ROUND_UV != 0
988+ vec4 ti_rounded = round_uv();
989+ vec2 st = ti_rounded.xy;
990+ vec2 st_int = ti_rounded.zw;
926991#else
927992 vec2 st = vsIn.ti.xy;
928993 vec2 st_int = vsIn.ti.zw;
0 commit comments