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