all repos — mgba @ 43ec351d498ef10b8e099c16ba106ab3aa54e1c6

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	memset(proxyRenderer->paletteDirtyBitmap, 0, sizeof(proxyRenderer->paletteDirtyBitmap));
 68	memset(proxyRenderer->regDirtyBitmap, 0, sizeof(proxyRenderer->regDirtyBitmap));
 69	proxyRenderer->threadState = PROXY_THREAD_IDLE;
 70	ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
 71}
 72
 73void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer) {
 74	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
 75	MutexLock(&proxyRenderer->mutex);
 76	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
 77		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
 78	}
 79	int i;
 80	for (i = 0; i < 128; ++i) {
 81		proxyRenderer->oamProxy.raw[i * 4] = 0x0200;
 82		proxyRenderer->oamProxy.raw[i * 4 + 1] = 0x0000;
 83		proxyRenderer->oamProxy.raw[i * 4 + 2] = 0x0000;
 84		proxyRenderer->oamProxy.raw[i * 4 + 3] = 0x0000;
 85	}
 86	proxyRenderer->backend->reset(proxyRenderer->backend);
 87	MutexUnlock(&proxyRenderer->mutex);
 88}
 89
 90void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
 91	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
 92	bool waiting = false;
 93	MutexLock(&proxyRenderer->mutex);
 94	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
 95		ConditionWake(&proxyRenderer->toThreadCond);
 96		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
 97	}
 98	if (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
 99		proxyRenderer->threadState = PROXY_THREAD_STOPPED;
100		ConditionWake(&proxyRenderer->toThreadCond);
101		waiting = true;
102	}
103	MutexUnlock(&proxyRenderer->mutex);
104	if (waiting) {
105		ThreadJoin(proxyRenderer->thread);
106	}
107	ConditionDeinit(&proxyRenderer->fromThreadCond);
108	ConditionDeinit(&proxyRenderer->toThreadCond);
109	MutexDeinit(&proxyRenderer->mutex);
110	proxyRenderer->backend->deinit(proxyRenderer->backend);
111
112	mappedMemoryFree(proxyRenderer->vramProxy, SIZE_VRAM);
113}
114
115uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
116	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
117	switch (address) {
118	case REG_BG0CNT:
119	case REG_BG1CNT:
120	case REG_BG2CNT:
121	case REG_BG3CNT:
122		value &= 0xFFCF;
123		break;
124	case REG_BG0HOFS:
125	case REG_BG0VOFS:
126	case REG_BG1HOFS:
127	case REG_BG1VOFS:
128	case REG_BG2HOFS:
129	case REG_BG2VOFS:
130	case REG_BG3HOFS:
131	case REG_BG3VOFS:
132		value &= 0x01FF;
133		break;
134	}
135	if (address > REG_BLDY) {
136		return value;
137	}
138	proxyRenderer->regProxy[address >> 1] = value;
139	int bit = 1 << ((address >> 1) & 31);
140	int base = address >> 6;
141	if (proxyRenderer->regDirtyBitmap[base] & bit) {
142		return value;
143	}
144	proxyRenderer->regDirtyBitmap[base] |= bit;
145
146	struct GBAVideoDirtyInfo dirty = {
147		DIRTY_REGISTER,
148		address
149	};
150	*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
151	return value;
152}
153
154void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
155	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
156	int bit = 1 << (address >> 12);
157	if (proxyRenderer->vramDirtyBitmap & bit) {
158		return;
159	}
160	proxyRenderer->vramDirtyBitmap |= bit;
161	struct GBAVideoDirtyInfo dirty = {
162		DIRTY_VRAM,
163		address
164	};
165	*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
166}
167
168void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
169	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
170	proxyRenderer->paletteProxy[address >> 1] = value;
171	int bit = 1 << ((address >> 1) & 31);
172	int base = address >> 6;
173	if (proxyRenderer->paletteDirtyBitmap[base] & bit) {
174		return;
175	}
176	proxyRenderer->paletteDirtyBitmap[base] |= bit;
177	struct GBAVideoDirtyInfo dirty = {
178		DIRTY_PALETTE,
179		address
180	};
181	*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
182}
183
184void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
185	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
186	proxyRenderer->oamProxy.raw[oam] = proxyRenderer->d.oam->raw[oam];
187	int bit = 1 << (oam & 31);
188	int base = oam >> 5;
189	if (proxyRenderer->oamDirtyBitmap[base] & bit) {
190		return;
191	}
192	proxyRenderer->oamDirtyBitmap[base] |= bit;
193	struct GBAVideoDirtyInfo dirty = {
194		DIRTY_OAM,
195		oam
196	};
197	*GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
198}
199
200void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
201	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
202	MutexLock(&proxyRenderer->mutex);
203	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
204		ConditionWake(&proxyRenderer->toThreadCond);
205		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
206	}
207	proxyRenderer->y = y;
208	ConditionWake(&proxyRenderer->toThreadCond);
209	while (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
210		ConditionWake(&proxyRenderer->toThreadCond);
211		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
212	}
213	MutexUnlock(&proxyRenderer->mutex);
214}
215
216void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
217	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
218	MutexLock(&proxyRenderer->mutex);
219	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
220		ConditionWake(&proxyRenderer->toThreadCond);
221		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
222	}
223	proxyRenderer->backend->finishFrame(proxyRenderer->backend);
224	MutexUnlock(&proxyRenderer->mutex);
225}
226
227void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels) {
228	// TODO
229}
230
231void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels) {
232	// TODO
233}
234
235
236static THREAD_ENTRY _proxyThread(void* renderer) {
237	struct GBAVideoThreadProxyRenderer* proxyRenderer = renderer;
238	ThreadSetName("Proxy Renderer Thread");
239
240	MutexLock(&proxyRenderer->mutex);
241	while (1) {
242		ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
243		if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
244			break;
245		}
246		proxyRenderer->threadState = PROXY_THREAD_BUSY;
247		proxyRenderer->vramDirtyBitmap = 0;
248		memset(proxyRenderer->oamDirtyBitmap, 0, sizeof(proxyRenderer->oamDirtyBitmap));
249		memset(proxyRenderer->paletteDirtyBitmap, 0, sizeof(proxyRenderer->paletteDirtyBitmap));
250		memset(proxyRenderer->regDirtyBitmap, 0, sizeof(proxyRenderer->regDirtyBitmap));
251		size_t queueSize = GBAVideoDirtyQueueSize(&proxyRenderer->dirtyQueue);
252		struct GBAVideoDirtyInfo* queue = malloc(queueSize * sizeof(struct GBAVideoDirtyInfo));
253		memcpy(queue, GBAVideoDirtyQueueGetPointer(&proxyRenderer->dirtyQueue, 0), queueSize * sizeof(struct GBAVideoDirtyInfo));
254		GBAVideoDirtyQueueClear(&proxyRenderer->dirtyQueue);
255		size_t i;
256		for (i = 0; i < queueSize; ++i) {
257			switch (queue[i].type) {
258			case DIRTY_REGISTER:
259				proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, queue[i].address, proxyRenderer->regProxy[queue[i].address >> 1]);
260				break;
261			case DIRTY_VRAM:
262				proxyRenderer->backend->writeVRAM(proxyRenderer->backend, queue[i].address);
263				memcpy(&proxyRenderer->vramProxy[(queue[i].address & ~0xFFF) >> 1], &proxyRenderer->d.vram[(queue[i].address & ~0xFFF) >> 1], 0x1000);
264				break;
265			case DIRTY_PALETTE:
266				proxyRenderer->backend->writePalette(proxyRenderer->backend, queue[i].address, proxyRenderer->paletteProxy[queue[i].address >> 1]);
267				break;
268			case DIRTY_OAM:
269				proxyRenderer->backend->writeOAM(proxyRenderer->backend, queue[i].address);
270				break;
271			}
272		}
273		free(queue);
274		ConditionWake(&proxyRenderer->fromThreadCond);
275		ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
276		MutexUnlock(&proxyRenderer->mutex);
277
278		proxyRenderer->backend->drawScanline(proxyRenderer->backend, proxyRenderer->y);
279
280		MutexLock(&proxyRenderer->mutex);
281		proxyRenderer->threadState = PROXY_THREAD_IDLE;
282		ConditionWake(&proxyRenderer->fromThreadCond);
283	}
284	MutexUnlock(&proxyRenderer->mutex);
285
286	return 0;
287}