all repos — mgba @ 558055277f97168f2b7652f9351b9af595206e90

mGBA Game Boy Advance Emulator

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, 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 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, bool borders) {
100	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
101
102	_init(proxyRenderer);
103
104	proxyRenderer->backend->init(proxyRenderer->backend, model, borders);
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}