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