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