all repos — mgba @ 055f705c915ba64159471bba291d88968c2d6e3f

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 - 1) << 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 DSGXSoftwareRendererInvalidateTex(struct DSGXRenderer* renderer, int slot);
 21static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount, bool wSort);
 22static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y);
 23static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, const color_t** output);
 24
 25static void _expandColor(uint16_t c15, uint8_t* r, uint8_t* g, uint8_t* b) {
 26	*r = ((c15 << 1) & 0x3E) | 1;
 27	*g = ((c15 >> 4) & 0x3E) | 1;
 28	*b = ((c15 >> 9) & 0x3E) | 1;
 29}
 30
 31static color_t _finishColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
 32#ifndef COLOR_16_BIT
 33	color_t rgba = (r << 2) & 0xF8;
 34	rgba |= (g << 10) & 0xF800;
 35	rgba |= (b << 18) & 0xF80000;
 36	rgba |= (a << 27) & 0xF8000000;
 37	return rgba;
 38#else
 39#error Unsupported color depth
 40#endif
 41}
 42
 43static unsigned _mix32(int weightA, unsigned colorA, int weightB, unsigned colorB) {
 44	unsigned c = 0;
 45	unsigned a, b;
 46#ifdef COLOR_16_BIT
 47#error Unsupported color depth
 48#else
 49	a = colorA & 0xFF;
 50	b = colorB & 0xFF;
 51	c |= ((a * weightA + b * weightB) / 32) & 0x1FF;
 52	if (c & 0x00000100) {
 53		c = 0x000000FF;
 54	}
 55
 56	a = colorA & 0xFF00;
 57	b = colorB & 0xFF00;
 58	c |= ((a * weightA + b * weightB) / 32) & 0x1FF00;
 59	if (c & 0x00010000) {
 60		c = (c & 0x000000FF) | 0x0000FF00;
 61	}
 62
 63	a = colorA & 0xFF0000;
 64	b = colorB & 0xFF0000;
 65	c |= ((a * weightA + b * weightB) / 32) & 0x1FF0000;
 66	if (c & 0x01000000) {
 67		c = (c & 0x0000FFFF) | 0x00FF0000;
 68	}
 69#endif
 70	return c;
 71}
 72
 73static unsigned _mixTexels(int weightA, unsigned colorA, int weightB, unsigned colorB) {
 74	unsigned c = 0;
 75	unsigned a, b;
 76	a = colorA & 0x7C1F;
 77	b = colorB & 0x7C1F;
 78	a |= (colorA & 0x3E0) << 16;
 79	b |= (colorB & 0x3E0) << 16;
 80	c = ((a * weightA + b * weightB) / 8);
 81	if (c & 0x04000000) {
 82		c = (c & ~0x07E00000) | 0x03E00000;
 83	}
 84	if (c & 0x0020) {
 85		c = (c & ~0x003F) | 0x001F;
 86	}
 87	if (c & 0x8000) {
 88		c = (c & ~0xF800) | 0x7C00;
 89	}
 90	c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
 91	return c;
 92}
 93
 94static color_t _lookupColor(struct DSGXSoftwareRenderer* renderer, struct DSGXSoftwareEndpoint* ep, struct DSGXSoftwarePolygon* poly) {
 95	// TODO: Optimize
 96	uint16_t texel;
 97
 98	int16_t s = ep->s >> 4;
 99	int16_t t = ep->t >> 4;
100	if (!DSGXTexParamsIsSRepeat(poly->poly->texParams)) {
101		if (s < 0) {
102			s = 0;
103		} else if (s >= poly->texW) {
104			s = poly->texW - 1;
105		}
106	} else if (DSGXTexParamsIsSMirror(poly->poly->texParams)) {
107		if (s & poly->texW) {
108			s = poly->texW - s - 1;
109		}
110		s &= poly->texW - 1;
111	} else {
112		s &= poly->texW - 1;
113	}
114	if (!DSGXTexParamsIsTRepeat(poly->poly->texParams)) {
115		if (t < 0) {
116			t = 0;
117		} else if (t >= poly->texH) {
118			t = poly->texW - 1;
119		}
120	} else if (DSGXTexParamsIsTMirror(poly->poly->texParams)) {
121		if (t & poly->texH) {
122			t = poly->texH - t - 1;
123		}
124		t &= poly->texH - 1;
125	} else {
126		t &= poly->texH - 1;
127	}
128
129	uint16_t texelCoord = s + t * poly->texW;
130	uint8_t ta = 0x1F;
131	uint8_t pa = DSGXPolygonAttrsGetAlpha(poly->poly->polyParams);
132	switch (poly->texFormat) {
133	case 0:
134	default:
135		return _finishColor(ep->cr, ep->cg, ep->cb, pa);
136	case 1:
137		texel = ((uint8_t*) poly->texBase)[texelCoord];
138		ta = (texel >> 5) & 0x7;
139		ta = (ta << 2) + (ta >> 1);
140		texel &= 0x1F;
141		break;
142	case 2:
143		texel = ((uint8_t*) poly->texBase)[texelCoord >> 2];
144		if (texelCoord & 0x3) {
145			texel >>= 2 * texel & 3;
146		}
147		texel &= 0x3;
148		break;
149	case 3:
150		texel = ((uint8_t*) poly->texBase)[texelCoord >> 1];
151		if (texelCoord & 0x1) {
152			texel >>= 4;
153		}
154		texel &= 0xF;
155		break;
156	case 4:
157		texel = ((uint8_t*) poly->texBase)[texelCoord];
158		break;
159	case 5:
160		texelCoord = (s & ~3) + (t & 3) + (t >> 2) * poly->texW;
161		texel = ((uint8_t*) poly->texBase)[texelCoord];
162		texel >>= (s & 3) * 2;
163		texel &= 3;
164		break;
165	case 6:
166		texel = ((uint8_t*) poly->texBase)[texelCoord];
167		ta = (texel >> 3) & 0x1F;
168		texel &= 0x7;
169		break;
170	case 7:
171		return _finishColor(0x3F, 0x3F, 0x3F, pa);
172	}
173	uint8_t r, g, b;
174	unsigned wr, wg, wb, wa;
175	if (poly->texFormat == 5) {
176		// TODO: Slot 2 uses upper half
177		uint16_t half = DSGXTexParamsGetVRAMBase(poly->poly->texParams) & 0x8000;
178		uint32_t slot1Base = (DSGXTexParamsGetVRAMBase(poly->poly->texParams) << 1) + (texelCoord >> 2) + half;
179		uint16_t texel2 = renderer->d.tex[1][slot1Base];
180		uint16_t texel2Base = (texel2 & 0x3FFF) << 1;
181		int a = 0x8;
182		int b = 0;
183		switch (texel2 >> 14) {
184		case 0:
185			if (texel == 3) {
186				ta = 0;
187			}
188			texel = poly->palBase[texel + texel2Base];
189			break;
190		case 1:
191			if (texel == 3) {
192				ta = 0;
193			}
194			if (texel != 2) {
195				texel = poly->palBase[texel + texel2Base];
196			} else {
197				texel = poly->palBase[texel2Base];
198				texel2 = poly->palBase[texel2Base + 1];
199				a = 4;
200				b = 4;
201			}
202			break;
203		case 2:
204			texel = poly->palBase[texel + texel2Base];
205			break;
206		case 3:
207			switch (texel) {
208			case 0:
209			case 1:
210				texel = poly->palBase[texel + texel2Base];
211				break;
212			case 2:
213				texel = poly->palBase[texel2Base];
214				texel2 = poly->palBase[texel2Base + 1];
215				a = 5;
216				b = 3;
217				break;
218			case 3:
219				texel = poly->palBase[texel2Base];
220				texel2 = poly->palBase[texel2Base + 1];
221				a = 3;
222				b = 5;
223				break;
224			}
225			break;
226		}
227		if (b) {
228			texel = _mixTexels(a, texel, b, texel2);
229		}
230	} else {
231		if (poly->texFormat < 5 && poly->texFormat > 1 && DSGXTexParamsIs0Transparent(poly->poly->texParams) && !texel) {
232			return 0;
233		}
234		texel = poly->palBase[texel];
235	}
236	_expandColor(texel, &r, &g, &b);
237	switch (poly->blendFormat) {
238	case 1:
239	default:
240		// TODO: Alpha
241		return _finishColor(r, g, b, pa);
242	case 0:
243		wr = ((r + 1) * (ep->cr + 1) - 1) >> 6;
244		wg = ((g + 1) * (ep->cg + 1) - 1) >> 6;
245		wb = ((b + 1) * (ep->cb + 1) - 1) >> 6;
246		wa = ((ta + 1) * (pa + 1) - 1) >> 5;
247		return _finishColor(wr, wg, wb, wa);
248	}
249}
250
251static bool _edgeToSpan(struct DSGXSoftwareSpan* span, const struct DSGXSoftwareEdge* edge, int index, int32_t y) {
252	int32_t height = edge->y1 - edge->y0;
253	int64_t yw = (y << 12) - edge->y0;
254	if (!height) {
255		return false;
256	}
257	// Clamp to bounds
258	if (yw < 0) {
259		return false;
260	} else if (yw > height) {
261		return false;
262	}
263	yw *= 0x100000000LL / height;
264
265	span->ep[index].x = (((int64_t) (edge->x1 - edge->x0) * yw) >> 32) + edge->x0;
266
267	if (index && span->ep[0].x > span->ep[index].x) {
268		int32_t temp = span->ep[index].x;
269		span->ep[index] = span->ep[0];
270		span->ep[0].x = temp;
271		index = 0;
272	}
273	int32_t w0 = edge->w0;
274	int32_t w1 = edge->w1;
275	int32_t w = (((int64_t) (edge->w1 - edge->w0) * yw) >> 32) + edge->w0;
276	int64_t wRecip;// = 0x1000000000000LL / w;
277	// XXX: Disable perspective correction until I figure out how to fix it
278	wRecip = 0x100000000;
279	w0 = 0x10000;
280	w1 = 0x10000;
281	span->ep[index].w = w;
282	span->ep[index].z = (((edge->z1 - edge->z0) * yw) >> 32) + edge->z0;
283	span->ep[index].cr = (((((edge->cr1 * (int64_t) w1 - edge->cr0 * (int64_t) w0) * yw) >> 32) + edge->cr0 * (int64_t) w0) * wRecip) >> 48;
284	span->ep[index].cg = (((((edge->cg1 * (int64_t) w1 - edge->cg0 * (int64_t) w0) * yw) >> 32) + edge->cg0 * (int64_t) w0) * wRecip) >> 48;
285	span->ep[index].cb = (((((edge->cb1 * (int64_t) w1 - edge->cb0 * (int64_t) w0) * yw) >> 32) + edge->cb0 * (int64_t) w0) * wRecip) >> 48;
286	span->ep[index].s = (((((edge->s1 * (int64_t) w1 - edge->s0 * (int64_t) w0) * yw) >> 32) + edge->s0 * (int64_t) w0) * wRecip) >> 48;
287	span->ep[index].t = (((((edge->t1 * (int64_t) w1 - edge->t0 * (int64_t) w0) * yw) >> 32) + edge->t0 * (int64_t) w0) * wRecip) >> 48;
288
289	return true;
290}
291
292static void _lerpEndpoint(const struct DSGXSoftwareSpan* span, struct DSGXSoftwareEndpoint* ep, unsigned x) {
293	int64_t width = span->ep[1].x - span->ep[0].x;
294	int64_t xw = ((uint64_t) x << 12) - span->ep[0].x;
295	if (!width) {
296		return; // TODO?
297	}
298	// Clamp to bounds
299	if (xw < 0) {
300		xw = 0;
301	} else if (xw > width) {
302		xw = width;
303	}
304	xw *= 0x100000000LL / width;
305	int32_t w0 = span->ep[0].w;
306	int32_t w1 = span->ep[1].w;
307	int64_t w = (((int64_t) (w1 - w0) * xw) >> 32) + w0;
308	int64_t wRecip;// = 0x1000000000000LL / w;
309	ep->w = w;
310	// XXX: Disable perspective correction until I figure out how to fix it
311	wRecip = 0x100000000;
312	w0 = 0x10000;
313	w1 = 0x10000;
314
315	ep->z = (((span->ep[1].z - span->ep[0].z) * xw) >> 32) + span->ep[0].z;
316
317	uint64_t r = (((span->ep[1].cr * (int64_t) w1 - span->ep[0].cr * (int64_t) w0) * xw) >> 32) + span->ep[0].cr * (int64_t) w0;
318	uint64_t g = (((span->ep[1].cg * (int64_t) w1 - span->ep[0].cg * (int64_t) w0) * xw) >> 32) + span->ep[0].cg * (int64_t) w0;
319	uint64_t b = (((span->ep[1].cb * (int64_t) w1 - span->ep[0].cb * (int64_t) w0) * xw) >> 32) + span->ep[0].cb * (int64_t) w0;
320	ep->cr = (r * wRecip) >> 48;
321	ep->cg = (g * wRecip) >> 48;
322	ep->cb = (b * wRecip) >> 48;
323
324	int32_t s = (((span->ep[1].s * (int64_t) w1 - span->ep[0].s * (int64_t) w0) * xw) >> 32) + span->ep[0].s * (int64_t) w0;
325	int32_t t = (((span->ep[1].t * (int64_t) w1 - span->ep[0].t * (int64_t) w0) * xw) >> 32) + span->ep[0].t * (int64_t) w0;
326	ep->s = (s * wRecip) >> 48;
327	ep->t = (t * wRecip) >> 48;
328}
329
330void DSGXSoftwareRendererCreate(struct DSGXSoftwareRenderer* renderer) {
331	renderer->d.init = DSGXSoftwareRendererInit;
332	renderer->d.reset = DSGXSoftwareRendererReset;
333	renderer->d.deinit = DSGXSoftwareRendererDeinit;
334	renderer->d.invalidateTex = DSGXSoftwareRendererInvalidateTex;
335	renderer->d.setRAM = DSGXSoftwareRendererSetRAM;
336	renderer->d.drawScanline = DSGXSoftwareRendererDrawScanline;
337	renderer->d.getScanline = DSGXSoftwareRendererGetScanline;
338}
339
340static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer) {
341	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
342	DSGXSoftwarePolygonListInit(&softwareRenderer->activePolys, DS_GX_POLYGON_BUFFER_SIZE / 4);
343	DSGXSoftwareEdgeListInit(&softwareRenderer->activeEdges, DS_GX_POLYGON_BUFFER_SIZE);
344	DSGXSoftwareSpanListInit(&softwareRenderer->activeSpans, DS_GX_POLYGON_BUFFER_SIZE / 2);
345	softwareRenderer->bucket = anonymousMemoryMap(sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
346	softwareRenderer->scanlineCache = anonymousMemoryMap(sizeof(color_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
347}
348
349static void DSGXSoftwareRendererReset(struct DSGXRenderer* renderer) {
350	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
351	softwareRenderer->flushPending = false;
352}
353
354static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer) {
355	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
356	DSGXSoftwarePolygonListDeinit(&softwareRenderer->activePolys);
357	DSGXSoftwareEdgeListDeinit(&softwareRenderer->activeEdges);	
358	DSGXSoftwareSpanListDeinit(&softwareRenderer->activeSpans);
359	mappedMemoryFree(softwareRenderer->bucket, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
360	mappedMemoryFree(softwareRenderer->scanlineCache, sizeof(color_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
361}
362
363static void DSGXSoftwareRendererInvalidateTex(struct DSGXRenderer* renderer, int slot) {
364	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
365	// TODO
366}
367
368static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount, bool wSort) {
369	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
370
371	softwareRenderer->flushPending = true;
372	softwareRenderer->wSort = wSort;
373	softwareRenderer->verts = verts;
374	DSGXSoftwarePolygonListClear(&softwareRenderer->activePolys);
375	DSGXSoftwareEdgeListClear(&softwareRenderer->activeEdges);
376	unsigned i;
377	for (i = 0; i < polyCount; ++i) {
378		struct DSGXSoftwarePolygon* poly = DSGXSoftwarePolygonListAppend(&softwareRenderer->activePolys);
379		struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
380		poly->poly = &polys[i];
381		poly->texFormat = DSGXTexParamsGetFormat(poly->poly->texParams);
382		poly->blendFormat = DSGXPolygonAttrsGetMode(poly->poly->polyParams);
383		poly->texW = 8 << DSGXTexParamsGetSSize(poly->poly->texParams);
384		poly->texH = 8 << DSGXTexParamsGetTSize(poly->poly->texParams);
385		switch (poly->texFormat) {
386		case 0:
387		case 7:
388			poly->texBase = NULL;
389			poly->palBase = NULL;
390			break;
391		case 2:
392			poly->texBase = &renderer->tex[DSGXTexParamsGetVRAMBase(poly->poly->texParams) >> VRAM_BLOCK_OFFSET][(DSGXTexParamsGetVRAMBase(poly->poly->texParams) << 2) & 0xFFFF];
393			poly->palBase = &renderer->texPal[poly->poly->palBase >> 11][(poly->poly->palBase << 2) & 0x1FFF];
394			break;
395		default:
396			poly->texBase = &renderer->tex[DSGXTexParamsGetVRAMBase(poly->poly->texParams) >> VRAM_BLOCK_OFFSET][(DSGXTexParamsGetVRAMBase(poly->poly->texParams) << 2) & 0xFFFF];
397			poly->palBase = &renderer->texPal[poly->poly->palBase >> 10][(poly->poly->palBase << 3) & 0x1FFF];
398			break;
399		}
400		edge->polyId = i;
401
402		struct DSGXVertex* v0 = &verts[poly->poly->vertIds[0]];
403		struct DSGXVertex* v1;
404
405		int v;
406		for (v = 1; v < poly->poly->verts; ++v) {
407			v1 = &verts[poly->poly->vertIds[v]];
408			if (v0->vy >= v1->vy) {
409				edge->y0 = SCREEN_SIZE - v0->vy;
410				edge->x0 = v0->vx;
411				edge->z0 = v0->vz;
412				edge->w0 = v0->vw;
413				_expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
414				edge->s0 = v0->vs;
415				edge->t0 = v0->vt;
416
417				edge->y1 = SCREEN_SIZE - v1->vy;
418				edge->x1 = v1->vx;
419				edge->z1 = v1->vz;
420				edge->w1 = v1->vw;
421				_expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
422				edge->s1 = v1->vs;
423				edge->t1 = v1->vt;
424			} else {
425				edge->y0 = SCREEN_SIZE - v1->vy;
426				edge->x0 = v1->vx;
427				edge->z0 = v1->vz;
428				edge->w0 = v1->vw;
429				_expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
430				edge->s0 = v1->vs;
431				edge->t0 = v1->vt;
432
433				edge->y1 = SCREEN_SIZE - v0->vy;
434				edge->x1 = v0->vx;
435				edge->z1 = v0->vz;
436				edge->w1 = v0->vw;
437				_expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
438				edge->s1 = v0->vs;
439				edge->t1 = v0->vt;
440			}
441
442			edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
443			edge->polyId = i;
444			v0 = v1;
445		}
446
447		v1 = &verts[poly->poly->vertIds[0]];
448		if (v0->vy >= v1->vy) {
449			edge->y0 = SCREEN_SIZE - v0->vy;
450			edge->x0 = v0->vx;
451			edge->z0 = v0->vz;
452			edge->w0 = v0->vw;
453			_expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
454			edge->s0 = v0->vs;
455			edge->t0 = v0->vt;
456
457			edge->y1 = SCREEN_SIZE - v1->vy;
458			edge->x1 = v1->vx;
459			edge->z1 = v1->vz;
460			edge->w1 = v1->vw;
461			_expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
462			edge->s1 = v1->vs;
463			edge->t1 = v1->vt;
464		} else {
465			edge->y0 = SCREEN_SIZE - v1->vy;
466			edge->x0 = v1->vx;
467			edge->w0 = v1->vw;
468			edge->z0 = v1->vz;
469			_expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
470			edge->s0 = v1->vs;
471			edge->t0 = v1->vt;
472
473			edge->y1 = SCREEN_SIZE - v0->vy;
474			edge->x1 = v0->vx;
475			edge->z1 = v0->vz;
476			edge->w1 = v0->vw;
477			_expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
478			edge->s1 = v0->vs;
479			edge->t1 = v0->vt;
480		}
481	}
482}
483
484static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y) {
485	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
486	if (!softwareRenderer->flushPending) {
487		return;
488	}
489	DSGXSoftwareSpanListClear(&softwareRenderer->activeSpans);
490	memset(softwareRenderer->bucket, 0, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
491	size_t i;
492	for (i = 0; i < DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges); ++i) {
493		struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, i);
494		if (edge->y1 >> 12 < y) {
495			continue;
496		} else if (edge->y0 >> 12 > y) {
497			continue;
498		}
499
500		unsigned poly = edge->polyId;
501		struct DSGXSoftwareSpan* span = softwareRenderer->bucket[poly];
502		if (span && !span->ep[1].w) {
503			if (_edgeToSpan(span, edge, 1, y)) {
504				softwareRenderer->bucket[poly] = NULL;
505			}
506		} else if (!span) {
507			span = DSGXSoftwareSpanListAppend(&softwareRenderer->activeSpans);
508			memset(&span->ep[1], 0, sizeof(span->ep[1]));
509			span->poly = DSGXSoftwarePolygonListGetPointer(&softwareRenderer->activePolys, poly);
510			if (!_edgeToSpan(span, edge, 0, y)) {
511				// Horizontal line
512				DSGXSoftwareSpanListShift(&softwareRenderer->activeSpans, DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans) - 1, 1);
513			} else {
514				softwareRenderer->bucket[poly] = span;
515			}
516		}
517	}
518
519	color_t* scanline = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
520	memset(scanline, 0, sizeof(color_t) * DS_VIDEO_HORIZONTAL_PIXELS);
521	for (i = 0; i < DS_VIDEO_HORIZONTAL_PIXELS; i += 4) {
522		softwareRenderer->depthBuffer[i] = INT32_MAX;
523		softwareRenderer->depthBuffer[i + 1] = INT32_MAX;
524		softwareRenderer->depthBuffer[i + 2] = INT32_MAX;
525		softwareRenderer->depthBuffer[i + 3] = INT32_MAX;
526	}
527
528	for (i = 0; i < DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans); ++i) {
529		struct DSGXSoftwareSpan* span = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, i);
530
531		int32_t x = span->ep[0].x >> 12;
532		if (x < 0) {
533			x = 0;
534		}
535		for (; x < span->ep[1].x >> 12 && x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
536			struct DSGXSoftwareEndpoint ep;
537			_lerpEndpoint(span, &ep, x);
538			color_t color = _lookupColor(softwareRenderer, &ep, span->poly);
539			unsigned a = color >> 27;
540			unsigned current = scanline[x];
541			unsigned b = current >> 27;
542			unsigned ab = a;
543			if (b > ab) {
544				ab = b;
545			}
546			if (a == 0x1F) {
547				if (softwareRenderer->wSort) {
548					if (ep.w < softwareRenderer->depthBuffer[x]) {
549						softwareRenderer->depthBuffer[x] = ep.w;
550						scanline[x] = color;
551					} else if (b < 0x1F) {
552						scanline[x] = _mix32(b, scanline[x], 0x1F - b, color) | a << 27;
553					}
554				} else {
555					if (ep.z < softwareRenderer->depthBuffer[x]) {
556						softwareRenderer->depthBuffer[x] = ep.z;
557						scanline[x] = color;
558					} else if (b < 0x1F) {
559						scanline[x] = _mix32(b, scanline[x], 0x1F - b, color) | a << 27;
560					}
561				}
562			} else if (a) {
563				// TODO: Disable alpha?
564				color = _mix32(a, color, 0x1F - a, current);
565				if (scanline[x] >> 27 > a) {
566					a = scanline[x] >> 27;
567				}
568				color |= a << 27;
569				if (softwareRenderer->wSort) {
570					if (ep.w < softwareRenderer->depthBuffer[x]) {
571						softwareRenderer->depthBuffer[x] = ep.w;
572						scanline[x] = color;
573					} else if (b < 0x1F) {
574						scanline[x] = _mix32(b, scanline[x], 0x1F - b, color) | ab << 27;
575					}
576				} else {
577					if (ep.z < softwareRenderer->depthBuffer[x]) {
578						softwareRenderer->depthBuffer[x] = ep.z;
579						scanline[x] = color;
580					} else if (b < 0x1F) {
581						scanline[x] = _mix32(b, scanline[x], 0x1F - b, color) | ab << 27;
582					}
583				}
584			}
585		}
586	}
587
588	if (y == DS_VIDEO_VERTICAL_PIXELS - 1) {
589		softwareRenderer->flushPending = false;
590	}
591}
592
593static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, const color_t** output) {
594	struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
595	*output = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
596}