all repos — mgba @ 1ecdcf31cc5da86c43f18f6117832dffc8e76767

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