mirror of https://github.com/mgba-emu/mgba.git
DS GX: Calculate needed spans
This commit is contained in:
parent
1cf587c6c3
commit
c0c7754ed8
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)) {
|
||||
|
|
Loading…
Reference in New Issue