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