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 DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount);
21static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y);
22static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output);
23
24static void _expandColor(uint16_t c15, int8_t* r, int8_t* g, int8_t* b) {
25 *r = ((c15 << 1) & 0x3E) | 1;
26 *g = ((c15 >> 4) & 0x3E) | 1;
27 *b = ((c15 >> 9) & 0x3E) | 1;
28}
29
30static int _edgeSort(const void* a, const void* b) {
31 const struct DSGXSoftwareEdge* ea = a;
32 const struct DSGXSoftwareEdge* eb = b;
33
34 // Sort upside down
35 if (ea->y0 < eb->y0) {
36 return 1;
37 }
38 if (ea->y0 > eb->y0) {
39 return -1;
40 }
41 if (ea->y1 < eb->y1) {
42 return 1;
43 }
44 if (ea->y1 > eb->y1) {
45 return -1;
46 }
47 return 0;
48}
49
50static bool _edgeToSpan(struct DSGXSoftwareSpan* span, const struct DSGXSoftwareEdge* edge, int index, int32_t y) {
51 int32_t height = edge->y1 - edge->y0;
52 int64_t yw = (y << 12) - edge->y0;
53 if (!height) {
54 return false;
55 }
56 // Clamp to bounds
57 if (yw < 0) {
58 yw = 0;
59 } else if (yw > height) {
60 yw = height;
61 }
62 span->ep[index].x = ((int64_t) (edge->x1 - edge->x0) * yw) / height + edge->x0;
63 if (index && span->ep[0].x > span->ep[index].x) {
64 int32_t temp = span->ep[index].x;
65 span->ep[index] = span->ep[0];
66 span->ep[0].x = temp;
67 index = 0;
68 }
69 int32_t w = ((int64_t) (edge->w1 - edge->w0) * yw) / height + edge->w0;
70 span->ep[index].w = w;
71 span->ep[index].cr = (((int32_t) (edge->cr1 * edge->w1 - edge->cr0 * edge->w0) * yw) / height + edge->cr0 * edge->w0) / w;
72 span->ep[index].cg = (((int32_t) (edge->cg1 * edge->w1 - edge->cg0 * edge->w0) * yw) / height + edge->cg0 * edge->w0) / w;
73 span->ep[index].cb = (((int32_t) (edge->cb1 * edge->w1 - edge->cb0 * edge->w0) * yw) / height + edge->cb0 * edge->w0) / w;
74 span->ep[index].s = (((int32_t) (edge->s1 * edge->w1 - edge->s0 * edge->w0) * yw) / height + edge->s0 * edge->w0) / w;
75 span->ep[index].t = (((int32_t) (edge->t1 * edge->w1 - edge->t0 * edge->w0) * yw) / height + edge->t0 * edge->w0) / w;
76
77 return true;
78}
79
80static int _spanSort(const void* a, const void* b) {
81 const struct DSGXSoftwareSpan* sa = a;
82 const struct DSGXSoftwareSpan* sb = b;
83
84 // Sort backwards
85 if (sa->ep[0].x < sb->ep[0].x) {
86 return 1;
87 }
88 if (sa->ep[0].x > sb->ep[0].x) {
89 return -1;
90 }
91 if (sa->ep[1].x < sb->ep[1].x) {
92 return 1;
93 }
94 if (sa->ep[1].x > sb->ep[1].x) {
95 return -1;
96 }
97 return 0;
98}
99
100static color_t _lerpColor(const struct DSGXSoftwareSpan* span, unsigned x) {
101 int64_t width = span->ep[1].x - span->ep[0].x;
102 int64_t xw = ((uint64_t) x << 12) - span->ep[0].x;
103 if (!width) {
104 return 0; // TODO?
105 }
106 // Clamp to bounds
107 if (xw < 0) {
108 xw = 0;
109 } else if (xw > width) {
110 xw = width;
111 }
112 int32_t w0 = span->ep[0].w;
113 int32_t w1 = span->ep[1].w;
114 int32_t w = ((int64_t) (w1 - w0) * xw) / width + w0;
115
116 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;
117 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;
118 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;
119 r /= w;
120 g /= w;
121 b /= w;
122#ifndef COLOR_16_BIT
123 color_t rgb = (r << 2) & 0xF8;
124 rgb |= (g << 10) & 0xF800;
125 rgb |= (b << 18) & 0xF80000;
126 return rgb;
127#else
128#error Unsupported color depth
129#endif
130}
131
132static int32_t _lerpDepth(const struct DSGXSoftwareSpan* span, unsigned x) {
133 int64_t width = span->ep[1].x - span->ep[0].x;
134 int64_t xw = ((uint64_t) x << 12) - span->ep[0].x;
135 if (!width) {
136 return 0; // TODO?
137 }
138 // Clamp to bounds
139 if (xw < 0) {
140 xw = 0;
141 } else if (xw > width) {
142 xw = width;
143 }
144 int32_t w0 = span->ep[0].w;
145 int32_t w1 = span->ep[1].w;
146 return ((int64_t) (w1 - w0) * xw) / width + w0;
147}
148
149void DSGXSoftwareRendererCreate(struct DSGXSoftwareRenderer* renderer) {
150 renderer->d.init = DSGXSoftwareRendererInit;
151 renderer->d.reset = DSGXSoftwareRendererReset;
152 renderer->d.deinit = DSGXSoftwareRendererDeinit;
153 renderer->d.setRAM = DSGXSoftwareRendererSetRAM;
154 renderer->d.drawScanline = DSGXSoftwareRendererDrawScanline;
155 renderer->d.getScanline = DSGXSoftwareRendererGetScanline;
156}
157
158static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer) {
159 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
160 DSGXSoftwarePolygonListInit(&softwareRenderer->activePolys, DS_GX_POLYGON_BUFFER_SIZE / 4);
161 DSGXSoftwareEdgeListInit(&softwareRenderer->activeEdges, DS_GX_POLYGON_BUFFER_SIZE);
162 DSGXSoftwareSpanListInit(&softwareRenderer->activeSpans, DS_GX_POLYGON_BUFFER_SIZE / 2);
163 softwareRenderer->bucket = anonymousMemoryMap(sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
164 softwareRenderer->scanlineCache = anonymousMemoryMap(sizeof(color_t) * DS_VIDEO_HORIZONTAL_PIXELS * 48);
165}
166
167static void DSGXSoftwareRendererReset(struct DSGXRenderer* renderer) {
168 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
169 // TODO
170}
171
172static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer) {
173 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
174 DSGXSoftwarePolygonListDeinit(&softwareRenderer->activePolys);
175 DSGXSoftwareEdgeListDeinit(&softwareRenderer->activeEdges);
176 DSGXSoftwareSpanListDeinit(&softwareRenderer->activeSpans);
177 mappedMemoryFree(softwareRenderer->bucket, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
178 mappedMemoryFree(softwareRenderer->scanlineCache, sizeof(color_t) * DS_VIDEO_HORIZONTAL_PIXELS * 48);
179}
180
181static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount) {
182 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
183
184 softwareRenderer->verts = verts;
185 DSGXSoftwarePolygonListClear(&softwareRenderer->activePolys);
186 DSGXSoftwareEdgeListClear(&softwareRenderer->activeEdges);
187 unsigned i;
188 for (i = 0; i < polyCount; ++i) {
189 struct DSGXSoftwarePolygon* poly = DSGXSoftwarePolygonListAppend(&softwareRenderer->activePolys);
190 struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
191 poly->poly = &polys[i];
192 edge->polyId = i;
193
194 struct DSGXVertex* v0 = &verts[poly->poly->vertIds[0]];
195 struct DSGXVertex* v1;
196
197 int v;
198 for (v = 1; v < poly->poly->verts; ++v) {
199 v1 = &verts[poly->poly->vertIds[v]];
200 if (v0->vy >= v1->vy) {
201 edge->y0 = SCREEN_SIZE - v0->vy;
202 edge->x0 = v0->vx;
203 edge->w0 = v0->vw;
204 _expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
205 edge->s0 = v0->s;
206 edge->t0 = v0->t;
207
208 edge->y1 = SCREEN_SIZE - v1->vy;
209 edge->x1 = v1->vx;
210 edge->w1 = v1->vw;
211 _expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
212 edge->s1 = v1->s;
213 edge->t1 = v1->t;
214 } else {
215 edge->y0 = SCREEN_SIZE - v1->vy;
216 edge->x0 = v1->vx;
217 edge->w0 = v1->vw;
218 _expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
219 edge->s0 = v1->s;
220 edge->t0 = v1->t;
221
222 edge->y1 = SCREEN_SIZE - v0->vy;
223 edge->x1 = v0->vx;
224 edge->w1 = v0->vw;
225 _expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
226 edge->s1 = v0->s;
227 edge->t1 = v0->t;
228 }
229
230 edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
231 edge->polyId = i;
232 v0 = v1;
233 }
234
235 v1 = &verts[poly->poly->vertIds[0]];
236 if (v0->vy >= v1->vy) {
237 edge->y0 = SCREEN_SIZE - v0->vy;
238 edge->x0 = v0->vx;
239 edge->w0 = v0->vw;
240 _expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
241 edge->s0 = v0->s;
242 edge->t0 = v0->t;
243
244 edge->y1 = SCREEN_SIZE - v1->vy;
245 edge->x1 = v1->vx;
246 edge->w1 = v1->vw;
247 _expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
248 edge->s1 = v1->s;
249 edge->t1 = v1->t;
250 } else {
251 edge->y0 = SCREEN_SIZE - v1->vy;
252 edge->x0 = v1->vx;
253 edge->w0 = v1->vw;
254 _expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
255 edge->s0 = v1->s;
256 edge->t0 = v1->t;
257
258 edge->y1 = SCREEN_SIZE - v0->vy;
259 edge->x1 = v0->vx;
260 edge->w1 = v0->vw;
261 _expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
262 edge->s1 = v0->s;
263 edge->t1 = v0->t;
264 }
265 }
266 qsort(DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, 0), DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges), sizeof(struct DSGXSoftwareEdge), _edgeSort);
267}
268
269static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y) {
270 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
271 DSGXSoftwareSpanListClear(&softwareRenderer->activeSpans);
272 memset(softwareRenderer->bucket, 0, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
273 size_t i;
274 for (i = DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges); i; --i) {
275 size_t idx = i - 1;
276 struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, idx);
277 if (edge->y1 >> 12 < y) {
278 DSGXSoftwareEdgeListShift(&softwareRenderer->activeEdges, idx, 1);
279 continue;
280 } else if (edge->y0 >> 12 > y) {
281 continue;
282 }
283
284 unsigned poly = edge->polyId;
285 struct DSGXSoftwareSpan* span = softwareRenderer->bucket[poly];
286 if (span && !span->ep[1].w) {
287 _edgeToSpan(span, edge, 1, y);
288 softwareRenderer->bucket[poly] = NULL;
289 } else if (!span) {
290 span = DSGXSoftwareSpanListAppend(&softwareRenderer->activeSpans);
291 memset(span, 0, sizeof(*span));
292 if (!_edgeToSpan(span, edge, 0, y)) {
293 // Horizontal line
294 DSGXSoftwareSpanListShift(&softwareRenderer->activeSpans, DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans) - 1, 1);
295 } else {
296 softwareRenderer->bucket[poly] = span;
297 }
298 }
299 }
300 qsort(DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, 0), DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans), sizeof(struct DSGXSoftwareSpan), _spanSort);
301
302 y %= 48;
303 color_t* scanline = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
304
305 size_t nextSpanX = DS_VIDEO_HORIZONTAL_PIXELS;
306 if (DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans)) {
307 nextSpanX = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans) - 1)->ep[0].x;
308 nextSpanX >>= 12;
309 }
310 for (i = 0; i < DS_VIDEO_HORIZONTAL_PIXELS; ++i) {
311 struct DSGXSoftwareSpan* span = NULL;
312 int32_t depth = INT32_MIN;
313 if (i >= nextSpanX) {
314 size_t nextSpanId = DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans);
315 span = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, nextSpanId - 1);
316 while (i > (uint32_t) (span->ep[1].x >> 12)) {
317 DSGXSoftwareSpanListShift(&softwareRenderer->activeSpans, nextSpanId - 1, 1);
318 --nextSpanId;
319 if (!nextSpanId) {
320 nextSpanX = DS_VIDEO_HORIZONTAL_PIXELS;
321 span = NULL;
322 break;
323 }
324 span = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, nextSpanId - 1);
325 nextSpanX = span->ep[0].x >> 12;
326 }
327 if (i < nextSpanX) {
328 span = NULL;
329 } else {
330 struct DSGXSoftwareSpan* testSpan = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, nextSpanId - 1);
331 while (i > (uint32_t) (testSpan->ep[0].x >> 12)) {
332 if (i <= (uint32_t) (testSpan->ep[1].x >> 12)) {
333 int32_t newDepth = _lerpDepth(testSpan, i);
334 if (newDepth > depth) {
335 depth = newDepth;
336 span = testSpan;
337 }
338 }
339 --nextSpanId;
340 if (!nextSpanId) {
341 break;
342 }
343 testSpan = DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, nextSpanId - 1);
344 }
345 }
346 }
347 if (span) {
348 scanline[i] = _lerpColor(span, i);
349 } else {
350 scanline[i] = FLAG_UNWRITTEN; // TODO
351 }
352 }
353}
354
355static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output) {
356 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
357 y %= 48;
358 *output = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
359}