DS GX: Calculate needed spans

This commit is contained in:
Vicki Pfau 2017-02-28 16:10:55 -08:00
parent 1cf587c6c3
commit c0c7754ed8
5 changed files with 171 additions and 38 deletions

View File

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

View File

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

View File

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

View File

@ -7,8 +7,11 @@
#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);
@ -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) {

View File

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