all repos — mgba @ d139a047f10ae5d62a8ce9dd6d8924dda6347254

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