all repos — mgba @ fbece0e7933c59579f7ae6494980d4eda6783254

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) & 0xF8;
 32	rgba |= (g << 10) & 0xF800;
 33	rgba |= (b << 18) & 0xF80000;
 34	rgba |= (a << 27) & 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	// TODO: Optimize
 94	uint16_t texel;
 95
 96	int16_t s = ep->s >> 4;
 97	int16_t t = ep->t >> 4;
 98	if (!DSGXTexParamsIsSRepeat(poly->poly->texParams)) {
 99		if (s < 0) {
100			s = 0;
101		} else if (s >= poly->texW) {
102			s = poly->texW - 1;
103		}
104	} else if (DSGXTexParamsIsSMirror(poly->poly->texParams)) {
105		if (s & poly->texW) {
106			s = poly->texW - s - 1;
107		}
108		s &= poly->texW - 1;
109	} else {
110		s &= poly->texW - 1;
111	}
112	if (!DSGXTexParamsIsTRepeat(poly->poly->texParams)) {
113		if (t < 0) {
114			t = 0;
115		} else if (t >= poly->texH) {
116			t = poly->texW - 1;
117		}
118	} else if (DSGXTexParamsIsTMirror(poly->poly->texParams)) {
119		if (t & poly->texH) {
120			t = poly->texH - t - 1;
121		}
122		t &= poly->texH - 1;
123	} else {
124		t &= poly->texH - 1;
125	}
126
127	uint16_t texelCoord = s + t * poly->texW;
128	uint8_t ta = 0x1F;
129	uint8_t pa = DSGXPolygonAttrsGetAlpha(poly->poly->polyParams);
130	switch (poly->texFormat) {
131	case 0:
132	default:
133		return _finishColor(ep->cr, ep->cg, ep->cb, pa);
134	case 1:
135		texel = ((uint8_t*) poly->texBase)[texelCoord];
136		ta = (texel >> 5) & 0x7;
137		ta = (ta << 2) + (ta >> 1);
138		texel &= 0x1F;
139		break;
140	case 2:
141		texel = ((uint8_t*) poly->texBase)[texelCoord >> 2];
142		if (texelCoord & 0x3) {
143			texel >>= 2 * texel & 3;
144		}
145		texel &= 0x3;
146		break;
147	case 3:
148		texel = ((uint8_t*) poly->texBase)[texelCoord >> 1];
149		if (texelCoord & 0x1) {
150			texel >>= 4;
151		}
152		texel &= 0xF;
153		break;
154	case 4:
155		texel = ((uint8_t*) poly->texBase)[texelCoord];
156		break;
157	case 5:
158		texelCoord = (s & ~3) + (t & 3) + (t >> 2) * poly->texW;
159		texel = ((uint8_t*) poly->texBase)[texelCoord];
160		texel >>= (s & 3) * 2;
161		texel &= 3;
162		break;
163	case 6:
164		texel = ((uint8_t*) poly->texBase)[texelCoord];
165		ta = (texel >> 3) & 0x1F;
166		texel &= 0x7;
167		break;
168	case 7:
169		return _finishColor(0x3F, 0x3F, 0x3F, pa);
170	}
171	uint8_t r, g, b;
172	unsigned wr, wg, wb, wa;
173	if (poly->texFormat == 5) {
174		// TODO: Slot 2 uses upper half
175		uint16_t half = DSGXTexParamsGetVRAMBase(poly->poly->texParams) & 0x8000;
176		uint32_t slot1Base = (DSGXTexParamsGetVRAMBase(poly->poly->texParams) << 1) + (texelCoord >> 2) + half;
177		uint16_t texel2 = renderer->d.tex[1][slot1Base];
178		uint16_t texel2Base = (texel2 & 0x3FFF) << 1;
179		int a = 0x8;
180		int b = 0;
181		switch (texel2 >> 14) {
182		case 0:
183			if (texel == 3) {
184				ta = 0;
185			}
186			texel = poly->palBase[texel + texel2Base];
187			break;
188		case 1:
189			if (texel == 3) {
190				ta = 0;
191			}
192			if (texel != 2) {
193				texel = poly->palBase[texel + texel2Base];
194			} else {
195				texel = poly->palBase[texel2Base];
196				texel2 = poly->palBase[texel2Base + 1];
197				a = 4;
198				b = 4;
199			}
200			break;
201		case 2:
202			texel = poly->palBase[texel + texel2Base];
203			break;
204		case 3:
205			switch (texel) {
206			case 0:
207			case 1:
208				texel = poly->palBase[texel + texel2Base];
209				break;
210			case 2:
211				texel = poly->palBase[texel2Base];
212				texel2 = poly->palBase[texel2Base + 1];
213				a = 5;
214				b = 3;
215				break;
216			case 3:
217				texel = poly->palBase[texel2Base];
218				texel2 = poly->palBase[texel2Base + 1];
219				a = 3;
220				b = 5;
221				break;
222			}
223			break;
224		}
225		if (b) {
226			texel = _mixTexels(a, texel, b, texel2);
227		}
228	} else {
229		if (poly->texFormat < 5 && poly->texFormat > 1 && DSGXTexParamsIs0Transparent(poly->poly->texParams) && !texel) {
230			return 0;
231		}
232		texel = poly->palBase[texel];
233	}
234	_expandColor(texel, &r, &g, &b);
235	switch (poly->blendFormat) {
236	case 1:
237	default:
238		// TODO: Alpha
239		return _finishColor(r, g, b, pa);
240	case 0:
241		wr = ((r + 1) * (ep->cr + 1) - 1) >> 6;
242		wg = ((g + 1) * (ep->cg + 1) - 1) >> 6;
243		wb = ((b + 1) * (ep->cb + 1) - 1) >> 6;
244		wa = ((ta + 1) * (pa + 1) - 1) >> 5;
245		return _finishColor(wr, wg, wb, wa);
246	}
247}
248
249static inline int32_t _interpolate(int32_t x0, int32_t x1, int64_t w0, int64_t w1, int64_t w, int32_t qr) {
250	// 32-bit -> 96-bit
251	int64_t x0b = (w0 & 0xFFFFFFFF) * x0;
252	int64_t x0t = (w0 >> 32) * x0;
253	int64_t x1b = (w1 & 0xFFFFFFFF) * x1;
254	int64_t x1t = (w1 >> 32) * x1;
255	// 96-bit -> 64-bit
256	int64_t xx0 = (x0t << 32) + x0b;
257	int64_t xx1 = (x1t << 32) + x1b;
258	xx1 -= xx0;
259	xx1 >>= 12;
260
261	int64_t qrb = xx1 * qr;
262	qrb += xx0;
263	return qrb / w;
264}
265
266static bool _edgeToSpan(struct DSGXSoftwareSpan* span, const struct DSGXSoftwareEdge* edge, int index, int32_t y) {
267	int32_t height = edge->y1 - edge->y0;
268	int64_t yw = y - edge->y0;
269	if (!height) {
270		return false;
271	}
272	// Clamp to bounds
273	if (yw < 0) {
274		return false;
275	} else if (yw > height) {
276		return false;
277	}
278
279	span->ep[index].x = (((int64_t) (edge->x1 - edge->x0) * yw) / height) + edge->x0;
280
281	if (index) {
282		if (span->ep[0].x == span->ep[index].x) {
283			return false;
284		}
285		if (span->ep[0].x > span->ep[index].x) {
286			int32_t temp = span->ep[index].x;
287			span->ep[index] = span->ep[0];
288			span->ep[0].x = temp;
289			index = 0;
290		}
291	}
292
293	int64_t w0 = 0x3FFFFFFFFFFFFFFF / edge->w0;
294	int64_t w1 = 0x3FFFFFFFFFFFFFFF / edge->w1;
295	int64_t w = w1 - w0;
296
297	// Losslessly interpolate two 64-bit values
298	int64_t wb = (w & 0xFFFFFFFF) * yw;
299	int64_t wt = (w >> 32) * yw;
300	int64_t div = wt / height;
301	int64_t rem = wt % height;
302	w = div << 32;
303	wb += rem << 32;
304	div = wb / height;
305	w += div;
306	w += w0;
307
308	span->ep[index].w = 0x3FFFFFFFFFFFFFFF / w;
309	int32_t qr = (yw << 12) / height;
310
311	span->ep[index].z  = _interpolate(edge->z0, edge->z1, w0, w1, w, qr);
312	span->ep[index].cr = _interpolate(edge->cr0, edge->cr1, w0, w1, w, qr);
313	span->ep[index].cg = _interpolate(edge->cg0, edge->cg1, w0, w1, w, qr);
314	span->ep[index].cb = _interpolate(edge->cb0, edge->cb1, w0, w1, w, qr);
315	span->ep[index].s  = _interpolate(edge->s0, edge->s1, w0, w1, w, qr);
316	span->ep[index].t  = _interpolate(edge->t0, edge->t1, w0, w1, w, qr);
317	return true;
318}
319
320static void _lerpEndpoint(const struct DSGXSoftwareSpan* span, struct DSGXSoftwareEndpoint* ep, int x) {
321	int64_t width = span->ep[1].x - span->ep[0].x;
322	int64_t xw = x - span->ep[0].x;
323	if (!width) {
324		return; // TODO?
325	}
326	// Clamp to bounds
327	if (xw < 0) {
328		xw = 0;
329	} else if (xw > width) {
330		xw = width;
331	}
332
333	int64_t w0 = 0x3FFFFFFFFFFFFFFF / span->ep[0].w;
334	int64_t w1 = 0x3FFFFFFFFFFFFFFF / span->ep[1].w;
335	int64_t w = w1 - w0;
336
337	// Losslessly interpolate two 64-bit values
338	int64_t wb = (w & 0xFFFFFFFF) * xw;
339	int64_t wt = (w >> 32) * xw;
340	int64_t div = wt / width;
341	int64_t rem = wt % width;
342	w = div << 32;
343	wb += rem << 32;
344	div = wb / width;
345	w += div;
346	w += w0;
347
348	ep->w = 0x3FFFFFFFFFFFFFFF / w;
349	int32_t qr = (xw << 12) / width;
350
351	ep->z  = _interpolate(span->ep[0].z, span->ep[1].z, w0, w1, w, qr);
352	ep->cr = _interpolate(span->ep[0].cr, span->ep[1].cr, w0, w1, w, qr);
353	ep->cg = _interpolate(span->ep[0].cg, span->ep[1].cg, w0, w1, w, qr);
354	ep->cb = _interpolate(span->ep[0].cb, span->ep[1].cb, w0, w1, w, qr);
355	ep->s  = _interpolate(span->ep[0].s, span->ep[1].s, w0, w1, w, qr);
356	ep->t  = _interpolate(span->ep[0].t, span->ep[1].t, w0, w1, w, qr);
357}
358
359void DSGXSoftwareRendererCreate(struct DSGXSoftwareRenderer* renderer) {
360	renderer->d.init = DSGXSoftwareRendererInit;
361	renderer->d.reset = DSGXSoftwareRendererReset;
362	renderer->d.deinit = DSGXSoftwareRendererDeinit;
363	renderer->d.invalidateTex = DSGXSoftwareRendererInvalidateTex;
364	renderer->d.setRAM = DSGXSoftwareRendererSetRAM;
365	renderer->d.drawScanline = DSGXSoftwareRendererDrawScanline;
366	renderer->d.getScanline = DSGXSoftwareRendererGetScanline;
367}
368
369static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer) {
370	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
371	DSGXSoftwarePolygonListInit(&softwareRenderer->activePolys, DS_GX_POLYGON_BUFFER_SIZE / 4);
372	DSGXSoftwareEdgeListInit(&softwareRenderer->activeEdges, DS_GX_POLYGON_BUFFER_SIZE);
373	DSGXSoftwareSpanListInit(&softwareRenderer->activeSpans, DS_GX_POLYGON_BUFFER_SIZE / 2);
374	softwareRenderer->bucket = anonymousMemoryMap(sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
375	softwareRenderer->scanlineCache = anonymousMemoryMap(sizeof(color_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
376}
377
378static void DSGXSoftwareRendererReset(struct DSGXRenderer* renderer) {
379	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
380	softwareRenderer->flushPending = false;
381}
382
383static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer) {
384	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
385	DSGXSoftwarePolygonListDeinit(&softwareRenderer->activePolys);
386	DSGXSoftwareEdgeListDeinit(&softwareRenderer->activeEdges);	
387	DSGXSoftwareSpanListDeinit(&softwareRenderer->activeSpans);
388	mappedMemoryFree(softwareRenderer->bucket, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
389	mappedMemoryFree(softwareRenderer->scanlineCache, sizeof(color_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
390}
391
392static void DSGXSoftwareRendererInvalidateTex(struct DSGXRenderer* renderer, int slot) {
393	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
394	// TODO
395}
396
397static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount, bool wSort) {
398	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
399
400	softwareRenderer->flushPending = true;
401	softwareRenderer->wSort = wSort;
402	softwareRenderer->verts = verts;
403	DSGXSoftwarePolygonListClear(&softwareRenderer->activePolys);
404	DSGXSoftwareEdgeListClear(&softwareRenderer->activeEdges);
405	unsigned i;
406	for (i = 0; i < polyCount; ++i) {
407		struct DSGXSoftwarePolygon* poly = DSGXSoftwarePolygonListAppend(&softwareRenderer->activePolys);
408		struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
409		poly->poly = &polys[i];
410		poly->texFormat = DSGXTexParamsGetFormat(poly->poly->texParams);
411		poly->blendFormat = DSGXPolygonAttrsGetMode(poly->poly->polyParams);
412		poly->texW = 8 << DSGXTexParamsGetSSize(poly->poly->texParams);
413		poly->texH = 8 << DSGXTexParamsGetTSize(poly->poly->texParams);
414		switch (poly->texFormat) {
415		case 0:
416		case 7:
417			poly->texBase = NULL;
418			poly->palBase = NULL;
419			break;
420		case 2:
421			poly->texBase = &renderer->tex[DSGXTexParamsGetVRAMBase(poly->poly->texParams) >> VRAM_BLOCK_OFFSET][(DSGXTexParamsGetVRAMBase(poly->poly->texParams) << 2) & 0xFFFF];
422			poly->palBase = &renderer->texPal[poly->poly->palBase >> 11][(poly->poly->palBase << 2) & 0x1FFF];
423			break;
424		default:
425			poly->texBase = &renderer->tex[DSGXTexParamsGetVRAMBase(poly->poly->texParams) >> VRAM_BLOCK_OFFSET][(DSGXTexParamsGetVRAMBase(poly->poly->texParams) << 2) & 0xFFFF];
426			poly->palBase = &renderer->texPal[poly->poly->palBase >> 10][(poly->poly->palBase << 3) & 0x1FFF];
427			break;
428		}
429		edge->polyId = i;
430
431		struct DSGXVertex* v0 = &verts[poly->poly->vertIds[0]];
432		struct DSGXVertex* v1;
433
434		int32_t v0x = (v0->vx + v0->vw) * (int64_t) (renderer->viewportWidth << 12) / (v0->vw * 2) + (renderer->viewportX << 12);
435		int32_t v0y = (-v0->vy + v0->vw) * (int64_t) (renderer->viewportHeight << 12) / (v0->vw * 2) + (renderer->viewportY << 12);
436
437		int v;
438		for (v = 1; v < poly->poly->verts; ++v) {
439			v1 = &verts[poly->poly->vertIds[v]];
440			int32_t v1x = (v1->vx + v1->vw) * (int64_t) (renderer->viewportWidth << 12) / (v1->vw * 2) + (renderer->viewportX << 12);
441			int32_t v1y = (-v1->vy + v1->vw) * (int64_t) (renderer->viewportHeight << 12) / (v1->vw * 2) + (renderer->viewportY << 12);
442
443			if (v0y <= v1y) {
444				edge->y0 = v0y;
445				edge->x0 = v0x;
446				edge->z0 = v0->vz;
447				edge->w0 = v0->vw;
448				_expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
449				edge->s0 = v0->vs;
450				edge->t0 = v0->vt;
451
452				edge->y1 = v1y;
453				edge->x1 = v1x;
454				edge->z1 = v1->vz;
455				edge->w1 = v1->vw;
456				_expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
457				edge->s1 = v1->vs;
458				edge->t1 = v1->vt;
459			} else {
460				edge->y0 = v1y;
461				edge->x0 = v1x;
462				edge->z0 = v1->vz;
463				edge->w0 = v1->vw;
464				_expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
465				edge->s0 = v1->vs;
466				edge->t0 = v1->vt;
467
468				edge->y1 = v0y;
469				edge->x1 = v0x;
470				edge->z1 = v0->vz;
471				edge->w1 = v0->vw;
472				_expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
473				edge->s1 = v0->vs;
474				edge->t1 = v0->vt;
475			}
476
477			edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
478			edge->polyId = i;
479			v0 = v1;
480			v0x = v1x;
481			v0y = v1y;
482		}
483
484		v1 = &verts[poly->poly->vertIds[0]];
485		int32_t v1x = (v1->vx + v1->vw) * (int64_t) (renderer->viewportWidth << 12) / (v1->vw * 2) + (renderer->viewportX << 12);
486		int32_t v1y = (-v1->vy + v1->vw) * (int64_t) (renderer->viewportHeight << 12) / (v1->vw * 2) + (renderer->viewportY << 12);
487
488		if (v0y <= v1y) {
489			edge->y0 = v0y;
490			edge->x0 = v0x;
491			edge->z0 = v0->vz;
492			edge->w0 = v0->vw;
493			_expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
494			edge->s0 = v0->vs;
495			edge->t0 = v0->vt;
496
497			edge->y1 = v1y;
498			edge->x1 = v1x;
499			edge->z1 = v1->vz;
500			edge->w1 = v1->vw;
501			_expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
502			edge->s1 = v1->vs;
503			edge->t1 = v1->vt;
504		} else {
505			edge->y0 = v1y;
506			edge->x0 = v1x;
507			edge->z0 = v1->vz;
508			edge->w0 = v1->vw;
509			_expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
510			edge->s0 = v1->vs;
511			edge->t0 = v1->vt;
512
513			edge->y1 = v0y;
514			edge->x1 = v0x;
515			edge->z1 = v0->vz;
516			edge->w1 = v0->vw;
517			_expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
518			edge->s1 = v0->vs;
519			edge->t1 = v0->vt;
520		}
521	}
522}
523
524static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y) {
525	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
526	if (!softwareRenderer->flushPending) {
527		return;
528	}
529	DSGXSoftwareSpanListClear(&softwareRenderer->activeSpans);
530	memset(softwareRenderer->bucket, 0, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
531	size_t i;
532	for (i = 0; i < DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges); ++i) {
533		struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, i);
534		if (edge->y1 < (y << 12)) {
535			continue;
536		} else if (edge->y0 > (y << 12)) {
537			continue;
538		}
539
540		unsigned poly = edge->polyId;
541		struct DSGXSoftwareSpan* span = softwareRenderer->bucket[poly];
542		if (span && !span->ep[1].w) {
543			if (_edgeToSpan(span, edge, 1, y << 12)) {
544				softwareRenderer->bucket[poly] = NULL;
545			}
546		} else if (!span) {
547			span = DSGXSoftwareSpanListAppend(&softwareRenderer->activeSpans);
548			memset(&span->ep[1], 0, sizeof(span->ep[1]));
549			span->poly = DSGXSoftwarePolygonListGetPointer(&softwareRenderer->activePolys, poly);
550			if (!_edgeToSpan(span, edge, 0, y << 12)) {
551				// Horizontal line
552				DSGXSoftwareSpanListShift(&softwareRenderer->activeSpans, DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans) - 1, 1);
553			} else {
554				softwareRenderer->bucket[poly] = span;
555			}
556		}
557	}
558
559	color_t* scanline = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
560	memset(scanline, 0, sizeof(color_t) * DS_VIDEO_HORIZONTAL_PIXELS);
561	for (i = 0; i < DS_VIDEO_HORIZONTAL_PIXELS; i += 4) {
562		softwareRenderer->depthBuffer[i] = INT32_MAX;
563		softwareRenderer->depthBuffer[i + 1] = INT32_MAX;
564		softwareRenderer->depthBuffer[i + 2] = INT32_MAX;
565		softwareRenderer->depthBuffer[i + 3] = INT32_MAX;
566	}
567
568	for (i = 0; i < DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans); ++i) {
569		struct DSGXSoftwareSpan* span = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, i);
570
571		int32_t x = span->ep[0].x >> 12;
572		if (x < 0) {
573			x = 0;
574		}
575		for (; x < (span->ep[1].x >> 12) && x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
576			struct DSGXSoftwareEndpoint ep;
577			_lerpEndpoint(span, &ep, x << 12);
578			color_t color = _lookupColor(softwareRenderer, &ep, span->poly);
579			unsigned a = color >> 27;
580			unsigned current = scanline[x];
581			unsigned b = current >> 27;
582			unsigned ab = a;
583			if (b > ab) {
584				ab = b;
585			}
586			if (a == 0x1F) {
587				if (softwareRenderer->wSort) {
588					if (ep.w < softwareRenderer->depthBuffer[x]) {
589						softwareRenderer->depthBuffer[x] = ep.w;
590						scanline[x] = color;
591					} else if (b < 0x1F) {
592						scanline[x] = _mix32(b, current, 0x1F - b, color) | a << 27;
593					}
594				} else {
595					if (ep.z < softwareRenderer->depthBuffer[x]) {
596						softwareRenderer->depthBuffer[x] = ep.z;
597						scanline[x] = color;
598					} else if (b < 0x1F) {
599						scanline[x] = _mix32(b, current, 0x1F - b, color) | a << 27;
600					}
601				}
602			} else if (a) {
603				// TODO: Disable alpha?
604				color = _mix32(a, color, 0x1F - a, current);
605				color |= ab << 27;
606				if (softwareRenderer->wSort) {
607					if (ep.w < softwareRenderer->depthBuffer[x]) {
608						softwareRenderer->depthBuffer[x] = ep.w;
609						scanline[x] = color;
610					} else if (b < 0x1F) {
611						scanline[x] = _mix32(b, current, 0x1F - b, color) | ab << 27;
612					}
613				} else {
614					if (ep.z < softwareRenderer->depthBuffer[x]) {
615						softwareRenderer->depthBuffer[x] = ep.z;
616						scanline[x] = color;
617					} else if (b < 0x1F) {
618						scanline[x] = _mix32(b, current, 0x1F - b, color) | ab << 27;
619					}
620				}
621			}
622		}
623	}
624
625	if (y == DS_VIDEO_VERTICAL_PIXELS - 1) {
626		softwareRenderer->flushPending = false;
627	}
628}
629
630static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, const color_t** output) {
631	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
632	*output = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
633}