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