all repos — mgba @ 5bed376e5cbc412db156b5046099b560a244d8f3

mGBA Game Boy Advance Emulator

src/gba/renderers/thread-proxy.c (view raw)

  1/* Copyright (c) 2013-2015 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 "thread-proxy.h"
  7
  8#include "gba/io.h"
  9
 10#include "util/memory.h"
 11
 12#ifndef DISABLE_THREADING
 13
 14enum GBAVideoDirtyType {
 15	DIRTY_DUMMY = 0,
 16	DIRTY_REGISTER,
 17	DIRTY_OAM,
 18	DIRTY_PALETTE,
 19	DIRTY_VRAM,
 20	DIRTY_SCANLINE,
 21	DIRTY_FLUSH
 22};
 23
 24struct GBAVideoDirtyInfo {
 25	enum GBAVideoDirtyType type;
 26	uint32_t address;
 27	uint16_t value;
 28	uint32_t padding;
 29};
 30
 31static void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer);
 32static void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer);
 33static void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer);
 34static uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
 35static void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
 36static void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
 37static void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
 38static void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
 39static void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer);
 40static void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels);
 41static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels);
 42
 43static THREAD_ENTRY _proxyThread(void* renderer);
 44
 45void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) {
 46	renderer->d.init = GBAVideoThreadProxyRendererInit;
 47	renderer->d.reset = GBAVideoThreadProxyRendererReset;
 48	renderer->d.deinit = GBAVideoThreadProxyRendererDeinit;
 49	renderer->d.writeVideoRegister = GBAVideoThreadProxyRendererWriteVideoRegister;
 50	renderer->d.writeVRAM = GBAVideoThreadProxyRendererWriteVRAM;
 51	renderer->d.writeOAM = GBAVideoThreadProxyRendererWriteOAM;
 52	renderer->d.writePalette = GBAVideoThreadProxyRendererWritePalette;
 53	renderer->d.drawScanline = GBAVideoThreadProxyRendererDrawScanline;
 54	renderer->d.finishFrame = GBAVideoThreadProxyRendererFinishFrame;
 55	renderer->d.getPixels = GBAVideoThreadProxyRendererGetPixels;
 56	renderer->d.putPixels = GBAVideoThreadProxyRendererPutPixels;
 57
 58	renderer->d.disableBG[0] = false;
 59	renderer->d.disableBG[1] = false;
 60	renderer->d.disableBG[2] = false;
 61	renderer->d.disableBG[3] = false;
 62	renderer->d.disableOBJ = false;
 63
 64	renderer->backend = backend;
 65}
 66
 67void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer) {
 68	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
 69	ConditionInit(&proxyRenderer->fromThreadCond);
 70	ConditionInit(&proxyRenderer->toThreadCond);
 71	MutexInit(&proxyRenderer->mutex);
 72	RingFIFOInit(&proxyRenderer->dirtyQueue, 0x40000, 0x1000);
 73	proxyRenderer->threadState = PROXY_THREAD_STOPPED;
 74
 75	proxyRenderer->vramProxy = anonymousMemoryMap(SIZE_VRAM);
 76	proxyRenderer->backend->palette = proxyRenderer->paletteProxy;
 77	proxyRenderer->backend->vram = proxyRenderer->vramProxy;
 78	proxyRenderer->backend->oam = &proxyRenderer->oamProxy;
 79
 80	proxyRenderer->backend->init(proxyRenderer->backend);
 81
 82	proxyRenderer->vramDirtyBitmap = 0;
 83	memset(proxyRenderer->oamDirtyBitmap, 0, sizeof(proxyRenderer->oamDirtyBitmap));
 84	proxyRenderer->threadState = PROXY_THREAD_IDLE;
 85	ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
 86}
 87
 88void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer) {
 89	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
 90	MutexLock(&proxyRenderer->mutex);
 91	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
 92		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
 93	}
 94	int i;
 95	for (i = 0; i < 128; ++i) {
 96		proxyRenderer->oamProxy.raw[i * 4] = 0x0200;
 97		proxyRenderer->oamProxy.raw[i * 4 + 1] = 0x0000;
 98		proxyRenderer->oamProxy.raw[i * 4 + 2] = 0x0000;
 99		proxyRenderer->oamProxy.raw[i * 4 + 3] = 0x0000;
100	}
101	proxyRenderer->backend->reset(proxyRenderer->backend);
102	MutexUnlock(&proxyRenderer->mutex);
103}
104
105void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
106	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
107	bool waiting = false;
108	MutexLock(&proxyRenderer->mutex);
109	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
110		ConditionWake(&proxyRenderer->toThreadCond);
111		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
112	}
113	if (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
114		proxyRenderer->threadState = PROXY_THREAD_STOPPED;
115		ConditionWake(&proxyRenderer->toThreadCond);
116		waiting = true;
117	}
118	MutexUnlock(&proxyRenderer->mutex);
119	if (waiting) {
120		ThreadJoin(proxyRenderer->thread);
121	}
122	ConditionDeinit(&proxyRenderer->fromThreadCond);
123	ConditionDeinit(&proxyRenderer->toThreadCond);
124	MutexDeinit(&proxyRenderer->mutex);
125	proxyRenderer->backend->deinit(proxyRenderer->backend);
126
127	mappedMemoryFree(proxyRenderer->vramProxy, SIZE_VRAM);
128}
129
130uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
131	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
132	switch (address) {
133	case REG_BG0CNT:
134	case REG_BG1CNT:
135	case REG_BG2CNT:
136	case REG_BG3CNT:
137		value &= 0xFFCF;
138		break;
139	case REG_BG0HOFS:
140	case REG_BG0VOFS:
141	case REG_BG1HOFS:
142	case REG_BG1VOFS:
143	case REG_BG2HOFS:
144	case REG_BG2VOFS:
145	case REG_BG3HOFS:
146	case REG_BG3VOFS:
147		value &= 0x01FF;
148		break;
149	}
150	if (address > REG_BLDY) {
151		return value;
152	}
153
154	struct GBAVideoDirtyInfo dirty = {
155		DIRTY_REGISTER,
156		address,
157		value,
158		0xDEADBEEF,
159	};
160	RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
161	ConditionWake(&proxyRenderer->toThreadCond);
162	return value;
163}
164
165void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
166	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
167	int bit = 1 << (address >> 12);
168	if (proxyRenderer->vramDirtyBitmap & bit) {
169		return;
170	}
171	proxyRenderer->vramDirtyBitmap |= bit;
172}
173
174void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
175	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
176	struct GBAVideoDirtyInfo dirty = {
177		DIRTY_PALETTE,
178		address,
179		value,
180		0xDEADBEEF,
181	};
182	RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
183	ConditionWake(&proxyRenderer->toThreadCond);
184}
185
186void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
187	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
188	int bit = 1 << (oam & 31);
189	int base = oam >> 5;
190	if (proxyRenderer->oamDirtyBitmap[base] & bit) {
191		return;
192	}
193	proxyRenderer->oamDirtyBitmap[base] |= bit;
194	struct GBAVideoDirtyInfo dirty = {
195		DIRTY_OAM,
196		oam,
197		proxyRenderer->d.oam->raw[oam],
198		0xDEADBEEF,
199	};
200	RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
201	ConditionWake(&proxyRenderer->toThreadCond);
202}
203
204void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
205	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
206	if (proxyRenderer->vramDirtyBitmap) {
207		int bitmap = proxyRenderer->vramDirtyBitmap;
208		proxyRenderer->vramDirtyBitmap = 0;
209		int j;
210		for (j = 0; j < 24; ++j) {
211			if (!(bitmap & (1 << j))) {
212				continue;
213			}
214			struct GBAVideoDirtyInfo dirty = {
215				DIRTY_VRAM,
216				j * 0x1000,
217				0xABCD,
218				0xDEADBEEF,
219			};
220			RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
221			RingFIFOWrite(&proxyRenderer->dirtyQueue, &proxyRenderer->d.vram[j * 0x800], 0x1000);
222		}
223	}
224	struct GBAVideoDirtyInfo dirty = {
225		DIRTY_SCANLINE,
226		y,
227		0,
228		0xDEADBEEF,
229	};
230	RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
231	ConditionWake(&proxyRenderer->toThreadCond);
232}
233
234void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
235	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
236	MutexLock(&proxyRenderer->mutex);
237	// Insert an extra item into the queue to make sure it gets flushed
238	struct GBAVideoDirtyInfo dirty = {
239		DIRTY_FLUSH,
240		0,
241		0,
242		0xDEADBEEF,
243	};
244	RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
245	do {
246		ConditionWake(&proxyRenderer->toThreadCond);
247		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
248	} while (proxyRenderer->threadState == PROXY_THREAD_BUSY);
249	proxyRenderer->backend->finishFrame(proxyRenderer->backend);
250	proxyRenderer->vramDirtyBitmap = 0;
251	memset(proxyRenderer->oamDirtyBitmap, 0, sizeof(proxyRenderer->oamDirtyBitmap));
252	MutexUnlock(&proxyRenderer->mutex);
253}
254
255void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels) {
256	// TODO
257}
258
259void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels) {
260	// TODO
261}
262
263static THREAD_ENTRY _proxyThread(void* renderer) {
264	struct GBAVideoThreadProxyRenderer* proxyRenderer = renderer;
265	ThreadSetName("Proxy Renderer Thread");
266
267	MutexLock(&proxyRenderer->mutex);
268	while (1) {
269		ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
270		if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
271			break;
272		}
273		proxyRenderer->threadState = PROXY_THREAD_BUSY;
274
275		MutexUnlock(&proxyRenderer->mutex);
276		struct GBAVideoDirtyInfo item;
277		while (RingFIFORead(&proxyRenderer->dirtyQueue, &item, sizeof(item))) {
278			switch (item.type) {
279			case DIRTY_REGISTER:
280				proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item.address, item.value);
281				break;
282			case DIRTY_PALETTE:
283				proxyRenderer->paletteProxy[item.address >> 1] = item.value;
284				proxyRenderer->backend->writePalette(proxyRenderer->backend, item.address, item.value);
285				break;
286			case DIRTY_OAM:
287				proxyRenderer->oamProxy.raw[item.address] = item.value;
288				proxyRenderer->backend->writeOAM(proxyRenderer->backend, item.address);
289				break;
290			case DIRTY_VRAM:
291				while (!RingFIFORead(&proxyRenderer->dirtyQueue, &proxyRenderer->vramProxy[item.address >> 1], 0x1000));
292				proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item.address);
293				break;
294			case DIRTY_SCANLINE:
295				proxyRenderer->backend->drawScanline(proxyRenderer->backend, item.address);
296				break;
297			case DIRTY_FLUSH:
298				// This is only here to ensure the queue gets flushed
299				break;
300			default:
301				// FIFO was corrupted
302				proxyRenderer->threadState = PROXY_THREAD_STOPPED;
303				break;
304			}
305		}
306		MutexLock(&proxyRenderer->mutex);
307		ConditionWake(&proxyRenderer->fromThreadCond);
308		if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
309			proxyRenderer->threadState = PROXY_THREAD_IDLE;
310		} else {
311			break;
312		}
313	}
314	MutexUnlock(&proxyRenderer->mutex);
315
316#ifdef _3DS
317	svcExitThread();
318#endif
319	return 0;
320}
321
322#endif