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