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->d.highlightBG = false;
52 renderer->d.highlightWIN = false;
53 int i;
54 for (i = 0; i < GB_VIDEO_MAX_OBJ; ++i) {
55 renderer->d.highlightOBJ[i] = false;
56 }
57 renderer->d.highlightColor = M_COLOR_WHITE;
58 renderer->d.highlightAmount = 0;
59
60 renderer->logger->context = renderer;
61 renderer->logger->parsePacket = _parsePacket;
62 renderer->logger->vramBlock = _vramBlock;
63 renderer->logger->paletteSize = 0;
64 renderer->logger->vramSize = GB_SIZE_VRAM;
65 renderer->logger->oamSize = GB_SIZE_OAM;
66
67 renderer->backend = backend;
68}
69
70static void _init(struct GBVideoProxyRenderer* proxyRenderer) {
71 mVideoLoggerRendererInit(proxyRenderer->logger);
72
73 if (proxyRenderer->logger->block) {
74 proxyRenderer->backend->vram = (uint8_t*) proxyRenderer->logger->vram;
75 proxyRenderer->backend->oam = (union GBOAM*) proxyRenderer->logger->oam;
76 proxyRenderer->backend->cache = NULL;
77 }
78}
79
80static void _reset(struct GBVideoProxyRenderer* proxyRenderer) {
81 memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, GB_SIZE_OAM);
82 memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, GB_SIZE_VRAM);
83
84 mVideoLoggerRendererReset(proxyRenderer->logger);
85}
86
87static void _copyExtraState(struct GBVideoProxyRenderer* proxyRenderer) {
88 proxyRenderer->backend->disableBG = proxyRenderer->d.disableBG;
89 proxyRenderer->backend->disableWIN = proxyRenderer->d.disableWIN;
90 proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ;
91 proxyRenderer->backend->highlightBG = proxyRenderer->d.highlightBG;
92 proxyRenderer->backend->highlightWIN = proxyRenderer->d.highlightWIN;
93 memcpy(proxyRenderer->backend->highlightOBJ, proxyRenderer->d.highlightOBJ, sizeof(proxyRenderer->backend->highlightOBJ));
94 proxyRenderer->backend->highlightAmount = proxyRenderer->d.highlightAmount;
95 proxyRenderer->backend->highlightColor = proxyRenderer->d.highlightColor;
96}
97
98void GBVideoProxyRendererShim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer) {
99 if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) {
100 return;
101 }
102 renderer->backend = video->renderer;
103 video->renderer = &renderer->d;
104 renderer->d.cache = renderer->backend->cache;
105 renderer->d.sgbRenderMode = renderer->backend->sgbRenderMode;
106 renderer->d.sgbCharRam = renderer->backend->sgbCharRam;
107 renderer->d.sgbMapRam = renderer->backend->sgbMapRam;
108 renderer->d.sgbPalRam = renderer->backend->sgbPalRam;
109 renderer->d.sgbAttributeFiles = renderer->backend->sgbAttributeFiles;
110 renderer->d.sgbAttributes = renderer->backend->sgbAttributes;
111 renderer->d.vram = video->vram;
112 renderer->d.oam = &video->oam;
113 _init(renderer);
114 _reset(renderer);
115}
116
117void GBVideoProxyRendererUnshim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer) {
118 if (video->renderer != &renderer->d) {
119 return;
120 }
121 renderer->backend->cache = video->renderer->cache;
122 video->renderer = renderer->backend;
123 renderer->backend->vram = video->vram;
124 renderer->backend->oam = &video->oam;
125
126 mVideoLoggerRendererDeinit(renderer->logger);
127}
128
129void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model, bool borders) {
130 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
131
132 _init(proxyRenderer);
133
134 proxyRenderer->model = model;
135 proxyRenderer->backend->init(proxyRenderer->backend, model, borders);
136}
137
138void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer) {
139 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
140
141 proxyRenderer->backend->deinit(proxyRenderer->backend);
142
143 mVideoLoggerRendererDeinit(proxyRenderer->logger);
144}
145
146static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) {
147 struct GBVideoProxyRenderer* proxyRenderer = logger->context;
148 uint8_t sgbPacket[16];
149 struct GBObj legacyBuffer[GB_VIDEO_MAX_OBJ];
150 switch (item->type) {
151 case DIRTY_REGISTER:
152 proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
153 break;
154 case DIRTY_PALETTE:
155 if (item->address < 64) {
156 proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
157 }
158 break;
159 case DIRTY_OAM:
160 if (item->address < GB_SIZE_OAM) {
161 ((uint8_t*) logger->oam)[item->address] = item->value;
162 proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
163 }
164 break;
165 case DIRTY_VRAM:
166 if (item->address <= GB_SIZE_VRAM - 0x1000) {
167 logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true);
168 proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address);
169 }
170 break;
171 case DIRTY_SCANLINE:
172 _copyExtraState(proxyRenderer);
173 if (item->address < GB_VIDEO_VERTICAL_PIXELS) {
174 proxyRenderer->backend->finishScanline(proxyRenderer->backend, item->address);
175 }
176 break;
177 case DIRTY_RANGE:
178 if (item->value < item->value2 && item->value2 <= GB_VIDEO_HORIZONTAL_PIXELS && item->address < GB_VIDEO_VERTICAL_PIXELS) {
179 proxyRenderer->backend->drawRange(proxyRenderer->backend, item->value, item->value2, item->address);
180 }
181 break;
182 case DIRTY_FRAME:
183 proxyRenderer->backend->finishFrame(proxyRenderer->backend);
184 break;
185 case DIRTY_BUFFER:
186 switch (item->address) {
187 case BUFFER_OAM:
188 if (item->value2 / sizeof(struct GBObj) > GB_VIDEO_MAX_OBJ) {
189 return false;
190 }
191 logger->readData(logger, legacyBuffer, item->value2, true);
192 break;
193 case BUFFER_SGB:
194 logger->readData(logger, sgbPacket, 16, true);
195 if (proxyRenderer->model & GB_MODEL_SGB) {
196 proxyRenderer->backend->writeSGBPacket(proxyRenderer->backend, sgbPacket);
197 }
198 break;
199 }
200 break;
201 case DIRTY_FLUSH:
202 return false;
203 default:
204 return false;
205 }
206 return true;
207}
208
209static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address) {
210 struct GBVideoProxyRenderer* proxyRenderer = logger->context;
211 return (uint16_t*) &proxyRenderer->d.vram[address];
212}
213
214uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {
215 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
216
217 mVideoLoggerRendererWriteVideoRegister(proxyRenderer->logger, address, value);
218 if (!proxyRenderer->logger->block) {
219 proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, value);
220 }
221 return value;
222}
223
224void GBVideoProxyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data) {
225 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
226 if (!proxyRenderer->logger->block) {
227 proxyRenderer->backend->writeSGBPacket(proxyRenderer->backend, data);
228 }
229 mVideoLoggerWriteBuffer(proxyRenderer->logger, BUFFER_SGB, 0, 16, data);
230}
231
232void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
233 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
234 mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address);
235 if (!proxyRenderer->logger->block) {
236 proxyRenderer->backend->writeVRAM(proxyRenderer->backend, address);
237 }
238 if (renderer->cache) {
239 mCacheSetWriteVRAM(renderer->cache, address);
240 }
241}
242
243void GBVideoProxyRendererWritePalette(struct GBVideoRenderer* renderer, int address, uint16_t value) {
244 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
245 mVideoLoggerRendererWritePalette(proxyRenderer->logger, address, value);
246 if (!proxyRenderer->logger->block) {
247 proxyRenderer->backend->writePalette(proxyRenderer->backend, address, value);
248 }
249 if (renderer->cache) {
250 mCacheSetWritePalette(renderer->cache, address, mColorFrom555(value));
251 }
252}
253
254void GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam) {
255 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
256 if (!proxyRenderer->logger->block) {
257 proxyRenderer->backend->writeOAM(proxyRenderer->backend, oam);
258 }
259 mVideoLoggerRendererWriteOAM(proxyRenderer->logger, oam, ((uint8_t*) proxyRenderer->d.oam->raw)[oam]);
260}
261
262void GBVideoProxyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y) {
263 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
264 if (!proxyRenderer->logger->block) {
265 _copyExtraState(proxyRenderer);
266 proxyRenderer->backend->drawRange(proxyRenderer->backend, startX, endX, y);
267 }
268 mVideoLoggerRendererDrawRange(proxyRenderer->logger, startX, endX, y);
269}
270
271void GBVideoProxyRendererFinishScanline(struct GBVideoRenderer* renderer, int y) {
272 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
273 if (!proxyRenderer->logger->block) {
274 proxyRenderer->backend->finishScanline(proxyRenderer->backend, y);
275 }
276 mVideoLoggerRendererDrawScanline(proxyRenderer->logger, y);
277 if (proxyRenderer->logger->block && proxyRenderer->logger->wake) {
278 proxyRenderer->logger->wake(proxyRenderer->logger, y);
279 }
280}
281
282void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer) {
283 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
284 if (!proxyRenderer->logger->block) {
285 proxyRenderer->backend->finishFrame(proxyRenderer->backend);
286 }
287 mVideoLoggerRendererFinishFrame(proxyRenderer->logger);
288 mVideoLoggerRendererFlush(proxyRenderer->logger);
289}
290
291static void GBVideoProxyRendererEnableSGBBorder(struct GBVideoRenderer* renderer, bool enable) {
292 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
293 if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
294 // Insert an extra item into the queue to make sure it gets flushed
295 mVideoLoggerRendererFlush(proxyRenderer->logger);
296 proxyRenderer->logger->wait(proxyRenderer->logger);
297 }
298 proxyRenderer->backend->enableSGBBorder(proxyRenderer->backend, enable);
299}
300
301static void GBVideoProxyRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels) {
302 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
303 if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
304 proxyRenderer->logger->lock(proxyRenderer->logger);
305 // Insert an extra item into the queue to make sure it gets flushed
306 mVideoLoggerRendererFlush(proxyRenderer->logger);
307 proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_GET_PIXELS);
308 mVideoLoggerRendererFlush(proxyRenderer->logger);
309 proxyRenderer->logger->unlock(proxyRenderer->logger);
310 *pixels = proxyRenderer->logger->pixelBuffer;
311 *stride = proxyRenderer->logger->pixelStride;
312 } else {
313 proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
314 }
315}
316
317static void GBVideoProxyRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels) {
318 struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
319 if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
320 proxyRenderer->logger->lock(proxyRenderer->logger);
321 }
322 proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
323 if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
324 proxyRenderer->logger->unlock(proxyRenderer->logger);
325 }
326}