diff --git a/include/mgba/internal/ds/gx.h b/include/mgba/internal/ds/gx.h index 857c09ce3..004e13b18 100644 --- a/include/mgba/internal/ds/gx.h +++ b/include/mgba/internal/ds/gx.h @@ -97,8 +97,8 @@ struct DSGXVertex { int32_t vz; // 16.16 int32_t vw; // 16.16 - uint16_t color; - // Texcoords + // Color/Texcoords + uint16_t color; // 5.5.5 int16_t s; // 12.4 int16_t t; // 12.4 }; diff --git a/include/mgba/internal/ds/gx/software.h b/include/mgba/internal/ds/gx/software.h index 54aafc2c2..32edb07fa 100644 --- a/include/mgba/internal/ds/gx/software.h +++ b/include/mgba/internal/ds/gx/software.h @@ -12,6 +12,7 @@ CXX_GUARD_START #include #include +#include #include struct DSGXSoftwarePolygon { @@ -22,30 +23,55 @@ struct DSGXSoftwarePolygon { }; struct DSGXSoftwareEdge { - struct DSGXPolygon* poly; - int32_t y0; - int32_t x0; - int32_t w0; - int32_t c0; // 6.6.6.6 ARGB + unsigned polyId; + int32_t y0; // 20.12 + int32_t x0; // 20.12 + int32_t w0; // 20.12 + int8_t cr0; + int8_t cg0; + int8_t cb0; int16_t s0; int16_t t0; - int32_t y1; - int32_t x1; - int32_t w1; - int32_t c1; // 6.6.6.6 ARGB + int32_t y1; // 20.12 + int32_t x1; // 20.12 + int32_t w1; // 20.12 + int8_t cr1; + int8_t cg1; + int8_t cb1; + int16_t s1; + int16_t t1; +}; + +struct DSGXSoftwareSpan { + int32_t x0; // 20.12 + int32_t w0; // 20.12 + int8_t cr0; + int8_t cg0; + int8_t cb0; + int16_t s0; + int16_t t0; + + int32_t x1; // 20.12 + int32_t w1; // 20.12 + int8_t cr1; + int8_t cg1; + int8_t cb1; int16_t s1; int16_t t1; }; DECLARE_VECTOR(DSGXSoftwarePolygonList, struct DSGXSoftwarePolygon); DECLARE_VECTOR(DSGXSoftwareEdgeList, struct DSGXSoftwareEdge); +DECLARE_VECTOR(DSGXSoftwareSpanList, struct DSGXSoftwareSpan); struct DSGXSoftwareRenderer { struct DSGXRenderer d; struct DSGXSoftwarePolygonList activePolys; struct DSGXSoftwareEdgeList activeEdges; + struct DSGXSoftwareSpanList activeSpans; + struct Table bucket; uint16_t depthBuffer[DS_VIDEO_HORIZONTAL_PIXELS]; color_t* scanlineCache; diff --git a/src/ds/gx.c b/src/ds/gx.c index d04521bc4..24984f69e 100644 --- a/src/ds/gx.c +++ b/src/ds/gx.c @@ -171,8 +171,10 @@ 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]); - gx->currentVertex.vx = (gx->currentVertex.vx + gx->currentVertex.vw) * gx->viewportWidth / (gx->currentVertex.vw * 2) + gx->viewportX1; - gx->currentVertex.vy = (gx->currentVertex.vy + gx->currentVertex.vw) * gx->viewportHeight / (gx->currentVertex.vw * 2) + gx->viewportY1; + // 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); struct DSGXVertex* vbuf = gx->vertexBuffer[gx->bufferIndex]; vbuf[gx->vertexIndex] = gx->currentVertex; diff --git a/src/ds/gx/software.c b/src/ds/gx/software.c index cdb87443a..bdd71b698 100644 --- a/src/ds/gx/software.c +++ b/src/ds/gx/software.c @@ -7,8 +7,11 @@ #include +#define SCREEN_SIZE (DS_VIDEO_VERTICAL_PIXELS << 12) + DEFINE_VECTOR(DSGXSoftwarePolygonList, struct DSGXSoftwarePolygon); DEFINE_VECTOR(DSGXSoftwareEdgeList, struct DSGXSoftwareEdge); +DEFINE_VECTOR(DSGXSoftwareSpanList, struct DSGXSoftwareSpan); static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer); static void DSGXSoftwareRendererReset(struct DSGXRenderer* renderer); @@ -17,20 +20,82 @@ static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSG static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y); static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output); +static void _expandColor(uint16_t c15, int8_t* r, int8_t* g, int8_t* b) { + *r = ((c15 << 1) & 0x3E) | 1; + *g = ((c15 >> 4) & 0x3E) | 1; + *b = ((c15 >> 9) & 0x3E) | 1; +} + static int _edgeSort(const void* a, const void* b) { const struct DSGXSoftwareEdge* ea = a; const struct DSGXSoftwareEdge* eb = b; + // Sort upside down if (ea->y0 < eb->y0) { - return -1; - } - if (ea->y0 > eb->y0) { return 1; } - if (ea->y1 < eb->y1) { + if (ea->y0 > eb->y0) { return -1; } + if (ea->y1 < eb->y1) { + return 1; + } if (ea->y1 > eb->y1) { + return -1; + } + return 0; +} + +static bool _edgeToSpan(struct DSGXSoftwareSpan* span, const struct DSGXSoftwareEdge* edge, int index, int32_t y) { + // TODO: Perspective correction + int32_t height = edge->y1 - edge->y0; + int64_t yw = (y << 12) - edge->y0; + if (height) { + yw <<= 19; + yw /= height; + } else { + return false; + } + // Clamp to bounds + if (yw < 0) { + yw = 0; + } else if (yw > height) { + yw = height; + } + if (!index) { + span->x0 = ((int64_t) (edge->x1 - edge->x0) * yw) / height + edge->x0; + span->w0 = ((int64_t) (edge->w1 - edge->w0) * yw) / height + edge->w0; + span->cr0 = ((int32_t) (edge->cr1 - edge->cr0) * yw) / height + edge->cr0; + span->cg0 = ((int32_t) (edge->cg1 - edge->cg0) * yw) / height + edge->cg0; + span->cb0 = ((int32_t) (edge->cb1 - edge->cb0) * yw) / height + edge->cb0; + span->s0 = ((int32_t) (edge->s1 - edge->s0) * yw) / height + edge->s0; + span->t0 = ((int32_t) (edge->t1 - edge->t0) * yw) / height + edge->t0; + } else { + span->x1 = ((int64_t) (edge->x1 - edge->x0) * yw) / height + edge->x0; + span->w1 = ((int64_t) (edge->w1 - edge->w0) * yw) / height + edge->w0; + span->cr1 = ((int32_t) (edge->cr1 - edge->cr0) * yw) / height + edge->cr0; + span->cg1 = ((int32_t) (edge->cg1 - edge->cg0) * yw) / height + edge->cg0; + span->cb1 = ((int32_t) (edge->cb1 - edge->cb0) * yw) / height + edge->cb0; + span->s1 = ((int32_t) (edge->s1 - edge->s0) * yw) / height + edge->s0; + span->t1 = ((int32_t) (edge->t1 - edge->t0) * yw) / height + edge->t0; + } + return true; +} + +static int _spanSort(const void* a, const void* b) { + const struct DSGXSoftwareSpan* sa = a; + const struct DSGXSoftwareSpan* sb = b; + + if (sa->x0 < sb->x0) { + return -1; + } + if (sa->x0 > sb->x0) { + return 1; + } + if (sa->x1 < sb->x1) { + return -1; + } + if (sa->x1 > sb->x1) { return 1; } return 0; @@ -49,6 +114,8 @@ static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer) { struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer; DSGXSoftwarePolygonListInit(&softwareRenderer->activePolys, DS_GX_POLYGON_BUFFER_SIZE / 4); DSGXSoftwareEdgeListInit(&softwareRenderer->activeEdges, DS_GX_POLYGON_BUFFER_SIZE); + DSGXSoftwareSpanListInit(&softwareRenderer->activeSpans, DS_GX_POLYGON_BUFFER_SIZE / 2); + TableInit(&softwareRenderer->bucket, DS_GX_POLYGON_BUFFER_SIZE / 8, NULL); softwareRenderer->scanlineCache = anonymousMemoryMap(sizeof(color_t) * DS_VIDEO_HORIZONTAL_PIXELS * 48); } @@ -61,6 +128,8 @@ static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer) { struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer; DSGXSoftwarePolygonListDeinit(&softwareRenderer->activePolys); DSGXSoftwareEdgeListDeinit(&softwareRenderer->activeEdges); + DSGXSoftwareSpanListDeinit(&softwareRenderer->activeSpans); + TableDeinit(&softwareRenderer->bucket); mappedMemoryFree(softwareRenderer->scanlineCache, sizeof(color_t) * DS_VIDEO_HORIZONTAL_PIXELS * 48); } @@ -75,6 +144,7 @@ static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSG struct DSGXSoftwarePolygon* poly = DSGXSoftwarePolygonListAppend(&softwareRenderer->activePolys); struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges); poly->poly = &polys[i]; + edge->polyId = i; struct DSGXVertex* v0 = &verts[poly->poly->vertIds[0]]; struct DSGXVertex* v1; @@ -82,67 +152,68 @@ static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSG int v; for (v = 1; v < poly->poly->verts; ++v) { v1 = &verts[poly->poly->vertIds[v]]; - if (v0->vy <= v1->vy) { - edge->y0 = v0->vy; + if (v0->vy >= v1->vy) { + edge->y0 = SCREEN_SIZE - v0->vy; edge->x0 = v0->vx; edge->w0 = v0->vw; - edge->c0 = v0->color; + _expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0); edge->s0 = v0->s; edge->t0 = v0->t; - edge->y1 = v1->vy; + edge->y1 = SCREEN_SIZE - v1->vy; edge->x1 = v1->vx; edge->w1 = v1->vw; - edge->c1 = v1->color; + _expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1); edge->s1 = v1->s; edge->t1 = v1->t; } else { - edge->y0 = v1->vy; + edge->y0 = SCREEN_SIZE - v1->vy; edge->x0 = v1->vx; edge->w0 = v1->vw; - edge->c0 = v1->color; + _expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0); edge->s0 = v1->s; edge->t0 = v1->t; - edge->y1 = v0->vy; + edge->y1 = SCREEN_SIZE - v0->vy; edge->x1 = v0->vx; edge->w1 = v0->vw; - edge->c1 = v0->color; + _expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1); edge->s1 = v0->s; edge->t1 = v0->t; } edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges); + edge->polyId = i; v0 = v1; } v1 = &verts[poly->poly->vertIds[0]]; - if (v0->vy <= v1->vy) { - edge->y0 = v0->vy; + if (v0->vy >= v1->vy) { + edge->y0 = SCREEN_SIZE - v0->vy; edge->x0 = v0->vx; edge->w0 = v0->vw; - edge->c0 = v0->color; + _expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0); edge->s0 = v0->s; edge->t0 = v0->t; - edge->y1 = v1->vy; + edge->y1 = SCREEN_SIZE - v1->vy; edge->x1 = v1->vx; edge->w1 = v1->vw; - edge->c1 = v1->color; + _expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1); edge->s1 = v1->s; edge->t1 = v1->t; } else { - edge->y0 = v1->vy; + edge->y0 = SCREEN_SIZE - v1->vy; edge->x0 = v1->vx; edge->w0 = v1->vw; - edge->c0 = v1->color; + _expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0); edge->s0 = v1->s; edge->t0 = v1->t; - edge->y1 = v0->vy; + edge->y1 = SCREEN_SIZE - v0->vy; edge->x1 = v0->vx; edge->w1 = v0->vw; - edge->c1 = v0->color; + _expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1); edge->s1 = v0->s; edge->t1 = v0->t; } @@ -152,7 +223,35 @@ static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSG static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y) { struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer; - // TODO + DSGXSoftwareSpanListClear(&softwareRenderer->activeSpans); + TableClear(&softwareRenderer->bucket); + size_t i; + for (i = DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges); i; --i) { + size_t idx = i - 1; + struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, idx); + if (edge->y1 >> 12 < y) { + DSGXSoftwareEdgeListShift(&softwareRenderer->activeEdges, idx, 1); + continue; + } else if (edge->y0 >> 12 > y) { + continue; + } + + unsigned poly = edge->polyId; + struct DSGXSoftwareSpan* span = TableLookup(&softwareRenderer->bucket, poly); + if (span) { + _edgeToSpan(span, edge, 1, y); + TableRemove(&softwareRenderer->bucket, poly); + } else { + span = DSGXSoftwareSpanListAppend(&softwareRenderer->activeSpans); + if (!_edgeToSpan(span, edge, 0, y)) { + // Horizontal line + DSGXSoftwareSpanListShift(&softwareRenderer->activeSpans, DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans) - 1, 1); + } else { + TableInsert(&softwareRenderer->bucket, poly, span); + } + } + } + qsort(DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, 0), DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans), sizeof(struct DSGXSoftwareSpan), _spanSort); } static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output) { diff --git a/src/ds/video.c b/src/ds/video.c index 38ee90fdf..7075cc4d7 100644 --- a/src/ds/video.c +++ b/src/ds/video.c @@ -319,8 +319,14 @@ void _startHblank9(struct mTiming* timing, void* context, uint32_t cyclesLate) { // Begin Hblank dispstat = GBARegisterDISPSTATFillInHblank(dispstat); - if (video->vcount < DS_VIDEO_VERTICAL_PIXELS && video->frameskipCounter <= 0) { - video->renderer->drawScanline(video->renderer, video->vcount); + if (video->frameskipCounter <= 0) { + if (video->vcount < DS_VIDEO_VERTICAL_PIXELS) { + video->p->gx.renderer->drawScanline(video->p->gx.renderer, video->vcount + 48); + video->renderer->drawScanline(video->renderer, video->vcount); + } + if (video->vcount >= DS_VIDEO_VERTICAL_TOTAL_PIXELS - 48) { + video->p->gx.renderer->drawScanline(video->p->gx.renderer, video->vcount + 48 - DS_VIDEO_VERTICAL_TOTAL_PIXELS); + } } if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {