all repos — mgba @ 0cfff99652a9d9324f9904b3223128d324096422

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