all repos — mgba @ 7a9e1e4600f0bba80a7734c6d0c3504b4495485d

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