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 << 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 color_t _lookupColor(struct DSGXSoftwareEndpoint* ep, struct DSGXSoftwarePolygon* poly) {
44 // TODO: Optimize
45 uint16_t texel;
46
47 int16_t s = ep->s >> 4;
48 int16_t t = ep->t >> 4;
49 if (!DSGXTexParamsIsSRepeat(poly->poly->texParams)) {
50 if (s < 0) {
51 s = 0;
52 } else if (s >= poly->texW) {
53 s = poly->texW - 1;
54 }
55 } else if (DSGXTexParamsIsSMirror(poly->poly->texParams)) {
56 if (s & poly->texW) {
57 s = poly->texW - s;
58 }
59 s &= poly->texW - 1;
60 } else {
61 s &= poly->texW - 1;
62 }
63 if (!DSGXTexParamsIsTRepeat(poly->poly->texParams)) {
64 if (t < 0) {
65 t = 0;
66 } else if (t >= poly->texH) {
67 t = poly->texW - 1;
68 }
69 } else if (DSGXTexParamsIsTMirror(poly->poly->texParams)) {
70 if (t & poly->texH) {
71 t = poly->texH - t;
72 }
73 t &= poly->texH - 1;
74 } else {
75 t &= poly->texH - 1;
76 }
77
78 uint16_t texelCoord = s + t * poly->texW;
79 uint8_t a = 0x1F;
80 switch (poly->texFormat) {
81 case 0:
82 default:
83 return _finishColor(ep->cr, ep->cg, ep->cb, 0x1F);
84 case 1:
85 texel = ((uint8_t*) poly->texBase)[texelCoord];
86 a = (texel >> 5) & 0x7;
87 a = (a << 2) + (a >> 1);
88 texel &= 0x1F;
89 break;
90 case 2:
91 texel = ((uint8_t*) poly->texBase)[texelCoord >> 2];
92 if (texelCoord & 0x3) {
93 texel >>= 2 * texel & 3;
94 }
95 texel &= 0x3;
96 break;
97 case 3:
98 texel = ((uint8_t*) poly->texBase)[texelCoord >> 1];
99 if (texelCoord & 0x1) {
100 texel >>= 4;
101 }
102 texel &= 0xF;
103 break;
104 case 4:
105 texel = ((uint8_t*) poly->texBase)[texelCoord];
106 break;
107 case 5:
108 return _finishColor(0x3F, 0, 0x3F, 0x1F);
109 case 6:
110 texel = ((uint8_t*) poly->texBase)[texelCoord];
111 a = (texel >> 3) & 0x1F;
112 texel &= 0x7;
113 break;
114 case 7:
115 return _finishColor(0x3F, 0x3F, 0x3F, 0x1F);
116 }
117 if (DSGXTexParamsIs0Transparent(poly->poly->texParams) && !texel) {
118 return 0;
119 }
120 uint8_t r, g, b;
121 unsigned wr, wg, wb;
122 texel = poly->palBase[texel];
123 _expandColor(texel, &r, &g, &b);
124 switch (poly->blendFormat) {
125 case 1:
126 default:
127 // TODO: Alpha
128 return _finishColor(r, g, b, a);
129 case 0:
130 wr = ((r + 1) * (ep->cr + 1) - 1) >> 6;
131 wg = ((g + 1) * (ep->cg + 1) - 1) >> 6;
132 wb = ((b + 1) * (ep->cb + 1) - 1) >> 6;
133 return _finishColor(wr, wg, wb, a);
134 }
135}
136
137static int _edgeSort(const void* a, const void* b) {
138 const struct DSGXSoftwareEdge* ea = a;
139 const struct DSGXSoftwareEdge* eb = b;
140
141 // Sort upside down
142 if (ea->y0 < eb->y0) {
143 return 1;
144 }
145 if (ea->y0 > eb->y0) {
146 return -1;
147 }
148 if (ea->y1 < eb->y1) {
149 return 1;
150 }
151 if (ea->y1 > eb->y1) {
152 return -1;
153 }
154 return 0;
155}
156
157static bool _edgeToSpan(struct DSGXSoftwareSpan* span, const struct DSGXSoftwareEdge* edge, int index, int32_t y) {
158 int32_t height = edge->y1 - edge->y0;
159 int64_t yw = (y << 12) - edge->y0;
160 if (!height) {
161 return false;
162 }
163 // Clamp to bounds
164 if (yw < 0) {
165 yw = 0;
166 } else if (yw > height) {
167 yw = height;
168 }
169 yw *= 0x100000000LL / height;
170
171 span->ep[index].x = (((int64_t) (edge->x1 - edge->x0) * yw) >> 32) + edge->x0;
172
173 if (index && span->ep[0].x > span->ep[index].x) {
174 int32_t temp = span->ep[index].x;
175 span->ep[index] = span->ep[0];
176 span->ep[0].x = temp;
177 index = 0;
178 }
179 int32_t w0 = edge->w0;
180 int32_t w1 = edge->w1;
181 int32_t w = (((int64_t) (edge->w1 - edge->w0) * yw) >> 32) + edge->w0;
182 int64_t wRecip;// = 0x1000000000000LL / w;
183 // XXX: Disable perspective correction until I figure out how to fix it
184 wRecip = 0x100000000;
185 w0 = 0x10000;
186 w1 = 0x10000;
187 span->ep[index].w = w;
188 span->ep[index].z = (((edge->z1 - edge->z0) * yw) >> 32) + edge->z0;
189 span->ep[index].cr = (((((edge->cr1 * (int64_t) w1 - edge->cr0 * (int64_t) w0) * yw) >> 32) + edge->cr0 * (int64_t) w0) * wRecip) >> 48;
190 span->ep[index].cg = (((((edge->cg1 * (int64_t) w1 - edge->cg0 * (int64_t) w0) * yw) >> 32) + edge->cg0 * (int64_t) w0) * wRecip) >> 48;
191 span->ep[index].cb = (((((edge->cb1 * (int64_t) w1 - edge->cb0 * (int64_t) w0) * yw) >> 32) + edge->cb0 * (int64_t) w0) * wRecip) >> 48;
192 span->ep[index].s = (((((edge->s1 * (int64_t) w1 - edge->s0 * (int64_t) w0) * yw) >> 32) + edge->s0 * (int64_t) w0) * wRecip) >> 48;
193 span->ep[index].t = (((((edge->t1 * (int64_t) w1 - edge->t0 * (int64_t) w0) * yw) >> 32) + edge->t0 * (int64_t) w0) * wRecip) >> 48;
194
195 return true;
196}
197
198static int _spanSort(const void* a, const void* b) {
199 const struct DSGXSoftwareSpan* sa = a;
200 const struct DSGXSoftwareSpan* sb = b;
201
202 // Sort backwards
203 if (sa->ep[0].x < sb->ep[0].x) {
204 return 1;
205 }
206 if (sa->ep[0].x > sb->ep[0].x) {
207 return -1;
208 }
209 if (sa->ep[0].w < sb->ep[0].w) {
210 return 1;
211 }
212 if (sa->ep[0].w > sb->ep[0].w) {
213 return -1;
214 }
215 return 0;
216}
217
218static void _lerpEndpoint(const struct DSGXSoftwareSpan* span, struct DSGXSoftwareEndpoint* ep, unsigned x) {
219 int64_t width = span->ep[1].x - span->ep[0].x;
220 int64_t xw = ((uint64_t) x << 12) - span->ep[0].x;
221 if (!width) {
222 return; // TODO?
223 }
224 // Clamp to bounds
225 if (xw < 0) {
226 xw = 0;
227 } else if (xw > width) {
228 xw = width;
229 }
230 xw *= 0x100000000LL / width;
231 int32_t w0 = span->ep[0].w;
232 int32_t w1 = span->ep[1].w;
233 int64_t w = (((int64_t) (w1 - w0) * xw) >> 32) + w0;
234 int64_t wRecip;// = 0x1000000000000LL / w;
235 ep->w = w;
236 // XXX: Disable perspective correction until I figure out how to fix it
237 wRecip = 0x100000000;
238 w0 = 0x10000;
239 w1 = 0x10000;
240
241 ep->z = (((span->ep[1].z - span->ep[0].z) * xw) >> 32) + span->ep[0].z;
242
243 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;
244 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;
245 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;
246 ep->cr = (r * wRecip) >> 48;
247 ep->cg = (g * wRecip) >> 48;
248 ep->cb = (b * wRecip) >> 48;
249
250 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;
251 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;
252 ep->s = (s * wRecip) >> 48;
253 ep->t = (t * wRecip) >> 48;
254}
255
256void DSGXSoftwareRendererCreate(struct DSGXSoftwareRenderer* renderer) {
257 renderer->d.init = DSGXSoftwareRendererInit;
258 renderer->d.reset = DSGXSoftwareRendererReset;
259 renderer->d.deinit = DSGXSoftwareRendererDeinit;
260 renderer->d.invalidateTex = DSGXSoftwareRendererInvalidateTex;
261 renderer->d.setRAM = DSGXSoftwareRendererSetRAM;
262 renderer->d.drawScanline = DSGXSoftwareRendererDrawScanline;
263 renderer->d.getScanline = DSGXSoftwareRendererGetScanline;
264}
265
266static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer) {
267 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
268 DSGXSoftwarePolygonListInit(&softwareRenderer->activePolys, DS_GX_POLYGON_BUFFER_SIZE / 4);
269 DSGXSoftwareEdgeListInit(&softwareRenderer->activeEdges, DS_GX_POLYGON_BUFFER_SIZE);
270 DSGXSoftwareSpanListInit(&softwareRenderer->activeSpans, DS_GX_POLYGON_BUFFER_SIZE / 2);
271 softwareRenderer->bucket = anonymousMemoryMap(sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
272 softwareRenderer->scanlineCache = anonymousMemoryMap(sizeof(color_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
273}
274
275static void DSGXSoftwareRendererReset(struct DSGXRenderer* renderer) {
276 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
277 softwareRenderer->flushPending = false;
278}
279
280static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer) {
281 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
282 DSGXSoftwarePolygonListDeinit(&softwareRenderer->activePolys);
283 DSGXSoftwareEdgeListDeinit(&softwareRenderer->activeEdges);
284 DSGXSoftwareSpanListDeinit(&softwareRenderer->activeSpans);
285 mappedMemoryFree(softwareRenderer->bucket, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
286 mappedMemoryFree(softwareRenderer->scanlineCache, sizeof(color_t) * DS_VIDEO_VERTICAL_PIXELS * DS_VIDEO_HORIZONTAL_PIXELS);
287}
288
289static void DSGXSoftwareRendererInvalidateTex(struct DSGXRenderer* renderer, int slot) {
290 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
291 // TODO
292}
293
294static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount, bool wSort) {
295 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
296
297 softwareRenderer->flushPending = true;
298 softwareRenderer->wSort = wSort;
299 softwareRenderer->verts = verts;
300 DSGXSoftwarePolygonListClear(&softwareRenderer->activePolys);
301 DSGXSoftwareEdgeListClear(&softwareRenderer->activeEdges);
302 unsigned i;
303 for (i = 0; i < polyCount; ++i) {
304 struct DSGXSoftwarePolygon* poly = DSGXSoftwarePolygonListAppend(&softwareRenderer->activePolys);
305 struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
306 poly->poly = &polys[i];
307 poly->texFormat = DSGXTexParamsGetFormat(poly->poly->texParams);
308 poly->blendFormat = DSGXPolygonAttrsGetMode(poly->poly->polyParams);
309 poly->texW = 8 << DSGXTexParamsGetSSize(poly->poly->texParams);
310 poly->texH = 8 << DSGXTexParamsGetTSize(poly->poly->texParams);
311 switch (poly->texFormat) {
312 case 0:
313 case 7:
314 poly->texBase = NULL;
315 poly->palBase = NULL;
316 break;
317 case 2:
318 poly->texBase = &renderer->tex[DSGXTexParamsGetVRAMBase(poly->poly->texParams) >> VRAM_BLOCK_OFFSET][(DSGXTexParamsGetVRAMBase(poly->poly->texParams) << 2) & 0xFFFF];
319 poly->palBase = &renderer->texPal[poly->poly->palBase >> 12][(poly->poly->palBase << 2) & 0x1FFF];
320 break;
321 default:
322 poly->texBase = &renderer->tex[DSGXTexParamsGetVRAMBase(poly->poly->texParams) >> VRAM_BLOCK_OFFSET][(DSGXTexParamsGetVRAMBase(poly->poly->texParams) << 2) & 0xFFFF];
323 poly->palBase = &renderer->texPal[poly->poly->palBase >> 11][(poly->poly->palBase << 3) & 0x1FFF];
324 break;
325 }
326 edge->polyId = i;
327
328 struct DSGXVertex* v0 = &verts[poly->poly->vertIds[0]];
329 struct DSGXVertex* v1;
330
331 int v;
332 for (v = 1; v < poly->poly->verts; ++v) {
333 v1 = &verts[poly->poly->vertIds[v]];
334 if (v0->vy >= v1->vy) {
335 edge->y0 = SCREEN_SIZE - v0->vy;
336 edge->x0 = v0->vx;
337 edge->z0 = v0->vz;
338 edge->w0 = v0->vw;
339 _expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
340 edge->s0 = v0->vs;
341 edge->t0 = v0->vt;
342
343 edge->y1 = SCREEN_SIZE - v1->vy;
344 edge->x1 = v1->vx;
345 edge->z1 = v1->vz;
346 edge->w1 = v1->vw;
347 _expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
348 edge->s1 = v1->vs;
349 edge->t1 = v1->vt;
350 } else {
351 edge->y0 = SCREEN_SIZE - v1->vy;
352 edge->x0 = v1->vx;
353 edge->z0 = v1->vz;
354 edge->w0 = v1->vw;
355 _expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
356 edge->s0 = v1->vs;
357 edge->t0 = v1->vt;
358
359 edge->y1 = SCREEN_SIZE - v0->vy;
360 edge->x1 = v0->vx;
361 edge->z1 = v0->vz;
362 edge->w1 = v0->vw;
363 _expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
364 edge->s1 = v0->vs;
365 edge->t1 = v0->vt;
366 }
367
368 edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
369 edge->polyId = i;
370 v0 = v1;
371 }
372
373 v1 = &verts[poly->poly->vertIds[0]];
374 if (v0->vy >= v1->vy) {
375 edge->y0 = SCREEN_SIZE - v0->vy;
376 edge->x0 = v0->vx;
377 edge->z0 = v0->vz;
378 edge->w0 = v0->vw;
379 _expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
380 edge->s0 = v0->vs;
381 edge->t0 = v0->vt;
382
383 edge->y1 = SCREEN_SIZE - v1->vy;
384 edge->x1 = v1->vx;
385 edge->z1 = v1->vz;
386 edge->w1 = v1->vw;
387 _expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
388 edge->s1 = v1->vs;
389 edge->t1 = v1->vt;
390 } else {
391 edge->y0 = SCREEN_SIZE - v1->vy;
392 edge->x0 = v1->vx;
393 edge->w0 = v1->vw;
394 edge->z0 = v1->vz;
395 _expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
396 edge->s0 = v1->vs;
397 edge->t0 = v1->vt;
398
399 edge->y1 = SCREEN_SIZE - v0->vy;
400 edge->x1 = v0->vx;
401 edge->z1 = v0->vz;
402 edge->w1 = v0->vw;
403 _expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
404 edge->s1 = v0->vs;
405 edge->t1 = v0->vt;
406 }
407 }
408 qsort(DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, 0), DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges), sizeof(struct DSGXSoftwareEdge), _edgeSort);
409}
410
411static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y) {
412 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
413 if (!softwareRenderer->flushPending) {
414 return;
415 }
416 DSGXSoftwareSpanListClear(&softwareRenderer->activeSpans);
417 memset(softwareRenderer->bucket, 0, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
418 int i;
419 for (i = DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges); i; --i) {
420 size_t idx = i - 1;
421 struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, idx);
422 if (edge->y1 >> 12 < y) {
423 DSGXSoftwareEdgeListShift(&softwareRenderer->activeEdges, idx, 1);
424 continue;
425 } else if (edge->y0 >> 12 > y) {
426 continue;
427 }
428
429 unsigned poly = edge->polyId;
430 struct DSGXSoftwareSpan* span = softwareRenderer->bucket[poly];
431 if (span && !span->ep[1].w) {
432 _edgeToSpan(span, edge, 1, y);
433 softwareRenderer->bucket[poly] = NULL;
434 } else if (!span) {
435 span = DSGXSoftwareSpanListAppend(&softwareRenderer->activeSpans);
436 memset(&span->ep[1], 0, sizeof(span->ep[1]));
437 span->poly = DSGXSoftwarePolygonListGetPointer(&softwareRenderer->activePolys, poly);
438 if (!_edgeToSpan(span, edge, 0, y)) {
439 // Horizontal line
440 DSGXSoftwareSpanListShift(&softwareRenderer->activeSpans, DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans) - 1, 1);
441 } else {
442 softwareRenderer->bucket[poly] = span;
443 }
444 }
445 }
446 qsort(DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, 0), DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans), sizeof(struct DSGXSoftwareSpan), _spanSort);
447
448 color_t* scanline = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
449
450 int nextSpanX = DS_VIDEO_HORIZONTAL_PIXELS;
451 if (DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans)) {
452 nextSpanX = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans) - 1)->ep[0].x;
453 nextSpanX >>= 12;
454 }
455 for (i = 0; i < DS_VIDEO_HORIZONTAL_PIXELS; ++i) {
456 struct DSGXSoftwareSpan* span = NULL;
457 struct DSGXSoftwareEndpoint ep;
458 int32_t depth = INT32_MAX;
459 scanline[i] = 0;
460 if (i >= nextSpanX) {
461 size_t nextSpanId = DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans);
462 span = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, nextSpanId - 1);
463 while (i > (span->ep[1].x >> 12) || !span->ep[1].x) {
464 DSGXSoftwareSpanListShift(&softwareRenderer->activeSpans, nextSpanId - 1, 1);
465 --nextSpanId;
466 if (!nextSpanId) {
467 nextSpanX = DS_VIDEO_HORIZONTAL_PIXELS;
468 span = NULL;
469 break;
470 }
471 span = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, nextSpanId - 1);
472 nextSpanX = span->ep[0].x >> 12;
473 }
474 while (span && i > (span->ep[0].x >> 12)) {
475 if (i <= (span->ep[1].x >> 12)) {
476 _lerpEndpoint(span, &ep, i);
477 color_t color = _lookupColor(&ep, span->poly);
478 if (color & 0xF8000000) {
479 // TODO: Alpha
480 if (softwareRenderer->wSort) {
481 if (ep.w < depth) {
482 depth = ep.w;
483 scanline[i] = color;
484 }
485 } else {
486 if (ep.z < depth) {
487 depth = ep.z;
488 scanline[i] = color;
489 }
490 }
491 }
492 }
493 --nextSpanId;
494 if (!nextSpanId) {
495 break;
496 }
497 span = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, nextSpanId - 1);
498 }
499 }
500 }
501 if (y == DS_VIDEO_VERTICAL_PIXELS - 1) {
502 softwareRenderer->flushPending = false;
503 }
504}
505
506static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, const color_t** output) {
507 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
508 *output = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
509}