all repos — mgba @ eab5ed6e142e75656adbe918f2422d936f67c5c2

mGBA Game Boy Advance Emulator

src/gba/renderers/thread-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/thread-proxy.h>
  7
  8#include <mgba/core/tile-cache.h>
  9#include <mgba/internal/gba/gba.h>
 10#include <mgba/internal/gba/io.h>
 11
 12#include <mgba-util/memory.h>
 13
 14#ifndef DISABLE_THREADING
 15
 16static void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer);
 17static void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer);
 18static void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer);
 19static uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
 20static void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
 21static void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
 22static void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
 23static void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
 24static void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer);
 25static void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels);
 26static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
 27
 28static THREAD_ENTRY _proxyThread(void* renderer);
 29
 30static bool _writeData(struct mVideoProxy* proxy, const void* data, size_t length);
 31static bool _readData(struct mVideoProxy* proxy, void* data, size_t length, bool block);
 32static bool _parsePacket(struct mVideoProxy* proxy, const struct mVideoProxyDirtyInfo* packet);
 33static uint16_t* _vramBlock(struct mVideoProxy* proxy, uint32_t address);
 34
 35void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) {
 36	renderer->d.init = GBAVideoThreadProxyRendererInit;
 37	renderer->d.reset = GBAVideoThreadProxyRendererReset;
 38	renderer->d.deinit = GBAVideoThreadProxyRendererDeinit;
 39	renderer->d.writeVideoRegister = GBAVideoThreadProxyRendererWriteVideoRegister;
 40	renderer->d.writeVRAM = GBAVideoThreadProxyRendererWriteVRAM;
 41	renderer->d.writeOAM = GBAVideoThreadProxyRendererWriteOAM;
 42	renderer->d.writePalette = GBAVideoThreadProxyRendererWritePalette;
 43	renderer->d.drawScanline = GBAVideoThreadProxyRendererDrawScanline;
 44	renderer->d.finishFrame = GBAVideoThreadProxyRendererFinishFrame;
 45	renderer->d.getPixels = GBAVideoThreadProxyRendererGetPixels;
 46	renderer->d.putPixels = GBAVideoThreadProxyRendererPutPixels;
 47
 48	renderer->d.disableBG[0] = false;
 49	renderer->d.disableBG[1] = false;
 50	renderer->d.disableBG[2] = false;
 51	renderer->d.disableBG[3] = false;
 52	renderer->d.disableOBJ = false;
 53
 54	renderer->proxy.context = renderer;
 55	renderer->proxy.writeData = _writeData;
 56	renderer->proxy.readData = _readData;
 57	renderer->proxy.parsePacket = _parsePacket;
 58	renderer->proxy.vramBlock = _vramBlock;
 59	renderer->proxy.paletteSize = SIZE_PALETTE_RAM;
 60	renderer->proxy.vramSize = SIZE_VRAM;
 61	renderer->proxy.oamSize = SIZE_OAM;
 62
 63	renderer->backend = backend;
 64}
 65
 66void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer) {
 67	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
 68	ConditionInit(&proxyRenderer->fromThreadCond);
 69	ConditionInit(&proxyRenderer->toThreadCond);
 70	MutexInit(&proxyRenderer->mutex);
 71	RingFIFOInit(&proxyRenderer->dirtyQueue, 0x40000);
 72
 73	mVideoProxyRendererInit(&proxyRenderer->proxy);
 74
 75	proxyRenderer->backend->palette = proxyRenderer->proxy.palette;
 76	proxyRenderer->backend->vram = proxyRenderer->proxy.vram;
 77	proxyRenderer->backend->oam = (union GBAOAM*) proxyRenderer->proxy.oam;
 78	proxyRenderer->backend->cache = NULL;
 79
 80	proxyRenderer->backend->init(proxyRenderer->backend);
 81
 82	proxyRenderer->threadState = PROXY_THREAD_IDLE;
 83	ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
 84}
 85
 86void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer) {
 87	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
 88	MutexLock(&proxyRenderer->mutex);
 89	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
 90		ConditionWake(&proxyRenderer->toThreadCond);
 91		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
 92	}
 93	memcpy(proxyRenderer->proxy.oam, &renderer->oam->raw, SIZE_OAM);
 94	memcpy(proxyRenderer->proxy.palette, renderer->palette, SIZE_PALETTE_RAM);
 95	memcpy(proxyRenderer->proxy.vram, renderer->vram, SIZE_VRAM);
 96
 97	mVideoProxyRendererReset(&proxyRenderer->proxy);
 98
 99	proxyRenderer->backend->reset(proxyRenderer->backend);
