all repos — mgba @ c24f31d2a740eb5970707e9d94532f42a1379a79

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