all repos — mgba @ 2b64d45906d5b687d4cf240a140dee7a48cf1aad

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	// TODO: Perspective correction
 52	int32_t height = edge->y1 - edge->y0;
 53	int64_t yw = (y << 12) - edge->y0;
 54	if (!height) {
 55		return false;
 56	}
 57	// Clamp to bounds
 58	if (yw < 0) {
 59		yw = 0;
 60	} else if (yw > height) {
 61		yw = height;
 62	}
 63	span->ep[index].x = ((int64_t) (edge->x1 - edge->x0) * yw) / height + edge->x0;
 64	if (index && span->ep[0].x > span->ep[index].x) {
 65		int32_t temp = span->ep[index].x;
 66		span->ep[index] = span->ep[0];
 67		span->ep[0].x = temp;
 68		index = 0;
 69	}
 70	span->ep[index].w = ((int64_t) (edge->w1 - edge->w0) * yw) / height + edge->w0;
 71	span->ep[index].cr = ((int32_t) (edge->cr1 - edge->cr0) * yw) / height + edge->cr0;
 72	span->ep[index].cg = ((int32_t) (edge->cg1 - edge->cg0) * yw) / height + edge->cg0;
 73	span->ep[index].cb = ((int32_t) (edge->cb1 - edge->cb0) * yw) / height + edge->cb0;
 74	span->ep[index].s = ((int32_t) (edge->s1 - edge->s0) * yw) / height + edge->s0;
 75	span->ep[index].t = ((int32_t) (edge->t1 - edge->t0) * yw) / height + edge->t0;
 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	uint32_t w = ((int64_t) (span->ep[0].w - span->ep[1].w) * xw) / width + span->ep[1].w;
