all repos — mgba @ 9264441e0991eb0baf2d180d2ffd16c5cf815456

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	proxyRenderer->threadState = PROXY_THREAD_IDLE;
 84	ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
 85}
 86
 87void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer) {
 88	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
 89	MutexLock(&proxyRenderer->mutex);
 90	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
 91		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
 92	}
 93	memcpy(&proxyRenderer->oamProxy.raw, &renderer->oam->raw, SIZE_OAM);
 94	memcpy(proxyRenderer->paletteProxy, renderer->palette, SIZE_PALETTE_RAM);
 95	memcpy(proxyRenderer->vramProxy, renderer->vram, SIZE_VRAM);
 96	proxyRenderer->backend->reset(proxyRenderer->backend);
 97	MutexUnlock(&proxyRenderer->mutex);
 98}
 99
100void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
101	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
102	bool waiting = false;
103	MutexLock(&proxyRenderer->mutex);
104	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
105		ConditionWake(&proxyRenderer->toThreadCond);
106		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
107	}
108	if (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
109		proxyRenderer->threadState = PROXY_THREAD_STOPPED;
110		ConditionWake(&proxyRenderer->toThreadCond);
111		waiting = true;
112	}
113	MutexUnlock(&proxyRenderer->mutex);
114	if (waiting) {
115		ThreadJoin(proxyRenderer->thread);
116	}
117	ConditionDeinit(&proxyRenderer->fromThreadCond);
118	ConditionDeinit(&proxyRenderer->toThreadCond);
119	MutexDeinit(&proxyRenderer->mutex);
120	proxyRenderer->backend->deinit(proxyRenderer->backend);
121
122	mappedMemoryFree(proxyRenderer->vramProxy, SIZE_VRAM);
123}
124
125uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
126	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
127	switch (address) {
128	case REG_BG0CNT:
129	case REG_BG1CNT:
130	case REG_BG2CNT:
131	case REG_BG3CNT:
132		value &= 0xFFCF;
133		break;
134	case REG_BG0HOFS:
135	case REG_BG0VOFS:
136	case REG_BG1HOFS:
137	case REG_BG1VOFS:
138	case REG_BG2HOFS:
139	case REG_BG2VOFS:
140	case REG_BG3HOFS:
141	case REG_BG3VOFS:
142		value &= 0x01FF;
143		break;
144	}
145	if (address > REG_BLDY) {
146		return value;
147	}
148
149	struct GBAVideoDirtyInfo dirty = {
150		DIRTY_REGISTER,
151		address,
152		value,
153		0xDEADBEEF,
154	};
155	RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
156	return value;
157}
158
159void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
160	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
161	int bit = 1 << (address >> 12);
162	if (proxyRenderer->vramDirtyBitmap & bit) {
163		return;
164	}
165	proxyRenderer->vramDirtyBitmap |= bit;
166}
167
168void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
169	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
170	struct GBAVideoDirtyInfo dirty = {
171		DIRTY_PALETTE,
172		address,
173		value,
174		0xDEADBEEF,
175	};
176	RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
177}
178
179void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
180	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
181	struct GBAVideoDirtyInfo dirty = {
182		DIRTY_OAM,
183		oam,
184		proxyRenderer->d.oam->raw[oam],
185		0xDEADBEEF,
186	};
187	RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
188}
189
190void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
191	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
192	if (proxyRenderer->vramDirtyBitmap) {
193		int bitmap = proxyRenderer->vramDirtyBitmap;
194		proxyRenderer->vramDirtyBitmap = 0;
195		int j;
196		for (j = 0; j < 24; ++j) {
197			if (!(bitmap & (1 << j))) {
198				continue;
199			}
200			struct GBAVideoDirtyInfo dirty = {
201				DIRTY_VRAM,
202				j * 0x1000,
203				0xABCD,
204				0xDEADBEEF,
205			};
206			RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
207			RingFIFOWrite(&proxyRenderer->dirtyQueue, &proxyRenderer->d.vram[j * 0x800], 0x1000);
208		}
209	}
210	struct GBAVideoDirtyInfo dirty = {
211		DIRTY_SCANLINE,
212		y,
213		0,
214		0xDEADBEEF,
215	};
216	RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
217	if ((y & 15) == 15) {
218		ConditionWake(&proxyRenderer->toThreadCond);
219	}
220}
221
222void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
223	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
224	MutexLock(&proxyRenderer->mutex);
225	// Insert an extra item into the queue to make sure it gets flushed
226	struct GBAVideoDirtyInfo dirty = {
227		DIRTY_FLUSH,
228		0,
229		0,
230		0xDEADBEEF,
231	};
232	RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
233	do {
234		ConditionWake(&proxyRenderer->toThreadCond);
235		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
236	} while (proxyRenderer->threadState == PROXY_THREAD_BUSY);
237	proxyRenderer->backend->finishFrame(proxyRenderer->backend);
238	proxyRenderer->vramDirtyBitmap = 0;
239	MutexUnlock(&proxyRenderer->mutex);
240}
241
242void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels) {
243	// TODO
244}
245
246void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels) {
247	// TODO
248}
249
250static THREAD_ENTRY _proxyThread(void* renderer) {
251	struct GBAVideoThreadProxyRenderer* proxyRenderer = renderer;
252	ThreadSetName("Proxy Renderer Thread");
253
254	MutexLock(&proxyRenderer->mutex);
255	while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
256		ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
257		if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
258			break;
259		}
260		proxyRenderer->threadState = PROXY_THREAD_BUSY;
261
262		MutexUnlock(&proxyRenderer->mutex);
263		struct GBAVideoDirtyInfo item;
264		while (RingFIFORead(&proxyRenderer->dirtyQueue, &item, sizeof(item))) {
265			switch (item.type) {
266			case DIRTY_REGISTER:
267				proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item.address, item.value);
268				break;
269			case DIRTY_PALETTE:
270				proxyRenderer->paletteProxy[item.address >> 1] = item.value;
271				proxyRenderer->backend->writePalette(proxyRenderer->backend, item.address, item.value);
272				break;
273			case DIRTY_OAM:
274				proxyRenderer->oamProxy.raw[item.address] = item.value;
275				proxyRenderer->backend->writeOAM(proxyRenderer->backend, item.address);
276				break;
277			case DIRTY_VRAM:
278				while (!RingFIFORead(&proxyRenderer->dirtyQueue, &proxyRenderer->vramProxy[item.address >> 1], 0x1000));
279				proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item.address);
280				break;
281			case DIRTY_SCANLINE:
282				proxyRenderer->backend->drawScanline(proxyRenderer->backend, item.address);
283				break;
284			case DIRTY_FLUSH:
285				// This is only here to ensure the queue gets flushed
286				break;
287			default:
288				// FIFO was corrupted
289				proxyRenderer->threadState = PROXY_THREAD_STOPPED;
290				break;
291			}
292		}
293		MutexLock(&proxyRenderer->mutex);
294		ConditionWake(&proxyRenderer->fromThreadCond);
295		if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
296			proxyRenderer->threadState = PROXY_THREAD_IDLE;
297		}
298	}
299	MutexUnlock(&proxyRenderer->mutex);
300
301#ifdef _3DS
302	svcExitThread();
303#endif
304	return 0;
305}
306
307#endif