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
132void DSGXSoftwareRendererCreate(struct DSGXSoftwareRenderer* renderer) {
133 renderer->d.init = DSGXSoftwareRendererInit;
134 renderer->d.reset = DSGXSoftwareRendererReset;
135 renderer->d.deinit = DSGXSoftwareRendererDeinit;
136 renderer->d.setRAM = DSGXSoftwareRendererSetRAM;
137 renderer->d.drawScanline = DSGXSoftwareRendererDrawScanline;
138 renderer->d.getScanline = DSGXSoftwareRendererGetScanline;
139}
140
141static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer) {
142 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
143 DSGXSoftwarePolygonListInit(&softwareRenderer->activePolys, DS_GX_POLYGON_BUFFER_SIZE / 4);
144 DSGXSoftwareEdgeListInit(&softwareRenderer->activeEdges, DS_GX_POLYGON_BUFFER_SIZE);
145 DSGXSoftwareSpanListInit(&softwareRenderer->activeSpans, DS_GX_POLYGON_BUFFER_SIZE / 2);
146 softwareRenderer->bucket = anonymousMemoryMap(sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
147 softwareRenderer->scanlineCache = anonymousMemoryMap(sizeof(color_t) * DS_VIDEO_HORIZONTAL_PIXELS * 48);
148}
149
150static void DSGXSoftwareRendererReset(struct DSGXRenderer* renderer) {
151 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
152 // TODO
153}
154
155static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer) {
156 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
157 DSGXSoftwarePolygonListDeinit(&softwareRenderer->activePolys);
158 DSGXSoftwareEdgeListDeinit(&softwareRenderer->activeEdges);
159 DSGXSoftwareSpanListDeinit(&softwareRenderer->activeSpans);
160 mappedMemoryFree(softwareRenderer->bucket, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
161 mappedMemoryFree(softwareRenderer->scanlineCache, sizeof(color_t) * DS_VIDEO_HORIZONTAL_PIXELS * 48);
162}
163
164static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount) {
165 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
166
167 softwareRenderer->verts = verts;
168 DSGXSoftwarePolygonListClear(&softwareRenderer->activePolys);
169 DSGXSoftwareEdgeListClear(&softwareRenderer->activeEdges);
170 unsigned i;
171 for (i = 0; i < polyCount; ++i) {
172 struct DSGXSoftwarePolygon* poly = DSGXSoftwarePolygonListAppend(&softwareRenderer->activePolys);
173 struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
174 poly->poly = &polys[i];
175 edge->polyId = i;
176
177 struct DSGXVertex* v0 = &verts[poly->poly->vertIds[0]];
178 struct DSGXVertex* v1;
179
180 int v;
181 for (v = 1; v < poly->poly->verts; ++v) {
182 v1 = &verts[poly->poly->vertIds[v]];
183 if (v0->vy >= v1->vy) {
184 edge->y0 = SCREEN_SIZE - v0->vy;
185 edge->x0 = v0->vx;
186 edge->w0 = v0->vw;
187 _expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
188 edge->s0 = v0->s;
189 edge->t0 = v0->t;
190
191 edge->y1 = SCREEN_SIZE - v1->vy;
192 edge->x1 = v1->vx;
193 edge->w1 = v1->vw;
194 _expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
195 edge->s1 = v1->s;
196 edge->t1 = v1->t;
197 } else {
198 edge->y0 = SCREEN_SIZE - v1->vy;
199 edge->x0 = v1->vx;
200 edge->w0 = v1->vw;
201 _expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
202 edge->s0 = v1->s;
203 edge->t0 = v1->t;
204
205 edge->y1 = SCREEN_SIZE - v0->vy;
206 edge->x1 = v0->vx;
207 edge->w1 = v0->vw;
208 _expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
209 edge->s1 = v0->s;
210 edge->t1 = v0->t;
211 }
212
213 edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
214 edge->polyId = i;
215 v0 = v1;
216 }
217
218 v1 = &verts[poly->poly->vertIds[0]];
219 if (v0->vy >= v1->vy) {
220 edge->y0 = SCREEN_SIZE - v0->vy;
221 edge->x0 = v0->vx;
222 edge->w0 = v0->vw;
223 _expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
224 edge->s0 = v0->s;
225 edge->t0 = v0->t;
226
227 edge->y1 = SCREEN_SIZE - v1->vy;
228 edge->x1 = v1->vx;
229 edge->w1 = v1->vw;
230 _expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
231 edge->s1 = v1->s;
232 edge->t1 = v1->t;
233 } else {
234 edge->y0 = SCREEN_SIZE - v1->vy;
235 edge->x0 = v1->vx;
236 edge->w0 = v1->vw;
237 _expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
238 edge->s0 = v1->s;
239 edge->t0 = v1->t;
240
241 edge->y1 = SCREEN_SIZE - v0->vy;
242 edge->x1 = v0->vx;
243 edge->w1 = v0->vw;
244 _expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
245 edge->s1 = v0->s;
246 edge->t1 = v0->t;
247 }
248 }
249 qsort(DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, 0), DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges), sizeof(struct DSGXSoftwareEdge), _edgeSort);
250}
251
252static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y) {
253 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
254 DSGXSoftwareSpanListClear(&softwareRenderer->activeSpans);
255 memset(softwareRenderer->bucket, 0, sizeof(*softwareRenderer->bucket) * DS_GX_POLYGON_BUFFER_SIZE);
256 size_t i;
257 for (i = DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges); i; --i) {
258 size_t idx = i - 1;
259 struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, idx);
260 if (edge->y1 >> 12 < y) {
261 DSGXSoftwareEdgeListShift(&softwareRenderer->activeEdges, idx, 1);
262 continue;
263 } else if (edge->y0 >> 12 > y) {
264 continue;
265 }
266
267 unsigned poly = edge->polyId;
268 struct DSGXSoftwareSpan* span = softwareRenderer->bucket[poly];
269 if (span && !span->ep[1].w) {
270 _edgeToSpan(span, edge, 1, y);
271 softwareRenderer->bucket[poly] = NULL;
272 } else if (!span) {
273 span = DSGXSoftwareSpanListAppend(&softwareRenderer->activeSpans);
274 memset(span, 0, sizeof(*span));
275 if (!_edgeToSpan(span, edge, 0, y)) {
276 // Horizontal line
277 DSGXSoftwareSpanListShift(&softwareRenderer->activeSpans, DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans) - 1, 1);
278 } else {
279 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)->ep[0].x;
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->ep[1].x >> 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->ep[0].x >> 12;
308 }
309 if (i < nextSpanX) {
310 span = NULL;
311 }
312 }
313 if (span) {
314 scanline[i] = _lerpColor(span, i);
315 } else {
316 scanline[i] = FLAG_UNWRITTEN; // TODO
317 }
318 }
319}
320
321static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output) {
322 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
323 y %= 48;
324 *output = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
325}