113
114	uint64_t r = ((span->ep[1].cr * (int64_t) span->ep[0].w - span->ep[0].cr * (int64_t) span->ep[1].w) * xw) / width + span->ep[0].cr * (int64_t) span->ep[1].w;
115	uint64_t g = ((span->ep[1].cg * (int64_t) span->ep[0].w - span->ep[0].cg * (int64_t) span->ep[1].w) * xw) / width + span->ep[0].cg * (int64_t) span->ep[1].w;
116	uint64_t b = ((span->ep[1].cb * (int64_t) span->ep[0].w - span->ep[0].cb * (int64_t) span->ep[1].w) * xw) / width + span->ep[0].cb * (int64_t) span->ep[1].w;
117	r /= w;
118	g /= w;
119	b /= w;
120#ifndef COLOR_16_BIT
121	color_t rgb = (r << 2) & 0xF8;
122	rgb |= (g << 10) & 0xF800;
123	rgb |= (b << 18) & 0xF80000;
124	return rgb;
125#else
126#error Unsupported color depth
127#endif
128}
129
130void DSGXSoftwareRendererCreate(struct DSGXSoftwareRenderer* renderer) {
131	renderer->d.init = DSGXSoftwareRendererInit;
132	renderer->d.reset = DSGXSoftwareRendererReset;
133	renderer->d.deinit = DSGXSoftwareRendererDeinit;
134	renderer->d.setRAM = DSGXSoftwareRendererSetRAM;
135	renderer->d.drawScanline = DSGXSoftwareRendererDrawScanline;
136	renderer->d.getScanline = DSGXSoftwareRendererGetScanline;
137}
138
139static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer) {
140	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
141	DSGXSoftwarePolygonListInit(&softwareRenderer->activePolys, DS_GX_POLYGON_BUFFER_SIZE / 4);
142	DSGXSoftwareEdgeListInit(&softwareRenderer->activeEdges, DS_GX_POLYGON_BUFFER_SIZE);
143	DSGXSoftwareSpanListInit(&softwareRenderer->activeSpans, DS_GX_POLYGON_BUFFER_SIZE / 2);
144	softwareRenderer->bucket = anonymousMemoryMap(sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
145	softwareRenderer->scanlineCache = anonymousMemoryMap(sizeof(color_t) * DS_VIDEO_HORIZONTAL_PIXELS * 48);
146}
147
148static void DSGXSoftwareRendererReset(struct DSGXRenderer* renderer) {
149	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
150	// TODO
151}
152
153static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer) {
154	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
155	DSGXSoftwarePolygonListDeinit(&softwareRenderer->activePolys);
156	DSGXSoftwareEdgeListDeinit(&softwareRenderer->activeEdges);	
157	DSGXSoftwareSpanListDeinit(&softwareRenderer->activeSpans);
158	mappedMemoryFree(softwareRenderer->bucket, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
159	mappedMemoryFree(softwareRenderer->scanlineCache, sizeof(color_t) * DS_VIDEO_HORIZONTAL_PIXELS * 48);
160}
161
162static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount) {
163	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
164
165	softwareRenderer->verts = verts;
166	DSGXSoftwarePolygonListClear(&softwareRenderer->activePolys);
167	DSGXSoftwareEdgeListClear(&softwareRenderer->activeEdges);
168	unsigned i;
169	for (i = 0; i < polyCount; ++i) {
170		struct DSGXSoftwarePolygon* poly = DSGXSoftwarePolygonListAppend(&softwareRenderer->activePolys);
171		struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
172		poly->poly = &polys[i];
173		edge->polyId = i;
174
175		struct DSGXVertex* v0 = &verts[poly->poly->vertIds[0]];
176		struct DSGXVertex* v1;
177
178		int v;
179		for (v = 1; v < poly->poly->verts; ++v) {
180			v1 = &verts[poly->poly->vertIds[v]];
181			if (v0->vy >= v1->vy) {
182				edge->y0 = SCREEN_SIZE - v0->vy;
183				edge->x0 = v0->vx;
184				edge->w0 = v0->vw;
185				_expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
186				edge->s0 = v0->s;
187				edge->t0 = v0->t;
188
189				edge->y1 = SCREEN_SIZE - v1->vy;
190				edge->x1 = v1->vx;
191				edge->w1 = v1->vw;
192				_expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
193				edge->s1 = v1->s;
194				edge->t1 = v1->t;
195			} else {
196				edge->y0 = SCREEN_SIZE - v1->vy;
197				edge->x0 = v1->vx;
198				edge->w0 = v1->vw;
199				_expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
200				edge->s0 = v1->s;
201				edge->t0 = v1->t;
202
203				edge->y1 = SCREEN_SIZE - v0->vy;
204				edge->x1 = v0->vx;
205				edge->w1 = v0->vw;
206				_expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
207				edge->s1 = v0->s;
208				edge->t1 = v0->t;
209			}
210
211			edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
212			edge->polyId = i;
213			v0 = v1;
214		}
215
216		v1 = &verts[poly->poly->vertIds[0]];
217		if (v0->vy >= v1->vy) {
218			edge->y0 = SCREEN_SIZE - v0->vy;
219			edge->x0 = v0->vx;
220			edge->w0 = v0->vw;
221			_expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
222			edge->s0 = v0->s;
223			edge->t0 = v0->t;
224
225			edge->y1 = SCREEN_SIZE - v1->vy;
226			edge->x1 = v1->vx;
227			edge->w1 = v1->vw;
228			_expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
229			edge->s1 = v1->s;
230			edge->t1 = v1->t;
231		} else {
232			edge->y0 = SCREEN_SIZE - v1->vy;
233			edge->x0 = v1->vx;
234			edge->w0 = v1->vw;
235			_expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
236			edge->s0 = v1->s;
237			edge->t0 = v1->t;
238
239			edge->y1 = SCREEN_SIZE - v0->vy;
240			edge->x1 = v0->vx;
241			edge->w1 = v0->vw;
242			_expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
243			edge->s1 = v0->s;
244			edge->t1 = v0->t;
245		}
246	}
247	qsort(DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, 0), DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges), sizeof(struct DSGXSoftwareEdge), _edgeSort);
248}
249
250static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y) {
251	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
252	DSGXSoftwareSpanListClear(&softwareRenderer->activeSpans);
253	memset(softwareRenderer->bucket, 0, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
254	size_t i;
255	for (i = DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges); i; --i) {
256		size_t idx = i - 1;
257		struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, idx);
258		if (edge->y1 >> 12 < y) {
259			DSGXSoftwareEdgeListShift(&softwareRenderer->activeEdges, idx, 1);
260			continue;
261		} else if (edge->y0 >> 12 > y) {
262			continue;
263		}
264
265		unsigned poly = edge->polyId;
266		struct DSGXSoftwareSpan* span = softwareRenderer->bucket[poly];
267		if (span && !span->ep[1].w) {
268			_edgeToSpan(span, edge, 1, y);
269			softwareRenderer->bucket[poly] = NULL;
270		} else if (!span) {
271			span = DSGXSoftwareSpanListAppend(&softwareRenderer->activeSpans);
272			memset(span, 0, sizeof(*span));
273			if (!_edgeToSpan(span, edge, 0, y)) {
274				// Horizontal line
275				DSGXSoftwareSpanListShift(&softwareRenderer->activeSpans, DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans) - 1, 1);
276			} else {
277				softwareRenderer->bucket[poly] = span;
278			}
279		}
280	}
281	qsort(DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, 0), DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans), sizeof(struct DSGXSoftwareSpan), _spanSort);
282
283	y %= 48;
284	color_t* scanline = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
285
286	size_t nextSpanX = DS_VIDEO_HORIZONTAL_PIXELS;
287	if (DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans)) {
288		nextSpanX = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans) - 1)->ep[0].x;
289		nextSpanX >>= 12;
290	}
291	for (i = 0; i < DS_VIDEO_HORIZONTAL_PIXELS; ++i) {
292		struct DSGXSoftwareSpan* span = NULL;
293		if (i >= nextSpanX) {
294			size_t nextSpanId = DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans);
295			span = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, nextSpanId - 1);
296			while (i > (uint32_t) (span->ep[1].x >> 12)) {
297				DSGXSoftwareSpanListShift(&softwareRenderer->activeSpans, nextSpanId - 1, 1);
298				--nextSpanId;
299				if (!nextSpanId) {
300					nextSpanX = DS_VIDEO_HORIZONTAL_PIXELS;
301					span = NULL;
302					break;
303				}
304				span = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, nextSpanId - 1);
305				nextSpanX = span->ep[0].x >> 12;
306			}
307			if (i < nextSpanX) {
308				span = NULL;
309			}
310		}
311		if (span) {
312			scanline[i] = _lerpColor(span, i);
313		} else {
314			scanline[i] = FLAG_UNWRITTEN; // TODO
315		}
316	}
317}
318
319static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output) {
320	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
321	y %= 48;
322	*output = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
323}