all repos — mgba @ d6cf1d2fe1241b82c09504b76c902bb6356c26a8

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 DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount);
 21static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y);
 22static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output);
 23
 24static void _expandColor(uint16_t c15, int8_t* r, int8_t* g, int8_t* b) {
 25	*r = ((c15 << 1) & 0x3E) | 1;
 26	*g = ((c15 >> 4) & 0x3E) | 1;
 27	*b = ((c15 >> 9) & 0x3E) | 1;
 28}
 29
 30static int _edgeSort(const void* a, const void* b) {
 31	const struct DSGXSoftwareEdge* ea = a;
 32	const struct DSGXSoftwareEdge* eb = b;
 33
 34	// Sort upside down
 35	if (ea->y0 < eb->y0) {
 36		return 1;
 37	}
 38	if (ea->y0 > eb->y0) {
 39		return -1;
 40	}
 41	if (ea->y1 < eb->y1) {
 42		return 1;
 43	}
 44	if (ea->y1 > eb->y1) {
 45		return -1;
 46	}
 47	return 0;
 48}
 49
 50static bool _edgeToSpan(struct DSGXSoftwareSpan* span, const struct DSGXSoftwareEdge* edge, int index, int32_t y) {
 51	int32_t height = edge->y1 - edge->y0;
 52	int64_t yw = (y << 12) - edge->y0;
 53	if (!height) {
 54		return false;
 55	}
 56	// Clamp to bounds
 57	if (yw < 0) {
 58		yw = 0;
 59	} else if (yw > height) {
 60		yw = height;
 61	}
 62	span->ep[index].x = ((int64_t) (edge->x1 - edge->x0) * yw) / height + edge->x0;
 63	if (index && span->ep[0].x > span->ep[index].x) {
 64		int32_t temp = span->ep[index].x;
 65		span->ep[index] = span->ep[0];
 66		span->ep[0].x = temp;
 67		index = 0;
 68	}
 69	int32_t w = ((int64_t) (edge->w1 - edge->w0) * yw) / height + edge->w0;
 70	span->ep[index].w = w;
 71	span->ep[index].cr = (((int32_t) (edge->cr1 * edge->w1 - edge->cr0 * edge->w0) * yw) / height + edge->cr0 * edge->w0) / w;
 72	span->ep[index].cg = (((int32_t) (edge->cg1 * edge->w1 - edge->cg0 * edge->w0) * yw) / height + edge->cg0 * edge->w0) / w;
 73	span->ep[index].cb = (((int32_t) (edge->cb1 * edge->w1 - edge->cb0 * edge->w0) * yw) / height + edge->cb0 * edge->w0) / w;
 74	span->ep[index].s = (((int32_t) (edge->s1 * edge->w1 - edge->s0 * edge->w0) * yw) / height + edge->s0 * edge->w0) / w;
 75	span->ep[index].t = (((int32_t) (edge->t1 * edge->w1 - edge->t0 * edge->w0) * yw) / height + edge->t0 * edge->w0) / w;
 76
 77	return true;
 78}
 79
 80static int _spanSort(const void* a, const void* b) {
 81	const struct DSGXSoftwareSpan* sa = a;
 82	const struct DSGXSoftwareSpan* sb = b;
 83
 84	// Sort backwards
 85	if (sa->ep[0].x < sb->ep[0].x) {
 86		return 1;
 87	}
 88	if (sa->ep[0].x > sb->ep[0].x) {
 89		return -1;
 90	}
 91	if (sa->ep[1].x < sb->ep[1].x) {
 92		return 1;
 93	}
 94	if (sa->ep[1].x > sb->ep[1].x) {
 95		return -1;
 96	}
 97	return 0;
 98}
 99
