all repos — mgba @ 619583775d79bf1aa396f1c854517a3ed840ebf5

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