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