100static color_t _lerpColor(const struct DSGXSoftwareSpan* span, unsigned x) {
101	int64_t width = span->ep[1].x - span->ep[0].x;
102	int64_t xw = ((uint64_t) x << 12) - span->ep[0].x;
103	if (!width) {
104		return 0; // TODO?
105	}
106	// Clamp to bounds
107	if (xw < 0) {
108		xw = 0;
109	} else if (xw > width) {
110		xw = width;
111	}
112	int32_t w0 = span->ep[0].w;
113	int32_t w1 = span->ep[1].w;
114	int32_t w = ((int64_t) (w1 - w0) * xw) / width + w0;
115
116	uint64_t r = ((span->ep[1].cr * (int64_t) w1 - span->ep[0].cr * (int64_t) w0) * xw) / width + span->ep[0].cr * (int64_t) w0;
117	uint64_t g = ((span->ep[1].cg * (int64_t) w1 - span->ep[0].cg * (int64_t) w0) * xw) / width + span->ep[0].cg * (int64_t) w0;
118	uint64_t b = ((span->ep[1].cb * (int64_t) w1 - span->ep[0].cb * (int64_t) w0) * xw) / width + span->ep[0].cb * (int64_t) w0;
119	r /= w;
120	g /= w;
121	b /= w;
122#ifndef COLOR_16_BIT
123	color_t rgb = (r << 2) & 0xF8;
124	rgb |= (g << 10) & 0xF800;
125	rgb |= (b << 18) & 0xF80000;
126	return rgb;
127#else
128#error Unsupported color depth
129#endif
130}
131
132static int32_t _lerpDepth(const struct DSGXSoftwareSpan* span, unsigned x) {
133	int64_t width = span->ep[1].x - span->ep[0].x;
134	int64_t xw = ((uint64_t) x << 12) - span->ep[0].x;
135	if (!width) {
136		return 0; // TODO?
137	}
138	// Clamp to bounds
139	if (xw < 0) {
140		xw = 0;
141	} else if (xw > width) {
142		xw = width;
143	}
144	int32_t w0 = span->ep[0].w;
145	int32_t w1 = span->ep[1].w;
146	return ((int64_t) (w1 - w0) * xw) / width + w0;
147}
148
149void DSGXSoftwareRendererCreate(struct DSGXSoftwareRenderer* renderer) {
150	renderer->d.init = DSGXSoftwareRendererInit;
151	renderer->d.reset = DSGXSoftwareRendererReset;
152	renderer->d.deinit = DSGXSoftwareRendererDeinit;
153	renderer->d.setRAM = DSGXSoftwareRendererSetRAM;
154	renderer->d.drawScanline = DSGXSoftwareRendererDrawScanline;
155	renderer->d.getScanline = DSGXSoftwareRendererGetScanline;
156}
157
158static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer) {
159	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
160	DSGXSoftwarePolygonListInit(&softwareRenderer->activePolys, DS_GX_POLYGON_BUFFER_SIZE / 4);
161	DSGXSoftwareEdgeListInit(&softwareRenderer->activeEdges, DS_GX_POLYGON_BUFFER_SIZE);
162	DSGXSoftwareSpanListInit(&softwareRenderer->activeSpans, DS_GX_POLYGON_BUFFER_SIZE / 2);
163	softwareRenderer->bucket = anonymousMemoryMap(sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
164	softwareRenderer->scanlineCache = anonymousMemoryMap(sizeof(color_t) * DS_VIDEO_HORIZONTAL_PIXELS * 48);
165}
166
167static void DSGXSoftwareRendererReset(struct DSGXRenderer* renderer) {
168	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
169	// TODO
170}
171
172static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer) {
173	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
174	DSGXSoftwarePolygonListDeinit(&softwareRenderer->activePolys);
175	DSGXSoftwareEdgeListDeinit(&softwareRenderer->activeEdges);	
176	DSGXSoftwareSpanListDeinit(&softwareRenderer->activeSpans);
177	mappedMemoryFree(softwareRenderer->bucket, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
178	mappedMemoryFree(softwareRenderer->scanlineCache, sizeof(color_t) * DS_VIDEO_HORIZONTAL_PIXELS * 48);
179}
180
181static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount) {
182	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
183
184	softwareRenderer->verts = verts;
185	DSGXSoftwarePolygonListClear(&softwareRenderer->activePolys);
186	DSGXSoftwareEdgeListClear(&softwareRenderer->activeEdges);
187	unsigned i;
188	for (i = 0; i < polyCount; ++i) {
189		struct DSGXSoftwarePolygon* poly = DSGXSoftwarePolygonListAppend(&softwareRenderer->activePolys);
190		struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
191		poly->poly = &polys[i];
192		edge->polyId = i;
193
194		struct DSGXVertex* v0 = &verts[poly->poly->vertIds[0]];
195		struct DSGXVertex* v1;
196
197		int v;
198		for (v = 1; v < poly->poly->verts; ++v) {
199			v1 = &verts[poly->poly->vertIds[v]];
200			if (v0->vy >= v1->vy) {
201				edge->y0 = SCREEN_SIZE - v0->vy;
202				edge->x0 = v0->vx;
203				edge->w0 = v0->vw;
204				_expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
205				edge->s0 = v0->s;
206				edge->t0 = v0->t;
207
208				edge->y1 = SCREEN_SIZE - v1->vy;
209				edge->x1 = v1->vx;
210				edge->w1 = v1->vw;
211				_expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
212				edge->s1 = v1->s;
213				edge->t1 = v1->t;
214			} else {
215				edge->y0 = SCREEN_SIZE - v1->vy;
216				edge->x0 = v1->vx;
217				edge->w0 = v1->vw;
218				_expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
219				edge->s0 = v1->s;
220				edge->t0 = v1->t;
221
222				edge->y1 = SCREEN_SIZE - v0->vy;
223				edge->x1 = v0->vx;
224				edge->w1 = v0->vw;
225				_expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
226				edge->s1 = v0->s;
227				edge->t1 = v0->t;
228			}
229
230			edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
231			edge->polyId = i;
232			v0 = v1;
233		}
234
235		v1 = &verts[poly->poly->vertIds[0]];
236		if (v0->vy >= v1->vy) {
237			edge->y0 = SCREEN_SIZE - v0->vy;
238			edge->x0 = v0->vx;
239			edge->w0 = v0->vw;
240			_expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
241			edge->s0 = v0->s;
242			edge->t0 = v0->t;
243
244			edge->y1 = SCREEN_SIZE - v1->vy;
245			edge->x1 = v1->vx;
246			edge->w1 = v1->vw;
247			_expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
248			edge->s1 = v1->s;
249			edge->t1 = v1->t;
250		} else {
251			edge->y0 = SCREEN_SIZE - v1->vy;
252			edge->x0 = v1->vx;
253			edge->w0 = v1->vw;
254			_expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
255			edge->s0 = v1->s;
256			edge->t0 = v1->t;
257
258			edge->y1 = SCREEN_SIZE - v0->vy;
259			edge->x1 = v0->vx;
260			edge->w1 = v0->vw;
261			_expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
262			edge->s1 = v0->s;
263			edge->t1 = v0->t;
264		}
265	}
266	qsort(DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, 0), DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges), sizeof(struct DSGXSoftwareEdge), _edgeSort);
267}
268
269static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y) {
270	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
271	DSGXSoftwareSpanListClear(&softwareRenderer->activeSpans);
272	memset(softwareRenderer->bucket, 0, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
273	size_t i;
274	for (i = DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges); i; --i) {
275		size_t idx = i - 1;
276		struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, idx);
277		if (edge->y1 >> 12 < y) {
278			DSGXSoftwareEdgeListShift(&softwareRenderer->activeEdges, idx, 1);
279			continue;
280		} else if (edge->y0 >> 12 > y) {
281			continue;
282		}
283
284		unsigned poly = edge->polyId;
285		struct DSGXSoftwareSpan* span = softwareRenderer->bucket[poly];
286		if (span && !span->ep[1].w) {
287			_edgeToSpan(span, edge, 1, y);
288			softwareRenderer->bucket[poly] = NULL;
289		} else if (!span) {
290			span = DSGXSoftwareSpanListAppend(&softwareRenderer->activeSpans);
291			memset(span, 0, sizeof(*span));
292			if (!_edgeToSpan(span, edge, 0, y)) {
293				// Horizontal line
294				DSGXSoftwareSpanListShift(&softwareRenderer->activeSpans, DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans) - 1, 1);
295			} else {
296				softwareRenderer->bucket[poly] = span;
297			}
298		}
299	}
300	qsort(DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, 0), DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans), sizeof(struct DSGXSoftwareSpan), _spanSort);
301
302	y %= 48;
303	color_t* scanline = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
304
305	size_t nextSpanX = DS_VIDEO_HORIZONTAL_PIXELS;
306	if (DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans)) {
307		nextSpanX = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans) - 1)->ep[0].x;
308		nextSpanX >>= 12;
309	}
310	for (i = 0; i < DS_VIDEO_HORIZONTAL_PIXELS; ++i) {
311		struct DSGXSoftwareSpan* span = NULL;
312		int32_t depth = INT32_MIN;
313		if (i >= nextSpanX) {
314			size_t nextSpanId = DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans);
315			span = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, nextSpanId - 1);
316			while (i > (uint32_t) (span->ep[1].x >> 12)) {
317				DSGXSoftwareSpanListShift(&softwareRenderer->activeSpans, nextSpanId - 1, 1);
318				--nextSpanId;
319				if (!nextSpanId) {
320					nextSpanX = DS_VIDEO_HORIZONTAL_PIXELS;
321					span = NULL;
322					break;
323				}
324				span = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, nextSpanId - 1);
325				nextSpanX = span->ep[0].x >> 12;
326			}
327			if (i < nextSpanX) {
328				span = NULL;
329			} else {
330				struct DSGXSoftwareSpan* testSpan = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, nextSpanId - 1);
331				while (i > (uint32_t) (testSpan->ep[0].x >> 12)) {
332					if (i <= (uint32_t) (testSpan->ep[1].x >> 12)) {
333						int32_t newDepth = _lerpDepth(testSpan, i);
334						if (newDepth > depth) {
335							depth = newDepth;
336							span = testSpan;
337						}
338					}
339					--nextSpanId;
340					if (!nextSpanId) {
341						break;
342					}
343					testSpan = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, nextSpanId - 1);
344				}
345			}
346		}
347		if (span) {
348			scanline[i] = _lerpColor(span, i);
349		} else {
350			scanline[i] = FLAG_UNWRITTEN; // TODO
351		}
352	}
353}
354
355static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output) {
356	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
357	y %= 48;
358	*output = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
359}