all repos — mgba @ eafcb0f5554ee6c21c0fb1ac73219ed6a2c99e1b

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