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