Skip to content

Commit dcd2d53

Browse files
committed
GS: Accurate UV rounding for axis-aligned prims.
1 parent 9321288 commit dcd2d53

31 files changed

+1175
-35
lines changed

bin/resources/shaders/dx11/tfx.fx

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#define VS_IIP 0
1414
#define VS_TME 1
1515
#define VS_FST 1
16+
#define VS_ROUND_UV 0
1617
#endif
1718

1819
#ifndef GS_IIP
@@ -104,6 +105,7 @@
104105
#define PS_TEX_IS_FB 0
105106
#define PS_COLOR_FEEDBACK 0
106107
#define PS_DEPTH_FEEDBACK 0
108+
#define PS_ROUND_UV 0
107109
#endif
108110

109111
#define SW_BLEND (PS_BLEND_A || PS_BLEND_B || PS_BLEND_D)
@@ -134,6 +136,10 @@ struct VS_OUTPUT
134136
#else
135137
nointerpolation float4 c : COLOR0;
136138
#endif
139+
140+
#if VS_ROUND_UV != 0
141+
nointerpolation uint4 rounduv : TEXCOORD3;
142+
#endif
137143
};
138144

139145
struct PS_INPUT
@@ -146,6 +152,9 @@ struct PS_INPUT
146152
#else
147153
nointerpolation float4 c : COLOR0;
148154
#endif
155+
#if PS_ROUND_UV != 0
156+
nointerpolation uint4 rounduv : TEXCOORD3;
157+
#endif
149158
#if (PS_DATE >= 1 && PS_DATE <= 3) || GS_FORWARD_PRIMID
150159
uint primid : SV_PrimitiveID;
151160
#endif
@@ -361,6 +370,37 @@ float4 clamp_wrap_uv(float4 uv)
361370
return uv;
362371
}
363372

373+
float4 round_uv(PS_INPUT input)
374+
{
375+
#if PS_ROUND_UV
376+
// Check if we're at the prim top or left.
377+
int2 topleft = int2(int2(input.p.xy) == int2(input.rounduv.xy));
378+
379+
// Get flags for whether to round U, V.
380+
int2 round_flags = int2(input.rounduv.zw);
381+
382+
// Being on the top or left pixels converts round down to round up.
383+
int2 round_down = int2(round_flags == PS_ROUND_UV_DOWN) & ~topleft;
384+
int2 round_up = int2(round_flags == PS_ROUND_UV_UP) |
385+
(int2(round_flags == PS_ROUND_UV_DOWN) & topleft);
386+
387+
float2 uv = input.ti.zw; // Unnormalized UVs.
388+
float2 uvi = round(input.ti.zw / 8.0f) * 8.0f; // Nearest half texel.
389+
390+
// Round only if close to a half texel.
391+
int2 close = int2(abs(uv - uvi) <= PS_ROUND_UV_THRESHOLD);
392+
round_down &= close;
393+
round_up &= close;
394+
395+
uv = bool2(round_down) ? uvi - PS_ROUND_UV_THRESHOLD : uv;
396+
uv = bool2(round_up) ? uvi + PS_ROUND_UV_THRESHOLD : uv;
397+
398+
return float4(uv / 16.0f / WH.xy, uv); // Return normalized and unnormalized coords.
399+
#else
400+
return float4(0.0f, 0.0f, 0.0f, 0.0f);
401+
#endif
402+
}
403+
364404
float4x4 sample_4c(float4 uv, float uv_w, int2 xy)
365405
{
366406
float4x4 c;
@@ -795,6 +835,10 @@ float4 ps_color(PS_INPUT input)
795835
#if PS_FST == 0
796836
float2 st = input.t.xy / input.t.w;
797837
float2 st_int = input.ti.zw / input.t.w;
838+
#elif PS_ROUND_UV != 0
839+
float4 ti_rounded = round_uv(input);
840+
float2 st = ti_rounded.xy;
841+
float2 st_int = ti_rounded.zw;
798842
#else
799843
float2 st = input.ti.xy;
800844
float2 st_int = input.ti.zw;
@@ -1317,6 +1361,17 @@ cbuffer cb0
13171361
uint BaseVertex; // Only used in DX11.
13181362
};
13191363

