all repos — mgba @ 30f124fae4ad0541ef77e85e03bf65cadd59fdc5

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