src/gb/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/gb/renderers/proxy.h>
7
8#include <mgba/core/cache-set.h>
9#include <mgba/internal/gb/gb.h>
10#include <mgba/internal/gb/io.h>
11
12#define BUFFER_OAM 1
13#define BUFFER_SGB 2
14
15static void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model, bool borders);
16static void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer);
17static uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
18static void GBVideoProxyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data);
19static void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
20static void GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam);
21static void GBVideoProxyRendererWritePalette(struct GBVideoRenderer* renderer, int address, uint16_t value);
22static void GBVideoProxyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y);
23static void GBVideoProxyRendererFinishScanline(struct GBVideoRenderer* renderer, int y);
24static void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer);
25static void GBVideoProxyRendererEnableSGBBorder(struct GBVideoRenderer* renderer, bool enable);
26static void GBVideoProxyRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels);
27static void GBVideoProxyRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels);
28
29static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet);
30static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address);
31
32void GBVideoProxyRendererCreate(struct GBVideoProxyRenderer* renderer, struct GBVideoRenderer* backend) {
33 renderer->d.init = GBVideoProxyRendererInit;
34 renderer->d.deinit = GBVideoProxyRendererDeinit;
35 renderer->d.writeVideoRegister = GBVideoProxyRendererWriteVideoRegister;
36 renderer->d.writeSGBPacket = GBVideoProxyRendererWriteSGBPacket;
37 renderer->d.writeVRAM = GBVideoProxyRendererWriteVRAM;
38 renderer->d.writeOAM = GBVideoProxyRendererWriteOAM;
39 renderer->d.writePalette = GBVideoProxyRendererWritePalette;
40 renderer->d.drawRange = GBVideoProxyRendererDrawRange;
41 renderer->d.finishScanline = GBVideoProxyRendererFinishScanline;
42 renderer->d.finishFrame = GBVideoProxyRendererFinishFrame;
43 renderer->d.enableSGBBorder = GBVideoProxyRendererEnableSGBBorder;
44 renderer->d.getPixels = GBVideoProxyRendererGetPixels;
45 renderer->d.putPixels = GBVideoProxyRendererPutPixels;
46
47 renderer->d.disableBG = false;
48 renderer->d.disableWIN = false;
49 renderer->d.disableOBJ = false;
50
51 renderer->logger->context = renderer;
52 renderer->logger->parsePacket = _parsePacket;
53 renderer->logger->vramBlock = _vramBlock;
54 renderer->logger->paletteSize = 0;
55 renderer->logger->vramSize = GB_SIZE_VRAM;
56 renderer->logger->oamSize = GB_SIZE_OAM;
57
58 renderer->backend = backend;
59}
60
61static void _init(struct GBVideoProxyRenderer* proxyRenderer) {
62 mVideoLoggerRendererInit(proxyRenderer->logger);
63
64 if (proxyRenderer->logger->block) {
65 proxyRenderer->backend->vram = (uint8_t*) proxyRenderer->logger->vram;
66 proxyRenderer->backend->oam = (union GBOAM*) proxyRenderer->logger->oam;
67 proxyRenderer->backend->cache = NULL;
68 }
69}
70
71static void _reset(struct GBVideoProxyRenderer* proxyRenderer) {
72 memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, GB_SIZE_OAM);
73 memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, GB_SIZE_VRAM);
74
75 mVideoLoggerRendererReset(proxyRenderer->logger);
76}
77
78void GBVideoProxyRendererShim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer) {
79 if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) {
80 return;
81 }
82 renderer->backend = video->renderer;
83 video->renderer = &renderer->d;
84 renderer->d.cache = renderer->backend->cache;
85 renderer->d.vram = video->vram;
86 renderer->d.oam = &video->oam;
87 _init(renderer);
88 _reset(renderer);
89}
90
91void GBVideoProxyRendererUnshim(struct GBVideo* video, struct GBVideoProxyRenderer* 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->vram = video->vram;
98 renderer->backend->oam = &video->oam;
99
100 mVideoLoggerRendererDeinit(renderer->logger);
101}
102
103void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model, bool borders) {
104 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
105
106 _init(proxyRenderer);
107
108 proxyRenderer->backend->init(proxyRenderer->backend, model, borders);
109}
110
111void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer) {
112 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
113
114 proxyRenderer->backend->deinit(proxyRenderer->backend);
115
116 mVideoLoggerRendererDeinit(proxyRenderer->logger);
117}
118
119static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) {
120 struct GBVideoProxyRenderer* proxyRenderer = logger->context;
121 uint8_t sgbPacket[16];
122 struct GBObj legacyBuffer[GB_VIDEO_MAX_OBJ];
123 switch (item->type) {
124 case DIRTY_REGISTER:
125 proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
126 break;
127 case DIRTY_PALETTE:
128 if (item->address < 64) {
129 proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
130 }
131 break;
132 case DIRTY_OAM:
133 if (item->address < GB_SIZE_OAM) {
134 ((uint8_t*) logger->oam)[item->address] = item->value;
135 proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
136 }
137 break;
138 case DIRTY_VRAM:
139 if (item->address <= GB_SIZE_VRAM - 0x1000) {
140 logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true);
141 proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address);
142 }
143 break;
144 case DIRTY_SCANLINE:
145 proxyRenderer->backend->disableBG = proxyRenderer->d.disableBG;
146 proxyRenderer->backend->disableWIN = proxyRenderer->d.disableWIN;
147 proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ;
148 if (item->address < GB_VIDEO_VERTICAL_PIXELS) {
149 proxyRenderer->backend->finishScanline(proxyRenderer->backend, item->address);
150 }
151 break;
152 case DIRTY_RANGE:
153 if (item->value < item->value2 && item->value2 <= GB_VIDEO_HORIZONTAL_PIXELS && item->address < GB_VIDEO_VERTICAL_PIXELS) {
154 proxyRenderer->backend->drawRange(proxyRenderer->backend, item->value, item->value2, item->address);
155 }
156 break;
157 case DIRTY_FRAME:
158 proxyRenderer->backend->finishFrame(proxyRenderer->backend);
159 break;
160 case DIRTY_BUFFER:
161 switch (item->address) {
162 case BUFFER_OAM:
163 if (item->value2 / sizeof(struct GBObj) > GB_VIDEO_MAX_OBJ) {
164 return false;
165 }
166 logger->readData(logger, legacyBuffer, item->value2, true);
167 break;
168 case BUFFER_SGB:
169 logger->readData(logger, sgbPacket, 16, true);
170 proxyRenderer->backend->writeSGBPacket(proxyRenderer->backend, sgbPacket);
171 break;
172 }
173 break;
174 case DIRTY_FLUSH:
175 return false;
176 default:
177 return false;
178 }
179 return true;
180}
181
182static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address) {
183 struct GBVideoProxyRenderer* proxyRenderer = logger->context;
184 return (uint16_t*) &proxyRenderer->d.vram[address];
185}
186
187uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {
188 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
189
190 mVideoLoggerRendererWriteVideoRegister(proxyRenderer->logger, address, value);
191 if (!proxyRenderer->logger->block) {
192 proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, value);
193 }
194 return value;
195}
196
197void GBVideoProxyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data) {
198 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
199 if (!proxyRenderer->logger->block) {
200 proxyRenderer->backend->writeSGBPacket(proxyRenderer->backend, data);
201 }
202 mVideoLoggerWriteBuffer(proxyRenderer->logger, BUFFER_SGB, 0, 16, data);
203}
204
205void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
206 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
207 mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address);
208 if (!proxyRenderer->logger->block) {
209 proxyRenderer->backend->writeVRAM(proxyRenderer->backend, address);
210 }
211 if (renderer->cache) {
212 mCacheSetWriteVRAM(renderer->cache, address);
213 }
214}
215
216void GBVideoProxyRendererWritePalette(struct GBVideoRenderer* renderer, int address, uint16_t value) {
217 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
218 mVideoLoggerRendererWritePalette(proxyRenderer->logger, address, value);
219 if (!proxyRenderer->logger->block) {
220 proxyRenderer->backend->writePalette(proxyRenderer->backend, address, value);
221 }
222 if (renderer->cache) {
223 mCacheSetWritePalette(renderer->cache, address, mColorFrom555(value));
224 }
225}
226
227void GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam) {
228 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
229 if (!proxyRenderer->logger->block) {
230 proxyRenderer->backend->writeOAM(proxyRenderer->backend, oam);
231 }
232 mVideoLoggerRendererWriteOAM(proxyRenderer->logger, oam, ((uint8_t*) proxyRenderer->d.oam->raw)[oam]);
233}
234
235void GBVideoProxyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y) {
236 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
237 if (!proxyRenderer->logger->block) {
238 proxyRenderer->backend->disableBG = proxyRenderer->d.disableBG;
239 proxyRenderer->backend->disableWIN = proxyRenderer->d.disableWIN;
240 proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ;
241 proxyRenderer->backend->drawRange(proxyRenderer->backend, startX, endX, y);
242 }
243 mVideoLoggerRendererDrawRange(proxyRenderer->logger, startX, endX, y);
244}
245
246void GBVideoProxyRendererFinishScanline(struct GBVideoRenderer* renderer, int y) {
247 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
248 if (!proxyRenderer->logger->block) {
249 proxyRenderer->backend->finishScanline(proxyRenderer->backend, y);
250 }
251 mVideoLoggerRendererDrawScanline(proxyRenderer->logger, y);
252 if (proxyRenderer->logger->block && proxyRenderer->logger->wake) {
253 proxyRenderer->logger->wake(proxyRenderer->logger, y);
254 }
255}
256
257void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer) {
258 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
259 if (!proxyRenderer->logger->block) {
260 proxyRenderer->backend->finishFrame(proxyRenderer->backend);
261 }
262 mVideoLoggerRendererFinishFrame(proxyRenderer->logger);
263 mVideoLoggerRendererFlush(proxyRenderer->logger);
264}
265
266static void GBVideoProxyRendererEnableSGBBorder(struct GBVideoRenderer* renderer, bool enable) {
267 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
268 if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
269 // Insert an extra item into the queue to make sure it gets flushed
270 mVideoLoggerRendererFlush(proxyRenderer->logger);
271 proxyRenderer->logger->wait(proxyRenderer->logger);
272 }
273 proxyRenderer->backend->enableSGBBorder(proxyRenderer->backend, enable);
274}
275
276static void GBVideoProxyRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels) {
277 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
278 if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
279 proxyRenderer->logger->lock(proxyRenderer->logger);
280 // Insert an extra item into the queue to make sure it gets flushed
281 mVideoLoggerRendererFlush(proxyRenderer->logger);
282 proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_GET_PIXELS);
283 mVideoLoggerRendererFlush(proxyRenderer->logger);
284 proxyRenderer->logger->unlock(proxyRenderer->logger);
285 *pixels = proxyRenderer->logger->pixelBuffer;
286 *stride = proxyRenderer->logger->pixelStride;
287 } else {
288 proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
289 }
290}
291
292static void GBVideoProxyRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels) {
293 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
294 if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
295 proxyRenderer->logger->lock(proxyRenderer->logger);
296 }
297 proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
298 if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
299 proxyRenderer->logger->unlock(proxyRenderer->logger);
300 }
301}