diff --git a/include/mgba/internal/ds/gx.h b/include/mgba/internal/ds/gx.h index 6ec413fac..9dafee99e 100644 --- a/include/mgba/internal/ds/gx.h +++ b/include/mgba/internal/ds/gx.h @@ -148,6 +148,11 @@ struct DSGXRenderer { uint16_t* tex[4]; uint16_t* texPal[6]; + + int viewportX; + int viewportY; + int viewportWidth; + int viewportHeight; }; struct DS; diff --git a/src/ds/gx.c b/src/ds/gx.c index 506c8e384..149cfb612 100644 --- a/src/ds/gx.c +++ b/src/ds/gx.c @@ -190,23 +190,21 @@ static void _updateClipMatrix(struct DSGX* gx) { static inline int32_t _lerp(int32_t x0, int32_t x1, int32_t q, int64_t r, int point) { int64_t x = x1 - x0; x *= q; - x >>= point >> 1; - x *= r; - x >>= 27 + (point >> 1); + x /= r; x += x0; - return x + 1; + return x; } static int _cohenSutherlandCode(const struct DSGX* gx, const struct DSGXVertex* v) { int code = 0; - if (v->vx < gx->viewportX1 << 12) { + if (v->vx < -v->vw) { code |= CS_LEFT; - } else if (v->vx > gx->viewportX2 << 12) { + } else if (v->vx > v->vw) { code |= CS_RIGHT; } - if (v->vy < gx->viewportY1 << 12) { + if (v->vy < -v->vw) { code |= CS_BOTTOM; - } else if (v->vy > gx->viewportY2 << 12) { + } else if (v->vy > v->vw) { code |= CS_TOP; } if (v->vz < -v->vw) { @@ -221,37 +219,33 @@ static bool _lerpVertex(const struct DSGXVertex* v0, const struct DSGXVertex* v1 if (!r) { return false; } - if (q < 0) { - return false; - } - if (q > r) { - return false; - } - - r = (INT64_MAX >> 24) / r; - int32_t w = _lerp(v0->vw, v1->vw, q, r, 12); - out->vw = w; - out->color = v0->color; // TODO out->vx = _lerp(v0->vx, v1->vx, q, r, 12); out->vy = _lerp(v0->vy, v1->vy, q, r, 12); out->vz = _lerp(v0->vz, v1->vz, q, r, 12); out->vw = _lerp(v0->vw, v1->vw, q, r, 12); + out->vs = _lerp(v0->vs, v1->vs, q, r, 12); out->vt = _lerp(v0->vt, v1->vt, q, r, 12); return true; } -static bool _lerpVertexX(const struct DSGXVertex* v0, const struct DSGXVertex* v1, struct DSGXVertex* out, int32_t xw) { - int32_t q = xw - v0->vx; - int64_t r = v1->vx - v0->vx; +static bool _lerpVertexX(const struct DSGXVertex* v0, const struct DSGXVertex* v1, struct DSGXVertex* out, int sign) { + int32_t q = v0->vw - sign * v0->vx; + int64_t r = q - (v1->vw - sign * v1->vx); return _lerpVertex(v0, v1, out, q, r); } -static bool _lerpVertexY(const struct DSGXVertex* v0, const struct DSGXVertex* v1, struct DSGXVertex* out, int32_t yw) { - int32_t q = yw - v0->vy; - int64_t r = v1->vy - v0->vy; +static bool _lerpVertexY(const struct DSGXVertex* v0, const struct DSGXVertex* v1, struct DSGXVertex* out, int sign) { + int32_t q = v0->vw - sign * v0->vy; + int64_t r = q - (v1->vw - sign * v1->vy); + return _lerpVertex(v0, v1, out, q, r); +} + +static bool _lerpVertexZ(const struct DSGXVertex* v0, const struct DSGXVertex* v1, struct DSGXVertex* out, int sign) { + int32_t q = v0->vw - sign * v0->vz; + int64_t r = q - (v1->vw - sign * v1->vz); return _lerpVertex(v0, v1, out, q, r); } @@ -340,7 +334,38 @@ static bool _clipPolygon(struct DSGX* gx, struct DSGXPolygon* poly) { outOffscreenVerts[newV] = offscreenVerts[v]; ++newV; } else { - // TODO + struct DSGXVertex* in = &inList[v]; + struct DSGXVertex* in2; + struct DSGXVertex* out; + int iv; + + if (v > 0) { + iv = v - 1; + } else { + iv = poly->verts - 1; + } + if (!(offscreenVerts[iv] & CS_NEAR)) { + in2 = &inList[iv]; + out = &outList[newV]; + if (_lerpVertexZ(in, in2, out, -1)) { + outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); + ++newV; + } + } + + if (v < poly->verts - 1) { + iv = v + 1; + } else { + iv = 0; + } + if (!(offscreenVerts[iv] & CS_NEAR)) { + in2 = &inList[iv]; + out = &outList[newV]; + if (_lerpVertexZ(in, in2, out, -1)) { + outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); + ++newV; + } + } } } poly->verts = newV; @@ -355,7 +380,44 @@ static bool _clipPolygon(struct DSGX* gx, struct DSGXPolygon* poly) { outOffscreenVerts[newV] = offscreenVerts[v]; ++newV; } else { - // TODO + if (!(offscreenVerts[v] & CS_NEAR)) { + outList[newV] = inList[v]; + outOffscreenVerts[newV] = offscreenVerts[v]; + ++newV; + } else { + struct DSGXVertex* in = &inList[v]; + struct DSGXVertex* in2; + struct DSGXVertex* out; + int iv; + + if (v > 0) { + iv = v - 1; + } else { + iv = poly->verts - 1; + } + if (!(offscreenVerts[iv] & CS_FAR)) { + in2 = &inList[iv]; + out = &outList[newV]; + if (_lerpVertexZ(in, in2, out, 1)) { + outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); + ++newV; + } + } + + if (v < poly->verts - 1) { + iv = v + 1; + } else { + iv = 0; + } + if (!(offscreenVerts[iv] & CS_FAR)) { + in2 = &inList[iv]; + out = &outList[newV]; + if (_lerpVertexZ(in, in2, out, 1)) { + outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); + ++newV; + } + } + } } } poly->verts = newV; @@ -373,27 +435,34 @@ static bool _clipPolygon(struct DSGX* gx, struct DSGXPolygon* poly) { struct DSGXVertex* in = &inList[v]; struct DSGXVertex* in2; struct DSGXVertex* out; + int iv; - out = &outList[newV]; if (v > 0) { - in2 = &inList[v - 1]; + iv = v - 1; } else { - in2 = &inList[poly->verts - 1]; + iv = poly->verts - 1; } - if (_lerpVertexX(in, in2, out, gx->viewportX1 << 12)) { - outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); - ++newV; + if (!(offscreenVerts[iv] & CS_LEFT)) { + in2 = &inList[iv]; + out = &outList[newV]; + if (_lerpVertexX(in, in2, out, -1)) { + outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); + ++newV; + } } - out = &outList[newV]; if (v < poly->verts - 1) { - in2 = &inList[v + 1]; + iv = v + 1; } else { - in2 = &inList[0]; + iv = 0; } - if (_lerpVertexX(in, in2, out, gx->viewportX1 << 12)) { - outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); - ++newV; + if (!(offscreenVerts[iv] & CS_LEFT)) { + in2 = &inList[iv]; + out = &outList[newV]; + if (_lerpVertexX(in, in2, out, -1)) { + outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); + ++newV; + } } } } @@ -412,27 +481,34 @@ static bool _clipPolygon(struct DSGX* gx, struct DSGXPolygon* poly) { struct DSGXVertex* in = &inList[v]; struct DSGXVertex* in2; struct DSGXVertex* out; + int iv; - out = &outList[newV]; if (v > 0) { - in2 = &inList[v - 1]; + iv = v - 1; } else { - in2 = &inList[poly->verts - 1]; + iv = poly->verts - 1; } - if (_lerpVertexX(in2, in, out, gx->viewportX2 << 12)) { - outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); - ++newV; + if (!(offscreenVerts[iv] & CS_RIGHT)) { + in2 = &inList[iv]; + out = &outList[newV]; + if (_lerpVertexX(in, in2, out, 1)) { + outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); + ++newV; + } } - out = &outList[newV]; if (v < poly->verts - 1) { - in2 = &inList[v + 1]; + iv = v + 1; } else { - in2 = &inList[0]; + iv = 0; } - if (_lerpVertexX(in2, in, out, gx->viewportX2 << 12)) { - outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); - ++newV; + if (!(offscreenVerts[iv] & CS_RIGHT)) { + in2 = &inList[iv]; + out = &outList[newV]; + if (_lerpVertexX(in, in2, out, 1)) { + outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); + ++newV; + } } } } @@ -451,27 +527,34 @@ static bool _clipPolygon(struct DSGX* gx, struct DSGXPolygon* poly) { struct DSGXVertex* in = &inList[v]; struct DSGXVertex* in2; struct DSGXVertex* out; + int iv; - out = &outList[newV]; if (v > 0) { - in2 = &inList[v - 1]; + iv = v - 1; } else { - in2 = &inList[poly->verts - 1]; + iv = poly->verts - 1; } - if (_lerpVertexY(in, in2, out, gx->viewportY1 << 12)) { - outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); - ++newV; + if (!(offscreenVerts[iv] & CS_BOTTOM)) { + in2 = &inList[iv]; + out = &outList[newV]; + if (_lerpVertexY(in, in2, out, -1)) { + outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); + ++newV; + } } - out = &outList[newV]; if (v < poly->verts - 1) { - in2 = &inList[v + 1]; + iv = v + 1; } else { - in2 = &inList[0]; + iv = 0; } - if (_lerpVertexY(in, in2, out, gx->viewportY1 << 12)) { - outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); - ++newV; + if (!(offscreenVerts[iv] & CS_BOTTOM)) { + in2 = &inList[iv]; + out = &outList[newV]; + if (_lerpVertexY(in, in2, out, -1)) { + outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); + ++newV; + } } } } @@ -490,27 +573,34 @@ static bool _clipPolygon(struct DSGX* gx, struct DSGXPolygon* poly) { struct DSGXVertex* in = &inList[v]; struct DSGXVertex* in2; struct DSGXVertex* out; + int iv; - out = &outList[newV]; if (v > 0) { - in2 = &inList[v - 1]; + iv = v - 1; } else { - in2 = &inList[poly->verts - 1]; + iv = poly->verts - 1; } - if (_lerpVertexY(in2, in, out, gx->viewportY2 << 12)) { - outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); - ++newV; + if (!(offscreenVerts[iv] & CS_TOP)) { + in2 = &inList[iv]; + out = &outList[newV]; + if (_lerpVertexY(in, in2, out, 1)) { + outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); + ++newV; + } } - out = &outList[newV]; if (v < poly->verts - 1) { - in2 = &inList[v + 1]; + iv = v + 1; } else { - in2 = &inList[0]; + iv = 0; } - if (_lerpVertexY(in2, in, out, gx->viewportY2 << 12)) { - outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); - ++newV; + if (!(offscreenVerts[iv] & CS_TOP)) { + in2 = &inList[iv]; + out = &outList[newV]; + if (_lerpVertexY(in, in2, out, 1)) { + outOffscreenVerts[newV] = _cohenSutherlandCode(gx, out); + ++newV; + } } } } @@ -600,12 +690,6 @@ static void _emitVertex(struct DSGX* gx, uint16_t x, uint16_t y, uint16_t z) { gx->currentVertex.vz = _dotViewport(&gx->currentVertex, &gx->clipMatrix.m[2]); gx->currentVertex.vw = _dotViewport(&gx->currentVertex, &gx->clipMatrix.m[3]); - // TODO: What to do if w is 0? - - gx->currentVertex.vx = (gx->currentVertex.vx + gx->currentVertex.vw) * (int64_t) (gx->viewportWidth << 12) / (gx->currentVertex.vw * 2) + (gx->viewportX1 << 12); - gx->currentVertex.vy = (gx->currentVertex.vy + gx->currentVertex.vw) * (int64_t) (gx->viewportHeight << 12) / (gx->currentVertex.vw * 2) + (gx->viewportY1 << 12); - //gx->currentVertex.vw = 0x40000000 / gx->currentVertex.vw; - if (DSGXTexParamsGetCoordTfMode(gx->currentPoly.texParams) > 0) { int32_t m12 = gx->texMatrix.m[12]; int32_t m13 = gx->texMatrix.m[13]; @@ -723,7 +807,6 @@ static bool _boxTestVertex(struct DSGX* gx, struct DSGXVertex* vertex) { if (vz > vw) { return false; } - // TODO: depth clipping return true; } @@ -1259,8 +1342,12 @@ static void _fifoRun(struct mTiming* timing, void* context, uint32_t cyclesLate) gx->viewportY1 = (uint8_t) entry.params[1]; gx->viewportX2 = (uint8_t) entry.params[2]; gx->viewportY2 = (uint8_t) entry.params[3]; - gx->viewportWidth = gx->viewportX2 - gx->viewportX1; - gx->viewportHeight = gx->viewportY2 - gx->viewportY1; + gx->viewportWidth = gx->viewportX2 - gx->viewportX1 + 1; + gx->viewportHeight = gx->viewportY2 - gx->viewportY1 + 1; + gx->renderer->viewportX = gx->viewportX1; + gx->renderer->viewportY = gx->viewportY1; + gx->renderer->viewportWidth = gx->viewportWidth; + gx->renderer->viewportHeight = gx->viewportHeight; break; case DS_GX_CMD_BOX_TEST: gxstat = DSRegGXSTATClearTestBusy(gxstat); @@ -1343,8 +1430,8 @@ void DSGXReset(struct DSGX* gx) { gx->viewportY1 = 0; gx->viewportX2 = DS_VIDEO_HORIZONTAL_PIXELS - 1; gx->viewportY2 = DS_VIDEO_VERTICAL_PIXELS - 1; - gx->viewportWidth = gx->viewportX2 - gx->viewportX1; - gx->viewportHeight = gx->viewportY2 - gx->viewportY1; + gx->viewportWidth = gx->viewportX2 - gx->viewportX1 + 1; + gx->viewportHeight = gx->viewportY2 - gx->viewportY1 + 1; memset(gx->outstandingParams, 0, sizeof(gx->outstandingParams)); memset(gx->outstandingCommand, 0, sizeof(gx->outstandingCommand)); diff --git a/src/ds/gx/software.c b/src/ds/gx/software.c index bb32ebbc2..0aae12cdd 100644 --- a/src/ds/gx/software.c +++ b/src/ds/gx/software.c @@ -8,8 +8,6 @@ #include #include "gba/renderers/software-private.h" -#define SCREEN_SIZE ((DS_VIDEO_VERTICAL_PIXELS - 1) << 12) - DEFINE_VECTOR(DSGXSoftwarePolygonList, struct DSGXSoftwarePolygon); DEFINE_VECTOR(DSGXSoftwareEdgeList, struct DSGXSoftwareEdge); DEFINE_VECTOR(DSGXSoftwareSpanList, struct DSGXSoftwareSpan); @@ -250,7 +248,7 @@ static color_t _lookupColor(struct DSGXSoftwareRenderer* renderer, struct DSGXSo static bool _edgeToSpan(struct DSGXSoftwareSpan* span, const struct DSGXSoftwareEdge* edge, int index, int32_t y) { int32_t height = edge->y1 - edge->y0; - int64_t yw = (y << 12) - edge->y0; + int64_t yw = y - edge->y0; if (!height) { return false; } @@ -260,15 +258,21 @@ static bool _edgeToSpan(struct DSGXSoftwareSpan* span, const struct DSGXSoftware } else if (yw > height) { return false; } - yw *= 0x100000000LL / height; + yw *= 0x100000000LL; + yw /= height; span->ep[index].x = (((int64_t) (edge->x1 - edge->x0) * yw) >> 32) + edge->x0; - if (index && span->ep[0].x > span->ep[index].x) { - int32_t temp = span->ep[index].x; - span->ep[index] = span->ep[0]; - span->ep[0].x = temp; - index = 0; + if (index) { + if (span->ep[0].x == span->ep[index].x) { + return false; + } + if (span->ep[0].x > span->ep[index].x) { + int32_t temp = span->ep[index].x; + span->ep[index] = span->ep[0]; + span->ep[0].x = temp; + index = 0; + } } int32_t w0 = edge->w0; int32_t w1 = edge->w1; @@ -291,7 +295,7 @@ static bool _edgeToSpan(struct DSGXSoftwareSpan* span, const struct DSGXSoftware static void _lerpEndpoint(const struct DSGXSoftwareSpan* span, struct DSGXSoftwareEndpoint* ep, unsigned x) { int64_t width = span->ep[1].x - span->ep[0].x; - int64_t xw = ((uint64_t) x << 12) - span->ep[0].x; + int64_t xw = x - span->ep[0].x; if (!width) { return; // TODO? } @@ -301,7 +305,8 @@ static void _lerpEndpoint(const struct DSGXSoftwareSpan* span, struct DSGXSoftwa } else if (xw > width) { xw = width; } - xw *= 0x100000000LL / width; + xw *= 0x100000000LL; + xw /= width; int32_t w0 = span->ep[0].w; int32_t w1 = span->ep[1].w; int64_t w = (((int64_t) (w1 - w0) * xw) >> 32) + w0; @@ -402,36 +407,42 @@ static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSG struct DSGXVertex* v0 = &verts[poly->poly->vertIds[0]]; struct DSGXVertex* v1; + int32_t v0x = (v0->vx + v0->vw) * (int64_t) renderer->viewportWidth / (v0->vw * 2) + renderer->viewportX; + int32_t v0y = (-v0->vy + v0->vw) * (int64_t) renderer->viewportHeight / (v0->vw * 2) + renderer->viewportY; + int v; for (v = 1; v < poly->poly->verts; ++v) { v1 = &verts[poly->poly->vertIds[v]]; - if (v0->vy >= v1->vy) { - edge->y0 = SCREEN_SIZE - v0->vy; - edge->x0 = v0->vx; + int32_t v1x = (v1->vx + v1->vw) * (int64_t) renderer->viewportWidth / (v1->vw * 2) + renderer->viewportX; + int32_t v1y = (-v1->vy + v1->vw) * (int64_t) renderer->viewportHeight / (v1->vw * 2) + renderer->viewportY; + + if (v0y <= v1y) { + edge->y0 = v0y; + edge->x0 = v0x; edge->z0 = v0->vz; edge->w0 = v0->vw; _expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0); edge->s0 = v0->vs; edge->t0 = v0->vt; - edge->y1 = SCREEN_SIZE - v1->vy; - edge->x1 = v1->vx; + edge->y1 = v1y; + edge->x1 = v1x; edge->z1 = v1->vz; edge->w1 = v1->vw; _expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1); edge->s1 = v1->vs; edge->t1 = v1->vt; } else { - edge->y0 = SCREEN_SIZE - v1->vy; - edge->x0 = v1->vx; + edge->y0 = v1y; + edge->x0 = v1x; edge->z0 = v1->vz; edge->w0 = v1->vw; _expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0); edge->s0 = v1->vs; edge->t0 = v1->vt; - edge->y1 = SCREEN_SIZE - v0->vy; - edge->x1 = v0->vx; + edge->y1 = v0y; + edge->x1 = v0x; edge->z1 = v0->vz; edge->w1 = v0->vw; _expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1); @@ -442,36 +453,41 @@ static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSG edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges); edge->polyId = i; v0 = v1; + v0x = v1x; + v0y = v1y; } v1 = &verts[poly->poly->vertIds[0]]; - if (v0->vy >= v1->vy) { - edge->y0 = SCREEN_SIZE - v0->vy; - edge->x0 = v0->vx; + int32_t v1x = (v1->vx + v1->vw) * (int64_t) renderer->viewportWidth / (v1->vw * 2) + renderer->viewportX; + int32_t v1y = (-v1->vy + v1->vw) * (int64_t) renderer->viewportHeight / (v1->vw * 2) + renderer->viewportY; + + if (v0y <= v1y) { + edge->y0 = v0y; + edge->x0 = v0x; edge->z0 = v0->vz; edge->w0 = v0->vw; _expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0); edge->s0 = v0->vs; edge->t0 = v0->vt; - edge->y1 = SCREEN_SIZE - v1->vy; - edge->x1 = v1->vx; + edge->y1 = v1y; + edge->x1 = v1x; edge->z1 = v1->vz; edge->w1 = v1->vw; _expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1); edge->s1 = v1->vs; edge->t1 = v1->vt; } else { - edge->y0 = SCREEN_SIZE - v1->vy; - edge->x0 = v1->vx; - edge->w0 = v1->vw; + edge->y0 = v1y; + edge->x0 = v1x; edge->z0 = v1->vz; + edge->w0 = v1->vw; _expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0); edge->s0 = v1->vs; edge->t0 = v1->vt; - edge->y1 = SCREEN_SIZE - v0->vy; - edge->x1 = v0->vx; + edge->y1 = v0y; + edge->x1 = v0x; edge->z1 = v0->vz; edge->w1 = v0->vw; _expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1); @@ -491,9 +507,9 @@ static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int size_t i; for (i = 0; i < DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges); ++i) { struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, i); - if (edge->y1 >> 12 < y) { + if (edge->y1 < y) { continue; - } else if (edge->y0 >> 12 > y) { + } else if (edge->y0 > y) { continue; } @@ -528,11 +544,11 @@ static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int for (i = 0; i < DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans); ++i) { struct DSGXSoftwareSpan* span = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, i); - int32_t x = span->ep[0].x >> 12; + int32_t x = span->ep[0].x; if (x < 0) { x = 0; } - for (; x < span->ep[1].x >> 12 && x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) { + for (; x < span->ep[1].x && x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) { struct DSGXSoftwareEndpoint ep; _lerpEndpoint(span, &ep, x); color_t color = _lookupColor(softwareRenderer, &ep, span->poly);