all repos — mgba @ 607a6921687fd12e119eeaed93d9f7bc0b0e1a14

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