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