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 unsigned 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 2: {
267 uint8_t tr, tg, tb;
268 _expandColor(renderer->d.toonTable[ep->cr >> 1], &tr, &tg, &tb);
269 // TODO: highlight mode
270 wr = ((r + 1) * (tr + 1) - 1) >> 6;
271 wg = ((g + 1) * (tg + 1) - 1) >> 6;
272 wb = ((b + 1) * (tb + 1) - 1) >> 6;
273 wa = ((ta + 1) * (pa + 1) - 1) >> 6;
274 return _finishColor(wr, wg, wb, wa);
275 }
276 case 3:
277 return _finishColor(r, g, b, pa);
278 }
279}
280
281static inline int32_t _interpolate(int32_t x0, int32_t x1, int64_t w0, int64_t w1, int64_t w, int32_t qr) {
282 // 32-bit -> 96-bit
283 int64_t x0b = (w0 & 0xFFFFFFFF) * x0;
284 int64_t x0t = (w0 >> 32) * x0;
285 int64_t x1b = (w1 & 0xFFFFFFFF) * x1;
286 int64_t x1t = (w1 >> 32) * x1;
287 // 96-bit -> 64-bit
288 int64_t xx0 = (x0t << 32) + x0b;
289 int64_t xx1 = (x1t << 32) + x1b;
290 xx1 -= xx0;
291 xx1 >>= 12;
292
293 int64_t qrb = xx1 * qr;
294 qrb += xx0;
295
296 return qrb / w;
297}
298
299static inline int32_t _divideBy(int64_t x, int32_t recip) {
300 int64_t x0 = (x & 0xFFFFFFFF) * recip;
301 int64_t x1 = (x >> 32) * recip;
302 x1 += x0 >> 32;
303 return x1 >> 31;
304}
305
306static bool _edgeToSpan(struct DSGXSoftwareSpan* span, const struct DSGXSoftwareEdge* edge, int index, int32_t y) {
307 int32_t height = edge->y1 - edge->y0;
308 int64_t yw = y - edge->y0;
309 if (!height) {
310 return false;
311 }
312 // Clamp to bounds
313 if (yw < 0) {
314 return false;
315 } else if (yw > height) {
316 return false;
317 }
318
319 span->ep[index].coord[0] = (((int64_t) (edge->x1 - edge->x0) * yw) / height) + edge->x0;
320
321 if (index) {
322 if (span->ep[0].coord[0] == span->ep[index].coord[0]) {
323 return false;
324 }
325 if (span->ep[0].coord[0] > span->ep[index].coord[0]) {
326 int32_t temp = span->ep[index].coord[0];
327 span->ep[index] = span->ep[0];
328 span->ep[0].coord[0] = temp;
329 index = 0;
330 }
331 }
332
333 int64_t w0 = edge->wr0;
334 int64_t w1 = edge->wr1;
335 int64_t w = w1 - w0;
336
337 // Losslessly interpolate two 64-bit values
338 int64_t wb = (w & 0xFFFFFFFF) * yw;
339 int64_t wt = (w >> 32) * yw;
340 int64_t div = wt / height;
341 int64_t rem = wt % height;
342 w = div << 32;
343 wb += rem << 32;
344 div = wb / height;
345 w += div;
346 w += w0;
347
348 span->ep[index].coord[3] = (0x7FFFFFFFFFFFFFFF / w) + 1;
349 span->ep[index].wRecip = w;
350 int32_t qr = (yw << 12) / height;
351
352 span->ep[index].coord[2] = _interpolate(edge->z0, edge->z1, w0, w1, w, qr);
353 span->ep[index].cr = _interpolate(edge->cr0, edge->cr1, w0, w1, w, qr);
354 span->ep[index].cg = _interpolate(edge->cg0, edge->cg1, w0, w1, w, qr);
355 span->ep[index].cb = _interpolate(edge->cb0, edge->cb1, w0, w1, w, qr);
356 span->ep[index].s = _interpolate(edge->s0, edge->s1, w0, w1, w, qr);
357 span->ep[index].t = _interpolate(edge->t0, edge->t1, w0, w1, w, qr);
358
359 return true;
360}
361
362static void _createStep(struct DSGXSoftwareSpan* span) {
363 int32_t width = (span->ep[1].coord[0] - span->ep[0].coord[0]) >> 7;
364
365 span->ep[0].stepW = span->ep[0].wRecip;
366 span->ep[0].stepZ = span->ep[0].coord[2] * span->ep[0].wRecip;
367 span->ep[0].stepR = span->ep[0].cr * span->ep[0].wRecip;
368 span->ep[0].stepG = span->ep[0].cg * span->ep[0].wRecip;
369 span->ep[0].stepB = span->ep[0].cb * span->ep[0].wRecip;
370 span->ep[0].stepS = span->ep[0].s * span->ep[0].wRecip;
371 span->ep[0].stepT = span->ep[0].t * span->ep[0].wRecip;
372
373 span->ep[1].stepW = span->ep[1].wRecip;
374 span->ep[1].stepZ = span->ep[1].coord[2] * span->ep[1].wRecip;
375 span->ep[1].stepR = span->ep[1].cr * span->ep[1].wRecip;
376 span->ep[1].stepG = span->ep[1].cg * span->ep[1].wRecip;
377 span->ep[1].stepB = span->ep[1].cb * span->ep[1].wRecip;
378 span->ep[1].stepS = span->ep[1].s * span->ep[1].wRecip;
379 span->ep[1].stepT = span->ep[1].t * span->ep[1].wRecip;
380
381 if (!width) {
382 return;
383 }
384 span->step.coord[0] = span->ep[1].coord[0] - span->ep[0].coord[0];
385 span->step.stepW = ((span->ep[1].stepW - span->ep[0].stepW) / width) << 5;
386 span->step.stepZ = ((span->ep[1].stepZ - span->ep[0].stepZ) / width) << 5;
387 span->step.stepR = ((span->ep[1].stepR - span->ep[0].stepR) / width) << 5;
388 span->step.stepG = ((span->ep[1].stepG - span->ep[0].stepG) / width) << 5;
389 span->step.stepB = ((span->ep[1].stepB - span->ep[0].stepB) / width) << 5;
390 span->step.stepS = ((span->ep[1].stepS - span->ep[0].stepS) / width) << 5;
391 span->step.stepT = ((span->ep[1].stepT - span->ep[0].stepT) / width) << 5;
392}
393
394static void _stepEndpoint(struct DSGXSoftwareSpan* span) {
395 int32_t nextX = (span->ep[0].coord[0] & ~0xFFF) + 0x1000;
396 span->ep[0].coord[0] = nextX;
397
398 span->ep[0].wRecip += span->step.stepW;
399 span->ep[0].coord[3] = (0x7FFFFFFFFFFFFFFF / span->ep[0].wRecip) + 1;
400
401 span->ep[0].stepZ += span->step.stepZ;
402 span->ep[0].coord[2] = _divideBy(span->ep[0].stepZ, span->ep[0].coord[3]);
403
404 span->ep[0].stepR += span->step.stepR;
405 span->ep[0].stepG += span->step.stepG;
406 span->ep[0].stepB += span->step.stepB;
407 span->ep[0].stepS += span->step.stepS;
408 span->ep[0].stepT += span->step.stepT;
409}
410
411static void _resolveEndpoint(struct DSGXSoftwareSpan* span) {
412 span->ep[0].cr = _divideBy(span->ep[0].stepR, span->ep[0].coord[3]);
413 span->ep[0].cg = _divideBy(span->ep[0].stepG, span->ep[0].coord[3]);
414 span->ep[0].cb = _divideBy(span->ep[0].stepB, span->ep[0].coord[3]);
415 span->ep[0].s = _divideBy(span->ep[0].stepS, span->ep[0].coord[3]);
416 span->ep[0].t = _divideBy(span->ep[0].stepT, span->ep[0].coord[3]);
417}
418
419void DSGXSoftwareRendererCreate(struct DSGXSoftwareRenderer* renderer) {
420 renderer->d.init = DSGXSoftwareRendererInit;
421 renderer->d.reset = DSGXSoftwareRendererReset;
422 renderer->d.deinit = DSGXSoftwareRendererDeinit;
423 renderer->d.invalidateTex = DSGXSoftwareRendererInvalidateTex;
424 renderer->d.setRAM = DSGXSoftwareRendererSetRAM;
425 renderer->d.drawScanline = DSGXSoftwareRendererDrawScanline;
426 renderer->d.getScanline = DSGXSoftwareRendererGetScanline;
427 renderer->d.writeRegister = DSGXSoftwareRendererWriteRegister;
428}
429
430static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer) {
431 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
432 DSGXSoftwarePolygonListInit(&softwareRenderer->activePolys, DS_GX_POLYGON_BUFFER_SIZE / 4);
433 DSGXSoftwareEdgeListInit(&softwareRenderer->activeEdges, 16);
434 softwareRenderer->scanlineCache = anonymousMemoryMap(sizeof(color_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
435 softwareRenderer->depthBuffer = anonymousMemoryMap(sizeof(int32_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
436 softwareRenderer->stencilBuffer = anonymousMemoryMap(sizeof(uint16_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
437}
438
439static void DSGXSoftwareRendererReset(struct DSGXRenderer* renderer) {
440 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
441 softwareRenderer->clearColor = 0;
442 softwareRenderer->clearStencil = 0;
443}
444
445static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer) {
446 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
447 DSGXSoftwarePolygonListDeinit(&softwareRenderer->activePolys);
448 DSGXSoftwareEdgeListDeinit(&softwareRenderer->activeEdges);
449 mappedMemoryFree(softwareRenderer->scanlineCache, sizeof(color_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
450 mappedMemoryFree(softwareRenderer->depthBuffer, sizeof(int32_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
451 mappedMemoryFree(softwareRenderer->stencilBuffer, sizeof(uint16_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
452}
453
454static void DSGXSoftwareRendererInvalidateTex(struct DSGXRenderer* renderer, int slot) {
455 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
456 // TODO
457}
458
459static void _preparePoly(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXSoftwarePolygon* poly) {
460 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
461 struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
462 poly->texParams = poly->poly->texParams;
463 poly->polyParams = poly->poly->polyParams;
464 poly->texFormat = DSGXTexParamsGetFormat(poly->texParams);
465 poly->blendFormat = DSGXPolygonAttrsGetMode(poly->polyParams);
466 poly->texW = 8 << DSGXTexParamsGetSSize(poly->texParams);
467 poly->texH = 8 << DSGXTexParamsGetTSize(poly->texParams);
468 poly->texBase = NULL;
469 poly->palBase = NULL;
470 if (renderer->tex[DSGXTexParamsGetVRAMBase(poly->texParams) >> VRAM_BLOCK_OFFSET]) {
471 poly->texBase = &renderer->tex[DSGXTexParamsGetVRAMBase(poly->poly->texParams) >> VRAM_BLOCK_OFFSET][(DSGXTexParamsGetVRAMBase(poly->poly->texParams) << 2) & 0xFFFF];
472 switch (poly->texFormat) {
473 case 0:
474 poly->texBase = NULL;
475 break;
476 case 2:
477 if (renderer->texPal[poly->poly->palBase >> 11]) {
478 poly->palBase = &renderer->texPal[poly->poly->palBase >> 11][(poly->poly->palBase << 2) & 0x1FFF];
479 }
480 break;
481 default:
482 if (renderer->texPal[poly->poly->palBase >> 10]) {
483 poly->palBase = &renderer->texPal[poly->poly->palBase >> 10][(poly->poly->palBase << 3) & 0x1FFF];
484 }
485 break;
486 }
487 }
488 edge->polyId = poly->polyId;
489 poly->minY = DS_VIDEO_VERTICAL_PIXELS - 1;
490 poly->maxY = 0;
491
492 struct DSGXVertex* v0 = &verts[poly->poly->vertIds[0]];
493 struct DSGXVertex* v1;
494
495 int32_t v0w = v0->viewCoord[3];
496 if (!v0w) {
497 v0w = 1;
498 }
499
500 int32_t v0x = (v0->viewCoord[0] + v0w) * (int64_t) (v0->viewportWidth << 12) / (v0w * 2) + (v0->viewportX << 12);
501 int32_t v0y = (-v0->viewCoord[1] + v0w) * (int64_t) (v0->viewportHeight << 12) / (v0w * 2) + (v0->viewportY << 12);
502 if (poly->minY > v0y >> 12) {
503 poly->minY = v0y >> 12;
504 }
505 if (poly->maxY < v0y >> 12) {
506 poly->maxY = v0y >> 12;
507 }
508
509 int v;
510 for (v = 1; v < poly->poly->verts; ++v) {
511 v1 = &verts[poly->poly->vertIds[v]];
512
513 int32_t v1w = v1->viewCoord[3];
514 if (!v1w) {
515 v1w = 1;
516 }
517 int32_t v1x = (v1->viewCoord[0] + v1w) * (int64_t) (v1->viewportWidth << 12) / (v1w * 2) + (v1->viewportX << 12);
518 int32_t v1y = (-v1->viewCoord[1] + v1w) * (int64_t) (v1->viewportHeight << 12) / (v1w * 2) + (v1->viewportY << 12);
519 if (poly->minY > v1y >> 12) {
520 poly->minY = v1y >> 12;
521 }
522 if (poly->maxY < v1y >> 12) {
523 poly->maxY = v1y >> 12;
524 }
525
526 if (v0y <= v1y) {
527 edge->y0 = v0y;
528 edge->x0 = v0x;
529 edge->z0 = v0->viewCoord[2];
530 edge->w0 = v0w;
531 _expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
532 edge->s0 = v0->vs;
533 edge->t0 = v0->vt;
534
535 edge->y1 = v1y;
536 edge->x1 = v1x;
537 edge->z1 = v1->viewCoord[2];
538 edge->w1 = v1w;
539 _expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
540 edge->s1 = v1->vs;
541 edge->t1 = v1->vt;
542 } else {
543 edge->y0 = v1y;
544 edge->x0 = v1x;
545 edge->z0 = v1->viewCoord[2];
546 edge->w0 = v1w;
547 _expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
548 edge->s0 = v1->vs;
549 edge->t0 = v1->vt;
550
551 edge->y1 = v0y;
552 edge->x1 = v0x;
553 edge->z1 = v0->viewCoord[2];
554 edge->w1 = v0w;
555 _expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
556 edge->s1 = v0->vs;
557 edge->t1 = v0->vt;
558 }
559 edge->wr0 = 0x7FFFFFFFFFFFFFFF / edge->w0;
560 edge->wr1 = 0x7FFFFFFFFFFFFFFF / edge->w1;
561
562 edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
563 edge->polyId = poly->polyId;
564 v0 = v1;
565 v0x = v1x;
566 v0y = v1y;
567 v0w = v1w;
568 }
569
570 v1 = &verts[poly->poly->vertIds[0]];
571
572 int32_t v1w = v1->viewCoord[3];
573 if (!v1w) {
574 v1w = 1;
575 }
576 int32_t v1x = (v1->viewCoord[0] + v1w) * (int64_t) (v1->viewportWidth << 12) / (v1w * 2) + (v1->viewportX << 12);
577 int32_t v1y = (-v1->viewCoord[1] + v1w) * (int64_t) (v1->viewportHeight << 12) / (v1w * 2) + (v1->viewportY << 12);
578
579 if (poly->minY > v1y >> 12) {
580 poly->minY = v1y >> 12;
581 }
582 if (poly->maxY < v1y >> 12) {
583 poly->maxY = v1y >> 12;
584 }
585
586 if (v0y <= v1y) {
587 edge->y0 = v0y;
588 edge->x0 = v0x;
589 edge->z0 = v0->viewCoord[2];
590 edge->w0 = v0w;
591 _expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
592 edge->s0 = v0->vs;
593 edge->t0 = v0->vt;
594
595 edge->y1 = v1y;
596 edge->x1 = v1x;
597 edge->z1 = v1->viewCoord[2];
598 edge->w1 = v1w;
599 _expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
600 edge->s1 = v1->vs;
601 edge->t1 = v1->vt;
602 } else {
603 edge->y0 = v1y;
604 edge->x0 = v1x;
605 edge->z0 = v1->viewCoord[2];
606 edge->w0 = v1w;
607 _expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
608 edge->s0 = v1->vs;
609 edge->t0 = v1->vt;
610
611 edge->y1 = v0y;
612 edge->x1 = v0x;
613 edge->z1 = v0->viewCoord[2];
614 edge->w1 = v0w;
615 _expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
616 edge->s1 = v0->vs;
617 edge->t1 = v0->vt;
618 }
619 edge->wr0 = 0x7FFFFFFFFFFFFFFF / edge->w0;
620 edge->wr1 = 0x7FFFFFFFFFFFFFFF / edge->w1;
621
622 if (poly->maxY >= DS_VIDEO_VERTICAL_PIXELS) {
623 poly->maxY = DS_VIDEO_VERTICAL_PIXELS - 1;
624 }
625 if (poly->minY < 0) {
626 poly->minY = 0;
627 }
628}
629
630static void _drawSpan(struct DSGXSoftwareRenderer* softwareRenderer, struct DSGXSoftwareSpan* span, int y) {
631 color_t* scanline = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
632 int32_t* depth = &softwareRenderer->depthBuffer[DS_VIDEO_HORIZONTAL_PIXELS * y];
633 uint16_t* stencil = &softwareRenderer->stencilBuffer[DS_VIDEO_HORIZONTAL_PIXELS * y];
634 int32_t x = span->ep[0].coord[0] >> 12;
635 if (x < 0) {
636 x = 0;
637 }
638 unsigned stencilValue = span->polyId;
639 stencilValue |= stencilValue << 8;
640 if (span->poly->blendFormat == 3) {
641 stencilValue |= SHADOW_BIT;
642 }
643 for (; x < (span->ep[1].coord[0] >> 12) && x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
644 if (span->ep[0].coord[softwareRenderer->sort] < depth[x]) {
645 _resolveEndpoint(span);
646 color_t color = _lookupColor(softwareRenderer, &span->ep[0], span->poly);
647 unsigned a = color >> 27;
648 unsigned current = scanline[x];
649 unsigned b = current >> 27;
650 unsigned ab = a;
651 unsigned s = stencilValue;
652 if (b > ab) {
653 ab = b;
654 }
655 if (a == 0x1F) {
656 depth[x] = span->ep[0].coord[softwareRenderer->sort];
657 scanline[x] = color;
658 stencil[x] &= 0x00FF;
659 stencil[x] |= s & 0x3F00;
660 } else if (a) {
661 // TODO: Disable alpha?
662 if (b) {
663 color = _mix32(a, color, 0x1F - a, current);
664 color |= ab << 27;
665 }
666 if ((stencil[x] & 0x3F) != (s & 0x3F)) {
667 if (!(s & SHADOW_BIT) || ((stencil[x] & 0x3F00) != (s & 0x3F00) && s & 0x3F00 && stencil[x] & SHADOW_BIT)) {
668 if (DSGXPolygonAttrsIsUpdateDepth(span->poly->poly->polyParams)) {
669 depth[x] = span->ep[0].coord[softwareRenderer->sort];
670 }
671 scanline[x] = color;
672 stencil[x] &= 0x3F00;
673 } else {
674 s = 0;
675 stencil[x] &= 0x3F3F;
676 }
677 }
678 s &= ~0x7F00;
679 stencil[x] |= s;
680 }
681 } else if ((stencilValue & 0x7F00) == SHADOW_BIT) {
682 _resolveEndpoint(span);
683 color_t color = _lookupColor(softwareRenderer, &span->ep[0], span->poly);
684 unsigned a = color >> 27;
685 if (a > 0) {
686 stencil[x] |= SHADOW_BIT;
687 }
688 }
689 _stepEndpoint(span);
690 }
691}
692
693static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount, bool wSort) {
694 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
695
696 softwareRenderer->sort = wSort ? 3 : 2;
697 softwareRenderer->verts = verts;
698 DSGXSoftwarePolygonListClear(&softwareRenderer->activePolys);
699 size_t i;
700 // Pass 1: Opaque
701 for (i = 0; i < polyCount; ++i) {
702 struct DSGXSoftwarePolygon* poly = NULL;
703 switch (DSGXTexParamsGetFormat(polys[i].texParams)) {
704 default:
705 if (DSGXPolygonAttrsGetAlpha(polys[i].polyParams) == 0x1F) {
706 poly = DSGXSoftwarePolygonListAppend(&softwareRenderer->activePolys);
707 break;
708 }
709 case 1:
710 case 6:
711 break;
712 }
713 if (!poly) {
714 continue;
715 }
716 poly->poly = &polys[i];
717 poly->polyId = DSGXSoftwarePolygonListSize(&softwareRenderer->activePolys) - 1;
718 }
719 // Pass 2: Translucent
720 for (i = 0; i < polyCount; ++i) {
721 struct DSGXSoftwarePolygon* poly = NULL;
722 switch (DSGXTexParamsGetFormat(polys[i].texParams)) {
723 default:
724 if (DSGXPolygonAttrsGetAlpha(polys[i].polyParams) == 0x1F) {
725 break;
726 }
727 case 1:
728 case 6:
729 poly = DSGXSoftwarePolygonListAppend(&softwareRenderer->activePolys);
730 break;
731 }
732 if (!poly) {
733 continue;
734 }
735 poly->poly = &polys[i];
736 poly->polyId = DSGXSoftwarePolygonListSize(&softwareRenderer->activePolys) - 1;
737 }
738
739 color_t clearColor = softwareRenderer->clearColor;
740 uint16_t clearStencil = softwareRenderer->clearStencil;
741
742 for (i = 0; i < DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS ; i += 4) {
743 softwareRenderer->depthBuffer[i] = INT32_MAX;
744 softwareRenderer->depthBuffer[i + 1] = INT32_MAX;
745 softwareRenderer->depthBuffer[i + 2] = INT32_MAX;
746 softwareRenderer->depthBuffer[i + 3] = INT32_MAX;
747 softwareRenderer->scanlineCache[i] = clearColor;
748 softwareRenderer->scanlineCache[i + 1] = clearColor;
749 softwareRenderer->scanlineCache[i + 2] = clearColor;
750 softwareRenderer->scanlineCache[i + 3] = clearColor;
751 softwareRenderer->stencilBuffer[i] = clearStencil;
752 softwareRenderer->stencilBuffer[i + 1] = clearStencil;
753 softwareRenderer->stencilBuffer[i + 2] = clearStencil;
754 softwareRenderer->stencilBuffer[i + 3] = clearStencil;
755 }
756 softwareRenderer->flushPending = true;
757}
758
759static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y) {
760 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
761 if (!softwareRenderer->flushPending || y) {
762 return;
763 }
764
765 size_t p;
766 for (p = 0; p < DSGXSoftwarePolygonListSize(&softwareRenderer->activePolys); ++p) {
767 struct DSGXSoftwarePolygon* poly = DSGXSoftwarePolygonListGetPointer(&softwareRenderer->activePolys, p);
768 DSGXSoftwareEdgeListClear(&softwareRenderer->activeEdges);
769 _preparePoly(renderer, softwareRenderer->verts, poly);
770 int y;
771 for (y = poly->minY; y <= poly->maxY; ++y) {
772 struct DSGXSoftwareSpan span = {
773 .poly = poly,
774 .polyId = DSGXPolygonAttrsGetId(poly->polyParams),
775 };
776 size_t i;
777 for (i = 0; i < DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges); ++i) {
778 struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, i);
779 if (edge->y1 < (y << 12)) {
780 continue;
781 } else if (edge->y0 > (y << 12)) {
782 continue;
783 }
784
785 if (span.ep[0].coord[3]) {
786 if (_edgeToSpan(&span, edge, 1, y << 12)) {
787 _createStep(&span);
788 _drawSpan(softwareRenderer, &span, y);
789 }
790 } else if (!_edgeToSpan(&span, edge, 0, y << 12)) {
791 // Horizontal line
792 continue;
793 }
794 }
795 }
796 }
797 softwareRenderer->flushPending = false;
798}
799
800static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, const color_t** output) {
801 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
802 *output = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
803}
804
805static void DSGXSoftwareRendererWriteRegister(struct DSGXRenderer* renderer, uint32_t address, uint16_t value) {
806 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
807 switch (address) {
808 case DS9_REG_CLEAR_COLOR_LO:
809 softwareRenderer->clearColor &= 0xFF000000;
810 softwareRenderer->clearColor |= (value & 0x001F) << 3;
811 softwareRenderer->clearColor |= (value & 0x03E0) << 6;
812 softwareRenderer->clearColor |= (value & 0x7C00) << 9;
813 break;
814 case DS9_REG_CLEAR_COLOR_HI:
815 softwareRenderer->clearColor &= 0x00FFFFFF;
816 softwareRenderer->clearColor |= (value & 0x001F) << 27;
817 softwareRenderer->clearStencil = (value & 0x3F00) >> 8;
818 break;
819 }
820}