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}