1364+
uint4 extract_round_uv_bits(float q)
1365+
{
1366+
uint qi = asuint(q);
1367+
return uint4(
1368+
(qi >> 0) & 0xFFF, // Prim left
1369+
(qi >> 12) & 0xFFF, // Prim top
1370+
(qi >> 24) & 0xF, // Round U flags
1371+
(qi >> 28) & 0xF // Round V flags
1372+
);
1373+
}
1374+
13201375
VS_OUTPUT vs_main(VS_INPUT input)
13211376
{
13221377
// Clamp to max depth, gs doesn't wrap
@@ -1336,7 +1391,11 @@ VS_OUTPUT vs_main(VS_INPUT input)
13361391

13371392
if(VS_TME)
13381393
{
1339-
float2 uv = input.uv - TextureOffset;
1394+
#if VS_ROUND_UV == 0
1395+
float2 uv = input.uv - TextureOffset;
1396+
#else
1397+
float2 uv = input.st - TextureOffset;
1398+
#endif
13401399
float2 st = input.st - TextureOffset;
13411400

13421401
// Integer nomalized
@@ -1355,12 +1414,21 @@ VS_OUTPUT vs_main(VS_INPUT input)
13551414
// Float coords
13561415
output.t.xy = st;
13571416
output.t.w = input.q;
1417+
1418+
// Get UV rounding info saved in Q.
1419+
#if VS_ROUND_UV
1420+
output.rounduv = extract_round_uv_bits(input.q);
1421+
output.t.w = 1.0f;
1422+
#endif
13581423
}
13591424
else
13601425
{
13611426
output.t.xy = 0;
13621427
output.t.w = 1.0f;
13631428
output.ti = 0;
1429+
#if VS_ROUND_UV
1430+
output.rounduv = 0;
1431+
#endif
13641432
}
13651433

13661434
output.c = input.c;

bin/resources/shaders/opengl/tfx_fs.glsl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ in SHADER
9595
#else
9696
flat vec4 c;
9797
#endif
98+
99+
#if PS_ROUND_UV != 0
100+
flat uvec4 rounduv;
101+
#endif
98102
} PSin;
99103

100104
#define TARGET_0_QUALIFIER out
@@ -302,6 +306,37 @@ vec4 clamp_wrap_uv(vec4 uv)
302306
return uv_out;
303307
}
304308

309+
vec4 round_uv()
310+
{
311+
#if PS_ROUND_UV
312+
// Check if we're at the prim top or left.
313+
ivec2 topleft = ivec2(equal(ivec2(gl_FragCoord.xy), ivec2(PSin.rounduv.xy)));
314+
315+
// Get flags for whether to round U, V.
316+
ivec2 round_flags = ivec2(PSin.rounduv.zw);
317+
318+
// Being on the top or left pixels converts round down to round up.
319+
ivec2 round_down = ivec2(equal(round_flags, ivec2(PS_ROUND_UV_DOWN))) & ~topleft;
320+
ivec2 round_up = ivec2(equal(round_flags, ivec2(PS_ROUND_UV_UP))) |
321+
(ivec2(equal(round_flags, ivec2(PS_ROUND_UV_DOWN))) & topleft);
322+
323+
vec2 uv = PSin.t_int.zw; // Unnormalized UVs.
324+
vec2 uvi = round(PSin.t_int.zw / 8.0f) * 8.0f; // Nearest half texel.
325+
326+
// Round only if close to a half texel.
327+
ivec2 close = ivec2(lessThanEqual(abs(uv - uvi), vec2(PS_ROUND_UV_THRESHOLD)));
328+
round_down &= close;
329+
round_up &= close;
330+
331+
uv = mix(uv, uvi - vec2(PS_ROUND_UV_THRESHOLD), bvec2(round_down));
332+
uv = mix(uv, uvi + vec2(PS_ROUND_UV_THRESHOLD), bvec2(round_up));
333+
334+
return vec4(uv / 16.0f / WH.xy, uv); // Return normalized and unnormalized coords.
335+
#else
336+
return vec4(0.0f);
337+
#endif
338+
}
339+
305340
mat4 sample_4c(vec4 uv)
306341
{
307342
mat4 c;
@@ -720,6 +755,10 @@ vec4 ps_color()
720755
#if (PS_FST == 0)
721756
vec2 st = PSin.t_float.xy / vec2(PSin.t_float.w);
722757
vec2 st_int = PSin.t_int.zw / vec2(PSin.t_float.w);
758+
#elif PS_ROUND_UV != 0
759+
vec4 t_int_rounded = round_uv();
760+
vec2 st = t_int_rounded.xy;
761+
vec2 st_int = t_int_rounded.zw;
723762
#else
724763
// Note xy are normalized coordinate
725764
vec2 st = PSin.t_int.xy;

bin/resources/shaders/opengl/tfx_vgs.glsl

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,24 @@ out SHADER
2727
#else
2828
flat vec4 c;
2929
#endif
30+
#if VS_ROUND_UV != 0
31+
flat uvec4 rounduv;
32+
#endif
3033
} VSout;
3134

