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