src/ds/renderers/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/renderers/software.h>
7#include "gba/renderers/software-private.h"
8
9#include <mgba/internal/arm/macros.h>
10#include <mgba/internal/ds/io.h>
11
12static void DSVideoSoftwareRendererInit(struct DSVideoRenderer* renderer);
13static void DSVideoSoftwareRendererDeinit(struct DSVideoRenderer* renderer);
14static void DSVideoSoftwareRendererReset(struct DSVideoRenderer* renderer);
15static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
16static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
17static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam);
18static void DSVideoSoftwareRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot);
19static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y);
20static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer);
21static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels);
22static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels);
23
24static bool _regenerateExtPalette(struct DSVideoSoftwareRenderer* renderer, bool engB, int slot) {
25 color_t* palette;
26 color_t* variantPalette;
27 struct GBAVideoSoftwareRenderer* softwareRenderer;
28 uint16_t* vram;
29 if (!engB) {
30 palette = &renderer->extPaletteA[slot * 4096];
31 variantPalette = &renderer->variantPaletteA[slot * 4096];
32 softwareRenderer = &renderer->engA;
33 vram = renderer->d.vramABGExtPal[slot];
34 } else {
35 palette = &renderer->extPaletteB[slot * 4096];
36 variantPalette = &renderer->variantPaletteB[slot * 4096];
37 softwareRenderer = &renderer->engB;
38 vram = renderer->d.vramBBGExtPal[slot];
39 }
40 if (!vram) {
41 return false;
42 }
43 int i;
44 for (i = 0; i < 4096; ++i) {
45 uint16_t value = vram[i];
46#ifdef COLOR_16_BIT
47#ifdef COLOR_5_6_5
48 unsigned color = 0;
49 color |= (value & 0x001F) << 11;
50 color |= (value & 0x03E0) << 1;
51 color |= (value & 0x7C00) >> 10;
52#else
53 unsigned color = value;
54#endif
55#else
56 unsigned color = 0;
57 color |= (value << 3) & 0xF8;
58 color |= (value << 6) & 0xF800;
59 color |= (value << 9) & 0xF80000;
60 color |= (color >> 5) & 0x070707;
61#endif
62 palette[i] = color;
63 if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
64 variantPalette[i] = _brighten(color, softwareRenderer->bldy);
65 } else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
66 variantPalette[i] = _darken(color, softwareRenderer->bldy);
67 }
68 }
69 return true;
70}
71
72
73void DSVideoSoftwareRendererCreate(struct DSVideoSoftwareRenderer* renderer) {
74 renderer->d.init = DSVideoSoftwareRendererInit;
75 renderer->d.reset = DSVideoSoftwareRendererReset;
76 renderer->d.deinit = DSVideoSoftwareRendererDeinit;
77 renderer->d.writeVideoRegister = DSVideoSoftwareRendererWriteVideoRegister;
78 renderer->d.writePalette = DSVideoSoftwareRendererWritePalette;
79 renderer->d.writeOAM = DSVideoSoftwareRendererWriteOAM;
80 renderer->d.invalidateExtPal = DSVideoSoftwareRendererInvalidateExtPal;
81 renderer->d.drawScanline = DSVideoSoftwareRendererDrawScanline;
82 renderer->d.finishFrame = DSVideoSoftwareRendererFinishFrame;
83 renderer->d.getPixels = DSVideoSoftwareRendererGetPixels;
84 renderer->d.putPixels = DSVideoSoftwareRendererPutPixels;
85
86 renderer->engA.d.cache = NULL;
87 GBAVideoSoftwareRendererCreate(&renderer->engA);
88 renderer->engB.d.cache = NULL;
89 GBAVideoSoftwareRendererCreate(&renderer->engB);
90}
91
92static void DSVideoSoftwareRendererInit(struct DSVideoRenderer* renderer) {
93 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
94 softwareRenderer->engA.d.palette = &renderer->palette[0];
95 softwareRenderer->engA.d.oam = &renderer->oam->oam[0];
96 softwareRenderer->engA.masterEnd = DS_VIDEO_HORIZONTAL_PIXELS;
97 softwareRenderer->engA.outputBufferStride = softwareRenderer->outputBufferStride;
98 softwareRenderer->engB.d.palette = &renderer->palette[512];
99 softwareRenderer->engB.d.oam = &renderer->oam->oam[1];
100 softwareRenderer->engB.masterEnd = DS_VIDEO_HORIZONTAL_PIXELS;
101 softwareRenderer->engB.outputBufferStride = softwareRenderer->outputBufferStride;
102
103 DSVideoSoftwareRendererReset(renderer);
104}
105
106static void DSVideoSoftwareRendererReset(struct DSVideoRenderer* renderer) {
107 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
108 softwareRenderer->engA.d.reset(&softwareRenderer->engA.d);
109 softwareRenderer->engB.d.reset(&softwareRenderer->engB.d);
110 softwareRenderer->powcnt = 0;
111 softwareRenderer->dispcntA = 0;
112 softwareRenderer->dispcntB = 0;
113}
114
115static void DSVideoSoftwareRendererDeinit(struct DSVideoRenderer* renderer) {
116 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
117 softwareRenderer->engA.d.deinit(&softwareRenderer->engA.d);
118 softwareRenderer->engB.d.deinit(&softwareRenderer->engB.d);
119}
120
121static void DSVideoSoftwareRendererUpdateDISPCNT(struct DSVideoSoftwareRenderer* softwareRenderer, bool engB) {
122 uint32_t dispcnt;
123 struct GBAVideoSoftwareRenderer* eng;
124 if (!engB) {
125 dispcnt = softwareRenderer->dispcntA;
126 eng = &softwareRenderer->engA;
127 } else {
128 dispcnt = softwareRenderer->dispcntB;
129 eng = &softwareRenderer->engB;
130 }
131 uint16_t fakeDispcnt = dispcnt & 0xFF87;
132 if (!DSRegisterDISPCNTIsTileObjMapping(dispcnt)) {
133 eng->tileStride = 0x20;
134 } else {
135 eng->tileStride = 0x20 << DSRegisterDISPCNTGetTileBoundary(dispcnt);
136 fakeDispcnt = GBARegisterDISPCNTFillObjCharacterMapping(fakeDispcnt);
137 }
138 eng->d.writeVideoRegister(&eng->d, DS9_REG_A_DISPCNT_LO, fakeDispcnt);
139 eng->dispcnt |= dispcnt & 0xFFFF0000;
140 if (DSRegisterDISPCNTIsBgExtPalette(dispcnt)) {
141 color_t* extPalette;
142 if (!engB) {
143 extPalette = softwareRenderer->extPaletteA;
144 } else {
145 extPalette = softwareRenderer->extPaletteB;
146 }
147 int i;
148 for (i = 0; i < 4; ++i) {
149 int slot = i;
150 if (i < 2 && GBARegisterBGCNTIsExtPaletteSlot(eng->bg[i].control)) {
151 slot += 2;
152 }
153 if (eng->bg[i].extPalette != &extPalette[slot * 4096] && _regenerateExtPalette(softwareRenderer, engB, slot)) {
154 eng->bg[i].extPalette = &extPalette[slot * 4096];
155 }
156 }
157 } else {
158 eng->bg[0].extPalette = NULL;
159 eng->bg[1].extPalette = NULL;
160 eng->bg[2].extPalette = NULL;
161 eng->bg[3].extPalette = NULL;
162 }
163 if (!engB) {
164 uint32_t charBase = DSRegisterDISPCNTGetCharBase(softwareRenderer->dispcntA) << 16;
165 uint32_t screenBase = DSRegisterDISPCNTGetScreenBase(softwareRenderer->dispcntA) << 16;
166 softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, DS9_REG_A_BG0CNT, softwareRenderer->engA.bg[0].control);
167 softwareRenderer->engA.bg[0].charBase += charBase;
168 softwareRenderer->engA.bg[0].screenBase &= ~0x70000;
169 softwareRenderer->engA.bg[0].screenBase |= screenBase;
170 softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, DS9_REG_A_BG1CNT, softwareRenderer->engA.bg[1].control);
171 softwareRenderer->engA.bg[1].charBase += charBase;
172 softwareRenderer->engA.bg[1].screenBase &= ~0x70000;
173 softwareRenderer->engA.bg[1].screenBase |= screenBase;
174 softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, DS9_REG_A_BG2CNT, softwareRenderer->engA.bg[2].control);
175 softwareRenderer->engA.bg[2].charBase += charBase;
176 softwareRenderer->engA.bg[2].screenBase &= ~0x70000;
177 softwareRenderer->engA.bg[2].screenBase |= screenBase;
178 softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, DS9_REG_A_BG3CNT, softwareRenderer->engA.bg[3].control);
179 softwareRenderer->engA.bg[3].charBase += charBase;
180 softwareRenderer->engA.bg[3].screenBase &= ~0x70000;
181 softwareRenderer->engA.bg[3].screenBase |= screenBase;
182 }
183}
184
185static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
186 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
187 if (address >= DS9_REG_A_BG0CNT && address <= DS9_REG_A_BLDY) {
188 softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, address, value);
189 } else if (address >= DS9_REG_B_BG0CNT && address <= DS9_REG_B_BLDY) {
190 softwareRenderer->engB.d.writeVideoRegister(&softwareRenderer->engB.d, address & 0xFF, value);
191 } else {
192 mLOG(DS_VIDEO, STUB, "Stub video register write: %04X:%04X", address, value);
193 }
194 switch (address) {
195 case DS9_REG_A_BG0CNT:
196 case DS9_REG_A_BG1CNT:
197 softwareRenderer->engA.bg[(address - DS9_REG_A_BG0CNT) >> 1].control = value;
198 break;
199 case DS9_REG_B_BG0CNT:
200 case DS9_REG_B_BG1CNT:
201 softwareRenderer->engB.bg[(address - DS9_REG_A_BG0CNT) >> 1].control = value;
202 break;
203 case DS9_REG_A_DISPCNT_LO:
204 softwareRenderer->dispcntA &= 0xFFFF0000;
205 softwareRenderer->dispcntA |= value;
206 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, false);
207 break;
208 case DS9_REG_A_DISPCNT_HI:
209 softwareRenderer->dispcntA &= 0x0000FFFF;
210 softwareRenderer->dispcntA |= value << 16;
211 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, false);
212 break;
213 case DS9_REG_B_DISPCNT_LO:
214 softwareRenderer->dispcntB &= 0xFFFF0000;
215 softwareRenderer->dispcntB |= value;
216 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, true);
217 break;
218 case DS9_REG_B_DISPCNT_HI:
219 softwareRenderer->dispcntB &= 0x0000FFFF;
220 softwareRenderer->dispcntB |= value << 16;
221 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, true);
222 break;
223 case DS9_REG_POWCNT1:
224 value &= 0x810F;
225 softwareRenderer->powcnt = value;
226 }
227 return value;
228}
229
230static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
231 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
232 if (address < 0x400) {
233 softwareRenderer->engA.d.writePalette(&softwareRenderer->engA.d, address & 0x3FF, value);
234 } else {
235 softwareRenderer->engB.d.writePalette(&softwareRenderer->engB.d, address & 0x3FF, value);
236 }
237}
238
239static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam) {
240 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
241 if (oam < 0x200) {
242 softwareRenderer->engA.d.writeOAM(&softwareRenderer->engA.d, oam & 0x1FF);
243 } else {
244 softwareRenderer->engB.d.writeOAM(&softwareRenderer->engB.d, oam & 0x1FF);
245 }
246}
247
248static void DSVideoSoftwareRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot) {
249 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
250 _regenerateExtPalette(softwareRenderer, engB, slot);
251}
252
253static void DSVideoSoftwareRendererDrawGBAScanline(struct GBAVideoRenderer* renderer, int y) {
254 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
255
256 color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
257 if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
258 int x;
259 for (x = 0; x < softwareRenderer->masterEnd; ++x) {
260 row[x] = GBA_COLOR_WHITE;
261 }
262 return;
263 }
264
265 GBAVideoSoftwareRendererPreprocessBuffer(softwareRenderer, y);
266 int spriteLayers = GBAVideoSoftwareRendererPreprocessSpriteLayer(softwareRenderer, y);
267
268 int w;
269 unsigned priority;
270 for (priority = 0; priority < 4; ++priority) {
271 softwareRenderer->end = 0;
272 for (w = 0; w < softwareRenderer->nWindows; ++w) {
273 softwareRenderer->start = softwareRenderer->end;
274 softwareRenderer->end = softwareRenderer->windows[w].endX;
275 softwareRenderer->currentWindow = softwareRenderer->windows[w].control;
276 if (spriteLayers & (1 << priority)) {
277 GBAVideoSoftwareRendererPostprocessSprite(softwareRenderer, priority);
278 }
279 if (TEST_LAYER_ENABLED(0)) {
280 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[0], y);
281 }
282 if (TEST_LAYER_ENABLED(1)) {
283 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[1], y);
284 }
285 if (TEST_LAYER_ENABLED(2)) {
286 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
287 case 0:
288 case 1:
289 case 3:
290 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[2], y);
291 break;
292 case 2:
293 case 4:
294 GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[2], y);
295 break;
296 }
297 }
298 if (TEST_LAYER_ENABLED(3)) {
299 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
300 case 0:
301 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[3], y);
302 break;
303 case 1:
304 case 2:
305 GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[3], y);
306 break;
307 }
308 }
309 }
310 }
311 softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx;
312 softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy;
313 softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx;
314 softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy;
315
316 GBAVideoSoftwareRendererPostprocessBuffer(softwareRenderer);
317
318#ifdef COLOR_16_BIT
319#if defined(__ARM_NEON) && !defined(__APPLE__)
320 _to16Bit(row, softwareRenderer->row, softwareRenderer->masterEnd);
321#else
322 for (x = 0; x < softwareRenderer->masterEnd; ++x) {
323 row[x] = softwareRenderer->row[x];
324 }
325#endif
326#else
327 memcpy(row, softwareRenderer->row, softwareRenderer->masterEnd * sizeof(*row));
328#endif
329}
330
331static void _drawScanlineA(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
332 memcpy(softwareRenderer->engA.d.vramBG, softwareRenderer->d.vramABG, sizeof(softwareRenderer->engA.d.vramBG));
333 memcpy(softwareRenderer->engA.d.vramOBJ, softwareRenderer->d.vramAOBJ, sizeof(softwareRenderer->engA.d.vramOBJ));
334 color_t* row = &softwareRenderer->engA.outputBuffer[softwareRenderer->outputBufferStride * y];
335
336 int x;
337 switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntA)) {
338 case 0:
339 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
340 row[x] = GBA_COLOR_WHITE;
341 }
342 return;
343 case 1:
344 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engA.d, y);
345 return;
346 case 2: {
347 uint16_t* vram = &softwareRenderer->d.vram[0x10000 * DSRegisterDISPCNTGetVRAMBlock(softwareRenderer->dispcntA)];
348 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
349 color_t color;
350 LOAD_16(color, (x + y * DS_VIDEO_HORIZONTAL_PIXELS) * 2, vram);
351#ifndef COLOR_16_BIT
352 unsigned color32 = 0;
353 color32 |= (color << 9) & 0xF80000;
354 color32 |= (color << 3) & 0xF8;
355 color32 |= (color << 6) & 0xF800;
356 color32 |= (color32 >> 5) & 0x070707;
357 color = color32;
358#elif COLOR_5_6_5
359 uint16_t color16 = 0;
360 color16 |= (color & 0x001F) << 11;
361 color16 |= (color & 0x03E0) << 1;
362 color16 |= (color & 0x7C00) >> 10;
363 color = color16;
364#endif
365 softwareRenderer->row[x] = color;
366 }
367 break;
368 }
369 case 3:
370 break;
371 }
372
373#ifdef COLOR_16_BIT
374#if defined(__ARM_NEON) && !defined(__APPLE__)
375 _to16Bit(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS);
376#else
377 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
378 row[x] = softwareRenderer->row[x];
379 }
380#endif
381#else
382 memcpy(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
383#endif
384}
385
386static void _drawScanlineB(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
387 memcpy(softwareRenderer->engB.d.vramBG, softwareRenderer->d.vramBBG, sizeof(softwareRenderer->engB.d.vramBG));
388 memcpy(softwareRenderer->engB.d.vramOBJ, softwareRenderer->d.vramBOBJ, sizeof(softwareRenderer->engB.d.vramOBJ));
389 color_t* row = &softwareRenderer->engB.outputBuffer[softwareRenderer->outputBufferStride * y];
390
391 int x;
392 switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntB)) {
393 case 0:
394 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
395 row[x] = GBA_COLOR_WHITE;
396 }
397 return;
398 case 1:
399 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engB.d, y);
400 return;
401 }
402
403#ifdef COLOR_16_BIT
404#if defined(__ARM_NEON) && !defined(__APPLE__)
405 _to16Bit(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS);
406#else
407 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
408 row[x] = softwareRenderer->row[x];
409 }
410#endif
411#else
412 memcpy(row, softwareRenderer->row, DS_VIDEO_HORIZONTAL_PIXELS * sizeof(*row));
413#endif
414}
415
416static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y) {
417 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
418 if (!DSRegisterPOWCNT1IsSwap(softwareRenderer->powcnt)) {
419 softwareRenderer->engA.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
420 softwareRenderer->engB.outputBuffer = softwareRenderer->outputBuffer;
421 } else {
422 softwareRenderer->engA.outputBuffer = softwareRenderer->outputBuffer;
423 softwareRenderer->engB.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
424 }
425
426 _drawScanlineA(softwareRenderer, y);
427 _drawScanlineB(softwareRenderer, y);
428}
429
430static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer) {
431 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
432 softwareRenderer->engA.d.finishFrame(&softwareRenderer->engA.d);
433 softwareRenderer->engB.d.finishFrame(&softwareRenderer->engB.d);
434}
435
436static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels) {
437 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
438#ifdef COLOR_16_BIT
439#error Not yet supported
440#else
441 *stride = softwareRenderer->outputBufferStride;
442 *pixels = softwareRenderer->outputBuffer;
443#endif
444}
445
446static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels) {
447}