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