all repos — mgba @ 908b0a425ec5e33391b47ac674e7cef9de01e8bc

mGBA Game Boy Advance Emulator

src/ds/gx/software.c (view raw)

  1/* Copyright (c) 2013-2017 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include <mgba/internal/ds/gx/software.h>
  7
  8#include <mgba-util/memory.h>
  9#include "gba/renderers/software-private.h"
 10
 11#define SCREEN_SIZE (DS_VIDEO_VERTICAL_PIXELS << 12)
 12
 13DEFINE_VECTOR(DSGXSoftwarePolygonList, struct DSGXSoftwarePolygon);
 14DEFINE_VECTOR(DSGXSoftwareEdgeList, struct DSGXSoftwareEdge);
 15DEFINE_VECTOR(DSGXSoftwareSpanList, struct DSGXSoftwareSpan);
 16
 17static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer);
 18static void DSGXSoftwareRendererReset(struct DSGXRenderer* renderer);
 19static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer);
 20static void DSGXSoftwareRendererInvalidateTex(struct DSGXRenderer* renderer, int slot);
 21static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount);
 22static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y);
 23static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output);
 24
 25static void _expandColor(uint16_t c15, uint8_t* r, uint8_t* g, uint8_t* b) {
 26	*r = ((c15 << 1) & 0x3E) | 1;
 27	*g = ((c15 >> 4) & 0x3E) | 1;
 28	*b = ((c15 >> 9) & 0x3E) | 1;
 29}
 30
 31static color_t _finishColor(uint8_t r, uint8_t g, uint8_t b) {
 32#ifndef COLOR_16_BIT
 33	color_t rgb = (r << 2) & 0xF8;
 34	rgb |= (g << 10) & 0xF800;
 35	rgb |= (b << 18) & 0xF80000;
 36	return rgb;
 37#else
 38#error Unsupported color depth
 39#endif
 40}
 41
 42static color_t _lookupColor(struct DSGXSoftwareEndpoint* ep, struct DSGXSoftwarePolygon* poly) {
 43	// TODO: Optimize
 44	uint16_t texel;
 45
 46	int16_t s = ep->s >> 4;
 47	int16_t t = ep->t >> 4;
 48	if (!DSGXTexParamsIsSRepeat(poly->poly->texParams)) {
 49		if (s < 0) {
 50			s = 0;
 51		} else if (s >= poly->texW) {
 52			s = poly->texW - 1;
 53		}
 54	} else if (DSGXTexParamsIsSMirror(poly->poly->texParams)) {
 55		if (s & poly->texW) {
 56			s = poly->texW - s;
 57		}
 58		s &= poly->texW - 1;
 59	} else {
 60		s &= poly->texW - 1;
 61	}
 62	if (!DSGXTexParamsIsTRepeat(poly->poly->texParams)) {
 63		if (t < 0) {
 64			t = 0;
 65		} else if (t >= poly->texH) {
 66			t = poly->texW - 1;
 67		}
 68	} else if (DSGXTexParamsIsTMirror(poly->poly->texParams)) {
 69		if (t & poly->texH) {
 70			t = poly->texH - t;
 71		}
 72		t &= poly->texH - 1;
 73	} else {
 74		t &= poly->texH - 1;
 75	}
 76
 77	uint16_t texelCoord = s + t * poly->texW;
 78	switch (poly->texFormat) {
 79	case 0:
 80	default:
 81		return _finishColor(ep->cr, ep->cg, ep->cb);
 82	case 1:
 83		return _finishColor(0, 0, 0x3F);
 84	case 2:
 85		return _finishColor(0, 0x3F, 0);
 86	case 3:
 87		texel = ((uint8_t*) poly->texBase)[texelCoord >> 1];
 88		if ((ep->s >> 4) & 0x1) {
 89			texel >>= 4;
 90		}
 91		texel &= 0xF;
 92		break;
 93	case 4:
 94		texel = ((uint8_t*) poly->texBase)[texelCoord];
 95		break;
 96	case 5:
 97		return _finishColor(0x3F, 0, 0x3F);
 98	case 6:
 99		return _finishColor(0x3F, 0x3F, 0);
100	case 7:
101		return _finishColor(0x3F, 0x3F, 0x3F);
102	}
103	if (DSGXTexParamsIs0Transparent(poly->poly->texParams) && !texel) {
104		return FLAG_UNWRITTEN;
105	}
106	uint8_t r, g, b;
107	texel = poly->palBase[texel];
108	_expandColor(texel, &r, &g, &b);
109	return _finishColor(r, g, b);
110}
111
112static int _edgeSort(const void* a, const void* b) {
113	const struct DSGXSoftwareEdge* ea = a;
114	const struct DSGXSoftwareEdge* eb = b;
115
116	// Sort upside down
117	if (ea->y0 < eb->y0) {
118		return 1;
119	}
120	if (ea->y0 > eb->y0) {
121		return -1;
122	}
123	if (ea->y1 < eb->y1) {
124		return 1;
125	}
126	if (ea->y1 > eb->y1) {
127		return -1;
128	}
129	return 0;
130}
131
132static bool _edgeToSpan(struct DSGXSoftwareSpan* span, const struct DSGXSoftwareEdge* edge, int index, int32_t y) {
133	int32_t height = edge->y1 - edge->y0;
134	int64_t yw = (y << 12) - edge->y0;
135	if (!height) {
136		return false;
137	}
138	// Clamp to bounds
139	if (yw < 0) {
140		yw = 0;
141	} else if (yw > height) {
142		yw = height;
143	}
144	int64_t heightRecip = 0x100000000LL / height;
145	span->ep[index].x = ((((int64_t) (edge->x1 - edge->x0) * yw) * heightRecip) >> 32) + edge->x0;
146	if (index && span->ep[0].x > span->ep[index].x) {
147		int32_t temp = span->ep[index].x;
148		span->ep[index] = span->ep[0];
149		span->ep[0].x = temp;
150		index = 0;
151	}
152	int32_t w = ((((int64_t) (edge->w1 - edge->w0) * yw) * heightRecip) >> 32) + edge->w0;
153	int64_t wRecip = 0x1000000000000LL / w;
154	span->ep[index].w = w;
155	span->ep[index].cr = ((((((int32_t) (edge->cr1 * edge->w1 - edge->cr0 * edge->w0) * yw) * heightRecip) >> 32) + edge->cr0 * edge->w0) * wRecip) >> 48;
156	span->ep[index].cg = ((((((int32_t) (edge->cg1 * edge->w1 - edge->cg0 * edge->w0) * yw) * heightRecip) >> 32) + edge->cg0 * edge->w0) * wRecip) >> 48;
157	span->ep[index].cb = ((((((int32_t) (edge->cb1 * edge->w1 - edge->cb0 * edge->w0) * yw) * heightRecip) >> 32) + edge->cb0 * edge->w0) * wRecip) >> 48;
158	span->ep[index].s = ((((((int32_t) (edge->s1 * edge->w1 - edge->s0 * edge->w0) * yw) * heightRecip) >> 32) + edge->s0 * edge->w0) * wRecip) >> 48;
159	span->ep[index].t = ((((((int32_t) (edge->t1 * edge->w1 - edge->t0 * edge->w0) * yw) * heightRecip) >> 32) + edge->t0 * edge->w0) * wRecip) >> 48;
160
161	return true;
162}
163
164static int _spanSort(const void* a, const void* b) {
165	const struct DSGXSoftwareSpan* sa = a;
166	const struct DSGXSoftwareSpan* sb = b;
167
168	// Sort backwards
169	if (sa->ep[0].x < sb->ep[0].x) {
170		return 1;
171	}
172	if (sa->ep[0].x > sb->ep[0].x) {
173		return -1;
174	}
175	if (sa->ep[0].w < sb->ep[0].w) {
176		return 1;
177	}
178	if (sa->ep[0].w > sb->ep[0].w) {
179		return -1;
180	}
181	return 0;
182}
183
184static void _lerpEndpoint(const struct DSGXSoftwareSpan* span, struct DSGXSoftwareEndpoint* ep, unsigned x) {
185	int64_t width = span->ep[1].x - span->ep[0].x;
186	int64_t xw = ((uint64_t) x << 12) - span->ep[0].x;
187	if (!width) {
188		return; // TODO?
189	}
190	// Clamp to bounds
191	if (xw < 0) {
192		xw = 0;
193	} else if (xw > width) {
194		xw = width;
195	}
196	int32_t w0 = span->ep[0].w;
197	int32_t w1 = span->ep[1].w;
198	int64_t widthRecip = 0x100000000LL / width;
199	int32_t w = ((((int64_t) (w1 - w0) * xw) * widthRecip) >> 32) + w0;
200	ep->w = w;
201	int64_t wRecip = 0x1000000000000LL / w;
202
203	uint64_t r = ((((span->ep[1].cr * (int64_t) w1 - span->ep[0].cr * (int64_t) w0) * xw) * widthRecip) >> 32) + span->ep[0].cr * (int64_t) w0;
204	uint64_t g = ((((span->ep[1].cg * (int64_t) w1 - span->ep[0].cg * (int64_t) w0) * xw) * widthRecip) >> 32) + span->ep[0].cg * (int64_t) w0;
205	uint64_t b = ((((span->ep[1].cb * (int64_t) w1 - span->ep[0].cb * (int64_t) w0) * xw) * widthRecip) >> 32) + span->ep[0].cb * (int64_t) w0;
206	ep->cr = (r * wRecip) >> 48;
207	ep->cg = (g * wRecip) >> 48;
208	ep->cb = (b * wRecip) >> 48;
209
210	int32_t s = ((((span->ep[1].s * (int64_t) w1 - span->ep[0].s * (int64_t) w0) * xw) * widthRecip) >> 32) + span->ep[0].s * (int64_t) w0;
211	int32_t t = ((((span->ep[1].t * (int64_t) w1 - span->ep[0].t * (int64_t) w0) * xw) * widthRecip) >> 32) + span->ep[0].t * (int64_t) w0;
212	ep->s = (s * wRecip) >> 48;
213	ep->t = (t * wRecip) >> 48;
214}
215
216void DSGXSoftwareRendererCreate(struct DSGXSoftwareRenderer* renderer) {
217	renderer->d.init = DSGXSoftwareRendererInit;
218	renderer->d.reset = DSGXSoftwareRendererReset;
219	renderer->d.deinit = DSGXSoftwareRendererDeinit;
220	renderer->d.invalidateTex = DSGXSoftwareRendererInvalidateTex;
221	renderer->d.setRAM = DSGXSoftwareRendererSetRAM;
222	renderer->d.drawScanline = DSGXSoftwareRendererDrawScanline;
223	renderer->d.getScanline = DSGXSoftwareRendererGetScanline;
224}
225
226static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer) {
227	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
228	DSGXSoftwarePolygonListInit(&softwareRenderer->activePolys, DS_GX_POLYGON_BUFFER_SIZE / 4);
229	DSGXSoftwareEdgeListInit(&softwareRenderer->activeEdges, DS_GX_POLYGON_BUFFER_SIZE);
230	DSGXSoftwareSpanListInit(&softwareRenderer->activeSpans, DS_GX_POLYGON_BUFFER_SIZE / 2);
231	softwareRenderer->bucket = anonymousMemoryMap(sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
232	softwareRenderer->scanlineCache = anonymousMemoryMap(sizeof(color_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
233}
234
235static void DSGXSoftwareRendererReset(struct DSGXRenderer* renderer) {
236	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
237	softwareRenderer->flushPending = false;
238}
239
240static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer) {
241	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
242	DSGXSoftwarePolygonListDeinit(&softwareRenderer->activePolys);
243	DSGXSoftwareEdgeListDeinit(&softwareRenderer->activeEdges);	
244	DSGXSoftwareSpanListDeinit(&softwareRenderer->activeSpans);
245	mappedMemoryFree(softwareRenderer->bucket, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
246	mappedMemoryFree(softwareRenderer->scanlineCache, sizeof(color_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
247}
248
249static void DSGXSoftwareRendererInvalidateTex(struct DSGXRenderer* renderer, int slot) {
250	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
251	// TODO
252}
253
254static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount) {
255	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
256
257	softwareRenderer->flushPending = true;
258	softwareRenderer->verts = verts;
259	DSGXSoftwarePolygonListClear(&softwareRenderer->activePolys);
260	DSGXSoftwareEdgeListClear(&softwareRenderer->activeEdges);
261	unsigned i;
262	for (i = 0; i < polyCount; ++i) {
263		struct DSGXSoftwarePolygon* poly = DSGXSoftwarePolygonListAppend(&softwareRenderer->activePolys);
264		struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
265		poly->poly = &polys[i];
266		poly->texFormat = DSGXTexParamsGetFormat(poly->poly->texParams);
267		poly->texW = 8 << DSGXTexParamsGetSSize(poly->poly->texParams);
268		poly->texH = 8 << DSGXTexParamsGetTSize(poly->poly->texParams);
269		switch (poly->texFormat) {
270		case 0:
271		case 7:
272			poly->texBase = NULL;
273			poly->palBase = NULL;
274			break;
275		default:
276			poly->texBase = &renderer->tex[DSGXTexParamsGetVRAMBase(poly->poly->texParams) >> VRAM_BLOCK_OFFSET][(DSGXTexParamsGetVRAMBase(poly->poly->texParams) << 2) & 0xFFFF];
277			poly->palBase = &renderer->texPal[poly->poly->palBase >> 11][(poly->poly->palBase << 3) & 0x1FFF];
278			break;
279		}
280		edge->polyId = i;
281
282		struct DSGXVertex* v0 = &verts[poly->poly->vertIds[0]];
283		struct DSGXVertex* v1;
284
285		int v;
286		for (v = 1; v < poly->poly->verts; ++v) {
287			v1 = &verts[poly->poly->vertIds[v]];
288			if (v0->vy >= v1->vy) {
289				edge->y0 = SCREEN_SIZE - v0->vy;
290				edge->x0 = v0->vx;
291				edge->w0 = v0->vw;
292				_expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
293				edge->s0 = v0->vs;
294				edge->t0 = v0->vt;
295
296				edge->y1 = SCREEN_SIZE - v1->vy;
297				edge->x1 = v1->vx;
298				edge->w1 = v1->vw;
299				_expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
300				edge->s1 = v1->vs;
301				edge->t1 = v1->vt;
302			} else {
303				edge->y0 = SCREEN_SIZE - v1->vy;
304				edge->x0 = v1->vx;
305				edge->w0 = v1->vw;
306				_expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
307				edge->s0 = v1->vs;
308				edge->t0 = v1->vt;
309
310				edge->y1 = SCREEN_SIZE - v0->vy;
311				edge->x1 = v0->vx;
312				edge->w1 = v0->vw;
313				_expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
314				edge->s1 = v0->vs;
315				edge->t1 = v0->vt;
316			}
317
318			edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
319			edge->polyId = i;
320			v0 = v1;
321		}
322
323		v1 = &verts[poly->poly->vertIds[0]];
324		if (v0->vy >= v1->vy) {
325			edge->y0 = SCREEN_SIZE - v0->vy;
326			edge->x0 = v0->vx;
327			edge->w0 = v0->vw;
328			_expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
329			edge->s0 = v0->vs;
330			edge->t0 = v0->vt;
331
332			edge->y1 = SCREEN_SIZE - v1->vy;
333			edge->x1 = v1->vx;
334			edge->w1 = v1->vw;
335			_expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
336			edge->s1 = v1->vs;
337			edge->t1 = v1->vt;
338		} else {
339			edge->y0 = SCREEN_SIZE - v1->vy;
340			edge->x0 = v1->vx;
341			edge->w0 = v1->vw;
342			_expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
343			edge->s0 = v1->vs;
344			edge->t0 = v1->vt;
345
346			edge->y1 = SCREEN_SIZE - v0->vy;
347			edge->x1 = v0->vx;
348			edge->w1 = v0->vw;
349			_expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
350			edge->s1 = v0->vs;
351			edge->t1 = v0->vt;
352		}
353	}
354	qsort(DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, 0), DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges), sizeof(struct DSGXSoftwareEdge), _edgeSort);
355}
356
357static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y) {
358	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
359	if (!softwareRenderer->flushPending) {
360		return;
361	}
362	DSGXSoftwareSpanListClear(&softwareRenderer->activeSpans);
363	memset(softwareRenderer->bucket, 0, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
364	int i;
365	for (i = DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges); i; --i) {
366		size_t idx = i - 1;
367		struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, idx);
368		if (edge->y1 >> 12 < y) {
369			DSGXSoftwareEdgeListShift(&softwareRenderer->activeEdges, idx, 1);
370			continue;
371		} else if (edge->y0 >> 12 > y) {
372			continue;
373		}
374
375		unsigned poly = edge->polyId;
376		struct DSGXSoftwareSpan* span = softwareRenderer->bucket[poly];
377		if (span && !span->ep[1].w) {
378			_edgeToSpan(span, edge, 1, y);
379			softwareRenderer->bucket[poly] = NULL;
380		} else if (!span) {
381			span = DSGXSoftwareSpanListAppend(&softwareRenderer->activeSpans);
382			memset(&span->ep[1], 0, sizeof(span->ep[1]));
383			span->poly = DSGXSoftwarePolygonListGetPointer(&softwareRenderer->activePolys, poly);
384			if (!_edgeToSpan(span, edge, 0, y)) {
385				// Horizontal line
386				DSGXSoftwareSpanListShift(&softwareRenderer->activeSpans, DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans) - 1, 1);
387			} else {
388				softwareRenderer->bucket[poly] = span;
389			}
390		}
391	}
392	qsort(DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, 0), DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans), sizeof(struct DSGXSoftwareSpan), _spanSort);
393
394	color_t* scanline = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
395
396	int nextSpanX = DS_VIDEO_HORIZONTAL_PIXELS;
397	if (DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans)) {
398		nextSpanX = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans) - 1)->ep[0].x;
399		nextSpanX >>= 12;
400	}
401	for (i = 0; i < DS_VIDEO_HORIZONTAL_PIXELS; ++i) {
402		struct DSGXSoftwareSpan* span = NULL;
403		struct DSGXSoftwareEndpoint ep;
404		int32_t depth = INT32_MIN;
405		scanline[i] = FLAG_UNWRITTEN;
406		if (i >= nextSpanX) {
407			size_t nextSpanId = DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans);
408			span = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, nextSpanId - 1);
409			while (i > (span->ep[1].x >> 12) || !span->ep[1].x) {
410				DSGXSoftwareSpanListShift(&softwareRenderer->activeSpans, nextSpanId - 1, 1);
411				--nextSpanId;
412				if (!nextSpanId) {
413					nextSpanX = DS_VIDEO_HORIZONTAL_PIXELS;
414					span = NULL;
415					break;
416				}
417				span = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, nextSpanId - 1);
418				nextSpanX = span->ep[0].x >> 12;
419			}
420			if (i < nextSpanX) {
421				span = NULL;
422			} else {
423				struct DSGXSoftwareSpan* testSpan = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, nextSpanId - 1);
424				while (i > (testSpan->ep[0].x >> 12)) {
425					if (i <= (testSpan->ep[1].x >> 12)) {
426						_lerpEndpoint(testSpan, &ep, i);
427						color_t color = _lookupColor(&ep, testSpan->poly);
428						if (scanline[i] == FLAG_UNWRITTEN) {
429							scanline[i] = color;
430						}
431						if (ep.w >= depth) {
432							depth = ep.w;
433							span = testSpan;
434							if (color != FLAG_UNWRITTEN) {
435								scanline[i] = color;
436							}
437						}
438					}
439					--nextSpanId;
440					if (!nextSpanId) {
441						break;
442					}
443					testSpan = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, nextSpanId - 1);
444				}
445			}
446		}
447	}
448	if (y == DS_VIDEO_VERTICAL_PIXELS - 1) {
449		softwareRenderer->flushPending = false;
450	}
451}
452
453static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output) {
454	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
455	*output = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
456}