all repos — mgba @ f41f3a847893450843f33e6c45c7025fdd8e1fc9

mGBA Game Boy Advance Emulator

src/gba/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/gba/renderers/proxy.h>
  7
  8#include <mgba/core/cache-set.h>
  9#include <mgba/internal/gba/gba.h>
 10#include <mgba/internal/gba/io.h>
 11
 12static void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer);
 13static void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer);
 14static void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer);
 15static uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
 16static void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
 17static void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
 18static void GBAVideoProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
 19static void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
 20static void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer);
 21static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels);
 22static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
 23
 24static void _handleEvent(struct mVideoLogger* logger, enum mVideoLoggerEvent event);
 25static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet);
 26static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address);
 27
 28void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend) {
 29	renderer->d.init = GBAVideoProxyRendererInit;
 30	renderer->d.reset = GBAVideoProxyRendererReset;
 31	renderer->d.deinit = GBAVideoProxyRendererDeinit;
 32	renderer->d.writeVideoRegister = GBAVideoProxyRendererWriteVideoRegister;
 33	renderer->d.writeVRAM = GBAVideoProxyRendererWriteVRAM;
 34	renderer->d.writeOAM = GBAVideoProxyRendererWriteOAM;
 35	renderer->d.writePalette = GBAVideoProxyRendererWritePalette;
 36	renderer->d.drawScanline = GBAVideoProxyRendererDrawScanline;
 37	renderer->d.finishFrame = GBAVideoProxyRendererFinishFrame;
 38	renderer->d.getPixels = GBAVideoProxyRendererGetPixels;
 39	renderer->d.putPixels = GBAVideoProxyRendererPutPixels;
 40
 41	renderer->d.disableBG[0] = false;
 42	renderer->d.disableBG[1] = false;
 43	renderer->d.disableBG[2] = false;
 44	renderer->d.disableBG[3] = false;
 45	renderer->d.disableOBJ = false;
 46
 47	renderer->logger->context = renderer;
 48	renderer->logger->parsePacket = _parsePacket;
 49	renderer->logger->handleEvent = _handleEvent;
 50	renderer->logger->vramBlock = _vramBlock;
 51	renderer->logger->paletteSize = SIZE_PALETTE_RAM;
 52	renderer->logger->vramSize = SIZE_VRAM;
 53	renderer->logger->oamSize = SIZE_OAM;
 54
 55	renderer->backend = backend;
 56}
 57
 58static void _init(struct GBAVideoProxyRenderer* proxyRenderer) {
 59	mVideoLoggerRendererInit(proxyRenderer->logger);
 60
 61	if (proxyRenderer->logger->block) {
 62		proxyRenderer->backend->palette = proxyRenderer->logger->palette;
 63		proxyRenderer->backend->vram = proxyRenderer->logger->vram;
 64		proxyRenderer->backend->oam = (union GBAOAM*) proxyRenderer->logger->oam;
 65		proxyRenderer->backend->cache = NULL;
 66	}
 67}
 68
 69static void _reset(struct GBAVideoProxyRenderer* proxyRenderer) {
 70	memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, SIZE_OAM);
 71	memcpy(proxyRenderer->logger->palette, proxyRenderer->d.palette, SIZE_PALETTE_RAM);
 72	memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, SIZE_VRAM);
 73
 74	mVideoLoggerRendererReset(proxyRenderer->logger);
 75}
 76
 77void GBAVideoProxyRendererShim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) {
 78	if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) {
 79		return;
 80	}
 81	renderer->backend = video->renderer;
 82	video->renderer = &renderer->d;
 83	renderer->d.cache = renderer->backend->cache;
 84	renderer->d.palette = video->palette;
 85	renderer->d.vram = video->vram;
 86	renderer->d.oam = &video->oam;
 87	_init(renderer);
 88	_reset(renderer);
 89}
 90
 91void GBAVideoProxyRendererUnshim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) {
 92	if (video->renderer != &renderer->d) {
 93		return;
 94	}
 95	renderer->backend->cache = video->renderer->cache;
 96	video->renderer = renderer->backend;
 97	renderer->backend->palette = video->palette;
 98	renderer->backend->vram = video->vram;
 99	renderer->backend->oam = &video->oam;
100
101	mVideoLoggerRendererDeinit(renderer->logger);
102}
103
104void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer) {
105	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
106
107	_init(proxyRenderer);
108	_reset(proxyRenderer);
109
110	if (!proxyRenderer->logger->block) {
111		proxyRenderer->backend->init(proxyRenderer->backend);
112	} else {
113		proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_INIT);
114	}
115}
116
117void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer) {
118	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
119
120	_reset(proxyRenderer);
121
122	if (!proxyRenderer->logger->block) {
123		proxyRenderer->backend->reset(proxyRenderer->backend);
124	} else {
125		proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_RESET);
126	}
127}
128
129void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
130	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
131
132	if (!proxyRenderer->logger->block) {
133		proxyRenderer->backend->deinit(proxyRenderer->backend);
134	} else {
135		proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_DEINIT);
136		mVideoLoggerRendererFlush(proxyRenderer->logger);
137	}
138
139	mVideoLoggerRendererDeinit(proxyRenderer->logger);
140}
141
142static void _handleEvent(struct mVideoLogger* logger, enum mVideoLoggerEvent event) {
143	struct GBAVideoProxyRenderer* proxyRenderer = logger->context;
144	switch (event) {
145	default:
146		break;
147	case LOGGER_EVENT_INIT:
148		proxyRenderer->backend->init(proxyRenderer->backend);
149		break;
150	case LOGGER_EVENT_DEINIT:
151		proxyRenderer->backend->deinit(proxyRenderer->backend);
152		break;
153	case LOGGER_EVENT_RESET:
154		proxyRenderer->backend->reset(proxyRenderer->backend);
155		break;
156	case LOGGER_EVENT_GET_PIXELS:
157		proxyRenderer->backend->getPixels(proxyRenderer->backend, &logger->pixelStride, &logger->pixelBuffer);
158		break;
159	}
160}
161
162static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) {
163	struct GBAVideoProxyRenderer* proxyRenderer = logger->context;
164	switch (item->type) {
165	case DIRTY_REGISTER:
166		proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
167		break;
168	case DIRTY_PALETTE:
169		if (item->address < SIZE_PALETTE_RAM) {
170			logger->palette[item->address >> 1] = item->value;
171			proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
172		}
173		break;
174	case DIRTY_OAM:
175		if (item->address < SIZE_OAM) {
176			logger->oam[item->address] = item->value;
177			proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
178		}
179		break;
180	case DIRTY_VRAM:
181		if (item->address <= SIZE_VRAM - 0x1000) {
182			logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true);
183			proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address);
184		} else {
185			logger->readData(logger, NULL, 0x1000, true);
186		}
187		break;
188	case DIRTY_SCANLINE:
189		proxyRenderer->backend->disableBG[0] = proxyRenderer->d.disableBG[0];
190		proxyRenderer->backend->disableBG[1] = proxyRenderer->d.disableBG[1];
191		proxyRenderer->backend->disableBG[2] = proxyRenderer->d.disableBG[2];
192		proxyRenderer->backend->disableBG[3] = proxyRenderer->d.disableBG[3];
193		proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ;
194		proxyRenderer->backend->highlightBG[0] = proxyRenderer->d.highlightBG[0];
195		proxyRenderer->backend->highlightBG[1] = proxyRenderer->d.highlightBG[1];
196		proxyRenderer->backend->highlightBG[2] = proxyRenderer->d.highlightBG[2];
197		proxyRenderer->backend->highlightBG[3] = proxyRenderer->d.highlightBG[3];
198		memcpy(proxyRenderer->backend->highlightOBJ, proxyRenderer->d.highlightOBJ, sizeof(proxyRenderer->backend->highlightOBJ));
199		proxyRenderer->backend->highlightAmount = proxyRenderer->d.highlightAmount;
200		if (item->address < GBA_VIDEO_VERTICAL_PIXELS) {
201			proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address);
202		}
203		break;
204	case DIRTY_FRAME:
205		proxyRenderer->backend->finishFrame(proxyRenderer->backend);
206		break;
207	case DIRTY_FLUSH:
208		return false;
209	default:
210		return false;
211	}
212	return true;
213}
214
215static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address) {
216	struct GBAVideoProxyRenderer* proxyRenderer = logger->context;
217	return &proxyRenderer->d.vram[address >> 1];
218}
219
220uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
221	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
222	switch (address) {
223	case REG_DISPCNT:
224		value &= 0xFFF7;
225		break;
226	case REG_BG0CNT:
227	case REG_BG1CNT:
228		value &= 0xDFFF;
229		break;
230	case REG_BG2CNT:
231	case REG_BG3CNT:
232		value &= 0xFFFF;
233		break;
234	case REG_BG0HOFS:
235	case REG_BG0VOFS:
236	case REG_BG1HOFS:
237	case REG_BG1VOFS:
238	case REG_BG2HOFS:
239	case REG_BG2VOFS:
240	case REG_BG3HOFS:
241	case REG_BG3VOFS:
242		value &= 0x01FF;
243		break;
244	}
245	if (address > REG_BLDY) {
246		return value;
247	}
248
249	mVideoLoggerRendererWriteVideoRegister(proxyRenderer->logger, address, value);
250	if (!proxyRenderer->logger->block) {
251		proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, value);
252	}
253	return value;
254}
255
256void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
257	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
258	mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address);
259	if (!proxyRenderer->logger->block) {
260		proxyRenderer->backend->writeVRAM(proxyRenderer->backend, address);
261	}
262	if (renderer->cache) {
263		mCacheSetWriteVRAM(renderer->cache, address);
264	}
265}
266
267void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
268	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
269	mVideoLoggerRendererWritePalette(proxyRenderer->logger, address, value);
270	if (!proxyRenderer->logger->block) {
271		proxyRenderer->backend->writePalette(proxyRenderer->backend, address, value);
272	}
273	if (renderer->cache) {
274		mCacheSetWritePalette(renderer->cache, address >> 1, mColorFrom555(value));
275	}
276}
277
278void GBAVideoProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
279	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
280	if (!proxyRenderer->logger->block) {
281		proxyRenderer->backend->writeOAM(proxyRenderer->backend, oam);
282	}
283	mVideoLoggerRendererWriteOAM(proxyRenderer->logger, oam, proxyRenderer->d.oam->raw[oam]);
284}
285
286void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
287	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
288	if (!proxyRenderer->logger->block) {
289		proxyRenderer->backend->drawScanline(proxyRenderer->backend, y);
290	}
291	mVideoLoggerRendererDrawScanline(proxyRenderer->logger, y);
292	if (proxyRenderer->logger->block && proxyRenderer->logger->wake) {
293		proxyRenderer->logger->wake(proxyRenderer->logger, y);
294	}
295}
296
297void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
298	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
299	if (!proxyRenderer->logger->block) {
300		proxyRenderer->backend->finishFrame(proxyRenderer->backend);
301	}
302	mVideoLoggerRendererFinishFrame(proxyRenderer->logger);
303	mVideoLoggerRendererFlush(proxyRenderer->logger);
304}
305
306static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
307	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
308	if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
309		// Insert an extra item into the queue to make sure it gets flushed
310		mVideoLoggerRendererFlush(proxyRenderer->logger);
311		proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_GET_PIXELS);
312		mVideoLoggerRendererFlush(proxyRenderer->logger);
313		*pixels = proxyRenderer->logger->pixelBuffer;
314		*stride = proxyRenderer->logger->pixelStride;
315	} else {
316		proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
317	}
318}
319
320static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
321	struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
322	if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
323		proxyRenderer->logger->lock(proxyRenderer->logger);
324	}
325	proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
326	if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
327		proxyRenderer->logger->unlock(proxyRenderer->logger);
328	}
329}