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