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