зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1637705 - fix SWGL's frustum clipping to work against all 6 sides. r=jimb
Differential Revision: https://phabricator.services.mozilla.com/D75166
This commit is contained in:
Родитель
2a3581f2ce
Коммит
b5a4c1c1ff
|
@ -3324,44 +3324,98 @@ static inline void draw_perspective_spans(int nump, Point3D* p,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clip a primitive against the near or far Z planes, producing intermediate
|
// Clip a primitive against both sides of a view-frustum axis, producing
|
||||||
// vertexes with interpolated attributes that will no longer intersect the
|
// intermediate vertexes with interpolated attributes that will no longer
|
||||||
// selected plane. This overwrites the vertexes in-place, producing at most
|
// intersect the selected axis planes. This assumes the primitive is convex
|
||||||
// N+1 vertexes for each invocation, so appropriate storage should be reserved
|
// and should produce at most N+2 vertexes for each invocation (only in the
|
||||||
// before calling.
|
// worst case where one point falls outside on each of the opposite sides
|
||||||
template <int SIDE>
|
// with the rest of the points inside).
|
||||||
static int clip_near_far(int nump, Point3D* p, Interpolants* interp) {
|
template <XYZW AXIS>
|
||||||
|
static int clip_side(int nump, Point3D* p, Interpolants* interp, Point3D* outP,
|
||||||
|
Interpolants* outInterp) {
|
||||||
int numClip = 0;
|
int numClip = 0;
|
||||||
Point3D prev = p[nump - 1];
|
Point3D prev = p[nump - 1];
|
||||||
Interpolants prevInterp = interp[nump - 1];
|
Interpolants prevInterp = interp[nump - 1];
|
||||||
float prevDist = SIDE * prev.z - prev.w;
|
float prevCoord = prev.select(AXIS);
|
||||||
// Loop through points, finding edges that cross the plane by evaluating
|
// Coordinate must satisfy -W <= C <= W. Determine if it is outside, and
|
||||||
// the plane equation at each point.
|
// if so, remember which side it is outside of.
|
||||||
|
int prevSide = prevCoord < -prev.w ? -1 : (prevCoord > prev.w ? 1 : 0);
|
||||||
|
// Loop through points, finding edges that cross the planes by evaluating
|
||||||
|
// the side at each point.
|
||||||
for (int i = 0; i < nump; i++) {
|
for (int i = 0; i < nump; i++) {
|
||||||
Point3D cur = p[i];
|
Point3D cur = p[i];
|
||||||
Interpolants curInterp = interp[i];
|
Interpolants curInterp = interp[i];
|
||||||
float curDist = SIDE * cur.z - cur.w;
|
float curCoord = cur.select(AXIS);
|
||||||
if (curDist < 0.0f && prevDist < 0.0f) {
|
int curSide = curCoord < -cur.w ? -1 : (curCoord > cur.w ? 1 : 0);
|
||||||
// The edge is inside the plane, so output point unmodified.
|
// Check if the previous and current end points are on different sides.
|
||||||
p[numClip] = cur;
|
if (curSide != prevSide) {
|
||||||
interp[numClip] = curInterp;
|
|
||||||
numClip++;
|
|
||||||
} else if (curDist < 0.0f || prevDist < 0.0f) {
|
|
||||||
// One of the edge's end points is outside the plane with the other
|
// One of the edge's end points is outside the plane with the other
|
||||||
// inside the plane. Find the offset where it crosses the plane and
|
// inside the plane. Find the offset where it crosses the plane and
|
||||||
// adjust the point and interpolants to there.
|
// adjust the point and interpolants to there.
|
||||||
float k = prevDist / (prevDist - curDist);
|
if (prevSide) {
|
||||||
p[numClip] = prev + (cur - prev) * k;
|
// Edge that was previously outside crosses inside.
|
||||||
interp[numClip] = prevInterp + (curInterp - prevInterp) * k;
|
// Evaluate plane equation for previous and current end-point
|
||||||
|
// based on previous side and calculate relative offset.
|
||||||
|
assert(numClip < nump + 2);
|
||||||
|
float prevDist = prevCoord - prevSide * prev.w;
|
||||||
|
float curDist = curCoord - prevSide * cur.w;
|
||||||
|
float k = prevDist / (prevDist - curDist);
|
||||||
|
outP[numClip] = prev + (cur - prev) * k;
|
||||||
|
outInterp[numClip] = prevInterp + (curInterp - prevInterp) * k;
|
||||||
|
numClip++;
|
||||||
|
}
|
||||||
|
if (curSide) {
|
||||||
|
// Edge that was previously inside crosses outside.
|
||||||
|
// Evaluate plane equation for previous and current end-point
|
||||||
|
// based on current side and calculate relative offset.
|
||||||
|
assert(numClip < nump + 2);
|
||||||
|
float prevDist = prevCoord - curSide * prev.w;
|
||||||
|
float curDist = curCoord - curSide * cur.w;
|
||||||
|
float k = prevDist / (prevDist - curDist);
|
||||||
|
outP[numClip] = prev + (cur - prev) * k;
|
||||||
|
outInterp[numClip] = prevInterp + (curInterp - prevInterp) * k;
|
||||||
|
numClip++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!curSide) {
|
||||||
|
// The current end point is inside the plane, so output point unmodified.
|
||||||
|
assert(numClip < nump + 2);
|
||||||
|
outP[numClip] = cur;
|
||||||
|
outInterp[numClip] = curInterp;
|
||||||
numClip++;
|
numClip++;
|
||||||
}
|
}
|
||||||
prev = cur;
|
prev = cur;
|
||||||
prevInterp = curInterp;
|
prevInterp = curInterp;
|
||||||
prevDist = curDist;
|
prevCoord = curCoord;
|
||||||
|
prevSide = curSide;
|
||||||
}
|
}
|
||||||
return numClip;
|
return numClip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to dispatch to perspective span drawing with points that
|
||||||
|
// have already been transformed and clipped.
|
||||||
|
static inline void draw_perspective_clipped(int nump, Point3D* p_clip,
|
||||||
|
Interpolants* interp_clip,
|
||||||
|
Texture& colortex, int layer,
|
||||||
|
Texture& depthtex) {
|
||||||
|
// If polygon is ouside clip rect, nothing to draw.
|
||||||
|
ClipRect clipRect(colortex);
|
||||||
|
if (!clipRect.overlaps(nump, p_clip)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally draw perspective-correct spans for the polygon.
|
||||||
|
if (colortex.internal_format == GL_RGBA8) {
|
||||||
|
draw_perspective_spans<uint32_t>(nump, p_clip, interp_clip, colortex,
|
||||||
|
layer, depthtex, clipRect);
|
||||||
|
} else if (colortex.internal_format == GL_R8) {
|
||||||
|
draw_perspective_spans<uint8_t>(nump, p_clip, interp_clip, colortex,
|
||||||
|
layer, depthtex, clipRect);
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Draws a perspective-correct 3D primitive with varying Z value, as opposed
|
// Draws a perspective-correct 3D primitive with varying Z value, as opposed
|
||||||
// to a simple 2D planar primitive with a constant Z value that could be
|
// to a simple 2D planar primitive with a constant Z value that could be
|
||||||
// trivially Z rejected. This requires clipping the primitive against the near
|
// trivially Z rejected. This requires clipping the primitive against the near
|
||||||
|
@ -3374,63 +3428,81 @@ static int clip_near_far(int nump, Point3D* p, Interpolants* interp) {
|
||||||
// This process is expensive and should be avoided if possible for primitive
|
// This process is expensive and should be avoided if possible for primitive
|
||||||
// batches that are known ahead of time to not need perspective-correction.
|
// batches that are known ahead of time to not need perspective-correction.
|
||||||
static void draw_perspective(int nump,
|
static void draw_perspective(int nump,
|
||||||
Interpolants interp_outs[6],
|
Interpolants interp_outs[4],
|
||||||
Texture& colortex, int layer,
|
Texture& colortex, int layer,
|
||||||
Texture& depthtex) {
|
Texture& depthtex) {
|
||||||
// Convert output of vertex shader to screen space.
|
// Convert output of vertex shader to screen space.
|
||||||
Point3D p[6];
|
|
||||||
vec4 pos = vertex_shader->gl_Position;
|
vec4 pos = vertex_shader->gl_Position;
|
||||||
vec3_scalar scale =
|
vec3_scalar scale =
|
||||||
vec3_scalar(ctx->viewport.width(), ctx->viewport.height(), 1) * 0.5f;
|
vec3_scalar(ctx->viewport.width(), ctx->viewport.height(), 1) * 0.5f;
|
||||||
vec3_scalar offset =
|
vec3_scalar offset =
|
||||||
vec3_scalar(ctx->viewport.x0, ctx->viewport.y0, 0.0f) + scale;
|
vec3_scalar(ctx->viewport.x0, ctx->viewport.y0, 0.0f) + scale;
|
||||||
if (test_none(pos.z < -pos.w || pos.z > pos.w)) {
|
if (test_none(pos.z <= -pos.w || pos.z >= pos.w)) {
|
||||||
// No points cross the near or far planes, so no clipping required.
|
// No points cross the near or far planes, so no clipping required.
|
||||||
// Just divide coords by W and convert to viewport.
|
// Just divide coords by W and convert to viewport.
|
||||||
Float w = 1.0f / pos.w;
|
Float w = 1.0f / pos.w;
|
||||||
vec3 screen = pos.sel(X, Y, Z) * w * scale + offset;
|
vec3 screen = pos.sel(X, Y, Z) * w * scale + offset;
|
||||||
p[0] = Point3D(screen.x.x, screen.y.x, screen.z.x, w.x);
|
Point3D p[4] = {
|
||||||
p[1] = Point3D(screen.x.y, screen.y.y, screen.z.y, w.y);
|
{screen.x.x, screen.y.x, screen.z.x, w.x},
|
||||||
p[2] = Point3D(screen.x.z, screen.y.z, screen.z.z, w.z);
|
{screen.x.y, screen.y.y, screen.z.y, w.y},
|
||||||
p[3] = Point3D(screen.x.w, screen.y.w, screen.z.w, w.w);
|
{screen.x.z, screen.y.z, screen.z.z, w.z},
|
||||||
|
{screen.x.w, screen.y.w, screen.z.w, w.w}
|
||||||
|
};
|
||||||
|
draw_perspective_clipped(nump, p, interp_outs, colortex, layer, depthtex);
|
||||||
} else {
|
} else {
|
||||||
// Points cross the near or far planes, so we need to clip...
|
// Points cross the near or far planes, so we need to clip.
|
||||||
p[0] = Point3D(pos.x.x, pos.y.x, pos.z.x, pos.w.x);
|
// Start with the original 3 or 4 points...
|
||||||
p[1] = Point3D(pos.x.y, pos.y.y, pos.z.y, pos.w.y);
|
Point3D p[4] = {
|
||||||
p[2] = Point3D(pos.x.z, pos.y.z, pos.z.z, pos.w.z);
|
{pos.x.x, pos.y.x, pos.z.x, pos.w.x},
|
||||||
p[3] = Point3D(pos.x.w, pos.y.w, pos.z.w, pos.w.w);
|
{pos.x.y, pos.y.y, pos.z.y, pos.w.y},
|
||||||
// Clip against near plane.
|
{pos.x.z, pos.y.z, pos.z.z, pos.w.z},
|
||||||
nump = clip_near_far<-1>(nump, p, interp_outs);
|
{pos.x.w, pos.y.w, pos.z.w, pos.w.w}
|
||||||
|
};
|
||||||
|
// Clipping can expand the points by 1 for each of 6 view frustum planes.
|
||||||
|
Point3D p_clip[4 + 6];
|
||||||
|
Interpolants interp_clip[4 + 6];
|
||||||
|
// Clip against near and far Z planes.
|
||||||
|
nump = clip_side<Z>(nump, p, interp_outs, p_clip, interp_clip);
|
||||||
|
// If no points are left inside the view frustum, there's nothing to draw.
|
||||||
if (nump < 3) {
|
if (nump < 3) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Clip against far plane.
|
// After clipping against only the near and far planes, we might still
|
||||||
nump = clip_near_far<1>(nump, p, interp_outs);
|
// produce points where W = 0, exactly at the camera plane. OpenGL specifies
|
||||||
if (nump < 3) {
|
// that for clip coordinates, points must satisfy:
|
||||||
return;
|
// -W <= X <= W
|
||||||
|
// -W <= Y <= W
|
||||||
|
// -W <= Z <= W
|
||||||
|
// When Z = W = 0, this is trivially satisfied, but when we transform and
|
||||||
|
// divide by W below it will produce a divide by 0. Usually we want to only
|
||||||
|
// clip Z to avoid the extra work of clipping X and Y. We can still project
|
||||||
|
// points that fall outside the view frustum X and Y so long as Z is valid.
|
||||||
|
// The span drawing code will then ensure X and Y are clamped to viewport
|
||||||
|
// boundaries. However, in the Z = W = 0 case, sometimes clipping X and Y,
|
||||||
|
// will push W further inside the view frustum so that it is no longer 0,
|
||||||
|
// allowing us to finally proceed to projecting the points to the screen.
|
||||||
|
for (int i = 0; i < nump; i++) {
|
||||||
|
// Found an invalid W, so need to clip against X and Y...
|
||||||
|
if (p_clip[i].w <= 0.0f) {
|
||||||
|
// Ping-pong p_clip -> p_tmp -> p_clip.
|
||||||
|
Point3D p_tmp[4 + 6];
|
||||||
|
Interpolants interp_tmp[4 + 6];
|
||||||
|
nump = clip_side<X>(nump, p_clip, interp_clip, p_tmp, interp_tmp);
|
||||||
|
if (nump < 3) return;
|
||||||
|
nump = clip_side<Y>(nump, p_tmp, interp_tmp, p_clip, interp_clip);
|
||||||
|
if (nump < 3) return;
|
||||||
|
// After clipping against X and Y planes, there's still points left
|
||||||
|
// to draw, so proceed to trying projection now...
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Divide coords by W and convert to viewport.
|
// Divide coords by W and convert to viewport.
|
||||||
for (int i = 0; i < nump; i++) {
|
for (int i = 0; i < nump; i++) {
|
||||||
float w = 1.0f / p[i].w;
|
float w = 1.0f / p_clip[i].w;
|
||||||
p[i] = Point3D(p[i].sel(X, Y, Z) * w * scale + offset, w);
|
p_clip[i] = Point3D(p_clip[i].sel(X, Y, Z) * w * scale + offset, w);
|
||||||
}
|
}
|
||||||
}
|
draw_perspective_clipped(nump, p_clip, interp_clip, colortex, layer,
|
||||||
|
depthtex);
|
||||||
// If polygon is ouside clip rect, nothing to draw.
|
|
||||||
ClipRect clipRect(colortex);
|
|
||||||
if (!clipRect.overlaps(nump, p)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally draw perspective-correct spans for the polygon.
|
|
||||||
if (colortex.internal_format == GL_RGBA8) {
|
|
||||||
draw_perspective_spans<uint32_t>(nump, p, interp_outs, colortex, layer,
|
|
||||||
depthtex, clipRect);
|
|
||||||
} else if (colortex.internal_format == GL_R8) {
|
|
||||||
draw_perspective_spans<uint8_t>(nump, p, interp_outs, colortex, layer,
|
|
||||||
depthtex, clipRect);
|
|
||||||
} else {
|
|
||||||
assert(false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3439,7 +3511,7 @@ static void draw_quad(int nump, Texture& colortex, int layer,
|
||||||
// Run vertex shader once for the primitive's vertices.
|
// Run vertex shader once for the primitive's vertices.
|
||||||
// Reserve space for 6 sets of interpolants, in case we need to clip against
|
// Reserve space for 6 sets of interpolants, in case we need to clip against
|
||||||
// near and far planes in the perspective case.
|
// near and far planes in the perspective case.
|
||||||
Interpolants interp_outs[6];
|
Interpolants interp_outs[4];
|
||||||
vertex_shader->run_primitive((char*)interp_outs, sizeof(Interpolants));
|
vertex_shader->run_primitive((char*)interp_outs, sizeof(Interpolants));
|
||||||
vec4 pos = vertex_shader->gl_Position;
|
vec4 pos = vertex_shader->gl_Position;
|
||||||
// Check if any vertex W is different from another. If so, use perspective.
|
// Check if any vertex W is different from another. If so, use perspective.
|
||||||
|
|
|
@ -1405,7 +1405,7 @@ struct vec4_scalar {
|
||||||
: x(x), y(y), z(z), w(w) {}
|
: x(x), y(y), z(z), w(w) {}
|
||||||
vec4_scalar(vec3_scalar xyz, float w) : x(xyz.x), y(xyz.y), z(xyz.z), w(w) {}
|
vec4_scalar(vec3_scalar xyz, float w) : x(xyz.x), y(xyz.y), z(xyz.z), w(w) {}
|
||||||
|
|
||||||
float& select(XYZW c) {
|
ALWAYS_INLINE float& select(XYZW c) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case X:
|
case X:
|
||||||
return x;
|
return x;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче