all repos — mgba @ c0c7754ed8d26666042b2d2d4d1d1e606e866392

mGBA Game Boy Advance Emulator

DS GX: Calculate needed spans
Vicki Pfau vi@endrift.com
Tue, 28 Feb 2017 16:10:55 -0800
commit

c0c7754ed8d26666042b2d2d4d1d1e606e866392

parent

1cf587c6c326bca26fc15f038719448995c76b76

M include/mgba/internal/ds/gx.hinclude/mgba/internal/ds/gx.h

@@ -97,8 +97,8 @@ int32_t vy; // 16.16

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 };
M include/mgba/internal/ds/gx/software.hinclude/mgba/internal/ds/gx/software.h

@@ -12,6 +12,7 @@ CXX_GUARD_START

#include <mgba/internal/ds/gx.h> #include <mgba/internal/ds/video.h> +#include <mgba-util/table.h> #include <mgba-util/vector.h> struct DSGXSoftwarePolygon {

@@ -22,30 +23,55 @@ int32_t topZ;

}; 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;
M src/ds/gx.csrc/ds/gx.c

@@ -171,8 +171,10 @@ gx->currentVertex.vy = _dotViewport(&gx->currentVertex, &gx->clipMatrix.m[1]);

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;
M src/ds/gx/software.csrc/ds/gx/software.c

@@ -7,8 +7,11 @@ #include <mgba/internal/ds/gx/software.h>

#include <mgba-util/memory.h> +#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);

@@ -16,21 +19,83 @@ static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer);

static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount); 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->y0 > eb->y0) { + if (ea->y1 < eb->y1) { return 1; } - if (ea->y1 < eb->y1) { + if (ea->y1 > eb->y1) { return -1; } - if (ea->y1 > eb->y1) { + 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 @@ for (i = 0; i < polyCount; ++i) {

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 @@

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 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) {
M src/ds/video.csrc/ds/video.c

@@ -319,8 +319,14 @@ mTimingSchedule(timing, &video->event9, (DS9_VIDEO_HBLANK_LENGTH * 2) - 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)) {