Skip to content

Commit

Permalink
RENDERER: Add option to filter out collinear vertices.
Browse files Browse the repository at this point in the history
This is a temporary workaround primarily targeted at macOS arm64 on which
the GPU has a high risk of clipping nearby triangles if zero sized triangles
are fed to the GPU. At best this bug introduces rendering glitches from looking
into the void, at worst it allows the player to see enemies behind walls in matches.

The workaround is force-enabled on macOS arm64. But there have been mysterious
rendering issues with missing surfaces in the past, so other targets may opt-in
to enable it and see if it helps.
  • Loading branch information
dsvensson committed Sep 14, 2024
1 parent bcbf931 commit 22f39e2
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 1 deletion.
17 changes: 17 additions & 0 deletions help_variables.json
Original file line number Diff line number Diff line change
Expand Up @@ -15627,6 +15627,23 @@
"remarks": "See also: r_nearclip.",
"type": "float"
},
"r_remove_collinear_vertices": {
"default": "0",
"desc": "Filter out vertices causing triangles to have zero area. These types of triangles are a sore spot in some GPUs that risk introducing glitches. An expected side effect is a risk of seeing a sparkling pixel.",
"group-id": "35",
"remarks": "For macOS arm64 this is force-enabled to prevent cheating. This filtering is a temporary workaround before adding code to avoid generating such triangles in the first place.",
"type": "boolean",
"values": [
{
"description": "Disable filtering of collinear vertices.",
"name": "false"
},
{
"description": "Enable filtering of collinear vertices.",
"name": "true"
}
]
},
"r_fastsky": {
"default": "0",
"group-id": "51",
Expand Down
60 changes: 59 additions & 1 deletion src/r_lightmaps.c
Original file line number Diff line number Diff line change
Expand Up @@ -590,9 +590,56 @@ static int LightmapAllocBlock(int w, int h, int *x, int *y)
return LightmapAllocBlock(w, h, x, y);
}

#define EPSILON 1e-6

// Check if triangle has a ~zero area
// https://en.wikipedia.org/wiki/Collinearity
static qbool R_ArePointsColinear(const vec3_t v1, const vec3_t v2, const vec3_t v3)
{
vec3_t d0, d1, cross;

VectorSubtract(v2, v1, d0);
VectorSubtract(v3, v2, d1);

CrossProduct(d0, d1, cross);

return DotProduct(cross, cross) < EPSILON;
}

static void R_RemoveColinearVertices(glpoly_t *poly, float new_verts[][VERTEXSIZE])
{
int i, v1_index, v2_index, v3_index, new_numverts = 0;
int numverts = poly->numverts;

v1_index = numverts - 1;
v2_index = 0;
v3_index = 1;

for (i = 0; i < numverts; i++) {
float *v1 = poly->verts[v1_index];
float *v2 = poly->verts[v2_index];
float *v3 = poly->verts[v3_index];

if (!R_ArePointsColinear(v1, v2, v3)) {
memcpy(new_verts[new_numverts], v2, sizeof(float) * VERTEXSIZE);
new_numverts++;
}

v1_index = v2_index;
v2_index = v3_index;
v3_index = (v3_index + 1) % numverts;
}

if (new_numverts > 0) {
memcpy(poly->verts, new_verts, new_numverts * sizeof(float) * VERTEXSIZE);
poly->numverts = new_numverts;
}
}

//
static void R_BuildSurfaceDisplayList(model_t* currentmodel, msurface_t *fa)
{
extern cvar_t r_remove_collinear_vertices;
int i, lindex, lnumverts;
medge_t *pedges, *r_pedge;
float *vec, s, t;
Expand Down Expand Up @@ -684,8 +731,19 @@ static void R_BuildSurfaceDisplayList(model_t* currentmodel, msurface_t *fa)
}
poly->numverts = lnumverts;

// Some GPUs misbehave if fed triangles of empty size.
if (r_remove_collinear_vertices.value) {
if (poly->numverts > 4) {
float (*new_verts)[VERTEXSIZE] = Q_malloc(poly->numverts * sizeof(float[VERTEXSIZE]));
R_RemoveColinearVertices(poly, new_verts);
Q_free(new_verts);
} else {
float new_verts[4][VERTEXSIZE];
R_RemoveColinearVertices(poly, new_verts);
}
}

R_BrushModelPolygonToTriangleStrip(poly);
return;
}

static void R_BuildLightmapData(msurface_t* surf, int surfnum)
Expand Down
13 changes: 13 additions & 0 deletions src/r_rmain.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,15 @@ static void OnDynamicLightingChange(cvar_t* var, char* value, qbool* cancel)
}
}

static void OnRemoveCollinearVerticesChanged(cvar_t* var, char* value, qbool* cancel)
{
#if defined(__APPLE__) && defined(__aarch64__)
// At least arm64 based MacBook laptops are known to require this workaround.
Cvar_SetIgnoreCallback(var, "1");
*cancel = true;
#endif
}

cvar_t cl_multiview = {"cl_multiview", "0" };
cvar_t cl_mvdisplayhud = {"cl_mvdisplayhud", "1"};
cvar_t cl_mvhudvertical = {"cl_mvhudvertical", "0"};
Expand Down Expand Up @@ -238,6 +247,8 @@ cvar_t gl_smoothmodels = {"gl_smoothmodels", "1"};

cvar_t gl_vbo_clientmemory = {"gl_vbo_clientmemory", "0", CVAR_LATCH_GFX };

cvar_t r_remove_collinear_vertices = {"r_remove_collinear_vertices", "0", 0, OnRemoveCollinearVerticesChanged};

//Returns true if the box is completely outside the frustom
qbool R_CullBox(vec3_t mins, vec3_t maxs)
{
Expand Down Expand Up @@ -750,6 +761,8 @@ void R_Init(void)
Cvar_Register(&cl_mvinset_top);
Cvar_Register(&cl_mvinset_right);

Cvar_Register(&r_remove_collinear_vertices);

Cvar_ResetCurrentGroup();

if (!hud_netgraph) {
Expand Down

0 comments on commit 22f39e2

Please sign in to comment.