all repos — mgba @ b1828dbc59b8a9cc080c8c7f9472c46a3d39e90c

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