all repos — mgba @ 55679df8fc49fca3d5f8584d16b90e9bb734a922

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
 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}