all repos — mgba @ f2fd53af075d8bab83d479f5db61b5912d60b75b

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
 10#define SCREEN_SIZE (DS_VIDEO_VERTICAL_PIXELS << 12)
 11
 12DEFINE_VECTOR(DSGXSoftwarePolygonList, struct DSGXSoftwarePolygon);
 13DEFINE_VECTOR(DSGXSoftwareEdgeList, struct DSGXSoftwareEdge);
 14DEFINE_VECTOR(DSGXSoftwareSpanList, struct DSGXSoftwareSpan);
 15
 16static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer);
 17static void DSGXSoftwareRendererReset(struct DSGXRenderer* renderer);
 18static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer);
 19static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount);
 20static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y);
 21static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output);
 22
 23static void _expandColor(uint16_t c15, int8_t* r, int8_t* g, int8_t* b) {
 24	*r = ((c15 << 1) & 0x3E) | 1;
 25	*g = ((c15 >> 4) & 0x3E) | 1;
 26	*b = ((c15 >> 9) & 0x3E) | 1;
 27}
 28
 29static int _edgeSort(const void* a, const void* b) {
 30	const struct DSGXSoftwareEdge* ea = a;
 31	const struct DSGXSoftwareEdge* eb = b;
 32
 33	// Sort upside down
 34	if (ea->y0 < eb->y0) {
 35		return 1;
 36	}
 37	if (ea->y0 > eb->y0) {
 38		return -1;
 39	}
 40	if (ea->y1 < eb->y1) {
 41		return 1;
 42	}
 43	if (ea->y1 > eb->y1) {
 44		return -1;
 45	}
 46	return 0;
 47}
 48
 49static bool _edgeToSpan(struct DSGXSoftwareSpan* span, const struct DSGXSoftwareEdge* edge, int index, int32_t y) {
 50	// TODO: Perspective correction
 51	int32_t height = edge->y1 - edge->y0;
 52	int64_t yw = (y << 12) - edge->y0;
 53	if (height) {
 54		yw <<= 19;
 55		yw /= height;
 56	} else {
 57		return false;
 58	}
 59	// Clamp to bounds
 60	if (yw < 0) {
 61		yw = 0;
 62	} else if (yw > height) {
 63		yw = height;
 64	}
 65	if (!index) {
 66		span->x0 = ((int64_t) (edge->x1 - edge->x0) * yw) / height + edge->x0;
 67		span->w0 = ((int64_t) (edge->w1 - edge->w0) * yw) / height + edge->w0;
 68		span->cr0 = ((int32_t) (edge->cr1 - edge->cr0) * yw) / height + edge->cr0;
 69		span->cg0 = ((int32_t) (edge->cg1 - edge->cg0) * yw) / height + edge->cg0;
 70		span->cb0 = ((int32_t) (edge->cb1 - edge->cb0) * yw) / height + edge->cb0;
 71		span->s0 = ((int32_t) (edge->s1 - edge->s0) * yw) / height + edge->s0;
 72		span->t0 = ((int32_t) (edge->t1 - edge->t0) * yw) / height + edge->t0;
 73	} else {
 74		span->x1 = ((int64_t) (edge->x1 - edge->x0) * yw) / height + edge->x0;
 75		span->w1 = ((int64_t) (edge->w1 - edge->w0) * yw) / height + edge->w0;
 76		span->cr1 = ((int32_t) (edge->cr1 - edge->cr0) * yw) / height + edge->cr0;
 77		span->cg1 = ((int32_t) (edge->cg1 - edge->cg0) * yw) / height + edge->cg0;
 78		span->cb1 = ((int32_t) (edge->cb1 - edge->cb0) * yw) / height + edge->cb0;
 79		span->s1 = ((int32_t) (edge->s1 - edge->s0) * yw) / height + edge->s0;
 80		span->t1 = ((int32_t) (edge->t1 - edge->t0) * yw) / height + edge->t0;
 81	}
 82	return true;
 83}
 84
 85static int _spanSort(const void* a, const void* b) {
 86	const struct DSGXSoftwareSpan* sa = a;
 87	const struct DSGXSoftwareSpan* sb = b;
 88
 89	if (sa->x0 < sb->x0) {
 90		return -1;
 91	}
 92	if (sa->x0 > sb->x0) {
 93		return 1;
 94	}
 95	if (sa->x1 < sb->x1) {
 96		return -1;
 97	}
 98	if (sa->x1 > sb->x1) {
 99		return 1;
100	}
101	return 0;
102}
103
104void DSGXSoftwareRendererCreate(struct DSGXSoftwareRenderer* renderer) {
105	renderer->d.init = DSGXSoftwareRendererInit;
106	renderer->d.reset = DSGXSoftwareRendererReset;
107	renderer->d.deinit = DSGXSoftwareRendererDeinit;
108	renderer->d.setRAM = DSGXSoftwareRendererSetRAM;
109	renderer->d.drawScanline = DSGXSoftwareRendererDrawScanline;
110	renderer->d.getScanline = DSGXSoftwareRendererGetScanline;
111}
112
113static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer) {
114	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
115	DSGXSoftwarePolygonListInit(&softwareRenderer->activePolys, DS_GX_POLYGON_BUFFER_SIZE / 4);
116	DSGXSoftwareEdgeListInit(&softwareRenderer->activeEdges, DS_GX_POLYGON_BUFFER_SIZE);
117	DSGXSoftwareSpanListInit(&softwareRenderer->activeSpans, DS_GX_POLYGON_BUFFER_SIZE / 2);
118	TableInit(&softwareRenderer->bucket, DS_GX_POLYGON_BUFFER_SIZE / 8, NULL);
119	softwareRenderer->scanlineCache = anonymousMemoryMap(sizeof(color_t) * DS_VIDEO_HORIZONTAL_PIXELS * 48);
120}
121
122static void DSGXSoftwareRendererReset(struct DSGXRenderer* renderer) {
123	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
124	// TODO
125}
126
127static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer) {
128	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
129	DSGXSoftwarePolygonListDeinit(&softwareRenderer->activePolys);
130	DSGXSoftwareEdgeListDeinit(&softwareRenderer->activeEdges);	
131	DSGXSoftwareSpanListDeinit(&softwareRenderer->activeSpans);
132	TableDeinit(&softwareRenderer->bucket);
133	mappedMemoryFree(softwareRenderer->scanlineCache, sizeof(color_t) * DS_VIDEO_HORIZONTAL_PIXELS * 48);
134}
135
136static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount) {
137	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
138
139	softwareRenderer->verts = verts;
140	DSGXSoftwarePolygonListClear(&softwareRenderer->activePolys);
141	DSGXSoftwareEdgeListClear(&softwareRenderer->activeEdges);
142	unsigned i;
143	for (i = 0; i < polyCount; ++i) {
144		struct DSGXSoftwarePolygon* poly = DSGXSoftwarePolygonListAppend(&softwareRenderer->activePolys);
145		struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
146		poly->poly = &polys[i];
147		edge->polyId = i;
148
149		struct DSGXVertex* v0 = &verts[poly->poly->vertIds[0]];
150		struct DSGXVertex* v1;
151
152		int v;
153		for (v = 1; v < poly->poly->verts; ++v) {
154			v1 = &verts[poly->poly->vertIds[v]];
155			if (v0->vy >= v1->vy) {
156				edge->y0 = SCREEN_SIZE - v0->vy;
157				edge->x0 = v0->vx;
158				edge->w0 = v0->vw;
159				_expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
160				edge->s0 = v0->s;
161				edge->t0 = v0->t;
162
163				edge->y1 = SCREEN_SIZE - v1->vy;
164				edge->x1 = v1->vx;
165				edge->w1 = v1->vw;
166				_expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
167				edge->s1 = v1->s;
168				edge->t1 = v1->t;
169			} else {
170				edge->y0 = SCREEN_SIZE - v1->vy;
171				edge->x0 = v1->vx;
172				edge->w0 = v1->vw;
173				_expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
174				edge->s0 = v1->s;
175				edge->t0 = v1->t;
176
177				edge->y1 = SCREEN_SIZE - v0->vy;
178				edge->x1 = v0->vx;
179				edge->w1 = v0->vw;
180				_expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
181				edge->s1 = v0->s;
182				edge->t1 = v0->t;
183			}
184
185			edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
186			edge->polyId = i;
187			v0 = v1;
188		}
189
190		v1 = &verts[poly->poly->vertIds[0]];
191		if (v0->vy >= v1->vy) {
192			edge->y0 = SCREEN_SIZE - v0->vy;
193			edge->x0 = v0->vx;
194			edge->w0 = v0->vw;
195			_expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
196			edge->s0 = v0->s;
197			edge->t0 = v0->t;
198
199			edge->y1 = SCREEN_SIZE - v1->vy;
200			edge->x1 = v1->vx;
201			edge->w1 = v1->vw;
202			_expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
203			edge->s1 = v1->s;
204			edge->t1 = v1->t;
205		} else {
206			edge->y0 = SCREEN_SIZE - v1->vy;
207			edge->x0 = v1->vx;
208			edge->w0 = v1->vw;
209			_expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
210			edge->s0 = v1->s;
211			edge->t0 = v1->t;
212
213			edge->y1 = SCREEN_SIZE - v0->vy;
214			edge->x1 = v0->vx;
215			edge->w1 = v0->vw;
216			_expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
217			edge->s1 = v0->s;
218			edge->t1 = v0->t;
219		}
220	}
221	qsort(DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, 0), DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges), sizeof(struct DSGXSoftwareEdge), _edgeSort);
222}
223
224static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y) {
225	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
226	DSGXSoftwareSpanListClear(&softwareRenderer->activeSpans);
227	TableClear(&softwareRenderer->bucket);
228	size_t i;
229	for (i = DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges); i; --i) {
230		size_t idx = i - 1;
231		struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, idx);
232		if (edge->y1 >> 12 < y) {
233			DSGXSoftwareEdgeListShift(&softwareRenderer->activeEdges, idx, 1);
234			continue;
235		} else if (edge->y0 >> 12 > y) {
236			continue;
237		}
238
239		unsigned poly = edge->polyId;
240		struct DSGXSoftwareSpan* span = TableLookup(&softwareRenderer->bucket, poly);
241		if (span) {
242			_edgeToSpan(span, edge, 1, y);
243			TableRemove(&softwareRenderer->bucket, poly);
244		} else {
245			span = DSGXSoftwareSpanListAppend(&softwareRenderer->activeSpans);
246			if (!_edgeToSpan(span, edge, 0, y)) {
247				// Horizontal line
248				DSGXSoftwareSpanListShift(&softwareRenderer->activeSpans, DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans) - 1, 1);
249			} else {
250				TableInsert(&softwareRenderer->bucket, poly, span);
251			}
252		}
253	}
254	qsort(DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, 0), DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans), sizeof(struct DSGXSoftwareSpan), _spanSort);
255}
256
257static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output) {
258	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
259	y %= 48;
260	*output = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
261}