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