all repos — mgba @ 68a0b5f48e3ec57c11f88d46b299459225413b23

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