all repos — mgba @ 1731d4f97581d1dfffb7b2aa8aa12661a7758671

mGBA Game Boy Advance Emulator

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