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