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/gx.h>
11#include <mgba/internal/ds/io.h>
12
13static void DSVideoSoftwareRendererInit(struct DSVideoRenderer* renderer);
14static void DSVideoSoftwareRendererDeinit(struct DSVideoRenderer* renderer);
15static void DSVideoSoftwareRendererReset(struct DSVideoRenderer* renderer);
16static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
17static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value);
18static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam);
19static void DSVideoSoftwareRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot);
20static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y);
21static void DSVideoSoftwareRendererDrawScanlineDirectly(struct DSVideoRenderer* renderer, int y, color_t* scanline);
22static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer);
23static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels);
24static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels);
25
26static void DSVideoSoftwareRendererDrawBackgroundExt0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY);
27static void DSVideoSoftwareRendererDrawBackgroundExt1(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY);
28static void DSVideoSoftwareRendererDrawBackgroundExt2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY);
29
30static bool _regenerateExtPalette(struct DSVideoSoftwareRenderer* renderer, bool obj, bool engB, int slot) {
31 color_t* palette;
32 color_t* variantPalette;
33 struct GBAVideoSoftwareRenderer* softwareRenderer;
34 uint16_t* vram;
35 if (!obj) {
36 if (!engB) {
37 palette = &renderer->extPaletteA[slot * 4096];
38 variantPalette = &renderer->variantPaletteA[slot * 4096];
39 softwareRenderer = &renderer->engA;
40 vram = renderer->d.vramABGExtPal[slot];
41 } else {
42 palette = &renderer->extPaletteB[slot * 4096];
43 variantPalette = &renderer->variantPaletteB[slot * 4096];
44 softwareRenderer = &renderer->engB;
45 vram = renderer->d.vramBBGExtPal[slot];
46 }
47 } else {
48 if (!engB) {
49 palette = renderer->objExtPaletteA;
50 variantPalette = renderer->variantPaletteA;
51 softwareRenderer = &renderer->engA;
52 vram = renderer->d.vramAOBJExtPal;
53 } else {
54 palette = renderer->objExtPaletteB;
55 variantPalette = renderer->variantPaletteB;
56 softwareRenderer = &renderer->engB;
57 vram = renderer->d.vramBOBJExtPal;
58 }
59 }
60 if (!vram) {
61 return false;
62 }
63 int i;
64 for (i = 0; i < 4096; ++i) {
65 uint16_t value = vram[i];
66#ifdef COLOR_16_BIT
67#ifdef COLOR_5_6_5
68 unsigned color = 0;
69 color |= (value & 0x001F) << 11;
70 color |= (value & 0x03E0) << 1;
71 color |= (value & 0x7C00) >> 10;
72#else
73 unsigned color = value;
74#endif
75#else
76 unsigned color = 0;
77 color |= (value << 3) & 0xF8;
78 color |= (value << 6) & 0xF800;
79 color |= (value << 9) & 0xF80000;
80 color |= (color >> 5) & 0x070707;
81#endif
82 palette[i] = color;
83 if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
84 variantPalette[i] = _brighten(color, softwareRenderer->bldy);
85 } else if (softwareRenderer->blendEffect == BLEND_DARKEN) {
86 variantPalette[i] = _darken(color, softwareRenderer->bldy);
87 }
88 }
89 if (obj) {
90 softwareRenderer->objExtPalette = palette;
91 softwareRenderer->objExtVariantPalette = variantPalette;
92 } else {
93 if (slot >= 2) {
94 if (GBARegisterBGCNTIsExtPaletteSlot(softwareRenderer->bg[slot - 2].control)) {
95 softwareRenderer->bg[slot - 2].extPalette = palette;
96 softwareRenderer->bg[slot - 2].variantPalette = variantPalette;
97 }
98 } else if (slot < 2 && !GBARegisterBGCNTIsExtPaletteSlot(softwareRenderer->bg[slot].control) ) {
99 softwareRenderer->bg[slot].extPalette = palette;
100 softwareRenderer->bg[slot].variantPalette = variantPalette;
101 }
102 softwareRenderer->bg[slot].extPalette = palette;
103 softwareRenderer->bg[slot].variantPalette = variantPalette;
104 }
105 return true;
106}
107
108static void _updateCharBase(struct DSVideoSoftwareRenderer* softwareRenderer, bool engB) {
109 struct GBAVideoSoftwareRenderer* eng;
110 if (!engB) {
111 eng = &softwareRenderer->engA;
112 } else {
113 eng = &softwareRenderer->engB;
114 }
115 int i;
116 uint32_t charBase = DSRegisterDISPCNTGetCharBase(softwareRenderer->dispcntA) << 16;
117 uint32_t screenBase = DSRegisterDISPCNTGetScreenBase(softwareRenderer->dispcntA) << 16;
118 for (i = 0; i < 4; ++i) {
119 if (!engB) {
120 uint32_t control = eng->bg[i].control;
121 eng->d.writeVideoRegister(&eng->d, DS9_REG_A_BG0CNT + i * 2, control);
122 eng->bg[i].control = control;
123 }
124
125 eng->bg[i].charBase = GBARegisterBGCNTGetCharBase(eng->bg[i].control) << 14;
126
127 if (!engB) {
128 softwareRenderer->engA.bg[i].charBase += charBase;
129 softwareRenderer->engA.bg[i].screenBase &= ~0x70000;
130 softwareRenderer->engA.bg[i].screenBase |= screenBase;
131 }
132 }
133}
134
135void DSVideoSoftwareRendererCreate(struct DSVideoSoftwareRenderer* renderer) {
136 renderer->d.init = DSVideoSoftwareRendererInit;
137 renderer->d.reset = DSVideoSoftwareRendererReset;
138 renderer->d.deinit = DSVideoSoftwareRendererDeinit;
139 renderer->d.writeVideoRegister = DSVideoSoftwareRendererWriteVideoRegister;
140 renderer->d.writePalette = DSVideoSoftwareRendererWritePalette;
141 renderer->d.writeOAM = DSVideoSoftwareRendererWriteOAM;
142 renderer->d.invalidateExtPal = DSVideoSoftwareRendererInvalidateExtPal;
143 renderer->d.drawScanline = DSVideoSoftwareRendererDrawScanline;
144 renderer->d.drawScanlineDirectly = DSVideoSoftwareRendererDrawScanlineDirectly;
145 renderer->d.finishFrame = DSVideoSoftwareRendererFinishFrame;
146 renderer->d.getPixels = DSVideoSoftwareRendererGetPixels;
147 renderer->d.putPixels = DSVideoSoftwareRendererPutPixels;
148
149 renderer->d.disableABG[0] = false;
150 renderer->d.disableABG[1] = false;
151 renderer->d.disableABG[2] = false;
152 renderer->d.disableABG[3] = false;
153 renderer->d.disableAOBJ = false;
154
155 renderer->d.disableBBG[0] = false;
156 renderer->d.disableBBG[1] = false;
157 renderer->d.disableBBG[2] = false;
158 renderer->d.disableBBG[3] = false;
159 renderer->d.disableBOBJ = false;
160
161 renderer->engA.d.cache = NULL;
162 GBAVideoSoftwareRendererCreate(&renderer->engA);
163 renderer->engA.combinedObjSort = true;
164
165 renderer->engB.d.cache = NULL;
166 GBAVideoSoftwareRendererCreate(&renderer->engB);
167 renderer->engB.combinedObjSort = true;
168}
169
170static void DSVideoSoftwareRendererInit(struct DSVideoRenderer* renderer) {
171 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
172 softwareRenderer->engA.d.palette = &renderer->palette[0];
173 softwareRenderer->engA.d.oam = &renderer->oam->oam[0];
174 softwareRenderer->engA.masterEnd = DS_VIDEO_HORIZONTAL_PIXELS;
175 softwareRenderer->engA.masterHeight = DS_VIDEO_VERTICAL_PIXELS;
176 softwareRenderer->engA.masterScanlines = DS_VIDEO_VERTICAL_TOTAL_PIXELS;
177 softwareRenderer->engA.outputBufferStride = softwareRenderer->outputBufferStride;
178 softwareRenderer->engB.d.palette = &renderer->palette[512];
179 softwareRenderer->engB.d.oam = &renderer->oam->oam[1];
180 softwareRenderer->engB.masterEnd = DS_VIDEO_HORIZONTAL_PIXELS;
181 softwareRenderer->engB.masterHeight = DS_VIDEO_VERTICAL_PIXELS;
182 softwareRenderer->engB.masterScanlines = DS_VIDEO_VERTICAL_TOTAL_PIXELS;
183 softwareRenderer->engB.outputBufferStride = softwareRenderer->outputBufferStride;
184
185 DSVideoSoftwareRendererReset(renderer);
186}
187
188static void DSVideoSoftwareRendererReset(struct DSVideoRenderer* renderer) {
189 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
190 softwareRenderer->engA.d.reset(&softwareRenderer->engA.d);
191 softwareRenderer->engB.d.reset(&softwareRenderer->engB.d);
192 softwareRenderer->powcnt = 0;
193 softwareRenderer->dispcntA = 0;
194 softwareRenderer->dispcntB = 0;
195}
196
197static void DSVideoSoftwareRendererDeinit(struct DSVideoRenderer* renderer) {
198 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
199 softwareRenderer->engA.d.deinit(&softwareRenderer->engA.d);
200 softwareRenderer->engB.d.deinit(&softwareRenderer->engB.d);
201}
202
203static void DSVideoSoftwareRendererUpdateDISPCNT(struct DSVideoSoftwareRenderer* softwareRenderer, bool engB) {
204 uint32_t dispcnt;
205 struct GBAVideoSoftwareRenderer* eng;
206 if (!engB) {
207 dispcnt = softwareRenderer->dispcntA;
208 eng = &softwareRenderer->engA;
209 } else {
210 dispcnt = softwareRenderer->dispcntB;
211 eng = &softwareRenderer->engB;
212 }
213 uint16_t fakeDispcnt = dispcnt & 0xFF87;
214 if (!DSRegisterDISPCNTIsTileObjMapping(dispcnt)) {
215 eng->tileStride = 0x20;
216 } else {
217 eng->tileStride = 0x20 << DSRegisterDISPCNTGetTileBoundary(dispcnt);
218 fakeDispcnt = GBARegisterDISPCNTFillObjCharacterMapping(fakeDispcnt);
219 }
220 eng->d.writeVideoRegister(&eng->d, DS9_REG_A_DISPCNT_LO, fakeDispcnt);
221 eng->dispcnt |= dispcnt & 0xFFFF0000;
222 if (DSRegisterDISPCNTIsBgExtPalette(dispcnt)) {
223 color_t* extPalette;
224 if (!engB) {
225 extPalette = softwareRenderer->extPaletteA;
226 } else {
227 extPalette = softwareRenderer->extPaletteB;
228 }
229 int i;
230 for (i = 0; i < 4; ++i) {
231 int slot = i;
232 if (i < 2 && GBARegisterBGCNTIsExtPaletteSlot(eng->bg[i].control)) {
233 slot += 2;
234 }
235 if (eng->bg[i].extPalette != &extPalette[slot * 4096]) {
236 _regenerateExtPalette(softwareRenderer, false, engB, slot);
237 }
238 }
239 } else {
240 eng->bg[0].extPalette = NULL;
241 eng->bg[1].extPalette = NULL;
242 eng->bg[2].extPalette = NULL;
243 eng->bg[3].extPalette = NULL;
244 }
245 if (DSRegisterDISPCNTIsObjExtPalette(dispcnt)) {
246 if (!engB) {
247 if (softwareRenderer->engA.objExtPalette != softwareRenderer->objExtPaletteA) {
248 _regenerateExtPalette(softwareRenderer, true, engB, 0);
249 }
250 } else {
251 if (softwareRenderer->engB.objExtPalette != softwareRenderer->objExtPaletteB) {
252 _regenerateExtPalette(softwareRenderer, true, engB, 0);
253 }
254 }
255 } else {
256 if (!engB) {
257 softwareRenderer->engA.objExtPalette = NULL;
258 } else {
259 softwareRenderer->engB.objExtPalette = NULL;
260 }
261 }
262 if (!engB) {
263 eng->dispcnt = DSRegisterDISPCNTClear3D(eng->dispcnt);
264 eng->dispcnt |= DSRegisterDISPCNTIs3D(dispcnt);
265 _updateCharBase(softwareRenderer, engB);
266 }
267}
268
269static uint16_t DSVideoSoftwareRendererWriteVideoRegister(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
270 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
271 if (address >= DS9_REG_A_BG0CNT && address <= DS9_REG_A_BLDY) {
272 softwareRenderer->engA.d.writeVideoRegister(&softwareRenderer->engA.d, address, value);
273 } else if (address >= DS9_REG_B_BG0CNT && address <= DS9_REG_B_BLDY) {
274 softwareRenderer->engB.d.writeVideoRegister(&softwareRenderer->engB.d, address & 0xFF, value);
275 } else {
276 mLOG(DS_VIDEO, STUB, "Stub video register write: %04X:%04X", address, value);
277 }
278 switch (address) {
279 case DS9_REG_A_BG0CNT:
280 case DS9_REG_A_BG1CNT:
281 softwareRenderer->engA.bg[(address - DS9_REG_A_BG0CNT) >> 1].control = value;
282 // Fall through
283 case DS9_REG_A_BG2CNT:
284 case DS9_REG_A_BG3CNT:
285 _updateCharBase(softwareRenderer, false);
286 break;
287 case DS9_REG_B_BG0CNT:
288 case DS9_REG_B_BG1CNT:
289 softwareRenderer->engB.bg[(address - DS9_REG_B_BG0CNT) >> 1].control = value;
290 // Fall through
291 case DS9_REG_B_BG2CNT:
292 case DS9_REG_B_BG3CNT:
293 _updateCharBase(softwareRenderer, true);
294 break;
295 case DS9_REG_A_MASTER_BRIGHT:
296 softwareRenderer->engA.masterBright = DSRegisterMASTER_BRIGHTGetMode(value);
297 softwareRenderer->engA.masterBrightY = DSRegisterMASTER_BRIGHTGetY(value);
298 if (softwareRenderer->engA.masterBrightY > 0x10) {
299 softwareRenderer->engA.masterBrightY = 0x10;
300 }
301 break;
302 case DS9_REG_B_MASTER_BRIGHT:
303 softwareRenderer->engB.masterBright = DSRegisterMASTER_BRIGHTGetMode(value);
304 softwareRenderer->engB.masterBrightY = DSRegisterMASTER_BRIGHTGetY(value);
305 if (softwareRenderer->engB.masterBrightY > 0x10) {
306 softwareRenderer->engB.masterBrightY = 0x10;
307 }
308 break;
309 case DS9_REG_A_BLDCNT:
310 case DS9_REG_A_BLDY:
311 // TODO: Optimize
312 _regenerateExtPalette(softwareRenderer, false, false, 0);
313 _regenerateExtPalette(softwareRenderer, false, false, 1);
314 _regenerateExtPalette(softwareRenderer, false, false, 2);
315 _regenerateExtPalette(softwareRenderer, false, false, 3);
316 _regenerateExtPalette(softwareRenderer, true, false, 0);
317 break;
318 case DS9_REG_B_BLDCNT:
319 case DS9_REG_B_BLDY:
320 // TODO: Optimize
321 _regenerateExtPalette(softwareRenderer, false, true, 0);
322 _regenerateExtPalette(softwareRenderer, false, true, 1);
323 _regenerateExtPalette(softwareRenderer, false, true, 2);
324 _regenerateExtPalette(softwareRenderer, false, true, 3);
325 _regenerateExtPalette(softwareRenderer, true, true, 0);
326 break;
327 case DS9_REG_A_DISPCNT_LO:
328 softwareRenderer->dispcntA &= 0xFFFF0000;
329 softwareRenderer->dispcntA |= value;
330 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, false);
331 break;
332 case DS9_REG_A_DISPCNT_HI:
333 softwareRenderer->dispcntA &= 0x0000FFFF;
334 softwareRenderer->dispcntA |= value << 16;
335 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, false);
336 break;
337 case DS9_REG_B_DISPCNT_LO:
338 softwareRenderer->dispcntB &= 0xFFFF0000;
339 softwareRenderer->dispcntB |= value;
340 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, true);
341 break;
342 case DS9_REG_B_DISPCNT_HI:
343 softwareRenderer->dispcntB &= 0x0000FFFF;
344 softwareRenderer->dispcntB |= value << 16;
345 DSVideoSoftwareRendererUpdateDISPCNT(softwareRenderer, true);
346 break;
347 case DS9_REG_POWCNT1:
348 value &= 0x810F;
349 softwareRenderer->powcnt = value;
350 }
351 return value;
352}
353
354static void DSVideoSoftwareRendererWritePalette(struct DSVideoRenderer* renderer, uint32_t address, uint16_t value) {
355 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
356 if (address < 0x400) {
357 softwareRenderer->engA.d.writePalette(&softwareRenderer->engA.d, address & 0x3FF, value);
358 } else {
359 softwareRenderer->engB.d.writePalette(&softwareRenderer->engB.d, address & 0x3FF, value);
360 }
361}
362
363static void DSVideoSoftwareRendererWriteOAM(struct DSVideoRenderer* renderer, uint32_t oam) {
364 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
365 if (oam < 0x200) {
366 softwareRenderer->engA.d.writeOAM(&softwareRenderer->engA.d, oam & 0x1FF);
367 } else {
368 softwareRenderer->engB.d.writeOAM(&softwareRenderer->engB.d, oam & 0x1FF);
369 }
370}
371
372static void DSVideoSoftwareRendererInvalidateExtPal(struct DSVideoRenderer* renderer, bool obj, bool engB, int slot) {
373 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
374 _regenerateExtPalette(softwareRenderer, obj, engB, slot);
375}
376
377static void DSVideoSoftwareRendererDrawGBAScanline(struct GBAVideoRenderer* renderer, struct DSGX* gx, int y) {
378 struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer;
379
380 int x;
381 color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
382 if (GBARegisterDISPCNTIsForcedBlank(softwareRenderer->dispcnt)) {
383 for (x = 0; x < softwareRenderer->masterEnd; ++x) {
384 row[x] = GBA_COLOR_WHITE;
385 }
386 return;
387 }
388
389 GBAVideoSoftwareRendererPreprocessBuffer(softwareRenderer, y);
390 int spriteLayers = GBAVideoSoftwareRendererPreprocessSpriteLayer(softwareRenderer, y);
391 memset(softwareRenderer->alphaA, softwareRenderer->blda, sizeof(softwareRenderer->alphaA));
392 memset(softwareRenderer->alphaB, softwareRenderer->bldb, sizeof(softwareRenderer->alphaB));
393
394 softwareRenderer->bg[2].sx = softwareRenderer->bg[2].refx + softwareRenderer->bg[2].dmx * y;
395 softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy + softwareRenderer->bg[2].dmy * y;
396 softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx + softwareRenderer->bg[3].dmx * y;
397 softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy + softwareRenderer->bg[3].dmy * y;
398
399 int w;
400 unsigned priority;
401 for (priority = 0; priority < 4; ++priority) {
402 softwareRenderer->end = 0;
403 for (w = 0; w < softwareRenderer->nWindows; ++w) {
404 softwareRenderer->start = softwareRenderer->end;
405 softwareRenderer->end = softwareRenderer->windows[w].endX;
406 softwareRenderer->currentWindow = softwareRenderer->windows[w].control;
407 if (spriteLayers & (1 << priority)) {
408 GBAVideoSoftwareRendererPostprocessSprite(softwareRenderer, priority);
409 }
410 if (TEST_LAYER_ENABLED(0)) {
411 if (DSRegisterDISPCNTIs3D(softwareRenderer->dispcnt) && gx) {
412 const color_t* scanline;
413 gx->renderer->getScanline(gx->renderer, y, &scanline);
414 uint32_t flags = (softwareRenderer->bg[0].priority << OFFSET_PRIORITY) | FLAG_IS_BACKGROUND;
415 flags |= FLAG_TARGET_2 * softwareRenderer->bg[0].target2;
416 flags |= FLAG_TARGET_1 * (softwareRenderer->bg[0].target1 && softwareRenderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(softwareRenderer->currentWindow.packed));
417 int x;
418 for (x = softwareRenderer->start; x < softwareRenderer->end; ++x) {
419 color_t color = scanline[x];
420 if (color & 0xFC000000) {
421 if (softwareRenderer->bg[0].target1 && GBAWindowControlIsBlendEnable(softwareRenderer->currentWindow.packed)) {
422 if (softwareRenderer->blendEffect == BLEND_DARKEN) {
423 color = _darken(color, softwareRenderer->bldy) | (color & 0xFF000000);
424 } else if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) {
425 color = _brighten(color, softwareRenderer->bldy) | (color & 0xFF000000);
426 }
427 }
428 if ((scanline[x] >> 28) != 0xF) {
429 // TODO: More precise values
430 softwareRenderer->alphaA[x] = (color >> 28) + 1;
431 softwareRenderer->alphaB[x] = 0xF - (color >> 28);
432 _compositeBlendNoObjwin(softwareRenderer, x, (color & 0x00FFFFFF) | flags | FLAG_TARGET_1, softwareRenderer->row[x]);
433 } else {
434 if (!(flags & FLAG_TARGET_2) || !(softwareRenderer->row[x] & FLAG_TARGET_1)) {
435 _compositeNoBlendNoObjwin(softwareRenderer, x, (color & 0x00FFFFFF) | flags, softwareRenderer->row[x]);
436 softwareRenderer->alphaA[x] = 0x10;
437 softwareRenderer->alphaB[x] = 0;
438 } else if (softwareRenderer->row[x] & FLAG_TARGET_1) {
439 _compositeBlendNoObjwin(softwareRenderer, x, (color & 0x00FFFFFF) | flags, softwareRenderer->row[x]);
440 }
441 }
442 }
443 }
444 } else {
445 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[0], y);
446 }
447 }
448 if (TEST_LAYER_ENABLED(1)) {
449 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[1], y);
450 }
451 if (TEST_LAYER_ENABLED(2)) {
452 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
453 case 0:
454 case 1:
455 case 3:
456 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[2], y);
457 break;
458 case 2:
459 case 4:
460 GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[2], y);
461 break;
462 case 5:
463 if (!GBARegisterBGCNTIsExtendedMode1(softwareRenderer->bg[2].control)) {
464 DSVideoSoftwareRendererDrawBackgroundExt0(softwareRenderer, &softwareRenderer->bg[2], y);
465 } else if (!GBARegisterBGCNTIsExtendedMode0(softwareRenderer->bg[2].control)) {
466 DSVideoSoftwareRendererDrawBackgroundExt1(softwareRenderer, &softwareRenderer->bg[2], y);
467 } else {
468 DSVideoSoftwareRendererDrawBackgroundExt2(softwareRenderer, &softwareRenderer->bg[2], y);
469 }
470 break;
471 }
472 }
473 if (TEST_LAYER_ENABLED(3)) {
474 switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) {
475 case 0:
476 GBAVideoSoftwareRendererDrawBackgroundMode0(softwareRenderer, &softwareRenderer->bg[3], y);
477 break;
478 case 1:
479 case 2:
480 GBAVideoSoftwareRendererDrawBackgroundMode2(softwareRenderer, &softwareRenderer->bg[3], y);
481 break;
482 case 3:
483 case 4:
484 case 5:
485 if (!GBARegisterBGCNTIsExtendedMode1(softwareRenderer->bg[3].control)) {
486 DSVideoSoftwareRendererDrawBackgroundExt0(softwareRenderer, &softwareRenderer->bg[3], y);
487 } else if (!GBARegisterBGCNTIsExtendedMode0(softwareRenderer->bg[3].control)) {
488 DSVideoSoftwareRendererDrawBackgroundExt1(softwareRenderer, &softwareRenderer->bg[3], y);
489 } else {
490 DSVideoSoftwareRendererDrawBackgroundExt2(softwareRenderer, &softwareRenderer->bg[3], y);
491 }
492 break;
493 }
494 }
495 }
496 }
497
498 GBAVideoSoftwareRendererPostprocessBuffer(softwareRenderer);
499}
500
501static void _drawScanlineA(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
502 memcpy(softwareRenderer->engA.d.vramBG, softwareRenderer->d.vramABG, sizeof(softwareRenderer->engA.d.vramBG));
503 memcpy(softwareRenderer->engA.d.vramOBJ, softwareRenderer->d.vramAOBJ, sizeof(softwareRenderer->engA.d.vramOBJ));
504 color_t* row = &softwareRenderer->engA.outputBuffer[softwareRenderer->outputBufferStride * y];
505
506 int x;
507 switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntA)) {
508 case 0:
509 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
510 row[x] = GBA_COLOR_WHITE;
511 }
512 return;
513 case 1:
514 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engA.d, softwareRenderer->d.gx, y);
515 break;
516 case 2: {
517 uint16_t* vram = &softwareRenderer->d.vram[0x10000 * DSRegisterDISPCNTGetVRAMBlock(softwareRenderer->dispcntA)];
518 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
519 color_t color;
520 LOAD_16(color, (x + y * DS_VIDEO_HORIZONTAL_PIXELS) * 2, vram);
521#ifndef COLOR_16_BIT
522 unsigned color32 = 0;
523 color32 |= (color << 9) & 0xF80000;
524 color32 |= (color << 3) & 0xF8;
525 color32 |= (color << 6) & 0xF800;
526 color32 |= (color32 >> 5) & 0x070707;
527 color = color32;
528#elif COLOR_5_6_5
529 uint16_t color16 = 0;
530 color16 |= (color & 0x001F) << 11;
531 color16 |= (color & 0x03E0) << 1;
532 color16 |= (color & 0x7C00) >> 10;
533 color = color16;
534#endif
535 softwareRenderer->engA.row[x] = color;
536 }
537 break;
538 }
539 case 3:
540 break;
541 }
542
543#ifdef COLOR_16_BIT
544#if defined(__ARM_NEON) && !defined(__APPLE__)
545 _to16Bit(row, softwareRenderer->engA.row, DS_VIDEO_HORIZONTAL_PIXELS);
546#else
547 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
548 row[x] = softwareRenderer->engA.row[x];
549 }
550#endif
551#else
552 switch (softwareRenderer->engA.masterBright) {
553 case 0:
554 default:
555 memcpy(row, softwareRenderer->engA.row, softwareRenderer->engA.masterEnd * sizeof(*row));
556 break;
557 case 1:
558 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
559 row[x] = _brighten(softwareRenderer->engA.row[x], softwareRenderer->engA.masterBrightY);
560 }
561 break;
562 case 2:
563 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
564 row[x] = _darken(softwareRenderer->engA.row[x], softwareRenderer->engA.masterBrightY);
565 }
566 break;
567 }
568#endif
569}
570
571static void _drawScanlineB(struct DSVideoSoftwareRenderer* softwareRenderer, int y) {
572 memcpy(softwareRenderer->engB.d.vramBG, softwareRenderer->d.vramBBG, sizeof(softwareRenderer->engB.d.vramBG));
573 memcpy(softwareRenderer->engB.d.vramOBJ, softwareRenderer->d.vramBOBJ, sizeof(softwareRenderer->engB.d.vramOBJ));
574 color_t* row = &softwareRenderer->engB.outputBuffer[softwareRenderer->outputBufferStride * y];
575
576 int x;
577 switch (DSRegisterDISPCNTGetDispMode(softwareRenderer->dispcntB)) {
578 case 0:
579 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
580 row[x] = GBA_COLOR_WHITE;
581 }
582 return;
583 case 1:
584 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engB.d, NULL, y);
585 break;
586 }
587
588#ifdef COLOR_16_BIT
589#if defined(__ARM_NEON) && !defined(__APPLE__)
590 _to16Bit(row, softwareRenderer->engB.row, DS_VIDEO_HORIZONTAL_PIXELS);
591#else
592 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
593 row[x] = softwareRenderer->engB.row[x];
594 }
595#endif
596#else
597 switch (softwareRenderer->engB.masterBright) {
598 case 0:
599 default:
600 memcpy(row, softwareRenderer->engB.row, softwareRenderer->engB.masterEnd * sizeof(*row));
601 break;
602 case 1:
603 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
604 row[x] = _brighten(softwareRenderer->engB.row[x], softwareRenderer->engB.masterBrightY);
605 }
606 break;
607 case 2:
608 for (x = 0; x < DS_VIDEO_HORIZONTAL_PIXELS; ++x) {
609 row[x] = _darken(softwareRenderer->engB.row[x], softwareRenderer->engB.masterBrightY);
610 }
611 break;
612 }
613#endif
614}
615
616static void DSVideoSoftwareRendererDrawScanline(struct DSVideoRenderer* renderer, int y) {
617 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
618 if (!DSRegisterPOWCNT1IsSwap(softwareRenderer->powcnt)) {
619 softwareRenderer->engA.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
620 softwareRenderer->engB.outputBuffer = softwareRenderer->outputBuffer;
621 } else {
622 softwareRenderer->engA.outputBuffer = softwareRenderer->outputBuffer;
623 softwareRenderer->engB.outputBuffer = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * DS_VIDEO_VERTICAL_PIXELS];
624 }
625
626 softwareRenderer->engA.d.disableBG[0] = softwareRenderer->d.disableABG[0];
627 softwareRenderer->engA.d.disableBG[1] = softwareRenderer->d.disableABG[1];
628 softwareRenderer->engA.d.disableBG[2] = softwareRenderer->d.disableABG[2];
629 softwareRenderer->engA.d.disableBG[3] = softwareRenderer->d.disableABG[3];
630 softwareRenderer->engA.d.disableOBJ = softwareRenderer->d.disableAOBJ;
631
632 softwareRenderer->engB.d.disableBG[0] = softwareRenderer->d.disableBBG[0];
633 softwareRenderer->engB.d.disableBG[1] = softwareRenderer->d.disableBBG[1];
634 softwareRenderer->engB.d.disableBG[2] = softwareRenderer->d.disableBBG[2];
635 softwareRenderer->engB.d.disableBG[3] = softwareRenderer->d.disableBBG[3];
636 softwareRenderer->engB.d.disableOBJ = softwareRenderer->d.disableBOBJ;
637
638 _drawScanlineA(softwareRenderer, y);
639 _drawScanlineB(softwareRenderer, y);
640}
641
642static void DSVideoSoftwareRendererDrawScanlineDirectly(struct DSVideoRenderer* renderer, int y, color_t* scanline) {
643 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
644 DSVideoSoftwareRendererDrawGBAScanline(&softwareRenderer->engA.d, softwareRenderer->d.gx, y);
645 memcpy(scanline, softwareRenderer->engA.row, softwareRenderer->engA.masterEnd * sizeof(*scanline));
646}
647
648static void DSVideoSoftwareRendererFinishFrame(struct DSVideoRenderer* renderer) {
649 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
650 softwareRenderer->engA.d.finishFrame(&softwareRenderer->engA.d);
651 softwareRenderer->engB.d.finishFrame(&softwareRenderer->engB.d);
652}
653
654static void DSVideoSoftwareRendererGetPixels(struct DSVideoRenderer* renderer, size_t* stride, const void** pixels) {
655 struct DSVideoSoftwareRenderer* softwareRenderer = (struct DSVideoSoftwareRenderer*) renderer;
656#ifdef COLOR_16_BIT
657#error Not yet supported
658#else
659 *stride = softwareRenderer->outputBufferStride;
660 *pixels = softwareRenderer->outputBuffer;
661#endif
662}
663
664static void DSVideoSoftwareRendererPutPixels(struct DSVideoRenderer* renderer, size_t stride, const void* pixels) {
665}
666
667#define EXT_0_COORD_OVERFLOW \
668 localX = x & (sizeAdjusted - 1); \
669 localY = y & (sizeAdjusted - 1); \
670
671#define EXT_0_COORD_NO_OVERFLOW \
672 if ((x | y) & ~(sizeAdjusted - 1)) { \
673 continue; \
674 } \
675 localX = x; \
676 localY = y;
677
678#define EXT_0_EXT_PAL \
679 paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \
680 palette = &mainPalette[paletteData];
681
682#define EXT_0_PAL palette = mainPalette;
683
684#define EXT_0_NO_MOSAIC(COORD, PAL) \
685 COORD \
686 uint32_t screenBase = background->screenBase + (localX >> 10) + (((localY >> 6) & 0xFE0) << background->size); \
687 uint16_t* screenBlock = renderer->d.vramBG[screenBase >> VRAM_BLOCK_OFFSET]; \
688 if (UNLIKELY(!screenBlock)) { \
689 continue; \
690 } \
691 LOAD_16(mapData, screenBase & (VRAM_BLOCK_MASK - 1), screenBlock); \
692 PAL; \
693 if (GBA_TEXT_MAP_VFLIP(mapData)) { \
694 localY = 0x7FF - localY; \
695 } \
696 if (GBA_TEXT_MAP_HFLIP(mapData)) { \
697 localX = 0x7FF - localX; \
698 } \
699 uint32_t charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + ((localY & 0x700) >> 5) + ((localX & 0x700) >> 8); \
700 uint16_t* vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \
701 if (UNLIKELY(!vram)) { \
702 continue; \
703 } \
704 pixelData = ((uint8_t*) vram)[charBase & VRAM_BLOCK_MASK];
705
706#define EXT_0_MOSAIC(COORD, PAL) \
707 if (!mosaicWait) { \
708 EXT_0_NO_MOSAIC(COORD, PAL) \
709 mosaicWait = mosaicH; \
710 } else { \
711 --mosaicWait; \
712 }
713
714#define EXT_0_LOOP(MOSAIC, COORD, PAL, BLEND, OBJWIN) \
715 for (outX = renderer->start, pixel = &renderer->row[outX]; outX < renderer->end; ++outX, ++pixel) { \
716 x += background->dx; \
717 y += background->dy; \
718 \
719 uint32_t current = *pixel; \
720 MOSAIC(COORD, PAL) \
721 if (pixelData) { \
722 COMPOSITE_256_ ## OBJWIN (BLEND, 0); \
723 } \
724 }
725
726#define DRAW_BACKGROUND_EXT_0(BLEND, OBJWIN) \
727 if (background->extPalette) { \
728 if (background->overflow) { \
729 if (mosaicH > 1) { \
730 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_OVERFLOW, EXT_0_EXT_PAL, BLEND, OBJWIN); \
731 } else { \
732 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_OVERFLOW, EXT_0_EXT_PAL, BLEND, OBJWIN); \
733 } \
734 } else { \
735 if (mosaicH > 1) { \
736 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_NO_OVERFLOW, EXT_0_EXT_PAL, BLEND, OBJWIN); \
737 } else { \
738 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_NO_OVERFLOW, EXT_0_EXT_PAL, BLEND, OBJWIN); \
739 } \
740 } \
741 } else { \
742 if (background->overflow) { \
743 if (mosaicH > 1) { \
744 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_OVERFLOW, EXT_0_PAL, BLEND, OBJWIN); \
745 } else { \
746 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_OVERFLOW, EXT_0_PAL, BLEND, OBJWIN); \
747 } \
748 } else { \
749 if (mosaicH > 1) { \
750 EXT_0_LOOP(EXT_0_MOSAIC, EXT_0_COORD_NO_OVERFLOW, EXT_0_PAL, BLEND, OBJWIN); \
751 } else { \
752 EXT_0_LOOP(EXT_0_NO_MOSAIC, EXT_0_COORD_NO_OVERFLOW, EXT_0_PAL, BLEND, OBJWIN); \
753 } \
754 } \
755 }
756
757void DSVideoSoftwareRendererDrawBackgroundExt0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
758 int sizeAdjusted = 0x8000 << background->size;
759
760 BACKGROUND_BITMAP_INIT;
761
762 color_t* mainPalette = background->extPalette;
763 if (variant) {
764 mainPalette = background->variantPalette;
765 }
766 if (!mainPalette) {
767 mainPalette = renderer->normalPalette;
768 if (variant) {
769 mainPalette = renderer->variantPalette;
770 }
771 }
772 int paletteData;
773
774 uint16_t mapData;
775 uint8_t pixelData = 0;
776
777 int outX;
778 uint32_t* pixel;
779
780 if (!objwinSlowPath) {
781 if (!(flags & FLAG_TARGET_2)) {
782 DRAW_BACKGROUND_EXT_0(NoBlend, NO_OBJWIN);
783 } else {
784 DRAW_BACKGROUND_EXT_0(Blend, NO_OBJWIN);
785 }
786 } else {
787 if (!(flags & FLAG_TARGET_2)) {
788 DRAW_BACKGROUND_EXT_0(NoBlend, OBJWIN);
789 } else {
790 DRAW_BACKGROUND_EXT_0(Blend, OBJWIN);
791 }
792 }
793}
794
795#define DS_BACKGROUND_BITMAP_ITERATE(W, H) \
796 x += background->dx; \
797 y += background->dy; \
798 \
799 if (background->overflow) { \
800 localX = x & ((W << 8) - 1); \
801 localY = y & ((H << 8) - 1); \
802 } else if (x < 0 || y < 0 || (x >> 8) >= W || (y >> 8) >= H) { \
803 continue; \
804 } else { \
805 localX = x; \
806 localY = y; \
807 }
808
809void DSVideoSoftwareRendererDrawBackgroundExt1(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
810 BACKGROUND_BITMAP_INIT;
811
812 uint32_t screenBase = (background->screenBase & 0xFF00) * 8;
813 uint8_t color;
814 int width, height;
815 switch (background->size) {
816 case 0:
817 width = 128;
818 height = 128;
819 break;
820 case 1:
821 width = 256;
822 height = 256;
823 break;
824 case 2:
825 width = 512;
826 height = 256;
827 break;
828 case 3:
829 width = 512;
830 height = 512;
831 break;
832 }
833
834 int outX;
835 for (outX = renderer->start; outX < renderer->end; ++outX) {
836 DS_BACKGROUND_BITMAP_ITERATE(width, height);
837
838 if (!mosaicWait) {
839 uint32_t address = (localX >> 8) + (localY >> 8) * width + screenBase;
840 uint8_t* vram = (uint8_t*) renderer->d.vramBG[address >> VRAM_BLOCK_OFFSET];
841 if (UNLIKELY(!vram)) {
842 continue;
843 }
844 color = vram[address & VRAM_BLOCK_MASK];
845 mosaicWait = mosaicH;
846 } else {
847 --mosaicWait;
848 }
849
850 uint32_t current = renderer->row[outX];
851 if (color && IS_WRITABLE(current)) {
852 if (!objwinSlowPath) {
853 _compositeBlendNoObjwin(renderer, outX, palette[color] | flags, current);
854 } else if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) {
855 color_t* currentPalette = (current & FLAG_OBJWIN) ? objwinPalette : palette;
856 unsigned mergedFlags = flags;
857 if (current & FLAG_OBJWIN) {
858 mergedFlags = objwinFlags;
859 }
860 _compositeBlendObjwin(renderer, outX, currentPalette[color] | mergedFlags, current);
861 }
862 }
863 }
864}
865
866void DSVideoSoftwareRendererDrawBackgroundExt2(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int inY) {
867 BACKGROUND_BITMAP_INIT;
868
869 uint32_t screenBase = (background->screenBase & 0xFF00) * 4;
870 uint32_t color;
871 int width, height;
872 switch (background->size) {
873 case 0:
874 width = 128;
875 height = 128;
876 break;
877 case 1:
878 width = 256;
879 height = 256;
880 break;
881 case 2:
882 width = 512;
883 height = 256;
884 break;
885 case 3:
886 width = 512;
887 height = 512;
888 break;
889 }
890
891 int outX;
892 for (outX = renderer->start; outX < renderer->end; ++outX) {
893 DS_BACKGROUND_BITMAP_ITERATE(width, height);
894
895 if (!mosaicWait) {
896 uint32_t address = ((localX >> 8) + (localY >> 8) * width + screenBase) << 1;
897 uint16_t* vram = renderer->d.vramBG[address >> VRAM_BLOCK_OFFSET];
898 if (UNLIKELY(!vram)) {
899 continue;
900 }
901 LOAD_16(color, address & VRAM_BLOCK_MASK, vram);
902#ifndef COLOR_16_BIT
903 unsigned color32;
904 color32 = 0;
905 color32 |= (color << 3) & 0xF8;
906 color32 |= (color << 6) & 0xF800;
907 color32 |= (color << 9) & 0xF80000;
908 color32 |= (color32 >> 5) & 0x070707;
909 color = color32;
910#elif COLOR_5_6_5
911 uint16_t color16 = 0;
912 color16 |= (color & 0x001F) << 11;
913 color16 |= (color & 0x03E0) << 1;
914 color16 |= (color & 0x7C00) >> 10;
915 color = color16;
916#endif
917 mosaicWait = mosaicH;
918 } else {
919 --mosaicWait;
920 }
921
922 uint32_t current = renderer->row[outX];
923 if (!objwinSlowPath || (!(current & FLAG_OBJWIN)) != objwinOnly) {
924 unsigned mergedFlags = flags;
925 if (current & FLAG_OBJWIN) {
926 mergedFlags = objwinFlags;
927 }
928 if (!variant) {
929 _compositeBlendObjwin(renderer, outX, color | mergedFlags, current);
930 } else if (renderer->blendEffect == BLEND_BRIGHTEN) {
931 _compositeBlendObjwin(renderer, outX, _brighten(color, renderer->bldy) | mergedFlags, current);
932 } else if (renderer->blendEffect == BLEND_DARKEN) {
933 _compositeBlendObjwin(renderer, outX, _darken(color, renderer->bldy) | mergedFlags, current);
934 }
935 }
936 }
937}