mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
GS/HW: Add AA1 triangle corner caps and edge extrapolation.
This commit is contained in:
@@ -1594,6 +1594,110 @@ VS_INPUT load_vertex(uint index)
|
||||
return vert;
|
||||
}
|
||||
|
||||
// Convert XY from NDC to GS pixel coordinates (i.e. 1.0 = 1 GS pixel).
|
||||
float2 get_xy_unscaled(float2 xy)
|
||||
{
|
||||
return round(xy / VertexScale) / 16.0f;
|
||||
}
|
||||
|
||||
// Get the XY deltas in GS pixel coordinates, using first vertex as the origin.
|
||||
float2x2 get_xy_deltas_unscaled(VS_OUTPUT v0, VS_OUTPUT v1, VS_OUTPUT v2)
|
||||
{
|
||||
float2 xy0 = get_xy_unscaled(v0.p.xy);
|
||||
float2 xy1 = get_xy_unscaled(v1.p.xy);
|
||||
float2 xy2 = get_xy_unscaled(v2.p.xy);
|
||||
return float2x2(xy1 - xy0, xy2 - xy0);
|
||||
}
|
||||
|
||||
// Get the AA1 outward expand direction to the edge formed by the first two vertices.
|
||||
// This is up or down for shallow (X dominant) edges, and right or left for steep (Y dominant) edges.
|
||||
// Similar expansion to line AA1 except instead of expanding on both sides of the line,
|
||||
// expand on on the side towards the outside of the triangle.
|
||||
float2 get_aa1_triangle_expand_dir(VS_OUTPUT v0, VS_OUTPUT v1, VS_OUTPUT v2)
|
||||
{
|
||||
float2x2 xy_deltas = get_xy_deltas_unscaled(v0, v1, v2);
|
||||
float2 line_delta = xy_deltas[0];
|
||||
float2 line_opposite = xy_deltas[1];
|
||||
|
||||
float2 line_normal = float2(line_delta.y, -line_delta.x);
|
||||
float2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? float2(0.0f, 1.0f) : float2(1.0f, 0.0f);
|
||||
|
||||
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(line_opposite, line_normal) >= 0.0f))
|
||||
{
|
||||
// Expand direction point towards the interior so flip it.
|
||||
line_expand = -line_expand;
|
||||
}
|
||||
|
||||
return line_expand;
|
||||
}
|
||||
|
||||
float2x2 get_inverse(float2x2 mat, float det)
|
||||
{
|
||||
return float2x2(mat[1][1], -mat[0][1], -mat[1][0], mat[0][0]) * (1 / det);
|
||||
}
|
||||
|
||||
// Extrapolate triangle attributes from the first vertex along the given direction.
|
||||
// dp_mat is derived from the input vertices, it is passed in to avoid recomputing.
|
||||
void extrapolate_aa1_triangle_edge(inout VS_OUTPUT v0, VS_OUTPUT v1, VS_OUTPUT v2, float2x2 dp_mat, float2 dp)
|
||||
{
|
||||
// Get texture deltas
|
||||
#if VS_TME
|
||||
#if VS_FST
|
||||
float2x2 dt = float2x2(v1.ti.zw - v0.ti.zw, v2.ti.zw - v0.ti.zw);
|
||||
#else
|
||||
float2x2 dt = float2x2(v1.t.xy - v0.t.xy, v2.t.xy - v0.t.xy);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Get color delta if interpolating
|
||||
#if VS_IIP
|
||||
float2x4 dc = float2x4(v1.c - v0.c, v2.c - v0.c);
|
||||
#endif
|
||||
|
||||
float2 dz = float2(v1.p.z - v0.p.z, v2.p.z - v0.p.z); // Z deltas
|
||||
|
||||
float2 df = float2(v1.t.z - v0.t.z, v2.t.z - v0.t.z); // Fog deltas
|
||||
|
||||
float2 dq = float2(v1.t.w - v0.t.w, v2.t.w - v0.t.w); // Q deltas
|
||||
|
||||
// To prevent unstable extrapolation, do not extrapolate if the
|
||||
// minimum perpendicular length of the triangle is < 2 pixels.
|
||||
float dp_det = determinant(dp_mat); // Twice signed triangle area.
|
||||
float len0 = length(dp_mat[0]);
|
||||
float len1 = length(dp_mat[1]);
|
||||
float len2 = length(dp_mat[1] - dp_mat[0]);
|
||||
float min_perp_length = abs(dp_det) / max(max(len0, len1), len2);
|
||||
|
||||
// Get the position -> barycentric weight matrix
|
||||
float2x2 inv_dp_mat = get_inverse(dp_mat, dp_det);
|
||||
|
||||
float2 weights = min_perp_length < 2 ? 0 : mul(dp, inv_dp_mat);
|
||||
|
||||
v0.p.xy += dp * PointSize; // Extrapolate position
|
||||
|
||||
// Extrapolate texture coords
|
||||
#if VS_TME
|
||||
#if VS_FST
|
||||
v0.ti.zw += mul(weights, dt);
|
||||
v0.ti.xy = v0.ti.zw * TextureScale;
|
||||
#else
|
||||
v0.t.xy += mul(weights, dt);
|
||||
v0.ti.zw = v0.t.xy / TextureScale;
|
||||
v0.t.w += dot(weights, dq);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Extrapolate and clamp color
|
||||
#if VS_IIP
|
||||
v0.c += mul(weights, dc);
|
||||
v0.c = clamp(v0.c, 0, 255);
|
||||
#endif
|
||||
|
||||
v0.p.z += dot(weights, dz); // Extrapolate depth
|
||||
|
||||
v0.t.z += dot(weights, df); // Extrapolate fog
|
||||
}
|
||||
|
||||
VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
|
||||
{
|
||||
#if VS_EXPAND == VS_EXPAND_POINT
|
||||
@@ -1622,13 +1726,12 @@ VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
|
||||
|
||||
// Use bottom minus top for delta regardless of which vertex we are expanding.
|
||||
float2 line_delta = is_bottom ? (vtx.p.xy - other.p.xy) : (other.p.xy - vtx.p.xy);
|
||||
float2 line_vector = normalize(line_delta);
|
||||
float2 line_vector = normalize(line_delta / VertexScale);
|
||||
#if VS_EXPAND == VS_EXPAND_LINE
|
||||
float2 line_expand = float2(line_vector.y, -line_vector.x);
|
||||
#elif VS_EXPAND == VS_EXPAND_LINE_AA1
|
||||
// Expand in y direction for shallow lines and x direction for steep lines.
|
||||
line_delta /= VertexScale;
|
||||
float2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? float2(0.0f, 2.0f) : float2(2.0f, 0.0f);
|
||||
float2 line_expand = abs(line_vector.x) >= abs(line_vector.y) ? float2(0.0f, 2.0f) : float2(2.0f, 0.0f);
|
||||
#endif
|
||||
float2 line_width = (line_expand * PointSize) / 2;
|
||||
float2 offset = is_right ? line_width : -line_width;
|
||||
@@ -1674,10 +1777,14 @@ VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
|
||||
// - Vertices 3-8: First edge expanded (2 triangles).
|
||||
// - Vertices 9-14: Second edge expanded (2 triangles).
|
||||
// - Vertices 15-20: Third edge expanded (2 triangles).
|
||||
// - Vertices 21-26: First corner cap (2 triangles).
|
||||
// - Vertices 27-32: Second corner cap (2 triangles).
|
||||
// - Vertices 33-38: Third corner cap (2 triangles).
|
||||
|
||||
uint prim_id = vid / 21;
|
||||
uint prim_offset = vid - 21 * prim_id; // range: 0-20
|
||||
uint prim_id = vid / 39;
|
||||
uint prim_offset = vid - 39 * prim_id; // range: 0-38
|
||||
bool interior = prim_offset < 3;
|
||||
bool edge = 3 <= prim_offset && prim_offset < 21;
|
||||
|
||||
VS_OUTPUT vtx;
|
||||
if (interior)
|
||||
@@ -1686,7 +1793,7 @@ VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
|
||||
vtx.inv_cov = 0.0f; // Full coverage
|
||||
vtx.interior = 1;
|
||||
}
|
||||
else
|
||||
else if (edge)
|
||||
{
|
||||
// Vertex indices for this edge. We need all 3 for determining exterior/interior.
|
||||
uint prim_offset_edges = prim_offset - 3; // range: 0-17
|
||||
@@ -1695,39 +1802,67 @@ VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
|
||||
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
|
||||
uint edge_offset = prim_offset_edges - 6 * i0; // range: 0-5
|
||||
|
||||
// Note: order of top/bottom, inside/outside order is arbitrary,
|
||||
// Note: order of top/bottom, inside/outside is arbitrary,
|
||||
// as long as it assembles into two triangles forming a quad.
|
||||
bool is_bottom = (2 <= edge_offset) && (edge_offset <= 4);
|
||||
bool is_outside = edge_offset & 1;
|
||||
|
||||
vtx = vs_main(load_vertex(load_index(3 * prim_id + i0)));
|
||||
VS_OUTPUT other = vs_main(load_vertex(load_index(3 * prim_id + i1)));
|
||||
vtx = vs_main(load_vertex(load_index(3 * prim_id + (is_bottom ? i1 : i0))));
|
||||
VS_OUTPUT other = vs_main(load_vertex(load_index(3 * prim_id + (is_bottom ? i0 : i1))));
|
||||
VS_OUTPUT opposite = vs_main(load_vertex(load_index(3 * prim_id + i2)));
|
||||
|
||||
// Similar expansion to line AA1 except instead of expanding on both sides of
|
||||
// the line we expand on on the side towards the outside of the triangle.
|
||||
float2 line_delta = vtx.p.xy - other.p.xy;
|
||||
float2 line_normal = normalize(float2(line_delta.y, -line_delta.x));
|
||||
float2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? float2(0.0f, 2.0f) : float2(2.0f, 0.0f);
|
||||
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(opposite.p.xy - vtx.p.xy, line_normal) >= 0.0f))
|
||||
{
|
||||
// Expand direction point towards the interior so flip it.
|
||||
line_expand = -line_expand;
|
||||
}
|
||||
float2 line_width = (line_expand * PointSize) / 2;
|
||||
float2x2 pos_deltas = get_xy_deltas_unscaled(vtx, other, opposite);
|
||||
|
||||
if (is_bottom)
|
||||
vtx = other;
|
||||
if (is_outside)
|
||||
{
|
||||
vtx.p.xy += line_width;
|
||||
vtx.inv_cov = 1.0f; // No coverage
|
||||
}
|
||||
else
|
||||
{
|
||||
vtx.inv_cov = 0.0f; // Full coverage
|
||||
}
|
||||
float2 expand_dir = is_outside ? get_aa1_triangle_expand_dir(vtx, other, opposite) : 0;
|
||||
|
||||
// Do actual extrapolation, or no-op if expand_dir == 0.
|
||||
extrapolate_aa1_triangle_edge(vtx, other, opposite, pos_deltas, expand_dir);
|
||||
|
||||
vtx.inv_cov = is_outside ? 1.0f : 0.0f; // No coverage on outside, otherwise full.
|
||||
|
||||
vtx.interior = 0;
|
||||
}
|
||||
else // Corner cap
|
||||
{
|
||||
// Vertex indices for this cap. We need all 3 for determining exterior/interior.
|
||||
uint prim_offset_cap = prim_offset - 21; // range: 0-8
|
||||
uint i0 = prim_offset_cap / 6;
|
||||
uint i1 = (i0 >= 2) ? i0 - 2 : i0 + 1;
|
||||
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
|
||||
uint cap_offset = prim_offset_cap - 6 * i0; // range: 0-5
|
||||
|
||||
bool is_near_corner = cap_offset == 0 || cap_offset == 3;
|
||||
bool is_far_corner = cap_offset == 2 || cap_offset == 5;
|
||||
bool is_first_tri = cap_offset < 3;
|
||||
|
||||
vtx = vs_main(load_vertex(load_index(3 * prim_id + i0)));
|
||||
VS_OUTPUT other = vs_main(load_vertex(load_index(3 * prim_id + (is_first_tri ? i1 : i2))));
|
||||
VS_OUTPUT opposite = vs_main(load_vertex(load_index(3 * prim_id + (is_first_tri ? i2 : i1))));
|
||||
|
||||
float2x2 pos_deltas = get_xy_deltas_unscaled(vtx, other, opposite);
|
||||
|
||||
// Get the edge expansion directions of both incident edges.
|
||||
float2 edge_expand_dir_0 = get_aa1_triangle_expand_dir(vtx, other, opposite);
|
||||
float2 edge_expand_dir_1 = get_aa1_triangle_expand_dir(vtx, opposite, other);
|
||||
|
||||
// Check if the corner is already filled by the expanded edges.
|
||||
// This happens if the expand directions are the same.
|
||||
// If so we output a degenerate triangle at this corner.
|
||||
bool corner_filled = all(edge_expand_dir_0 == edge_expand_dir_1);
|
||||
|
||||
// Nothing if corner is filled, otherwise opposite to the bisector of the corner angle.
|
||||
float2 far_corner_dir = corner_filled ? 0 : -normalize((pos_deltas[0] + pos_deltas[1]) / 2);
|
||||
|
||||
// Determine the expand direction.
|
||||
float2 expand_dir = is_near_corner ? 0 : // No extrapolation
|
||||
is_far_corner ? far_corner_dir : // Opposite to the angle bisector of corner
|
||||
edge_expand_dir_0; // Standard AA1 edge expansion
|
||||
|
||||
// Do the actual extrapolation (no-op if expand_dir == 0).
|
||||
extrapolate_aa1_triangle_edge(vtx, other, opposite, pos_deltas, expand_dir);
|
||||
|
||||
vtx.inv_cov = is_near_corner ? 0.0f : 1.0f; // Full coverage at near corner, otherwise none.
|
||||
|
||||
vtx.interior = 0;
|
||||
|
||||
#if !VS_IIP
|
||||
|
||||
@@ -182,6 +182,110 @@ ProcessedVertex load_vertex(uint index)
|
||||
return vtx;
|
||||
}
|
||||
|
||||
// Convert XY from NDC to GS pixel coordinates (i.e. 1.0 = 1 GS pixel).
|
||||
vec2 get_xy_unscaled(vec2 xy)
|
||||
{
|
||||
return round(xy / VertexScale) / 16.0f;
|
||||
}
|
||||
|
||||
// Get the XY deltas in GS pixel coordinates, using first vertex as the origin.
|
||||
mat2 get_xy_deltas_unscaled(ProcessedVertex v0, ProcessedVertex v1, ProcessedVertex v2)
|
||||
{
|
||||
vec2 xy0 = get_xy_unscaled(v0.p.xy);
|
||||
vec2 xy1 = get_xy_unscaled(v1.p.xy);
|
||||
vec2 xy2 = get_xy_unscaled(v2.p.xy);
|
||||
return mat2(xy1 - xy0, xy2 - xy0);
|
||||
}
|
||||
|
||||
// Get the AA1 outward expand direction to the edge formed by the first two vertices.
|
||||
// This is up or down for shallow (X dominant) edges, and right or left for steep (Y dominant) edges.
|
||||
// Similar expansion to line AA1 except instead of expanding on both sides of the line,
|
||||
// expand on on the side towards the outside of the triangle.
|
||||
vec2 get_aa1_triangle_expand_dir(ProcessedVertex v0, ProcessedVertex v1, ProcessedVertex v2)
|
||||
{
|
||||
mat2 xy_deltas = get_xy_deltas_unscaled(v0, v1, v2);
|
||||
vec2 line_delta = xy_deltas[0];
|
||||
vec2 line_opposite = xy_deltas[1];
|
||||
|
||||
vec2 line_normal = vec2(line_delta.y, -line_delta.x);
|
||||
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 1.0f) : vec2(1.0f, 0.0f);
|
||||
|
||||
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(line_opposite, line_normal) >= 0.0f))
|
||||
{
|
||||
// Expand direction point towards the interior so flip it.
|
||||
line_expand = -line_expand;
|
||||
}
|
||||
|
||||
return line_expand;
|
||||
}
|
||||
|
||||
mat2 get_inverse(mat2 mat, float det)
|
||||
{
|
||||
return mat2(mat[1][1], -mat[0][1], -mat[1][0], mat[0][0]) * (1 / det);
|
||||
}
|
||||
|
||||
// Extrapolate triangle attributes from the first vertex along the given direction.
|
||||
// dp_mat is derived from the input vertices, it is passed in to avoid recomputing.
|
||||
void extrapolate_aa1_triangle_edge(inout ProcessedVertex v0, ProcessedVertex v1, ProcessedVertex v2, mat2 dp_mat, vec2 dp)
|
||||
{
|
||||
// Get texture deltas
|
||||
#if VS_TME
|
||||
#if VS_FST
|
||||
mat2 dt = mat2(v1.t_int.zw - v0.t_int.zw, v2.t_int.zw - v0.t_int.zw);
|
||||
#else
|
||||
mat2 dt = mat2(v1.t_float.xy - v0.t_float.xy, v2.t_float.xy - v0.t_float.xy);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Get color delta if interpolating
|
||||
#if VS_IIP
|
||||
mat2x4 dc = mat2x4(v1.c - v0.c, v2.c - v0.c);
|
||||
#endif
|
||||
|
||||
vec2 dz = vec2(v1.p.z - v0.p.z, v2.p.z - v0.p.z); // Z deltas
|
||||
|
||||
vec2 df = vec2(v1.t_float.z - v0.t_float.z, v2.t_float.z - v0.t_float.z); // Fog deltas
|
||||
|
||||
vec2 dq = vec2(v1.t_float.w - v0.t_float.w, v2.t_float.w - v0.t_float.w); // Q deltas
|
||||
|
||||
// To prevent unstable extrapolation, do not extrapolate if the
|
||||
// minimum perpendicular length of the triangle is < 2 pixels.
|
||||
float dp_det = determinant(dp_mat); // Twice signed triangle area.
|
||||
float len0 = length(dp_mat[0]);
|
||||
float len1 = length(dp_mat[1]);
|
||||
float len2 = length(dp_mat[1] - dp_mat[0]);
|
||||
float min_perp_length = abs(dp_det) / max(max(len0, len1), len2);
|
||||
|
||||
// Get the position -> barycentric weight matrix
|
||||
mat2 inv_dp_mat = get_inverse(dp_mat, dp_det);
|
||||
|
||||
vec2 weights = min_perp_length < 2 ? vec2(0) : inv_dp_mat * dp;
|
||||
|
||||
v0.p.xy += dp * PointSize; // Extrapolate position
|
||||
|
||||
// Extrapolate texture coords
|
||||
#if VS_TME
|
||||
#if VS_FST
|
||||
v0.t_int.zw += dt * weights;
|
||||
v0.t_int.xy = v0.t_int.zw * TextureScale;
|
||||
#else
|
||||
v0.t_float.xy += dt * weights;
|
||||
v0.t_int.zw = v0.t_float.xy / TextureScale;
|
||||
v0.t_float.w += dot(dq, weights);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Extrapolate and clamp color
|
||||
#if VS_IIP
|
||||
v0.c += dc * weights;
|
||||
v0.c = clamp(v0.c, vec4(0), vec4(255));
|
||||
#endif
|
||||
|
||||
v0.p.z += dot(dz, weights); // Extrapolate depth
|
||||
|
||||
v0.t_float.z += dot(df, weights); // Extrapolate fog
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ProcessedVertex vtx;
|
||||
@@ -206,13 +310,12 @@ void main()
|
||||
|
||||
// Use bottom minus top for delta regardless of which vertex we are expanding.
|
||||
vec2 line_delta = is_bottom ? (vtx.p.xy - other.p.xy) : (other.p.xy - vtx.p.xy);
|
||||
vec2 line_vector = normalize(line_delta);
|
||||
vec2 line_vector = normalize(line_delta / VertexScale);
|
||||
#if VS_EXPAND == VS_EXPAND_LINE
|
||||
vec2 line_expand = vec2(line_vector.y, -line_vector.x);
|
||||
#elif VS_EXPAND == VS_EXPAND_LINE_AA1
|
||||
// Expand in y direction for shallow lines and x direction for steep lines.
|
||||
line_delta /= VertexScale;
|
||||
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
|
||||
vec2 line_expand = abs(line_vector.x) >= abs(line_vector.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
|
||||
#endif
|
||||
vec2 line_width = (line_expand * PointSize) / 2;
|
||||
vec2 offset = is_right ? line_width : -line_width;
|
||||
@@ -254,10 +357,14 @@ void main()
|
||||
// - Vertices 3-8: First edge expanded (2 triangles).
|
||||
// - Vertices 9-14: Second edge expanded (2 triangles).
|
||||
// - Vertices 15-20: Third edge expanded (2 triangles).
|
||||
// - Vertices 21-26: First corner cap (2 triangles).
|
||||
// - Vertices 27-32: Second corner cap (2 triangles).
|
||||
// - Vertices 33-38: Third corner cap (2 triangles).
|
||||
|
||||
uint prim_id = vid / 21;
|
||||
uint prim_offset = vid - 21 * prim_id; // range: 0-20
|
||||
uint prim_id = vid / 39;
|
||||
uint prim_offset = vid - 39 * prim_id; // range: 0-38
|
||||
bool interior = prim_offset < 3;
|
||||
bool edge = 3 <= prim_offset && prim_offset < 21;
|
||||
|
||||
if (interior)
|
||||
{
|
||||
@@ -265,7 +372,7 @@ void main()
|
||||
VSout.inv_cov = 0.0f; // Full coverage
|
||||
VSout.interior = 1;
|
||||
}
|
||||
else
|
||||
else if (edge)
|
||||
{
|
||||
// Vertex indices for this edge. We need all 3 for determining exterior/interior.
|
||||
uint prim_offset_edges = prim_offset - 3; // range: 0-17
|
||||
@@ -274,39 +381,68 @@ void main()
|
||||
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
|
||||
uint edge_offset = prim_offset_edges - 6 * i0; // range: 0-5
|
||||
|
||||
// Note: order of top/bottom, inside/outside order is arbitrary,
|
||||
// Note: order of top/bottom, inside/outside is arbitrary,
|
||||
// as long as it assembles into two triangles forming a quad.
|
||||
bool is_bottom = (2 <= edge_offset) && (edge_offset <= 4);
|
||||
bool is_outside = (edge_offset & 1) != 0;
|
||||
bool is_outside = (edge_offset & 1u) != 0;
|
||||
|
||||
vtx = load_vertex(load_index(3 * prim_id + i0));
|
||||
ProcessedVertex other = load_vertex(load_index(3 * prim_id + i1));
|
||||
vtx = load_vertex(load_index(3 * prim_id + (is_bottom ? i1 : i0)));
|
||||
ProcessedVertex other = load_vertex(load_index(3 * prim_id + (is_bottom ? i0 : i1)));
|
||||
ProcessedVertex opposite = load_vertex(load_index(3 * prim_id + i2));
|
||||
|
||||
// Similar expansion to line AA1 except instead of expanding on both sides of
|
||||
// the line we expand on on the side towards the outside of the triangle.
|
||||
vec2 line_delta = vtx.p.xy - other.p.xy;
|
||||
vec2 line_normal = normalize(vec2(line_delta.y, -line_delta.x));
|
||||
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
|
||||
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(opposite.p.xy - vtx.p.xy, line_normal) >= 0.0f))
|
||||
{
|
||||
// Expand direction point towards the interior so flip it.
|
||||
line_expand = -line_expand;
|
||||
}
|
||||
vec2 line_width = (line_expand * PointSize) / 2;
|
||||
mat2 pos_deltas = get_xy_deltas_unscaled(vtx, other, opposite);
|
||||
|
||||
if (is_bottom)
|
||||
vtx = other;
|
||||
if (is_outside)
|
||||
{
|
||||
vtx.p.xy += line_width;
|
||||
VSout.inv_cov = 1.0f; // No coverage
|
||||
}
|
||||
else
|
||||
{
|
||||
VSout.inv_cov = 0.0f; // Full coverage
|
||||
}
|
||||
vec2 expand_dir = is_outside ? get_aa1_triangle_expand_dir(vtx, other, opposite) : vec2(0);
|
||||
|
||||
// Do actual extrapolation, or no-op if expand_dir == 0.
|
||||
extrapolate_aa1_triangle_edge(vtx, other, opposite, pos_deltas, expand_dir);
|
||||
|
||||
VSout.inv_cov = is_outside ? 1.0f : 0.0f; // No coverage on outside, otherwise full.
|
||||
|
||||
VSout.interior = 0;
|
||||
}
|
||||
else // Corner cap
|
||||
{
|
||||
// Vertex indices for this cap. We need all 3 for determining exterior/interior.
|
||||
uint prim_offset_cap = prim_offset - 21; // range: 0-8
|
||||
uint i0 = prim_offset_cap / 6;
|
||||
uint i1 = (i0 >= 2) ? i0 - 2 : i0 + 1;
|
||||
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
|
||||
uint cap_offset = prim_offset_cap - 6 * i0; // range: 0-5
|
||||
|
||||
bool is_near_corner = cap_offset == 0 || cap_offset == 3;
|
||||
bool is_far_corner = cap_offset == 2 || cap_offset == 5;
|
||||
bool is_first_tri = cap_offset < 3;
|
||||
|
||||
// First triangle is on the side of vertex i1 and second is on the side of vertex i2.
|
||||
vtx = load_vertex(load_index(3 * prim_id + i0));
|
||||
ProcessedVertex other = load_vertex(load_index(3 * prim_id + (is_first_tri ? i1 : i2)));
|
||||
ProcessedVertex opposite = load_vertex(load_index(3 * prim_id + (is_first_tri ? i2 : i1)));
|
||||
|
||||
mat2 pos_deltas = get_xy_deltas_unscaled(vtx, other, opposite);
|
||||
|
||||
// Get the edge expansion directions of both incident edges.
|
||||
vec2 edge_expand_dir_0 = get_aa1_triangle_expand_dir(vtx, other, opposite);
|
||||
vec2 edge_expand_dir_1 = get_aa1_triangle_expand_dir(vtx, opposite, other);
|
||||
|
||||
// Check if the corner is already filled by the expanded edges.
|
||||
// This happens if the expand directions are the same.
|
||||
// If so we output a degenerate triangle at this corner.
|
||||
bool corner_filled = all(equal(edge_expand_dir_0, edge_expand_dir_1));
|
||||
|
||||
// Nothing if corner is filled, otherwise opposite to the bisector of the corner angle.
|
||||
vec2 far_corner_dir = corner_filled ? vec2(0) : -normalize((pos_deltas[0] + pos_deltas[1]) / 2);
|
||||
|
||||
// Determine the expand direction.
|
||||
vec2 expand_dir = is_near_corner ? vec2(0) : // No extrapolation
|
||||
is_far_corner ? far_corner_dir : // Opposite to the angle bisector of corner
|
||||
edge_expand_dir_0; // Standard AA1 edge expansion
|
||||
|
||||
// Do the actual extrapolation (no-op if expand_dir == 0).
|
||||
extrapolate_aa1_triangle_edge(vtx, other, opposite, pos_deltas, expand_dir);
|
||||
|
||||
VSout.inv_cov = is_near_corner ? 0.0f : 1.0f; // Full coverage at near corner, otherwise none.
|
||||
|
||||
VSout.interior = 0;
|
||||
|
||||
#if !VS_IIP
|
||||
|
||||
@@ -189,6 +189,110 @@ ProcessedVertex load_vertex(uint index)
|
||||
return vtx;
|
||||
}
|
||||
|
||||
// Convert XY from NDC to GS pixel coordinates (i.e. 1.0 = 1 GS pixel).
|
||||
vec2 get_xy_unscaled(vec2 xy)
|
||||
{
|
||||
return round(xy / VertexScale) / 16.0f;
|
||||
}
|
||||
|
||||
// Get the XY deltas in GS pixel coordinates, using first vertex as the origin.
|
||||
mat2 get_xy_deltas_unscaled(ProcessedVertex v0, ProcessedVertex v1, ProcessedVertex v2)
|
||||
{
|
||||
vec2 xy0 = get_xy_unscaled(v0.p.xy);
|
||||
vec2 xy1 = get_xy_unscaled(v1.p.xy);
|
||||
vec2 xy2 = get_xy_unscaled(v2.p.xy);
|
||||
return mat2(xy1 - xy0, xy2 - xy0);
|
||||
}
|
||||
|
||||
// Get the AA1 outward expand direction to the edge formed by the first two vertices.
|
||||
// This is up or down for shallow (X dominant) edges, and right or left for steep (Y dominant) edges.
|
||||
// Similar expansion to line AA1 except instead of expanding on both sides of the line,
|
||||
// expand on on the side towards the outside of the triangle.
|
||||
vec2 get_aa1_triangle_expand_dir(ProcessedVertex v0, ProcessedVertex v1, ProcessedVertex v2)
|
||||
{
|
||||
mat2 xy_deltas = get_xy_deltas_unscaled(v0, v1, v2);
|
||||
vec2 line_delta = xy_deltas[0];
|
||||
vec2 line_opposite = xy_deltas[1];
|
||||
|
||||
vec2 line_normal = vec2(line_delta.y, -line_delta.x);
|
||||
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 1.0f) : vec2(1.0f, 0.0f);
|
||||
|
||||
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(line_opposite, line_normal) >= 0.0f))
|
||||
{
|
||||
// Expand direction point towards the interior so flip it.
|
||||
line_expand = -line_expand;
|
||||
}
|
||||
|
||||
return line_expand;
|
||||
}
|
||||
|
||||
mat2 get_inverse(mat2 mat, float det)
|
||||
{
|
||||
return mat2(mat[1][1], -mat[0][1], -mat[1][0], mat[0][0]) * (1 / det);
|
||||
}
|
||||
|
||||
// Extrapolate triangle attributes from the first vertex along the given direction.
|
||||
// dp_mat is derived from the input vertices, it is passed in to avoid recomputing.
|
||||
void extrapolate_aa1_triangle_edge(inout ProcessedVertex v0, ProcessedVertex v1, ProcessedVertex v2, mat2 dp_mat, vec2 dp)
|
||||
{
|
||||
// Get texture deltas
|
||||
#if VS_TME
|
||||
#if VS_FST
|
||||
mat2 dt = mat2(v1.ti.zw - v0.ti.zw, v2.ti.zw - v0.ti.zw);
|
||||
#else
|
||||
mat2 dt = mat2(v1.t.xy - v0.t.xy, v2.t.xy - v0.t.xy);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Get color delta if interpolating
|
||||
#if VS_IIP
|
||||
mat2x4 dc = mat2x4(v1.c - v0.c, v2.c - v0.c);
|
||||
#endif
|
||||
|
||||
vec2 dz = vec2(v1.p.z - v0.p.z, v2.p.z - v0.p.z); // Z deltas
|
||||
|
||||
vec2 df = vec2(v1.t.z - v0.t.z, v2.t.z - v0.t.z); // Fog deltas
|
||||
|
||||
vec2 dq = vec2(v1.t.w - v0.t.w, v2.t.w - v0.t.w); // Q deltas
|
||||
|
||||
// To prevent unstable extrapolation, do not extrapolate if the
|
||||
// minimum perpendicular length of the triangle is < 2 pixels.
|
||||
float dp_det = determinant(dp_mat); // Twice signed triangle area.
|
||||
float len0 = length(dp_mat[0]);
|
||||
float len1 = length(dp_mat[1]);
|
||||
float len2 = length(dp_mat[1] - dp_mat[0]);
|
||||
float min_perp_length = abs(dp_det) / max(max(len0, len1), len2);
|
||||
|
||||
// Get the position -> barycentric weight matrix
|
||||
mat2 inv_dp_mat = get_inverse(dp_mat, dp_det);
|
||||
|
||||
vec2 weights = min_perp_length < 2 ? vec2(0) : inv_dp_mat * dp;
|
||||
|
||||
v0.p.xy += dp * PointSize; // Extrapolate position
|
||||
|
||||
// Extrapolate texture coords
|
||||
#if VS_TME
|
||||
#if VS_FST
|
||||
v0.ti.zw += dt * weights;
|
||||
v0.ti.xy = v0.ti.zw * TextureScale;
|
||||
#else
|
||||
v0.t.xy += dt * weights;
|
||||
v0.ti.zw = v0.t.xy / TextureScale;
|
||||
v0.t.w += dot(dq, weights);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Extrapolate and clamp color
|
||||
#if VS_IIP
|
||||
v0.c += dc * weights;
|
||||
v0.c = clamp(v0.c, vec4(0), vec4(255));
|
||||
#endif
|
||||
|
||||
v0.p.z += dot(dz, weights); // Extrapolate depth
|
||||
|
||||
v0.t.z += dot(df, weights); // Extrapolate fog
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ProcessedVertex vtx;
|
||||
@@ -214,13 +318,12 @@ void main()
|
||||
|
||||
// Use bottom minus top for delta regardless of which vertex we are expanding.
|
||||
vec2 line_delta = is_bottom ? (vtx.p.xy - other.p.xy) : (other.p.xy - vtx.p.xy);
|
||||
vec2 line_vector = normalize(line_delta);
|
||||
vec2 line_vector = normalize(line_delta / VertexScale);
|
||||
#if VS_EXPAND == VS_EXPAND_LINE
|
||||
vec2 line_expand = vec2(line_vector.y, -line_vector.x);
|
||||
#elif VS_EXPAND == VS_EXPAND_LINE_AA1
|
||||
// Expand in y direction for shallow lines and x direction for steep lines.
|
||||
line_delta /= VertexScale;
|
||||
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
|
||||
vec2 line_expand = abs(line_vector.x) >= abs(line_vector.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
|
||||
#endif
|
||||
vec2 line_width = (line_expand * PointSize) / 2;
|
||||
vec2 offset = is_right ? line_width : -line_width;
|
||||
@@ -262,10 +365,14 @@ void main()
|
||||
// - Vertices 3-8: First edge expanded (2 triangles).
|
||||
// - Vertices 9-14: Second edge expanded (2 triangles).
|
||||
// - Vertices 15-20: Third edge expanded (2 triangles).
|
||||
// - Vertices 21-26: First corner cap (2 triangles).
|
||||
// - Vertices 27-32: Second corner cap (2 triangles).
|
||||
// - Vertices 33-38: Third corner cap (2 triangles).
|
||||
|
||||
uint prim_id = vid / 21;
|
||||
uint prim_offset = vid - 21 * prim_id; // range: 0-20
|
||||
uint prim_id = vid / 39;
|
||||
uint prim_offset = vid - 39 * prim_id; // range: 0-38
|
||||
bool interior = prim_offset < 3;
|
||||
bool edge = 3 <= prim_offset && prim_offset < 21;
|
||||
|
||||
if (interior)
|
||||
{
|
||||
@@ -273,7 +380,7 @@ void main()
|
||||
vsOut.inv_cov = 0.0f; // Full coverage
|
||||
vsOut.interior = 1;
|
||||
}
|
||||
else
|
||||
else if (edge)
|
||||
{
|
||||
// Vertex indices for this edge. We need all 3 for determining exterior/interior.
|
||||
uint prim_offset_edges = prim_offset - 3; // range: 0-17
|
||||
@@ -282,39 +389,68 @@ void main()
|
||||
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
|
||||
uint edge_offset = prim_offset_edges - 6 * i0; // range: 0-5
|
||||
|
||||
// Note: order of top/bottom, inside/outside order is arbitrary,
|
||||
// Note: order of top/bottom, inside/outside is arbitrary,
|
||||
// as long as it assembles into two triangles forming a quad.
|
||||
bool is_bottom = (2 <= edge_offset) && (edge_offset <= 4);
|
||||
bool is_outside = (edge_offset & 1) != 0;
|
||||
|
||||
vtx = load_vertex(load_index(3 * prim_id + i0));
|
||||
ProcessedVertex other = load_vertex(load_index(3 * prim_id + i1));
|
||||
vtx = load_vertex(load_index(3 * prim_id + (is_bottom ? i1 : i0)));
|
||||
ProcessedVertex other = load_vertex(load_index(3 * prim_id + (is_bottom ? i0 : i1)));
|
||||
ProcessedVertex opposite = load_vertex(load_index(3 * prim_id + i2));
|
||||
|
||||
// Similar expansion to line AA1 except instead of expanding on both sides of
|
||||
// the line we expand on on the side towards the outside of the triangle.
|
||||
vec2 line_delta = vtx.p.xy - other.p.xy;
|
||||
vec2 line_normal = normalize(vec2(line_delta.y, -line_delta.x));
|
||||
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
|
||||
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(opposite.p.xy - vtx.p.xy, line_normal) >= 0.0f))
|
||||
{
|
||||
// Expand direction point towards the interior so flip it.
|
||||
line_expand = -line_expand;
|
||||
}
|
||||
vec2 line_width = (line_expand * PointSize) / 2;
|
||||
mat2 pos_deltas = get_xy_deltas_unscaled(vtx, other, opposite);
|
||||
|
||||
if (is_bottom)
|
||||
vtx = other;
|
||||
if (is_outside)
|
||||
{
|
||||
vtx.p.xy += line_width;
|
||||
vsOut.inv_cov = 1.0f; // No coverage
|
||||
}
|
||||
else
|
||||
{
|
||||
vsOut.inv_cov = 0.0f; // Full coverage
|
||||
}
|
||||
vec2 expand_dir = is_outside ? get_aa1_triangle_expand_dir(vtx, other, opposite) : vec2(0);
|
||||
|
||||
// Do actual extrapolation, or no-op if expand_dir == 0.
|
||||
extrapolate_aa1_triangle_edge(vtx, other, opposite, pos_deltas, expand_dir);
|
||||
|
||||
vsOut.inv_cov = is_outside ? 1.0f : 0.0f; // No coverage on outside, otherwise full.
|
||||
|
||||
vsOut.interior = 0;
|
||||
}
|
||||
else // Corner cap
|
||||
{
|
||||
// Vertex indices for this cap. We need all 3 for determining exterior/interior.
|
||||
uint prim_offset_cap = prim_offset - 21; // range: 0-8
|
||||
uint i0 = prim_offset_cap / 6;
|
||||
uint i1 = (i0 >= 2) ? i0 - 2 : i0 + 1;
|
||||
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
|
||||
uint cap_offset = prim_offset_cap - 6 * i0; // range: 0-5
|
||||
|
||||
bool is_near_corner = cap_offset == 0 || cap_offset == 3;
|
||||
bool is_far_corner = cap_offset == 2 || cap_offset == 5;
|
||||
bool is_first_tri = cap_offset < 3;
|
||||
|
||||
// First triangle is on the side of vertex i1 and second is on the side of vertex i2.
|
||||
vtx = load_vertex(load_index(3 * prim_id + i0));
|
||||
ProcessedVertex other = load_vertex(load_index(3 * prim_id + (is_first_tri ? i1 : i2)));
|
||||
ProcessedVertex opposite = load_vertex(load_index(3 * prim_id + (is_first_tri ? i2 : i1)));
|
||||
|
||||
mat2 pos_deltas = get_xy_deltas_unscaled(vtx, other, opposite);
|
||||
|
||||
// Get the edge expansion directions of both incident edges.
|
||||
vec2 edge_expand_dir_0 = get_aa1_triangle_expand_dir(vtx, other, opposite);
|
||||
vec2 edge_expand_dir_1 = get_aa1_triangle_expand_dir(vtx, opposite, other);
|
||||
|
||||
// Check if the corner is already filled by the expanded edges.
|
||||
// This happens if the expand directions are the same.
|
||||
// If so we output a degenerate triangle at this corner.
|
||||
bool corner_filled = all(equal(edge_expand_dir_0, edge_expand_dir_1));
|
||||
|
||||
// Nothing if corner is filled, otherwise opposite to the bisector of the corner angle.
|
||||
vec2 far_corner_dir = corner_filled ? vec2(0) : -normalize((pos_deltas[0] + pos_deltas[1]) / 2);
|
||||
|
||||
// Determine the expand direction.
|
||||
vec2 expand_dir = is_near_corner ? vec2(0) : // No extrapolation
|
||||
is_far_corner ? far_corner_dir : // Opposite to the angle bisector of corner
|
||||
edge_expand_dir_0; // Standard AA1 edge expansion
|
||||
|
||||
// Do the actual extrapolation (no-op if expand_dir == 0).
|
||||
extrapolate_aa1_triangle_edge(vtx, other, opposite, pos_deltas, expand_dir);
|
||||
|
||||
vsOut.inv_cov = is_near_corner ? 0.0f : 1.0f; // Full coverage at near corner, otherwise none.
|
||||
|
||||
vsOut.interior = 0;
|
||||
|
||||
#if !VS_IIP
|
||||
|
||||
@@ -894,7 +894,7 @@ static inline u32 GetExpansionFactor(GSHWDrawConfig::VSExpand expand)
|
||||
case GSHWDrawConfig::VSExpand::Sprite:
|
||||
return 2;
|
||||
case GSHWDrawConfig::VSExpand::TriangleAA1:
|
||||
return 7;
|
||||
return 13;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -253,6 +253,119 @@ static MainVSIn load_vertex(GSMTLMainVertex base)
|
||||
return out;
|
||||
}
|
||||
|
||||
// Convert XY from NDC to GS pixel coordinates (i.e. 1.0 = 1 GS pixel).
|
||||
static float2 get_xy_unscaled(float2 xy, constant GSMTLMainVSUniform& cb [[buffer(GSMTLBufferIndexHWUniforms)]])
|
||||
{
|
||||
return round(xy / cb.vertex_scale) / 16.0f;
|
||||
}
|
||||
|
||||
// Get the XY deltas in GS pixel coordinates, using first vertex as the origin.
|
||||
static float2x2 get_xy_deltas_unscaled(thread const MainVSOut& v0, thread const MainVSOut& v1, thread const MainVSOut& v2,
|
||||
constant GSMTLMainVSUniform& cb [[buffer(GSMTLBufferIndexHWUniforms)]])
|
||||
{
|
||||
float2 xy0 = get_xy_unscaled(v0.p.xy, cb);
|
||||
float2 xy1 = get_xy_unscaled(v1.p.xy, cb);
|
||||
float2 xy2 = get_xy_unscaled(v2.p.xy, cb);
|
||||
return float2x2(xy1 - xy0, xy2 - xy0);
|
||||
}
|
||||
|
||||
// Get the AA1 outward expand direction to the edge formed by the first two vertices.
|
||||
// This is up or down for shallow (X dominant) edges, and right or left for steep (Y dominant) edges.
|
||||
// Similar expansion to line AA1 except instead of expanding on both sides of the line,
|
||||
// expand on on the side towards the outside of the triangle.
|
||||
float2 get_aa1_triangle_expand_dir(thread const MainVSOut& v0, thread const MainVSOut& v1, thread const MainVSOut& v2,
|
||||
constant GSMTLMainVSUniform& cb [[buffer(GSMTLBufferIndexHWUniforms)]])
|
||||
{
|
||||
float2x2 xy_deltas = get_xy_deltas_unscaled(v0, v1, v2, cb);
|
||||
float2 line_delta = xy_deltas[0];
|
||||
float2 line_opposite = xy_deltas[1];
|
||||
|
||||
float2 line_normal = float2(line_delta.y, -line_delta.x);
|
||||
float2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? float2(0.0f, 1.0f) : float2(1.0f, 0.0f);
|
||||
|
||||
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(line_opposite, line_normal) >= 0.0f))
|
||||
{
|
||||
// Expand direction point towards the interior so flip it.
|
||||
line_expand = -line_expand;
|
||||
}
|
||||
|
||||
return line_expand;
|
||||
}
|
||||
|
||||
float2x2 get_inverse(const thread float2x2& mat, float det)
|
||||
{
|
||||
return float2x2(mat[1][1], -mat[0][1], -mat[1][0], mat[0][0]) * (1 / det);
|
||||
}
|
||||
|
||||
// Extrapolate triangle attributes from the first vertex along the given direction.
|
||||
// dp_mat is derived from the input vertices, it is passed in to avoid recomputing.
|
||||
void extrapolate_aa1_triangle_edge(thread MainVSOut& v0, thread const MainVSOut& v1, thread const MainVSOut& v2,
|
||||
thread const float2x2& dp_mat, float2 dp, constant GSMTLMainVSUniform& cb [[buffer(GSMTLBufferIndexHWUniforms)]])
|
||||
{
|
||||
// Get texture deltas
|
||||
float2x2 dt;
|
||||
if (FST)
|
||||
{
|
||||
dt = float2x2(v1.ti.zw - v0.ti.zw, v2.ti.zw - v0.ti.zw);
|
||||
}
|
||||
else
|
||||
{
|
||||
dt = float2x2(v1.t.xy - v0.t.xy, v2.t.xy - v0.t.xy);
|
||||
}
|
||||
|
||||
// Get color delta if interpolating
|
||||
float2x4 dc;
|
||||
if (IIP)
|
||||
{
|
||||
dc = float2x4(v1.c - v0.c, v2.c - v0.c);
|
||||
}
|
||||
|
||||
float2 dz = float2(v1.p.z - v0.p.z, v2.p.z - v0.p.z); // Z deltas
|
||||
|
||||
float2 df = float2(v1.t.z - v0.t.z, v2.t.z - v0.t.z); // Fog deltas
|
||||
|
||||
float2 dq = float2(v1.t.w - v0.t.w, v2.t.w - v0.t.w); // Q deltas
|
||||
|
||||
// To prevent unstable extrapolation, do not extrapolate if the
|
||||
// minimum perpendicular length of the triangle is < 2 pixels.
|
||||
float dp_det = determinant(dp_mat); // Twice signed triangle area.
|
||||
float len0 = length(dp_mat[0]);
|
||||
float len1 = length(dp_mat[1]);
|
||||
float len2 = length(dp_mat[1] - dp_mat[0]);
|
||||
float min_perp_length = abs(dp_det) / max(max(len0, len1), len2);
|
||||
|
||||
// Get the position -> barycentric weight matrix
|
||||
float2x2 inv_dp_mat = get_inverse(dp_mat, dp_det);
|
||||
|
||||
float2 weights = min_perp_length < 2 ? 0 : inv_dp_mat * dp;
|
||||
|
||||
v0.p.xy += dp * cb.point_size; // Extrapolate position
|
||||
|
||||
// Extrapolate texture coords
|
||||
if (FST)
|
||||
{
|
||||
v0.ti.zw += dt * weights;
|
||||
v0.ti.xy = v0.ti.zw * cb.texture_scale;
|
||||
}
|
||||
else
|
||||
{
|
||||
v0.t.xy += dt * weights;
|
||||
v0.ti.zw = v0.t.xy / cb.texture_scale;
|
||||
v0.t.w += dot(dq, weights);
|
||||
}
|
||||
|
||||
// Extrapolate and clamp color
|
||||
if (IIP)
|
||||
{
|
||||
v0.c += dc * weights;
|
||||
v0.c = clamp(v0.c, 0, 255);
|
||||
}
|
||||
|
||||
v0.p.z += dot(dz, weights); // Extrapolate depth
|
||||
|
||||
v0.t.z += dot(df, weights); // Extrapolate fog
|
||||
}
|
||||
|
||||
vertex MainVSOut vs_main_expand(
|
||||
uint vid [[vertex_id]],
|
||||
device const GSMTLMainVertex* vertices [[buffer(GSMTLBufferIndexHWVertices)]],
|
||||
@@ -284,17 +397,16 @@ vertex MainVSOut vs_main_expand(
|
||||
|
||||
// Use bottom minus top for delta regardless of which vertex we are expanding.
|
||||
float2 line_delta = is_bottom ? point.p.xy - other.p.xy : other.p.xy - point.p.xy;
|
||||
float2 line_vector = normalize(line_delta / cb.vertex_scale);
|
||||
float2 line_expand;
|
||||
if (VS_EXPAND_TYPE == VSExpand::Line)
|
||||
{
|
||||
float2 line_vector = normalize(line_delta);
|
||||
line_expand = float2(line_vector.y, -line_vector.x);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Expand in y direction for shallow lines and x direction for steep lines.
|
||||
line_delta /= cb.vertex_scale;
|
||||
line_expand = abs(line_delta.x) >= abs(line_delta.y) ? float2(0, 2) : float2(2, 0);
|
||||
line_expand = abs(line_vector.x) >= abs(line_vector.y) ? float2(0, 2) : float2(2, 0);
|
||||
}
|
||||
float2 line_width = (line_expand * cb.point_size) / 2;
|
||||
float2 offset = is_right ? line_width : -line_width;
|
||||
@@ -345,56 +457,92 @@ vertex MainVSOut vs_main_expand(
|
||||
// - Vertices 3-8: First edge expanded (2 triangles).
|
||||
// - Vertices 9-14: Second edge expanded (2 triangles).
|
||||
// - Vertices 15-20: Third edge expanded (2 triangles).
|
||||
// - Vertices 21-26: First corner cap (2 triangles).
|
||||
// - Vertices 27-32: Second corner cap (2 triangles).
|
||||
// - Vertices 33-38: Third corner cap (2 triangles).
|
||||
|
||||
uint prim_id = vid / 21;
|
||||
uint prim_offset = vid - 21 * prim_id; // range: 0-20
|
||||
uint prim_id = vid / 39;
|
||||
uint prim_offset = vid - 39 * prim_id; // range: 0-38
|
||||
bool interior = prim_offset < 3;
|
||||
uint i0 = interior ? prim_offset : (prim_offset - 3) / 6;
|
||||
uint i1 = (i0 >= 2) ? i0 - 2 : i0 + 1;
|
||||
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
|
||||
MainVSOut out = vs_main_run(load_vertex(vertices[indices[3 * prim_id + i0]]), cb);
|
||||
MainVSOut other = vs_main_run(load_vertex(vertices[indices[3 * prim_id + i1]]), cb);
|
||||
MainVSOut opposite = vs_main_run(load_vertex(vertices[indices[3 * prim_id + i2]]), cb);
|
||||
bool edge = 3 <= prim_offset && prim_offset < 21;
|
||||
|
||||
MainVSOut out;
|
||||
if (interior)
|
||||
{
|
||||
out = vs_main_run(load_vertex(vertices[indices[3 * prim_id + prim_offset]]), cb);
|
||||
out.inv_cov = 0.f;
|
||||
out.interior = 1;
|
||||
}
|
||||
else
|
||||
else if (edge)
|
||||
{
|
||||
// Vertex indices for this edge. We need all 3 for determining exterior/interior.
|
||||
uint prim_offset_edges = prim_offset - 3; // range: 0-17
|
||||
uint i0 = prim_offset_edges / 6;
|
||||
uint i1 = (i0 >= 2) ? i0 - 2 : i0 + 1;
|
||||
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
|
||||
uint edge_offset = prim_offset_edges - 6 * i0; // range: 0-5
|
||||
|
||||
// Note: order of top/bottom, inside/outside order is arbitrary,
|
||||
// Note: order of top/bottom, inside/outside is arbitrary,
|
||||
// as long as it assembles into two triangles forming a quad.
|
||||
bool is_bottom = (2 <= edge_offset) && (edge_offset <= 4);
|
||||
bool is_outside = (edge_offset & 1) != 0;
|
||||
bool is_outside = edge_offset & 1;
|
||||
|
||||
// Similar expansion to line AA1 except instead of expanding on both sides of
|
||||
// the line we expand on on the side towards the outside of the triangle.
|
||||
float2 line_delta = out.p.xy - other.p.xy;
|
||||
float2 line_normal = normalize(float2(line_delta.y, -line_delta.x));
|
||||
float2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? float2(0, 2) : float2(2, 0);
|
||||
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(opposite.p.xy - out.p.xy, line_normal) >= 0.0f))
|
||||
{
|
||||
// Expand direction point towards the interior so flip it.
|
||||
line_expand = -line_expand;
|
||||
}
|
||||
float2 line_width = (line_expand * cb.point_size) / 2;
|
||||
out = vs_main_run(load_vertex(vertices[indices[3 * prim_id + (is_bottom ? i1 : i0)]]), cb);
|
||||
MainVSOut other = vs_main_run(load_vertex(vertices[indices[3 * prim_id + (is_bottom ? i0 : i1)]]), cb);
|
||||
MainVSOut opposite = vs_main_run(load_vertex(vertices[indices[3 * prim_id + i2]]), cb);
|
||||
|
||||
if (is_bottom)
|
||||
out = other;
|
||||
if (is_outside)
|
||||
{
|
||||
out.p.xy += line_width;
|
||||
out.inv_cov = 1.0f; // No coverage
|
||||
}
|
||||
else
|
||||
{
|
||||
out.inv_cov = 0.0f; // Full coverage
|
||||
}
|
||||
float2x2 pos_deltas = get_xy_deltas_unscaled(out, other, opposite, cb);
|
||||
|
||||
float2 expand_dir = is_outside ? get_aa1_triangle_expand_dir(out, other, opposite, cb) : 0;
|
||||
|
||||
// Do actual extrapolation, or no-op if expand_dir == 0.
|
||||
extrapolate_aa1_triangle_edge(out, other, opposite, pos_deltas, expand_dir, cb);
|
||||
|
||||
out.inv_cov = is_outside ? 1.0f : 0.0f; // No coverage on outside, otherwise full.
|
||||
|
||||
out.interior = 0;
|
||||
}
|
||||
else // Corner cap
|
||||
{
|
||||
// Vertex indices for this cap. We need all 3 for determining exterior/interior.
|
||||
uint prim_offset_cap = prim_offset - 21; // range: 0-8
|
||||
uint i0 = prim_offset_cap / 6;
|
||||
uint i1 = (i0 >= 2) ? i0 - 2 : i0 + 1;
|
||||
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
|
||||
uint cap_offset = prim_offset_cap - 6 * i0; // range: 0-5
|
||||
|
||||
bool is_near_corner = cap_offset == 0 || cap_offset == 3;
|
||||
bool is_far_corner = cap_offset == 2 || cap_offset == 5;
|
||||
bool is_first_tri = cap_offset < 3;
|
||||
|
||||
out = vs_main_run(load_vertex(vertices[indices[3 * prim_id + i0]]), cb);
|
||||
MainVSOut other = vs_main_run(load_vertex(vertices[indices[3 * prim_id + (is_first_tri ? i1 : i2)]]), cb);
|
||||
MainVSOut opposite = vs_main_run(load_vertex(vertices[indices[3 * prim_id + (is_first_tri ? i2 : i1)]]), cb);
|
||||
|
||||
float2x2 pos_deltas = get_xy_deltas_unscaled(out, other, opposite, cb);
|
||||
|
||||
// Get the edge expansion directions of both incident edges.
|
||||
float2 edge_expand_dir_0 = get_aa1_triangle_expand_dir(out, other, opposite, cb);
|
||||
float2 edge_expand_dir_1 = get_aa1_triangle_expand_dir(out, opposite, other, cb);
|
||||
|
||||
// Check if the corner is already filled by the expanded edges.
|
||||
// This happens if the expand directions are the same.
|
||||
// If so we output a degenerate triangle at this corner.
|
||||
bool corner_filled = all(edge_expand_dir_0 == edge_expand_dir_1);
|
||||
|
||||
// Nothing if corner is filled, otherwise opposite to the bisector of the corner angle.
|
||||
float2 far_corner_dir = corner_filled ? 0 : -normalize((pos_deltas[0] + pos_deltas[1]) / 2);
|
||||
|
||||
// Determine the expand direction.
|
||||
float2 expand_dir = is_near_corner ? 0 : // No extrapolation
|
||||
is_far_corner ? far_corner_dir : // Opposite to the angle bisector of corner
|
||||
edge_expand_dir_0; // Standard AA1 edge expansion
|
||||
|
||||
// Do the actual extrapolation (no-op if expand_dir == 0).
|
||||
extrapolate_aa1_triangle_edge(out, other, opposite, pos_deltas, expand_dir, cb);
|
||||
|
||||
out.inv_cov = is_near_corner ? 0.0f : 1.0f; // Full coverage at near corner, otherwise none.
|
||||
|
||||
out.interior = 0;
|
||||
|
||||
if (NOT_IIP)
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
/// Version number for GS and other shaders. Increment whenever any of the contents of the
|
||||
/// shaders change, to invalidate the cache.
|
||||
static constexpr u32 SHADER_CACHE_VERSION = 94; // Last changed in PR 14461
|
||||
static constexpr u32 SHADER_CACHE_VERSION = 95; // Last changed in PR 14439
|
||||
|
||||
Reference in New Issue
Block a user