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