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
10#define SCREEN_SIZE (DS_VIDEO_VERTICAL_PIXELS << 12)
11
12DEFINE_VECTOR(DSGXSoftwarePolygonList, struct DSGXSoftwarePolygon);
13DEFINE_VECTOR(DSGXSoftwareEdgeList, struct DSGXSoftwareEdge);
14DEFINE_VECTOR(DSGXSoftwareSpanList, struct DSGXSoftwareSpan);
15
16static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer);
17static void DSGXSoftwareRendererReset(struct DSGXRenderer* renderer);
18static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer);
19static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount);
20static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y);
21static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output);
22
23static void _expandColor(uint16_t c15, int8_t* r, int8_t* g, int8_t* b) {
24 *r = ((c15 << 1) & 0x3E) | 1;
25 *g = ((c15 >> 4) & 0x3E) | 1;
26 *b = ((c15 >> 9) & 0x3E) | 1;
27}
28
29static int _edgeSort(const void* a, const void* b) {
30 const struct DSGXSoftwareEdge* ea = a;
31 const struct DSGXSoftwareEdge* eb = b;
32
33 // Sort upside down
34 if (ea->y0 < eb->y0) {
35 return 1;
36 }
37 if (ea->y0 > eb->y0) {
38 return -1;
39 }
40 if (ea->y1 < eb->y1) {
41 return 1;
42 }
43 if (ea->y1 > eb->y1) {
44 return -1;
45 }
46 return 0;
47}
48
49static bool _edgeToSpan(struct DSGXSoftwareSpan* span, const struct DSGXSoftwareEdge* edge, int index, int32_t y) {
50 // TODO: Perspective correction
51 int32_t height = edge->y1 - edge->y0;
52 int64_t yw = (y << 12) - edge->y0;
53 if (height) {
54 yw <<= 19;
55 yw /= height;
56 } else {
57 return false;
58 }
59 // Clamp to bounds
60 if (yw < 0) {
61 yw = 0;
62 } else if (yw > height) {
63 yw = height;
64 }
65 if (!index) {
66 span->x0 = ((int64_t) (edge->x1 - edge->x0) * yw) / height + edge->x0;
67 span->w0 = ((int64_t) (edge->w1 - edge->w0) * yw) / height + edge->w0;
68 span->cr0 = ((int32_t) (edge->cr1 - edge->cr0) * yw) / height + edge->cr0;
69 span->cg0 = ((int32_t) (edge->cg1 - edge->cg0) * yw) / height + edge->cg0;
70 span->cb0 = ((int32_t) (edge->cb1 - edge->cb0) * yw) / height + edge->cb0;
71 span->s0 = ((int32_t) (edge->s1 - edge->s0) * yw) / height + edge->s0;
72 span->t0 = ((int32_t) (edge->t1 - edge->t0) * yw) / height + edge->t0;
73 } else {
74 span->x1 = ((int64_t) (edge->x1 - edge->x0) * yw) / height + edge->x0;
75 span->w1 = ((int64_t) (edge->w1 - edge->w0) * yw) / height + edge->w0;
76 span->cr1 = ((int32_t) (edge->cr1 - edge->cr0) * yw) / height + edge->cr0;
77 span->cg1 = ((int32_t) (edge->cg1 - edge->cg0) * yw) / height + edge->cg0;
78 span->cb1 = ((int32_t) (edge->cb1 - edge->cb0) * yw) / height + edge->cb0;
79 span->s1 = ((int32_t) (edge->s1 - edge->s0) * yw) / height + edge->s0;
80 span->t1 = ((int32_t) (edge->t1 - edge->t0) * yw) / height + edge->t0;
81 }
82 return true;
83}
84
85static int _spanSort(const void* a, const void* b) {
86 const struct DSGXSoftwareSpan* sa = a;
87 const struct DSGXSoftwareSpan* sb = b;
88
89 if (sa->x0 < sb->x0) {
90 return -1;
91 }
92 if (sa->x0 > sb->x0) {
93 return 1;
94 }
95 if (sa->x1 < sb->x1) {
96 return -1;
97 }
98 if (sa->x1 > sb->x1) {
99 return 1;
100 }
101 return 0;
102}
103
104void DSGXSoftwareRendererCreate(struct DSGXSoftwareRenderer* renderer) {
105 renderer->d.init = DSGXSoftwareRendererInit;
106 renderer->d.reset = DSGXSoftwareRendererReset;
107 renderer->d.deinit = DSGXSoftwareRendererDeinit;
108 renderer->d.setRAM = DSGXSoftwareRendererSetRAM;
109 renderer->d.drawScanline = DSGXSoftwareRendererDrawScanline;
110 renderer->d.getScanline = DSGXSoftwareRendererGetScanline;
111}
112
113static void DSGXSoftwareRendererInit(struct DSGXRenderer* renderer) {
114 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
115 DSGXSoftwarePolygonListInit(&softwareRenderer->activePolys, DS_GX_POLYGON_BUFFER_SIZE / 4);
116 DSGXSoftwareEdgeListInit(&softwareRenderer->activeEdges, DS_GX_POLYGON_BUFFER_SIZE);
117 DSGXSoftwareSpanListInit(&softwareRenderer->activeSpans, DS_GX_POLYGON_BUFFER_SIZE / 2);
118 TableInit(&softwareRenderer->bucket, DS_GX_POLYGON_BUFFER_SIZE / 8, NULL);
119 softwareRenderer->scanlineCache = anonymousMemoryMap(sizeof(color_t) * DS_VIDEO_HORIZONTAL_PIXELS * 48);
120}
121
122static void DSGXSoftwareRendererReset(struct DSGXRenderer* renderer) {
123 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
124 // TODO
125}
126
127static void DSGXSoftwareRendererDeinit(struct DSGXRenderer* renderer) {
128 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
129 DSGXSoftwarePolygonListDeinit(&softwareRenderer->activePolys);
130 DSGXSoftwareEdgeListDeinit(&softwareRenderer->activeEdges);
131 DSGXSoftwareSpanListDeinit(&softwareRenderer->activeSpans);
132 TableDeinit(&softwareRenderer->bucket);
133 mappedMemoryFree(softwareRenderer->scanlineCache, sizeof(color_t) * DS_VIDEO_HORIZONTAL_PIXELS * 48);
134}
135
136static void DSGXSoftwareRendererSetRAM(struct DSGXRenderer* renderer, struct DSGXVertex* verts, struct DSGXPolygon* polys, unsigned polyCount) {
137 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
138
139 softwareRenderer->verts = verts;
140 DSGXSoftwarePolygonListClear(&softwareRenderer->activePolys);
141 DSGXSoftwareEdgeListClear(&softwareRenderer->activeEdges);
142 unsigned i;
143 for (i = 0; i < polyCount; ++i) {
144 struct DSGXSoftwarePolygon* poly = DSGXSoftwarePolygonListAppend(&softwareRenderer->activePolys);
145 struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
146 poly->poly = &polys[i];
147 edge->polyId = i;
148
149 struct DSGXVertex* v0 = &verts[poly->poly->vertIds[0]];
150 struct DSGXVertex* v1;
151
152 int v;
153 for (v = 1; v < poly->poly->verts; ++v) {
154 v1 = &verts[poly->poly->vertIds[v]];
155 if (v0->vy >= v1->vy) {
156 edge->y0 = SCREEN_SIZE - v0->vy;
157 edge->x0 = v0->vx;
158 edge->w0 = v0->vw;
159 _expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
160 edge->s0 = v0->s;
161 edge->t0 = v0->t;
162
163 edge->y1 = SCREEN_SIZE - v1->vy;
164 edge->x1 = v1->vx;
165 edge->w1 = v1->vw;
166 _expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
167 edge->s1 = v1->s;
168 edge->t1 = v1->t;
169 } else {
170 edge->y0 = SCREEN_SIZE - v1->vy;
171 edge->x0 = v1->vx;
172 edge->w0 = v1->vw;
173 _expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
174 edge->s0 = v1->s;
175 edge->t0 = v1->t;
176
177 edge->y1 = SCREEN_SIZE - v0->vy;
178 edge->x1 = v0->vx;
179 edge->w1 = v0->vw;
180 _expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
181 edge->s1 = v0->s;
182 edge->t1 = v0->t;
183 }
184
185 edge = DSGXSoftwareEdgeListAppend(&softwareRenderer->activeEdges);
186 edge->polyId = i;
187 v0 = v1;
188 }
189
190 v1 = &verts[poly->poly->vertIds[0]];
191 if (v0->vy >= v1->vy) {
192 edge->y0 = SCREEN_SIZE - v0->vy;
193 edge->x0 = v0->vx;
194 edge->w0 = v0->vw;
195 _expandColor(v0->color, &edge->cr0, &edge->cg0, &edge->cb0);
196 edge->s0 = v0->s;
197 edge->t0 = v0->t;
198
199 edge->y1 = SCREEN_SIZE - v1->vy;
200 edge->x1 = v1->vx;
201 edge->w1 = v1->vw;
202 _expandColor(v1->color, &edge->cr1, &edge->cg1, &edge->cb1);
203 edge->s1 = v1->s;
204 edge->t1 = v1->t;
205 } else {
206 edge->y0 = SCREEN_SIZE - v1->vy;
207 edge->x0 = v1->vx;
208 edge->w0 = v1->vw;
209 _expandColor(v1->color, &edge->cr0, &edge->cg0, &edge->cb0);
210 edge->s0 = v1->s;
211 edge->t0 = v1->t;
212
213 edge->y1 = SCREEN_SIZE - v0->vy;
214 edge->x1 = v0->vx;
215 edge->w1 = v0->vw;
216 _expandColor(v0->color, &edge->cr1, &edge->cg1, &edge->cb1);
217 edge->s1 = v0->s;
218 edge->t1 = v0->t;
219 }
220 }
221 qsort(DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, 0), DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges), sizeof(struct DSGXSoftwareEdge), _edgeSort);
222}
223
224static void DSGXSoftwareRendererDrawScanline(struct DSGXRenderer* renderer, int y) {
225 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
226 DSGXSoftwareSpanListClear(&softwareRenderer->activeSpans);
227 TableClear(&softwareRenderer->bucket);
228 size_t i;
229 for (i = DSGXSoftwareEdgeListSize(&softwareRenderer->activeEdges); i; --i) {
230 size_t idx = i - 1;
231 struct DSGXSoftwareEdge* edge = DSGXSoftwareEdgeListGetPointer(&softwareRenderer->activeEdges, idx);
232 if (edge->y1 >> 12 < y) {
233 DSGXSoftwareEdgeListShift(&softwareRenderer->activeEdges, idx, 1);
234 continue;
235 } else if (edge->y0 >> 12 > y) {
236 continue;
237 }
238
239 unsigned poly = edge->polyId;
240 struct DSGXSoftwareSpan* span = TableLookup(&softwareRenderer->bucket, poly);
241 if (span) {
242 _edgeToSpan(span, edge, 1, y);
243 TableRemove(&softwareRenderer->bucket, poly);
244 } else {
245 span = DSGXSoftwareSpanListAppend(&softwareRenderer->activeSpans);
246 if (!_edgeToSpan(span, edge, 0, y)) {
247 // Horizontal line
248 DSGXSoftwareSpanListShift(&softwareRenderer->activeSpans, DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans) - 1, 1);
249 } else {
250 TableInsert(&softwareRenderer->bucket, poly, span);
251 }
252 }
253 }
254 qsort(DSGXSoftwareSpanListGetPointer(&softwareRenderer->activeSpans, 0), DSGXSoftwareSpanListSize(&softwareRenderer->activeSpans), sizeof(struct DSGXSoftwareSpan), _spanSort);
255}
256
257static void DSGXSoftwareRendererGetScanline(struct DSGXRenderer* renderer, int y, color_t** output) {
258 struct DSGXSoftwareRenderer* softwareRenderer = (struct DSGXSoftwareRenderer*) renderer;
259 y %= 48;
260 *output = &softwareRenderer->scanlineCache[DS_VIDEO_HORIZONTAL_PIXELS * y];
261}