all repos — mgba @ 56ad96c16e70df9e63d0224e50043dc0be3ccff5

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
 11DEFINE_VECTOR(DSGXSoftwarePolygonList, struct DSGXSoftwarePolygon);
 12DEFINE_VECTOR(DSGXSoftwareEdgeList, struct DSGXSoftwareEdge);
 13DEFINE_VECTOR(DSGXSoftwareSpanList, struct DSGXSoftwareSpan);
 14
 15static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer);
 16static void DSGXSoftwareRendererReset(struct DSGXRenderer* renderer);
 17static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer);
 18static void DSGXSoftwareRendererInvalidateTex(struct DSGXRenderer* renderer, int slot);
 19static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount, bool wSort);
 20static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y);
 21static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, const color_t** output);
 22
 23static void _expandColor(uint16_t c15, uint8_t* r, uint8_t* g, uint8_t* b) {
 24	*r = ((c15 << 1) & 0x3E) | 1;
 25	*g = ((c15 >> 4) & 0x3E) | 1;
 26	*b = ((c15 >> 9) & 0x3E) | 1;
 27}
 28
 29static color_t _finishColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
 30#ifndef COLOR_16_BIT
 31	color_t rgba = (r << 2) & 0xFC;
 32	rgba |= (g << 10) & 0xFC00;
 33	rgba |= (b << 18) & 0xFC0000;
 34	rgba |= (a << 26) & 0xF8000000;
 35	return rgba;
 36#else
 37#error Unsupported color depth
 38#endif
 39}
 40
 41static unsigned _mix32(int weightA, unsigned colorA, int weightB, unsigned colorB) {
 42	unsigned c = 0;
 43	unsigned a, b;
 44#ifdef COLOR_16_BIT
 45#error Unsupported color depth
 46#else
 47	a = colorA & 0xFF;
 48	b = colorB & 0xFF;
 49	c |= ((a * weightA + b * weightB) / 32) & 0x1FF;
 50	if (c & 0x00000100) {
 51		c = 0x000000FF;
 52	}
 53
 54	a = colorA & 0xFF00;
 55	b = colorB & 0xFF00;
 56	c |= ((a * weightA + b * weightB) / 32) & 0x1FF00;
 57	if (c & 0x00010000) {
 58		c = (c & 0x000000FF) | 0x0000FF00;
 59	}
 60
 61	a = colorA & 0xFF0000;
 62	b = colorB & 0xFF0000;
 63	c |= ((a * weightA + b * weightB) / 32) & 0x1FF0000;
 64	if (c & 0x01000000) {
 65		c = (c & 0x0000FFFF) | 0x00FF0000;
 66	}
 67#endif
 68	return c;
 69}
 70
 71static unsigned _mixTexels(int weightA, unsigned colorA, int weightB, unsigned colorB) {
 72	unsigned c = 0;
 73	unsigned a, b;
 74	a = colorA & 0x7C1F;
 75	b = colorB & 0x7C1F;
 76	a |= (colorA & 0x3E0) << 16;
 77	b |= (colorB & 0x3E0) << 16;
 78	c = ((a * weightA + b * weightB) / 8);
 79	if (c & 0x04000000) {
 80		c = (c & ~0x07E00000) | 0x03E00000;
 81	}
 82	if (c & 0x0020) {
 83		c = (c & ~0x003F) | 0x001F;
 84	}
 85	if (c & 0x8000) {
 86		c = (c & ~0xF800) | 0x7C00;
 87	}
 88	c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
 89	return c;
 90}
 91
 92static color_t _lookupColor(struct DSGXSoftwareRenderer* renderer, struct DSGXSoftwareEndpoint* ep, struct DSGXSoftwarePolygon* poly) {
 93	if (!poly->texBase && poly->texFormat) {
 94		return 0;
 95	}
 96	// TODO: Optimize
 97	uint16_t texel;
 98
 99	int16_t s = ep->s >> 4;
100	int16_t t = ep->t >> 4;
101	if (!DSGXTexParamsIsSRepeat(poly->poly->texParams)) {
102		if (s < 0) {
103			s = 0;
104		} else if (s >= poly->texW) {
105			s = poly->texW - 1;
106		}
107	} else if (DSGXTexParamsIsSMirror(poly->poly->texParams)) {
108		if (s & poly->texW) {
109			s = poly->texW - s - 1;
110		}
111		s &= poly->texW - 1;
112	} else {
113		s &= poly->texW - 1;
114	}
115	if (!DSGXTexParamsIsTRepeat(poly->poly->texParams)) {
116		if (t < 0) {
117			t = 0;
118		} else if (t >= poly->texH) {
119			t = poly->texW - 1;
120		}
121	} else if (DSGXTexParamsIsTMirror(poly->poly->texParams)) {
122		if (t & poly->texH) {
123			t = poly->texH - t - 1;
124		}
125		t &= poly->texH - 1;
126	} else {
127		t &= poly->texH - 1;
128	}
129
130	uint16_t texelCoord = s + t * poly->texW;
131	uint8_t ta = 0x1F;
132	uint8_t pa = DSGXPolygonAttrsGetAlpha(poly->poly->polyParams);
133	if (pa) {
134		pa = (pa << 1) + 1;
135	}
136	switch (poly->texFormat) {
137	case 0:
138	default:
139		return _finishColor(ep->cr, ep->cg, ep->cb, pa);
140	case 1:
141		texel = ((uint8_t*) poly->texBase)[texelCoord];
142		ta = (texel >> 5) & 0x7;
143		ta = (ta << 2) + (ta >> 1);
144		texel &= 0x1F;
145		break;
146	case 2:
147		texel = ((uint8_t*) poly->texBase)[texelCoord >> 2];
148		if (texelCoord & 0x3) {
149			texel >>= 2 * texel & 3;
150		}
151		texel &= 0x3;
152		break;
153	case 3:
154		texel = ((uint8_t*) poly->texBase)[texelCoord >> 1];
155		if (texelCoord & 0x1) {
156			texel >>= 4;
157		}
158		texel &= 0xF;
159		break;
160	case 4:
161		texel = ((uint8_t*) poly->texBase)[texelCoord];
162		break;
163	case 5:
164		texelCoord = (s & ~3) + (t & 3) + (t >> 2) * poly->texW;
165		texel = ((uint8_t*) poly->texBase)[texelCoord];
166		texel >>= (s & 3) * 2;
167		texel &= 3;
168		break;
169	case 6:
170		texel = ((uint8_t*) poly->texBase)[texelCoord];
171		ta = (texel >> 3) & 0x1F;
172		texel &= 0x7;
173		break;
174	case 7:
175		return _finishColor(0x3F, 0x3F, 0x3F, pa);
176	}
177	uint8_t r, g, b;
178	unsigned wr, wg, wb, wa;
179	if (poly->texFormat == 5) {
180		if (!renderer->d.tex[1]) {
181			return 0;
182		}
183		uint16_t half = DSGXTexParamsGetVRAMBase(poly->poly->texParams) & 0x8000;
184		uint32_t slot1Base = (DSGXTexParamsGetVRAMBase(poly->poly->texParams) << 1) + (texelCoord >> 2) + half;
185		uint16_t texel2 = renderer->d.tex[1][slot1Base];
186		uint16_t texel2Base = (texel2 & 0x3FFF) << 1;
187		int a = 0x8;
188		int b = 0;
189		switch (texel2 >> 14) {
190		case 0:
191			if (texel == 3) {
192				ta = 0;
193			}
194			texel = poly->palBase[texel + texel2Base];
195			break;
196		case 1:
197			if (texel == 3) {
198				ta = 0;
199			}
200			if (texel != 2) {
201				texel = poly->palBase[texel + texel2Base];
202			} else {
203				texel = poly->palBase[texel2Base];
204				texel2 = poly->palBase[texel2Base + 1];
205				a = 4;
206				b = 4;
207			}
208			break;
209		case 2:
210			texel = poly->palBase[texel + texel2Base];
211			break;
212		case 3:
213			switch (texel) {
214			case 0:
215			case 1:
216				texel = poly->palBase[texel + texel2Base];
217				break;
218			case 2:
219				texel = poly->palBase[texel2Base];
220				texel2 = poly->palBase[texel2Base + 1];
221				a = 5;
222				b = 3;
223				break;
224			case 3:
225				texel = poly->palBase[texel2Base];
226				texel2 = poly->palBase[texel2Base + 1];
227				a = 3;
228				b = 5;
229				break;
230			}
231			break;
232		}
233		if (b) {
234			texel = _mixTexels(a, texel, b, texel2);
235		}
236	} else {
237		if (poly->texFormat < 5 && poly->texFormat > 1 && DSGXTexParamsIs0Transparent(poly->poly->texParams) && !texel) {
238			return 0;
239		}
240		texel = poly->palBase[texel];
241	}
242	_expandColor(texel, &r, &g, &b);
243	if (ta) {
244		ta = (ta << 1) + 1;
245	}
246	switch (poly->blendFormat) {
247	default:
248		return _finishColor(r, g, b, pa);
249	case 0:
250		wr = ((r + 1) * (ep->cr + 1) - 1) >> 6;
251		wg = ((g + 1) * (ep->cg + 1) - 1) >> 6;
252		wb = ((b + 1) * (ep->cb + 1) - 1) >> 6;
253		wa = ((ta + 1) * (pa + 1) - 1) >> 6;
254		return _finishColor(wr, wg, wb, wa);
255	case 1:
256		wr = (r * ta + ep->cr * (63 - ta)) >> 6;
257		wg = (g * ta + ep->cg * (63 - ta)) >> 6;
258		wb = (b * ta + ep->cb * (63 - ta)) >> 6;
259		return _finishColor(wr, wg, wb, pa);
260	case 3:
261		return _finishColor(r, g, b, pa);
262	}
263}
264
265static inline int32_t _interpolate(int32_t x0, int32_t x1, int64_t w0, int64_t w1, int64_t w, int32_t qr) {
266	// 32-bit -> 96-bit
267	int64_t x0b = (w0 & 0xFFFFFFFF) * x0;
268	int64_t x0t = (w0 >> 32) * x0;
269	int64_t x1b = (w1 & 0xFFFFFFFF) * x1;
270	int64_t x1t = (w1 >> 32) * x1;
271	// 96-bit -> 64-bit
272	int64_t xx0 = (x0t << 32) + x0b;
273	int64_t xx1 = (x1t << 32) + x1b;
274	xx1 -= xx0;
275	xx1 >>= 12;
276
277	int64_t qrb = xx1 * qr;
278	qrb += xx0;
279
280	return qrb / w;
281}
282
283static inline int32_t _divideBy(int64_t x, int32_t recip) {
284	int64_t x0 = (x & 0xFFFFFFFF) * recip;
285	int64_t x1 = (x >> 32) * recip;
286	x1 += x0 >> 32;
287	return x1 >> 31;
288}
289
290static bool _edgeToSpan(struct DSGXSoftwareSpan* span, const struct DSGXSoftwareEdge* edge, int index, int32_t y) {
291	int32_t height = edge->y1 - edge->y0;
292	int64_t yw = y - edge->y0;
293	if (!height) {
294		return false;
295	}
296	// Clamp to bounds
297	if (yw < 0) {
298		return false;
299	} else if (yw > height) {
300		return false;
301	}
302
303	span->ep[index].coord[0] = (((int64_t) (edge->x1 - edge->x0) * yw) / height) + edge->x0;
304
305	if (index) {
306		if (span->ep[0].coord[0] == span->ep[index].coord[0]) {
307			return false;
308		}
309		if (span->ep[0].coord[0] > span->ep[index].coord[0]) {
310			int32_t temp = span->ep[index].coord[0];
311			span->ep[index] = span->ep[0];
312			span->ep[0].coord[0] = temp;
313			index = 0;
314		}
315	}
316
317	int64_t w0 = 0x7FFFFFFFFFFFFFFF / edge->w0;
318	int64_t w1 = 0x7FFFFFFFFFFFFFFF / edge->w1;
319	int64_t w = w1 - w0;
320
321	// Losslessly interpolate two 64-bit values
322	int64_t wb = (w & 0xFFFFFFFF) * yw;
323	int64_t wt = (w >> 32) * yw;
324	int64_t div = wt / height;
325	int64_t rem = wt % height;
326	w = div << 32;
327	wb += rem << 32;
328	div = wb / height;
329	w += div;
330	w += w0;
331
332	span->ep[index].coord[3] = (0x7FFFFFFFFFFFFFFF / w) + 1;
333	span->ep[index].wRecip = w;
334	int32_t qr = (yw << 12) / height;
335
336	span->ep[index].coord[2]  = _interpolate(edge->z0, edge->z1, w0, w1, w, qr);
337	span->ep[index].cr = _interpolate(edge->cr0, edge->cr1, w0, w1, w, qr);
338	span->ep[index].cg = _interpolate(edge->cg0, edge->cg1, w0, w1, w, qr);
339	span->ep[index].cb = _interpolate(edge->cb0, edge->cb1, w0, w1, w, qr);
340	span->ep[index].s  = _interpolate(edge->s0, edge->s1, w0, w1, w, qr);
341	span->ep[index].t  = _interpolate(edge->t0, edge->t1, w0, w1, w, qr);
342
343	return true;
344}
345
346static void _createStep(struct DSGXSoftwareSpan* span) {
347	int32_t width = (span->ep[1].coord[0] - span->ep[0].coord[0]) >> 7;
348
349	span->ep[0].stepW = span->ep[0].wRecip;
350	span->ep[0].stepZ = span->ep[0].coord[2] * span->ep[0].wRecip;
351	span->ep[0].stepR = span->ep[0].cr * span->ep[0].wRecip;
352	span->ep[0].stepG = span->ep[0].cg * span->ep[0].wRecip;
353	span->ep[0].stepB = span->ep[0].cb * span->ep[0].wRecip;
354	span->ep[0].stepS = span->ep[0].s  * span->ep[0].wRecip;
355	span->ep[0].stepT = span->ep[0].t  * span->ep[0].wRecip;
356
357	span->ep[1].stepW = span->ep[1].wRecip;
358	span->ep[1].stepZ = span->ep[1].coord[2] * span->ep[1].wRecip;
359	span->ep[1].stepR = span->ep[1].cr * span->ep[1].wRecip;
360	span->ep[1].stepG = span->ep[1].cg * span->ep[1].wRecip;
361	span->ep[1].stepB = span->ep[1].cb * span->ep[1].wRecip;
362	span->ep[1].stepS = span->ep[1].s  * span->ep[1].wRecip;
363	span->ep[1].stepT = span->ep[1].t  * span->ep[1].wRecip;
364
365	if (!width) {
366		return;
367	}
368	span->step.coord[0] = span->ep[1].coord[0] - span->ep[0].coord[0];
369	span->step.stepW = (span->ep[1].stepW - span->ep[0].stepW) / width;
370	span->step.stepZ = (span->ep[1].stepZ - span->ep[0].stepZ) / width;
371	span->step.stepR = (span->ep[1].stepR - span->ep[0].stepR) / width;
372	span->step.stepG = (span->ep[1].stepG - span->ep[0].stepG) / width;
373	span->step.stepB = (span->ep[1].stepB - span->ep[0].stepB) / width;
374	span->step.stepS = (span->ep[1].stepS - span->ep[0].stepS) / width;
375	span->step.stepT = (span->ep[1].stepT - span->ep[0].stepT) / width;
376}
377
378static void _stepEndpoint(struct DSGXSoftwareSpan* span) {
379	int i = 28;
380	int32_t nextX = (span->ep[0].coord[0] & ~0xFFF) + 0x1000;
381	span->ep[0].coord[0] += 0x80 * i;
382	while (span->ep[0].coord[0] < nextX) {
383		span->ep[0].coord[0] += 0x80;
384		++i;
385	}
386	span->ep[0].wRecip += span->step.stepW * i;
387	span->ep[0].coord[3] = (0x7FFFFFFFFFFFFFFF / span->ep[0].wRecip) + 1;
388
389	span->ep[0].stepZ += span->step.stepZ * i;
390	span->ep[0].coord[2] = _divideBy(span->ep[0].stepZ, span->ep[0].coord[3]);
391
392	span->ep[0].stepR += span->step.stepR * i;
393	span->ep[0].stepG += span->step.stepG * i;
394	span->ep[0].stepB += span->step.stepB * i;
395	span->ep[0].stepS += span->step.stepS * i;
396	span->ep[0].stepT += span->step.stepT * i;
397}
398
399static void _resolveEndpoint(struct DSGXSoftwareSpan* span) {
400	span->ep[0].cr = _divideBy(span->ep[0].stepR, span->ep[0].coord[3]);
401	span->ep[0].cg = _divideBy(span->ep[0].stepG, span->ep[0].coord[3]);
402	span->ep[0].cb = _divideBy(span->ep[0].stepB, span->ep[0].coord[3]);
403	span->ep[0].s = _divideBy(span->ep[0].stepS, span->ep[0].coord[3]);
404	span->ep[0].t = _divideBy(span->ep[0].stepT, span->ep[0].coord[3]);
405}
406
407void DSGXSoftwareRendererCreate(struct DSGXSoftwareRenderer* renderer) {
408	renderer->d.init = DSGXSoftwareRendererInit;
409	renderer->d.reset = DSGXSoftwareRendererReset;
410	renderer->d.deinit = DSGXSoftwareRendererDeinit;
411	renderer->d.invalidateTex = DSGXSoftwareRendererInvalidateTex;
412	renderer->d.setRAM = DSGXSoftwareRendererSetRAM;
413	renderer->d.drawScanline = DSGXSoftwareRendererDrawScanline;
414	renderer->d.getScanline = DSGXSoftwareRendererGetScanline;
415}
416
417static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer) {
418	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
419	DSGXSoftwarePolygonListInit(&softwareRenderer->activePolys, DS_GX_POLYGON_BUFFER_SIZE / 4);
420	DSGXSoftwareEdgeListInit(&softwareRenderer->activeEdges, DS_GX_POLYGON_BUFFER_SIZE);
421	DSGXSoftwareSpanListInit(&softwareRenderer->activeSpans, DS_GX_POLYGON_BUFFER_SIZE / 2);
422	softwareRenderer->bucket = anonymousMemoryMap(sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
423	softwareRenderer->scanlineCache = anonymousMemoryMap(sizeof(color_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
424}
425
426static void DSGXSoftwareRendererReset(struct DSGXRenderer* renderer) {
427	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
428	softwareRenderer->flushPending = false;
429}
430
431static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer) {
432	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
433	DSGXSoftwarePolygonListDeinit(&softwareRenderer->activePolys);
434	DSGXSoftwareEdgeListDeinit(&softwareRenderer->activeEdges);	
435	DSGXSoftwareSpanListDeinit(&softwareRenderer->activeSpans);
436	mappedMemoryFree(softwareRenderer->bucket, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
437	mappedMemoryFree(softwareRenderer->scanlineCache, sizeof(color_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
438}
439
440static void DSGXSoftwareRendererInvalidateTex(struct DSGXRenderer* renderer, int slot) {
441	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
442	// TODO
443}
444
445static void _preparePoly(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXSoftwarePolygon* poly, int polyId) {
446	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
447	struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
448	poly->texFormat = DSGXTexParamsGetFormat(poly->poly->texParams);
449	poly->blendFormat = DSGXPolygonAttrsGetMode(poly->poly->polyParams);
450	poly->texW = 8 << DSGXTexParamsGetSSize(poly->poly->texParams);
451	poly->texH = 8 << DSGXTexParamsGetTSize(poly->poly->texParams);
452	if (!renderer->tex[DSGXTexParamsGetVRAMBase(poly->poly->texParams) >> VRAM_BLOCK_OFFSET]) {
453		poly->texBase = NULL;
454		poly->palBase = NULL;
455	} else {
456		switch (poly->texFormat) {
457		case 0:
458		case 7:
459			poly->texBase = NULL;
460			poly->palBase = NULL;
461			break;
462		case 2:
463			poly->texBase = &renderer->tex[DSGXTexParamsGetVRAMBase(poly->poly->texParams) >> VRAM_BLOCK_OFFSET][(DSGXTexParamsGetVRAMBase(poly->poly->texParams) << 2) & 0xFFFF];
464			poly->palBase = &renderer->texPal[poly->poly->palBase >> 11][(poly->poly->palBase << 2) & 0x1FFF];
465			break;
466		default:
467			poly->texBase = &renderer->tex[DSGXTexParamsGetVRAMBase(poly->poly->texParams) >> VRAM_BLOCK_OFFSET][(DSGXTexParamsGetVRAMBase(poly->poly->texParams) << 2) & 0xFFFF];
468			poly->palBase = &renderer->texPal[poly->poly->palBase >> 10][(poly->poly->palBase << 3) & 0x1FFF];
469			break;
470		}
471	}
472	edge->polyId = polyId;
473
474	struct DSGXVertex* v0 = &verts[poly->poly->vertIds[0]];
475	struct DSGXVertex* v1;
476
477	int32_t v0x = (v0->viewCoord[0] + v0->viewCoord[3]) * (int64_t) (renderer->viewportWidth << 12) / (v0->viewCoord[3] * 2) + (renderer->viewportX << 12);
478	int32_t v0y = (-v0->viewCoord[1] + v0->viewCoord[3]) * (int64_t) (renderer->viewportHeight << 12) / (v0->viewCoord[3] * 2) + (renderer->viewportY << 12);
479
480	int v;
481	for (v = 1; v < poly->poly->verts; ++v) {
482		v1 = &verts[poly->poly->vertIds[v]];
483		int32_t v1x = (v1->viewCoord[0] + v1->viewCoord[3]) * (int64_t) (renderer->viewportWidth << 12) / (v1->viewCoord[3] * 2) + (renderer->viewportX << 12);
484		int32_t v1y = (-v1->viewCoord[1] + v1->viewCoord[3]) * (int64_t) (renderer->viewportHeight << 12) / (v1->viewCoord[3] * 2) + (renderer->viewportY << 12);
485
486		if (v0y <= v1y) {
487			edge->y0 = v0y;
488			edge->x0 = v0x;
489			edge->z0 = v0->viewCoord[2];
490			edge->w0 = v0->viewCoord[3];
491			_expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
492			edge->s0 = v0->vs;
493			edge->t0 = v0->vt;
494
495			edge->y1 = v1y;
496			edge->x1 = v1x;
497			edge->z1 = v1->viewCoord[2];
498			edge->w1 = v1->viewCoord[3];
499			_expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
500			edge->s1 = v1->vs;
501			edge->t1 = v1->vt;
502		} else {
503			edge->y0 = v1y;
504			edge->x0 = v1x;
505			edge->z0 = v1->viewCoord[2];
506			edge->w0 = v1->viewCoord[3];
507			_expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
508			edge->s0 = v1->vs;
509			edge->t0 = v1->vt;
510
511			edge->y1 = v0y;
512			edge->x1 = v0x;
513			edge->z1 = v0->viewCoord[2];
514			edge->w1 = v0->viewCoord[3];
515			_expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
516			edge->s1 = v0->vs;
517			edge->t1 = v0->vt;
518		}
519
520		edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
521		edge->polyId = polyId;
522		v0 = v1;
523		v0x = v1x;
524		v0y = v1y;
525	}
526
527	v1 = &verts[poly->poly->vertIds[0]];
528	int32_t v1x = (v1->viewCoord[0] + v1->viewCoord[3]) * (int64_t) (renderer->viewportWidth << 12) / (v1->viewCoord[3] * 2) + (renderer->viewportX << 12);
529	int32_t v1y = (-v1->viewCoord[1] + v1->viewCoord[3]) * (int64_t) (renderer->viewportHeight << 12) / (v1->viewCoord[3] * 2) + (renderer->viewportY << 12);
530
531	if (v0y <= v1y) {
532		edge->y0 = v0y;
533		edge->x0 = v0x;
534		edge->z0 = v0->viewCoord[2];
535		edge->w0 = v0->viewCoord[3];
536		_expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
537		edge->s0 = v0->vs;
538		edge->t0 = v0->vt;
539
540		edge->y1 = v1y;
541		edge->x1 = v1x;
542		edge->z1 = v1->viewCoord[2];
543		edge->w1 = v1->viewCoord[3];
544		_expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
545		edge->s1 = v1->vs;
546		edge->t1 = v1->vt;
547	} else {
548		edge->y0 = v1y;
549		edge->x0 = v1x;
550		edge->z0 = v1->viewCoord[2];
551		edge->w0 = v1->viewCoord[3];
552		_expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
553		edge->s0 = v1->vs;
554		edge->t0 = v1->vt;
555
556		edge->y1 = v0y;
557		edge->x1 = v0x;
558		edge->z1 = v0->viewCoord[2];
559		edge->w1 = v0->viewCoord[3];
560		_expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
561		edge->s1 = v0->vs;
562		edge->t1 = v0->vt;
563	}
564}
565
566static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount, bool wSort) {
567	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
568
569	softwareRenderer->flushPending = true;
570	softwareRenderer->sort = wSort ? 3 : 2;
571	softwareRenderer->verts = verts;
572	DSGXSoftwarePolygonListClear(&softwareRenderer->activePolys);
573	DSGXSoftwareEdgeListClear(&softwareRenderer->activeEdges);
574	unsigned i;
575	// Pass 1: Opaque
576	for (i = 0; i < polyCount; ++i) {
577		struct DSGXSoftwarePolygon* poly = NULL;
578		switch (DSGXTexParamsGetFormat(polys[i].texParams)) {
579		default:
580			if (DSGXPolygonAttrsGetAlpha(polys[i].polyParams) == 0x1F) {
581				poly = DSGXSoftwarePolygonListAppend(&softwareRenderer->activePolys);
582				break;
583			}
584		case 1:
585		case 6:
586			break;
587		}
588		if (!poly) {
589			continue;
590		}
591		poly->poly = &polys[i];
592		_preparePoly(renderer, verts, poly, DSGXSoftwarePolygonListSize(&softwareRenderer->activePolys) - 1);
593	}
594	// Pass 2: Translucent
595	for (i = 0; i < polyCount; ++i) {
596		struct DSGXSoftwarePolygon* poly = NULL;
597		switch (DSGXTexParamsGetFormat(polys[i].texParams)) {
598		default:
599			if (DSGXPolygonAttrsGetAlpha(polys[i].polyParams) == 0x1F) {
600				break;
601			}
602		case 1:
603		case 6:
604			poly = DSGXSoftwarePolygonListAppend(&softwareRenderer->activePolys);
605			break;
606		}
607		if (!poly) {
608			continue;
609		}
610		poly->poly = &polys[i];
611		_preparePoly(renderer, verts, poly, DSGXSoftwarePolygonListSize(&softwareRenderer->activePolys) - 1);
612	}
613}
614
615static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y) {
616	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
617	if (!softwareRenderer->flushPending) {
618		return;
619	}
620	DSGXSoftwareSpanListClear(&softwareRenderer->activeSpans);
621	memset(softwareRenderer->bucket, 0, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
622	size_t i;
623	for (i = 0; i < DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges); ++i) {
624		struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, i);
625		if (edge->y1 < (y << 12)) {
626			continue;
627		} else if (edge->y0 > (y << 12)) {
628			continue;
629		}
630
631		unsigned poly = edge->polyId;
632		struct DSGXSoftwareSpan* span = softwareRenderer->bucket[poly];
633		if (span && !span->ep[1].coord[3]) {
634			if (_edgeToSpan(span, edge, 1, y << 12)) {
635				_createStep(span);
636				softwareRenderer->bucket[poly] = NULL;
637			}
638		} else if (!span) {
639			span = DSGXSoftwareSpanListAppend(&softwareRenderer->activeSpans);
640			memset(&span->ep[1], 0, sizeof(span->ep[1]));
641			span->poly = DSGXSoftwarePolygonListGetPointer(&softwareRenderer->activePolys, poly);
642			span->polyId = DSGXPolygonAttrsGetId(span->poly->poly->polyParams);
643			if (!_edgeToSpan(span, edge, 0, y << 12)) {
644				// Horizontal line
645				DSGXSoftwareSpanListShift(&softwareRenderer->activeSpans, DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans) - 1, 1);
646			} else {
647				softwareRenderer->bucket[poly] = span;
648			}
649		}
650	}
651
652	color_t* scanline = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
653	memset(scanline, 0, sizeof(color_t) * DS_VIDEO_HORIZONTAL_PIXELS);
654	memset(softwareRenderer->stencilBuffer, 0, sizeof(softwareRenderer->stencilBuffer[0]) * DS_VIDEO_HORIZONTAL_PIXELS);
655	for (i = 0; i < DS_VIDEO_HORIZONTAL_PIXELS; i += 4) {
656		softwareRenderer->depthBuffer[i] = INT32_MAX;
657		softwareRenderer->depthBuffer[i + 1] = INT32_MAX;
658		softwareRenderer->depthBuffer[i + 2] = INT32_MAX;
659		softwareRenderer->depthBuffer[i + 3] = INT32_MAX;
660	}
661
662	for (i = 0; i < DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans); ++i) {
663		struct DSGXSoftwareSpan* span = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, i);
664
665		int32_t x = span->ep[0].coord[0] >> 12;
666		if (x < 0) {
667			x = 0;
668		}
669		unsigned stencilValue = span->polyId;
670		if (span->poly->blendFormat == 3) {
671			stencilValue |= 0x40;
672		}
673		for (; x < (span->ep[1].coord[0] >> 12) && x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
674			if (span->ep[0].coord[softwareRenderer->sort] < softwareRenderer->depthBuffer[x]) {
675				_resolveEndpoint(span);
676				color_t color = _lookupColor(softwareRenderer, &span->ep[0], span->poly);
677				unsigned a = color >> 27;
678				unsigned current = scanline[x];
679				unsigned b = current >> 27;
680				unsigned ab = a;
681				unsigned s = stencilValue;
682				if (b > ab) {
683					ab = b;
684				}
685				if (a == 0x1F) {
686					if (!(s == 0x40 || (softwareRenderer->stencilBuffer[x] & 0x40))) {
687						softwareRenderer->depthBuffer[x] = span->ep[0].coord[softwareRenderer->sort];
688						scanline[x] = color;
689						s &= ~0x40;
690					}
691					softwareRenderer->stencilBuffer[x] = s;
692				} else if (a) {
693					// TODO: Disable alpha?
694					if (b) {
695						color = _mix32(a, color, 0x1F - a, current);
696						color |= ab << 27;
697					}
698					if (softwareRenderer->stencilBuffer[x] != s) {
699						if (!(s == 0x40 || (softwareRenderer->stencilBuffer[x] & 0x40))) {
700							if (DSGXPolygonAttrsIsUpdateDepth(span->poly->poly->polyParams)) {
701								softwareRenderer->depthBuffer[x] = span->ep[0].coord[softwareRenderer->sort];
702							}
703							scanline[x] = color;
704							s &= ~0x40;
705						}
706						softwareRenderer->stencilBuffer[x] = s;
707					}
708				}
709			}
710			_stepEndpoint(span);
711		}
712	}
713
714	if (y == DS_VIDEO_VERTICAL_PIXELS - 1) {
715		softwareRenderer->flushPending = false;
716	}
717}
718
719static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, const color_t** output) {
720	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
721	*output = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
722}