3235
const float exp_min32 = exp2(-32.0f);
3336

37+
uvec4 extract_round_uv_bits(float q)
38+
{
39+
uint qi = floatBitsToUint(q);
40+
return uvec4(
41+
(qi >> 0) & 0xFFF, // Prim left
42+
(qi >> 12) & 0xFFF, // Prim top
43+
(qi >> 24) & 0xF, // Round U flags
44+
(qi >> 28) & 0xF // Round V flags
45+
);
46+
}
47+
3448
#if VS_EXPAND == 0
3549

3650
layout(location = 0) in vec2 i_st;
@@ -43,7 +57,11 @@ layout(location = 7) in vec4 i_f;
4357

4458
void texture_coord()
4559
{
46-
vec2 uv = vec2(i_uv) - TextureOffset;
60+
#if VS_ROUND_UV == 0
61+
vec2 uv = vec2(i_uv) - TextureOffset;
62+
#else
63+
vec2 uv = i_st - TextureOffset;
64+
#endif
4765
vec2 st = i_st - TextureOffset;
4866

4967
// Float coordinate
@@ -59,6 +77,12 @@ void texture_coord()
5977
// Some games uses float coordinate for post-processing effect
6078
VSout.t_int.zw = st / TextureScale;
6179
#endif
80+
81+
// Get UV rounding info saved in Q.
82+
#if VS_ROUND_UV
83+
VSout.rounduv = extract_round_uv_bits(i_q);
84+
VSout.t_float.w = 1.0f;
85+
#endif
6286
}
6387

6488
void vs_main()
@@ -78,7 +102,7 @@ void vs_main()
78102
texture_coord();
79103

80104
VSout.c = i_c;
81-
VSout.t_float.z = i_f.x; // pack for with texture
105+
VSout.t_float.z = i_f.x; // pack fog with texture
82106

83107
#if VS_POINT_SIZE
84108
gl_PointSize = PointSize.x;
@@ -108,6 +132,9 @@ struct ProcessedVertex
108132
vec4 t_float;
109133
vec4 t_int;
110134
vec4 c;
135+
#if VS_ROUND_UV
136+
uvec4 rounduv;
137+
#endif
111138
};
112139

113140
ProcessedVertex load_vertex(uint index)
@@ -131,7 +158,11 @@ ProcessedVertex load_vertex(uint index)
131158
vtx.p.z = float(z) * exp_min32;
132159
vtx.p.w = 1.0f;
133160

134-
vec2 uv = vec2(i_uv) - TextureOffset;
161+
#if VS_ROUND_UV == 0
162+
vec2 uv = vec2(i_uv) - TextureOffset;
163+
#else
164+
vec2 uv = i_st - TextureOffset;
165+
#endif
135166
vec2 st = i_st - TextureOffset;
136167

137168
vtx.t_float.xy = st;
@@ -144,6 +175,12 @@ ProcessedVertex load_vertex(uint index)
144175
vtx.t_int.zw = st / TextureScale;
145176
#endif
146177

178+
// Get UV rounding info saved in Q.
179+
#if VS_ROUND_UV
180+
vtx.rounduv = extract_round_uv_bits(i_q);
181+
vtx.t_float.w = 1.0f;
182+
#endif
183+
147184
vtx.c = i_c;
148185
vtx.t_float.z = i_f.x;
149186

@@ -210,6 +247,9 @@ void main()
210247
VSout.t_float = vtx.t_float;
211248
VSout.t_int = vtx.t_int;
212249
VSout.c = vtx.c;
250+
#if VS_ROUND_UV
251+
VSout.rounduv = vtx.rounduv;
252+
#endif
213253
}
214254

215255
#endif // VS_EXPAND

0 commit comments

Comments
 (0)