src/gba/extra/proxy.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/gba/renderers/proxy.h>
7
8#include <mgba/core/cache-set.h>
9#include <mgba/internal/gba/gba.h>
10#include <mgba/internal/gba/io.h>
11
12static void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer);
13static void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer);
14static void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer);
15static uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
16static void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
17static void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
18static void GBAVideoProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
19static void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
20static void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer);
21static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels);
22static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
23
24static void _handleEvent(struct mVideoLogger* logger, enum mVideoLoggerEvent event);
25static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet);
26static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address);
27
28void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend) {
29 renderer->d.init = GBAVideoProxyRendererInit;
30 renderer->d.reset = GBAVideoProxyRendererReset;
31 renderer->d.deinit = GBAVideoProxyRendererDeinit;
32 renderer->d.writeVideoRegister = GBAVideoProxyRendererWriteVideoRegister;
33 renderer->d.writeVRAM = GBAVideoProxyRendererWriteVRAM;
34 renderer->d.writeOAM = GBAVideoProxyRendererWriteOAM;
35 renderer->d.writePalette = GBAVideoProxyRendererWritePalette;
36 renderer->d.drawScanline = GBAVideoProxyRendererDrawScanline;
37 renderer->d.finishFrame = GBAVideoProxyRendererFinishFrame;
38 renderer->d.getPixels = GBAVideoProxyRendererGetPixels;
39 renderer->d.putPixels = GBAVideoProxyRendererPutPixels;
40
41 renderer->d.disableBG[0] = false;
42 renderer->d.disableBG[1] = false;
43 renderer->d.disableBG[2] = false;
44 renderer->d.disableBG[3] = false;
45 renderer->d.disableOBJ = false;
46
47 renderer->logger->context = renderer;
48 renderer->logger->parsePacket = _parsePacket;
49 renderer->logger->handleEvent = _handleEvent;
50 renderer->logger->vramBlock = _vramBlock;
51 renderer->logger->paletteSize = SIZE_PALETTE_RAM;
52 renderer->logger->vramSize = SIZE_VRAM;
53 renderer->logger->oamSize = SIZE_OAM;
54
55 renderer->backend = backend;
56}
57
58static void _init(struct GBAVideoProxyRenderer* proxyRenderer) {
59 mVideoLoggerRendererInit(proxyRenderer->logger);
60
61 if (proxyRenderer->logger->block) {
62 proxyRenderer->backend->palette = proxyRenderer->logger->palette;
63 memset(proxyRenderer->backend->vramBG, 0, sizeof(proxyRenderer->backend->vramBG));
64 proxyRenderer->backend->vramBG[0] = &proxyRenderer->logger->vram[0x0000];
65 proxyRenderer->backend->vramBG[1] = &proxyRenderer->logger->vram[0x2000];
66 proxyRenderer->backend->vramBG[2] = &proxyRenderer->logger->vram[0x4000];
67 proxyRenderer->backend->vramBG[3] = &proxyRenderer->logger->vram[0x6000];
68 memset(proxyRenderer->backend->vramOBJ, 0, sizeof(proxyRenderer->backend->vramOBJ));
69 proxyRenderer->backend->vramOBJ[0] = &proxyRenderer->logger->vram[0x8000];
70 proxyRenderer->backend->vramOBJ[1] = &proxyRenderer->logger->vram[0xA000];
71 proxyRenderer->backend->oam = (union GBAOAM*) proxyRenderer->logger->oam;
72 proxyRenderer->backend->cache = NULL;
73 }
74}
75
76static void _reset(struct GBAVideoProxyRenderer* proxyRenderer) {
77 memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, SIZE_OAM);
78 memcpy(proxyRenderer->logger->palette, proxyRenderer->d.palette, SIZE_PALETTE_RAM);
79 memcpy(&proxyRenderer->logger->vram[0x0000], proxyRenderer->d.vramBG[0], 0x4000);
80 memcpy(&proxyRenderer->logger->vram[0x2000], proxyRenderer->d.vramBG[1], 0x4000);
81 memcpy(&proxyRenderer->logger->vram[0x4000], proxyRenderer->d.vramBG[2], 0x4000);
82 memcpy(&proxyRenderer->logger->vram[0x6000], proxyRenderer->d.vramBG[3], 0x4000);
83 memcpy(&proxyRenderer->logger->vram[0x8000], proxyRenderer->d.vramOBJ[0], 0x4000);
84 memcpy(&proxyRenderer->logger->vram[0xA000], proxyRenderer->d.vramOBJ[1], 0x4000);
85
86 mVideoLoggerRendererReset(proxyRenderer->logger);
87}
88
89void GBAVideoProxyRendererShim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) {
90 if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) {
91 return;
92 }
93 renderer->backend = video->renderer;
94 video->renderer = &renderer->d;
95 renderer->d.cache = renderer->backend->cache;
96 renderer->d.palette = video->palette;
97 memcpy(renderer->d.vramBG, renderer->backend->vramBG, sizeof(renderer->backend->vramBG));
98 memcpy(renderer->d.vramOBJ, renderer->backend->vramOBJ, sizeof(renderer->backend->vramOBJ));
99 renderer->d.oam = &video->oam;
100 _init(renderer);
101 _reset(renderer);
102}
103
104void GBAVideoProxyRendererUnshim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) {
105 if (video->renderer != &renderer->d) {
106 return;
107 }
108 renderer->backend->cache = video->renderer->cache;
109 video->renderer = renderer->backend;
110 renderer->backend->palette = video->palette;
111 memcpy(renderer->backend->vramBG, renderer->d.vramBG, sizeof(renderer->backend->vramBG));
112 memcpy(renderer->backend->vramOBJ, renderer->d.vramOBJ, sizeof(renderer->backend->vramOBJ));
113 renderer->backend->oam = &video->oam;
114
115 mVideoLoggerRendererDeinit(renderer->logger);
116}
117
118void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer) {
119 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
120
121 _init(proxyRenderer);
122 _reset(proxyRenderer);
123
124 if (!proxyRenderer->logger->block) {
125 proxyRenderer->backend->init(proxyRenderer->backend);
126 } else {
127 proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_INIT);
128 }
129}
130
131void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer) {
132 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
133
134 _reset(proxyRenderer);
135
136 if (!proxyRenderer->logger->block) {
137 proxyRenderer->backend->reset(proxyRenderer->backend);
138 } else {
139 proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_RESET);
140 }
141}
142
143void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
144 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
145
146 if (!proxyRenderer->logger->block) {
147 proxyRenderer->backend->deinit(proxyRenderer->backend);
148 } else {
149 proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_DEINIT);
150 mVideoLoggerRendererFlush(proxyRenderer->logger);
151 }
152
153 mVideoLoggerRendererDeinit(proxyRenderer->logger);
154}
155
156static void _handleEvent(struct mVideoLogger* logger, enum mVideoLoggerEvent event) {
157 struct GBAVideoProxyRenderer* proxyRenderer = logger->context;
158 switch (event) {
159 default:
160 break;
161 case LOGGER_EVENT_INIT:
162 proxyRenderer->backend->init(proxyRenderer->backend);
163 break;
164 case LOGGER_EVENT_DEINIT:
165 proxyRenderer->backend->deinit(proxyRenderer->backend);
166 break;
167 case LOGGER_EVENT_RESET:
168 proxyRenderer->backend->reset(proxyRenderer->backend);
169 break;
170 case LOGGER_EVENT_GET_PIXELS:
171 proxyRenderer->backend->getPixels(proxyRenderer->backend, &logger->pixelStride, &logger->pixelBuffer);
172 break;
173 }
174}
175
176static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) {
177 struct GBAVideoProxyRenderer* proxyRenderer = logger->context;
178 switch (item->type) {
179 case DIRTY_REGISTER:
180 proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
181 break;
182 case DIRTY_PALETTE:
183 if (item->address < SIZE_PALETTE_RAM) {
184 logger->palette[item->address >> 1] = item->value;
185 proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
186 }
187 break;
188 case DIRTY_OAM:
189 if (item->address < SIZE_OAM) {
190 logger->oam[item->address] = item->value;
191 proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
192 }
193 break;
194 case DIRTY_VRAM:
195 if (item->address <= SIZE_VRAM - 0x1000) {
196 logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true);
197 proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address);
198 } else {
199 logger->readData(logger, NULL, 0x1000, true);
200 }
201 break;
202 case DIRTY_SCANLINE:
203 proxyRenderer->backend->disableBG[0] = proxyRenderer->d.disableBG[0];
204 proxyRenderer->backend->disableBG[1] = proxyRenderer->d.disableBG[1];
205 proxyRenderer->backend->disableBG[2] = proxyRenderer->d.disableBG[2];
206 proxyRenderer->backend->disableBG[3] = proxyRenderer->d.disableBG[3];
207 proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ;
208 if (item->address < GBA_VIDEO_VERTICAL_PIXELS) {
209 proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address);
210 }
211 break;
212 case DIRTY_FRAME:
213 proxyRenderer->backend->finishFrame(proxyRenderer->backend);
214 break;
215 case DIRTY_FLUSH:
216 return false;
217 default:
218 return false;
219 }
220 return true;
221}
222
223static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address) {
224 struct GBAVideoProxyRenderer* proxyRenderer = logger->context;
225 if (address < 0x10000) {
226 return &proxyRenderer->d.vramBG[address >> VRAM_BLOCK_OFFSET][(address & VRAM_BLOCK_MASK) >> 1];
227 } else {
228 return &proxyRenderer->d.vramOBJ[(address & 0x7FFF) >> VRAM_BLOCK_OFFSET][(address & VRAM_BLOCK_MASK) >> 1];
229 }
230}
231
232uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
233 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
234 switch (address) {
235 case REG_DISPCNT:
236 value &= 0xFFF7;
237 break;
238 case REG_BG0CNT:
239 case REG_BG1CNT:
240 value &= 0xDFFF;
241 break;
242 case REG_BG2CNT:
243 case REG_BG3CNT:
244 value &= 0xFFFF;
245 break;
246 case REG_BG0HOFS:
247 case REG_BG0VOFS:
248 case REG_BG1HOFS:
249 case REG_BG1VOFS:
250 case REG_BG2HOFS:
251 case REG_BG2VOFS:
252 case REG_BG3HOFS:
253 case REG_BG3VOFS:
254 value &= 0x01FF;
255 break;
256 }
257 if (address > REG_BLDY) {
258 return value;
259 }
260
261 mVideoLoggerRendererWriteVideoRegister(proxyRenderer->logger, address, value);
262 if (!proxyRenderer->logger->block) {
263 proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, value);
264 }
265 return value;
266}
267
268void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
269 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
270 mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address);
271 if (!proxyRenderer->logger->block) {
272 proxyRenderer->backend->writeVRAM(proxyRenderer->backend, address);
273 }
274 if (renderer->cache) {
275 mCacheSetWriteVRAM(renderer->cache, address);
276 }
277}
278
279void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
280 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
281 mVideoLoggerRendererWritePalette(proxyRenderer->logger, address, value);
282 if (!proxyRenderer->logger->block) {
283 proxyRenderer->backend->writePalette(proxyRenderer->backend, address, value);
284 }
285 if (renderer->cache) {
286 mCacheSetWritePalette(renderer->cache, address >> 1, mColorFrom555(value));
287 }
288}
289
290void GBAVideoProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
291 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
292 if (!proxyRenderer->logger->block) {
293 proxyRenderer->backend->writeOAM(proxyRenderer->backend, oam);
294 }
295 mVideoLoggerRendererWriteOAM(proxyRenderer->logger, oam, proxyRenderer->d.oam->raw[oam]);
296}
297
298void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
299 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
300 if (!proxyRenderer->logger->block) {
301 proxyRenderer->backend->drawScanline(proxyRenderer->backend, y);
302 }
303 mVideoLoggerRendererDrawScanline(proxyRenderer->logger, y);
304 if (proxyRenderer->logger->block && proxyRenderer->logger->wake) {
305 proxyRenderer->logger->wake(proxyRenderer->logger, y);
306 }
307}
308
309void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
310 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
311 if (!proxyRenderer->logger->block) {
312 proxyRenderer->backend->finishFrame(proxyRenderer->backend);
313 }
314 mVideoLoggerRendererFinishFrame(proxyRenderer->logger);
315 mVideoLoggerRendererFlush(proxyRenderer->logger);
316}
317
318static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
319 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
320 if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
321 // Insert an extra item into the queue to make sure it gets flushed
322 mVideoLoggerRendererFlush(proxyRenderer->logger);
323 proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_GET_PIXELS);
324 mVideoLoggerRendererFlush(proxyRenderer->logger);
325 *pixels = proxyRenderer->logger->pixelBuffer;
326 *stride = proxyRenderer->logger->pixelStride;
327 } else {
328 proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
329 }
330}
331
332static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
333 struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
334 if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
335 proxyRenderer->logger->lock(proxyRenderer->logger);
336 }
337 proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
338 if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
339 proxyRenderer->logger->unlock(proxyRenderer->logger);
340 }
341}