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