100	MutexUnlock(&proxyRenderer->mutex);
101}
102
103void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
104	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
105	bool waiting = false;
106	MutexLock(&proxyRenderer->mutex);
107	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
108		ConditionWake(&proxyRenderer->toThreadCond);
109		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
110	}
111	if (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
112		proxyRenderer->threadState = PROXY_THREAD_STOPPED;
113		ConditionWake(&proxyRenderer->toThreadCond);
114		waiting = true;
115	}
116	MutexUnlock(&proxyRenderer->mutex);
117	if (waiting) {
118		ThreadJoin(proxyRenderer->thread);
119	}
120	ConditionDeinit(&proxyRenderer->fromThreadCond);
121	ConditionDeinit(&proxyRenderer->toThreadCond);
122	MutexDeinit(&proxyRenderer->mutex);
123	proxyRenderer->backend->deinit(proxyRenderer->backend);
124
125	mVideoProxyRendererDeinit(&proxyRenderer->proxy);
126}
127
128void _proxyThreadRecover(struct GBAVideoThreadProxyRenderer* proxyRenderer) {
129	MutexLock(&proxyRenderer->mutex);
130	if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
131		MutexUnlock(&proxyRenderer->mutex);
132		return;
133	}
134	RingFIFOClear(&proxyRenderer->dirtyQueue);
135	MutexUnlock(&proxyRenderer->mutex);
136	ThreadJoin(proxyRenderer->thread);
137	proxyRenderer->threadState = PROXY_THREAD_IDLE;
138	ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
139}
140
141static bool _writeData(struct mVideoProxy* proxy, const void* data, size_t length) {
142	struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context;
143	while (!RingFIFOWrite(&proxyRenderer->dirtyQueue, data, length)) {
144		mLOG(GBA_VIDEO, DEBUG, "Can't write %"PRIz"u bytes. Proxy thread asleep?", length);
145		MutexLock(&proxyRenderer->mutex);
146		if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
147			mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
148			MutexUnlock(&proxyRenderer->mutex);
149			return false;
150		}
151		ConditionWake(&proxyRenderer->toThreadCond);
152		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
153		MutexUnlock(&proxyRenderer->mutex);
154	}
155	return true;
156}
157
158static bool _readData(struct mVideoProxy* proxy, void* data, size_t length, bool block) {
159	struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context;
160	bool read = false;
161	while (true) {
162		read = RingFIFORead(&proxyRenderer->dirtyQueue, data, length);
163		if (!block || read) {
164			break;
165		}
166		mLOG(GBA_VIDEO, DEBUG, "Proxy thread can't read VRAM. CPU thread asleep?");
167		MutexLock(&proxyRenderer->mutex);
168		ConditionWake(&proxyRenderer->fromThreadCond);
169		ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
170		MutexUnlock(&proxyRenderer->mutex);
171	}
172	return read;
173}
174
175static bool _parsePacket(struct mVideoProxy* proxy, const struct mVideoProxyDirtyInfo* item) {
176	struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context;
177	switch (item->type) {
178	case DIRTY_REGISTER:
179		proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
180		break;
181	case DIRTY_PALETTE:
182		proxy->palette[item->address >> 1] = item->value;
183		proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
184		break;
185	case DIRTY_OAM:
186		proxy->oam[item->address] = item->value;
187		proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
188		break;
189	case DIRTY_VRAM:
190		proxy->readData(proxy, &proxy->vram[item->address >> 1], 0x1000, true);
191		proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address);
192		break;
193	case DIRTY_SCANLINE:
194		proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address);
195		break;
196	case DIRTY_FLUSH:
197		return false;
198	default:
199		return false;
200	}
201	return true;
202}
203
204static uint16_t* _vramBlock(struct mVideoProxy* proxy, uint32_t address) {
205	struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context;
206	return &proxyRenderer->d.vram[address >> 1];
207}
208
209uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
210	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
211	switch (address) {
212	case REG_BG0CNT:
213	case REG_BG1CNT:
214	case REG_BG2CNT:
215	case REG_BG3CNT:
216		value &= 0xFFCF;
217		break;
218	case REG_BG0HOFS:
219	case REG_BG0VOFS:
220	case REG_BG1HOFS:
221	case REG_BG1VOFS:
222	case REG_BG2HOFS:
223	case REG_BG2VOFS:
224	case REG_BG3HOFS:
225	case REG_BG3VOFS:
226		value &= 0x01FF;
227		break;
228	}
229	if (address > REG_BLDY) {
230		return value;
231	}
232
233	mVideoProxyRendererWriteVideoRegister(&proxyRenderer->proxy, address, value);
234	return value;
235}
236
237void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
238	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
239	mVideoProxyRendererWriteVRAM(&proxyRenderer->proxy, address);
240	if (renderer->cache) {
241		mTileCacheWriteVRAM(renderer->cache, address);
242	}
243}
244
245void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
246	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
247	mVideoProxyRendererWritePalette(&proxyRenderer->proxy, address, value);
248	if (renderer->cache) {
249		mTileCacheWritePalette(renderer->cache, address);
250	}
251}
252
253void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
254	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
255	mVideoProxyRendererWriteOAM(&proxyRenderer->proxy, oam, proxyRenderer->d.oam->raw[oam]);
256}
257
258void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
259	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
260	mVideoProxyRendererDrawScanline(&proxyRenderer->proxy, y);
261	if ((y & 15) == 15) {
262		ConditionWake(&proxyRenderer->toThreadCond);
263	}
264}
265
266void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
267	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
268	if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
269		mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
270		_proxyThreadRecover(proxyRenderer);
271		return;
272	}
273	MutexLock(&proxyRenderer->mutex);
274	// Insert an extra item into the queue to make sure it gets flushed
275	mVideoProxyRendererFlush(&proxyRenderer->proxy);
276	do {
277		ConditionWake(&proxyRenderer->toThreadCond);
278		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
279	} while (proxyRenderer->threadState == PROXY_THREAD_BUSY);
280	proxyRenderer->backend->finishFrame(proxyRenderer->backend);
281	MutexUnlock(&proxyRenderer->mutex);
282}
283
284static void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
285	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
286	MutexLock(&proxyRenderer->mutex);
287	// Insert an extra item into the queue to make sure it gets flushed
288	mVideoProxyRendererFlush(&proxyRenderer->proxy);
289	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
290		ConditionWake(&proxyRenderer->toThreadCond);
291		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
292	}
293	proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
294	MutexUnlock(&proxyRenderer->mutex);
295}
296
297static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
298	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
299	MutexLock(&proxyRenderer->mutex);
300	// Insert an extra item into the queue to make sure it gets flushed
301	mVideoProxyRendererFlush(&proxyRenderer->proxy);
302	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
303		ConditionWake(&proxyRenderer->toThreadCond);
304		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
305	}
306	proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
307	MutexUnlock(&proxyRenderer->mutex);
308}
309
310static THREAD_ENTRY _proxyThread(void* renderer) {
311	struct GBAVideoThreadProxyRenderer* proxyRenderer = renderer;
312	ThreadSetName("Proxy Renderer Thread");
313
314	MutexLock(&proxyRenderer->mutex);
315	while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
316		ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
317		if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
318			break;
319		}
320		proxyRenderer->threadState = PROXY_THREAD_BUSY;
321		MutexUnlock(&proxyRenderer->mutex);
322		if (!mVideoProxyRendererRun(&proxyRenderer->proxy)) {
323			// FIFO was corrupted
324			proxyRenderer->threadState = PROXY_THREAD_STOPPED;
325			mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!");
326		}
327		MutexLock(&proxyRenderer->mutex);
328		ConditionWake(&proxyRenderer->fromThreadCond);
329		if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
330			proxyRenderer->threadState = PROXY_THREAD_IDLE;
331		}
332	}
333	MutexUnlock(&proxyRenderer->mutex);
334
335#ifdef _3DS
336	svcExitThread();
337#endif
338	return 0;
339}
340
341#endif