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}
448
449static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer) {
450 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
451 DSGXSoftwarePolygonListDeinit(&softwareRenderer->activePolys);
452 DSGXSoftwareEdgeListDeinit(&softwareRenderer->activeEdges);
453 mappedMemoryFree(softwareRenderer->scanlineCache, sizeof(color_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
454 mappedMemoryFree(softwareRenderer->depthBuffer, sizeof(int32_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
455 mappedMemoryFree(softwareRenderer->stencilBuffer, sizeof(uint16_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
456}
457
458static void DSGXSoftwareRendererInvalidateTex(struct DSGXRenderer* renderer, int slot) {
459 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
460 // TODO
461}
462
463static void _preparePoly(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXSoftwarePolygon* poly) {
464 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
465 struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
466 poly->texParams = poly->poly->texParams;
467 poly->polyParams = poly->poly->polyParams;
468 poly->texFormat = DSGXTexParamsGetFormat(poly->texParams);
469 poly->blendFormat = DSGXPolygonAttrsGetMode(poly->polyParams);
470 poly->texW = 8 << DSGXTexParamsGetSSize(poly->texParams);
471 poly->texH = 8 << DSGXTexParamsGetTSize(poly->texParams);
472 poly->texBase = NULL;
473 poly->palBase = NULL;
474 if (renderer->tex[DSGXTexParamsGetVRAMBase(poly->texParams) >> VRAM_BLOCK_OFFSET]) {
475 poly->texBase = &renderer->tex[DSGXTexParamsGetVRAMBase(poly->poly->texParams) >> VRAM_BLOCK_OFFSET][(DSGXTexParamsGetVRAMBase(poly->poly->texParams) << 2) & 0xFFFF];
476 switch (poly->texFormat) {
477 case 0:
478 poly->texBase = NULL;
479 break;
480 case 2:
481 if (renderer->texPal[poly->poly->palBase >> 11]) {
482 poly->palBase = &renderer->texPal[poly->poly->palBase >> 11][(poly->poly->palBase << 2) & 0x1FFF];
483 }
484 break;
485 default:
486 if (renderer->texPal[poly->poly->palBase >> 10]) {
487 poly->palBase = &renderer->texPal[poly->poly->palBase >> 10][(poly->poly->palBase << 3) & 0x1FFF];
488 }
489 break;
490 }
491 }
492 edge->polyId = poly->polyId;
493 poly->minY = DS_VIDEO_VERTICAL_PIXELS - 1;
494 poly->maxY = 0;
495
496 struct DSGXVertex* v0 = &verts[poly->poly->vertIds[0]];
497 struct DSGXVertex* v1;
498
499 int32_t v0w = v0->viewCoord[3];
500 if (!v0w) {
501 v0w = 1;
502 }
503
504 int32_t v0x = (v0->viewCoord[0] + v0w) * (int64_t) (v0->viewportWidth << 12) / (v0w * 2) + (v0->viewportX << 12);
505 int32_t v0y = (DS_VIDEO_VERTICAL_PIXELS << 12) - ((v0->viewCoord[1] + v0w) * (int64_t) (v0->viewportHeight << 12) / (v0w * 2) + (v0->viewportY << 12));
506 if (poly->minY > v0y >> 12) {
507 poly->minY = v0y >> 12;
508 }
509 if (poly->maxY < v0y >> 12) {
510 poly->maxY = v0y >> 12;
511 }
512
513 int v;
514 for (v = 1; v < poly->poly->verts; ++v) {
515 v1 = &verts[poly->poly->vertIds[v]];
516
517 int32_t v1w = v1->viewCoord[3];
518 if (!v1w) {
519 v1w = 1;
520 }
521 int32_t v1x = (v1->viewCoord[0] + v1w) * (int64_t) (v1->viewportWidth << 12) / (v1w * 2) + (v1->viewportX << 12);
522 int32_t v1y = (DS_VIDEO_VERTICAL_PIXELS << 12) - ((v1->viewCoord[1] + v1w) * (int64_t) (v1->viewportHeight << 12) / (v1w * 2) + (v1->viewportY << 12));
523 if (poly->minY > v1y >> 12) {
524 poly->minY = v1y >> 12;
525 }
526 if (poly->maxY < v1y >> 12) {
527 poly->maxY = v1y >> 12;
528 }
529
530 if (v0y <= v1y) {
531 edge->y0 = v0y;
532 edge->x0 = v0x;
533 edge->z0 = v0->viewCoord[2];
534 edge->w0 = v0w;
535 _expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
536 edge->s0 = v0->vs;
537 edge->t0 = v0->vt;
538
539 edge->y1 = v1y;
540 edge->x1 = v1x;
541 edge->z1 = v1->viewCoord[2];
542 edge->w1 = v1w;
543 _expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
544 edge->s1 = v1->vs;
545 edge->t1 = v1->vt;
546 } else {
547 edge->y0 = v1y;
548 edge->x0 = v1x;
549 edge->z0 = v1->viewCoord[2];
550 edge->w0 = v1w;
551 _expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
552 edge->s0 = v1->vs;
553 edge->t0 = v1->vt;
554
555 edge->y1 = v0y;
556 edge->x1 = v0x;
557 edge->z1 = v0->viewCoord[2];
558 edge->w1 = v0w;
559 _expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
560 edge->s1 = v0->vs;
561 edge->t1 = v0->vt;
562 }
563 edge->wr0 = 0x7FFFFFFFFFFFFFFF / edge->w0;
564 edge->wr1 = 0x7FFFFFFFFFFFFFFF / edge->w1;
565
566 edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
567 edge->polyId = poly->polyId;
568 v0 = v1;
569 v0x = v1x;
570 v0y = v1y;
571 v0w = v1w;
572 }
573
574 v1 = &verts[poly->poly->vertIds[0]];
575
576 int32_t v1w = v1->viewCoord[3];
577 if (!v1w) {
578 v1w = 1;
579 }
580 int32_t v1x = (v1->viewCoord[0] + v1w) * (int64_t) (v1->viewportWidth << 12) / (v1w * 2) + (v1->viewportX << 12);
581 int32_t v1y = (DS_VIDEO_VERTICAL_PIXELS << 12) - ((v1->viewCoord[1] + v1w) * (int64_t) (v1->viewportHeight << 12) / (v1w * 2) + (v1->viewportY << 12));
582
583 if (poly->minY > v1y >> 12) {
584 poly->minY = v1y >> 12;
585 }
586 if (poly->maxY < v1y >> 12) {
587 poly->maxY = v1y >> 12;
588 }
589
590 if (v0y <= v1y) {
591 edge->y0 = v0y;
592 edge->x0 = v0x;
593 edge->z0 = v0->viewCoord[2];
594 edge->w0 = v0w;
595 _expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
596 edge->s0 = v0->vs;
597 edge->t0 = v0->vt;
598
599 edge->y1 = v1y;
600 edge->x1 = v1x;
601 edge->z1 = v1->viewCoord[2];
602 edge->w1 = v1w;
603 _expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
604 edge->s1 = v1->vs;
605 edge->t1 = v1->vt;
606 } else {
607 edge->y0 = v1y;
608 edge->x0 = v1x;
609 edge->z0 = v1->viewCoord[2];
610 edge->w0 = v1w;
611 _expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
612 edge->s0 = v1->vs;
613 edge->t0 = v1->vt;
614
615 edge->y1 = v0y;
616 edge->x1 = v0x;
617 edge->z1 = v0->viewCoord[2];
618 edge->w1 = v0w;
619 _expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
620 edge->s1 = v0->vs;
621 edge->t1 = v0->vt;
622 }
623 edge->wr0 = 0x7FFFFFFFFFFFFFFF / edge->w0;
624 edge->wr1 = 0x7FFFFFFFFFFFFFFF / edge->w1;
625
626 if (poly->maxY >= DS_VIDEO_VERTICAL_PIXELS) {
627 poly->maxY = DS_VIDEO_VERTICAL_PIXELS - 1;
628 }
629 if (poly->minY < 0) {
630 poly->minY = 0;
631 }
632}
633
634static void _drawSpan(struct DSGXSoftwareRenderer* softwareRenderer, struct DSGXSoftwareSpan* span, int y) {
635 color_t* scanline = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
636 int32_t* depth = &softwareRenderer->depthBuffer[DS_VIDEO_HORIZONTAL_PIXELS * y];
637 uint16_t* stencil = &softwareRenderer->stencilBuffer[DS_VIDEO_HORIZONTAL_PIXELS * y];
638 int32_t x = span->ep[0].coord[0] >> 12;
639 if (x < 0) {
640 x = 0;
641 }
642 unsigned stencilValue = span->polyId;
643 stencilValue |= stencilValue << 8;
644 if (span->poly->blendFormat == 3) {
645 stencilValue |= S_BIT_SHADOW;
646 }
647 for (; x < (span->ep[1].coord[0] >> 12) && x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
648 if (span->ep[0].coord[softwareRenderer->sort] < depth[x]) {
649 _resolveEndpoint(span);
650 color_t color = _lookupColor(softwareRenderer, &span->ep[0], span->poly);
651 unsigned a = color >> 27;
652 unsigned current = scanline[x];
653 unsigned b = current >> 27;
654 unsigned ab = a;
655 unsigned s = stencilValue;
656 if (b > ab) {
657 ab = b;
658 }
659 if (a == 0x1F) {
660 // Opaque blending
661 depth[x] = span->ep[0].coord[softwareRenderer->sort];
662 scanline[x] = color;
663 stencil[x] &= S_BIT_POLY_ID;
664 stencil[x] |= s & S_BIT_POLY_SHADOW_ID;
665 } else if (a) {
666 // TODO: Disable alpha?
667 if (b) {
668 color = _mix32(a, color, 0x1F - a, current);
669 color |= ab << 27;
670 }
671
672 if ((stencil[x] ^ s) & S_BIT_POLY_ID) {
673 if (!(s & S_BIT_SHADOW) || (((stencil[x] ^ s) & S_BIT_POLY_SHADOW_ID) && s & S_BIT_POLY_SHADOW_ID && stencil[x] & S_BIT_SHADOW)) {
674 if (DSGXPolygonAttrsIsUpdateDepth(span->poly->poly->polyParams)) {
675 depth[x] = span->ep[0].coord[softwareRenderer->sort];
676 }
677 scanline[x] = color;
678 stencil[x] &= S_BIT_POLY_SHADOW_ID;
679 } else {
680 s = 0;
681 stencil[x] &= S_BIT_POLY_SHADOW_ID | S_BIT_POLY_ID;
682 }
683 }
684
685 s &= ~(S_BIT_POLY_SHADOW_ID | S_BIT_SHADOW);
686 stencil[x] |= s;
687 }
688 } else if ((stencilValue & 0x7F00) == S_BIT_SHADOW) {
689 // Set stencil buffer bit if depth test fails
690 _resolveEndpoint(span);
691 color_t color = _lookupColor(softwareRenderer, &span->ep[0], span->poly);
692 unsigned a = color >> 27;
693 if (a > 0) {
694 stencil[x] |= S_BIT_SHADOW;
695 }
696 }
697 _stepEndpoint(span);
698 }
699}
700
701static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount, bool wSort) {
702 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
703
704 softwareRenderer->sort = wSort ? 3 : 2;
705 softwareRenderer->verts = verts;
706 DSGXSoftwarePolygonListClear(&softwareRenderer->activePolys);
707 size_t i;
708 // Pass 1: Opaque
709 for (i = 0; i < polyCount; ++i) {
710 struct DSGXSoftwarePolygon* poly = NULL;
711 switch (DSGXTexParamsGetFormat(polys[i].texParams)) {
712 default:
713 if (DSGXPolygonAttrsGetAlpha(polys[i].polyParams) == 0x1F) {
714 poly = DSGXSoftwarePolygonListAppend(&softwareRenderer->activePolys);
715 break;
716 }
717 case 1:
718 case 6:
719 break;
720 }
721 if (!poly) {
722 continue;
723 }
724 poly->poly = &polys[i];
725 poly->polyId = DSGXSoftwarePolygonListSize(&softwareRenderer->activePolys) - 1;
726 }
727 // Pass 2: Translucent
728 for (i = 0; i < polyCount; ++i) {
729 struct DSGXSoftwarePolygon* poly = NULL;
730 switch (DSGXTexParamsGetFormat(polys[i].texParams)) {
731 default:
732 if (DSGXPolygonAttrsGetAlpha(polys[i].polyParams) == 0x1F) {
733 break;
734 }
735 case 1:
736 case 6:
737 poly = DSGXSoftwarePolygonListAppend(&softwareRenderer->activePolys);
738 break;
739 }
740 if (!poly) {
741 continue;
742 }
743 poly->poly = &polys[i];
744 poly->polyId = DSGXSoftwarePolygonListSize(&softwareRenderer->activePolys) - 1;
745 }
746
747 color_t clearColor = softwareRenderer->clearColor;
748 uint16_t clearStencil = softwareRenderer->clearStencil;
749 uint32_t clearDepth = softwareRenderer->clearDepth;
750
751 for (i = 0; i < DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS ; i += 4) {
752 softwareRenderer->depthBuffer[i] = clearDepth;
753 softwareRenderer->depthBuffer[i + 1] = clearDepth;
754 softwareRenderer->depthBuffer[i + 2] = clearDepth;
755 softwareRenderer->depthBuffer[i + 3] = clearDepth;
756 softwareRenderer->scanlineCache[i] = clearColor;
757 softwareRenderer->scanlineCache[i + 1] = clearColor;
758 softwareRenderer->scanlineCache[i + 2] = clearColor;
759 softwareRenderer->scanlineCache[i + 3] = clearColor;
760 softwareRenderer->stencilBuffer[i] = clearStencil;
761 softwareRenderer->stencilBuffer[i + 1] = clearStencil;
762 softwareRenderer->stencilBuffer[i + 2] = clearStencil;
763 softwareRenderer->stencilBuffer[i + 3] = clearStencil;
764 }
765 softwareRenderer->flushPending = true;
766}
767
768static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y) {
769 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
770 if (!softwareRenderer->flushPending || y) {
771 return;
772 }
773
774 size_t p;
775 for (p = 0; p < DSGXSoftwarePolygonListSize(&softwareRenderer->activePolys); ++p) {
776 struct DSGXSoftwarePolygon* poly = DSGXSoftwarePolygonListGetPointer(&softwareRenderer->activePolys, p);
777 DSGXSoftwareEdgeListClear(&softwareRenderer->activeEdges);
778 _preparePoly(renderer, softwareRenderer->verts, poly);
779 int y;
780 for (y = poly->minY; y <= poly->maxY; ++y) {
781 struct DSGXSoftwareSpan span = {
782 .poly = poly,
783 .polyId = DSGXPolygonAttrsGetId(poly->polyParams),
784 };
785 size_t i;
786 for (i = 0; i < DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges); ++i) {
787 struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, i);
788 if (edge->y1 < (y << 12)) {
789 continue;
790 } else if (edge->y0 > (y << 12)) {
791 continue;
792 }
793
794 if (span.ep[0].coord[3]) {
795 if (_edgeToSpan(&span, edge, 1, y << 12)) {
796 _createStep(&span);
797 _drawSpan(softwareRenderer, &span, y);
798 }
799 } else if (!_edgeToSpan(&span, edge, 0, y << 12)) {
800 // Horizontal line
801 continue;
802 }
803 }
804 }
805 }
806 softwareRenderer->flushPending = false;
807}
808
809static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, const color_t** output) {
810 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
811 *output = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
812}
813
814static void DSGXSoftwareRendererWriteRegister(struct DSGXRenderer* renderer, uint32_t address, uint16_t value) {
815 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
816 switch (address) {
817 case DS9_REG_CLEAR_COLOR_LO:
818 softwareRenderer->clearColor &= 0xFF000000;
819 softwareRenderer->clearColor |= (value & 0x001F) << 3;
820 softwareRenderer->clearColor |= (value & 0x03E0) << 6;
821 softwareRenderer->clearColor |= (value & 0x7C00) << 9;
822 break;
823 case DS9_REG_CLEAR_COLOR_HI:
824 softwareRenderer->clearColor &= 0x00FFFFFF;
825 softwareRenderer->clearColor |= (value & 0x001F) << 27;
826 softwareRenderer->clearStencil = (value & 0x3F00) >> 8;
827 softwareRenderer->clearStencil |= value & S_BIT_FOG;
828 break;
829 case DS9_REG_CLEAR_DEPTH:
830 softwareRenderer->clearDepth = (value & 0x7FFF) * 0x200 + 0x1FF;
831 break;
832 }
833}