all repos — mgba @ fbb02475dae7e39e7b29341a8b62031cb68f2420

mGBA Game Boy Advance Emulator

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

  1/* Copyright (c) 2013-2017 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 <mgba/internal/gba/renderers/thread-proxy.h>
  7
  8#include <mgba/core/tile-cache.h>
  9#include <mgba/internal/gba/gba.h>
 10#include <mgba/internal/gba/io.h>
 11
 12#ifndef DISABLE_THREADING
 13
 14static void GBAVideoThreadProxyRendererInit(struct GBAVideoProxyRenderer* renderer);
 15static void GBAVideoThreadProxyRendererReset(struct GBAVideoProxyRenderer* renderer);
 16static void GBAVideoThreadProxyRendererDeinit(struct GBAVideoProxyRenderer* renderer);
 17
 18static THREAD_ENTRY _proxyThread(void* renderer);
 19
 20static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length);
 21static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block);
 22
 23static void _lock(struct GBAVideoProxyRenderer* proxyRenderer);
 24static void _unlock(struct GBAVideoProxyRenderer* proxyRenderer);
 25static void _wait(struct GBAVideoProxyRenderer* proxyRenderer);
 26static void _wake(struct GBAVideoProxyRenderer* proxyRenderer, int y);
 27
 28void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) {
 29	renderer->d.block = true;
 30	GBAVideoProxyRendererCreate(&renderer->d, backend, false);
 31
 32	renderer->d.init = GBAVideoThreadProxyRendererInit;
 33	renderer->d.reset = GBAVideoThreadProxyRendererReset;
 34	renderer->d.deinit = GBAVideoThreadProxyRendererDeinit;
 35	renderer->d.lock = _lock;
 36	renderer->d.unlock = _unlock;
 37	renderer->d.wait = _wait;
 38	renderer->d.wake = _wake;
 39
 40	renderer->d.logger.writeData = _writeData;
 41	renderer->d.logger.readData = _readData;
 42	renderer->d.logger.vf = NULL;
 43}
 44
 45void GBAVideoThreadProxyRendererInit(struct GBAVideoProxyRenderer* renderer) {
 46	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
 47	ConditionInit(&proxyRenderer->fromThreadCond);
 48	ConditionInit(&proxyRenderer->toThreadCond);
 49	MutexInit(&proxyRenderer->mutex);
 50	RingFIFOInit(&proxyRenderer->dirtyQueue, 0x40000);
 51
 52	proxyRenderer->threadState = PROXY_THREAD_IDLE;
 53	ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
 54}
 55
 56void GBAVideoThreadProxyRendererReset(struct GBAVideoProxyRenderer* renderer) {
 57	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
 58	MutexLock(&proxyRenderer->mutex);
 59	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
 60		ConditionWake(&proxyRenderer->toThreadCond);
 61		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
 62	}
 63	MutexUnlock(&proxyRenderer->mutex);
 64}
 65
 66void GBAVideoThreadProxyRendererDeinit(struct GBAVideoProxyRenderer* renderer) {
 67	struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
 68	bool waiting = false;
 69	MutexLock(&proxyRenderer->mutex);
 70	while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
 71		ConditionWake(&proxyRenderer->toThreadCond);
 72		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
 73	}
 74	if (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
 75		proxyRenderer->threadState = PROXY_THREAD_STOPPED;
 76		ConditionWake(&proxyRenderer->toThreadCond);
 77		waiting = true;
 78	}
 79	MutexUnlock(&proxyRenderer->mutex);
 80	if (waiting) {
 81		ThreadJoin(proxyRenderer->thread);
 82	}
 83	ConditionDeinit(&proxyRenderer->fromThreadCond);
 84	ConditionDeinit(&proxyRenderer->toThreadCond);
 85	MutexDeinit(&proxyRenderer->mutex);
 86}
 87
 88void _proxyThreadRecover(struct GBAVideoThreadProxyRenderer* proxyRenderer) {
 89	MutexLock(&proxyRenderer->mutex);
 90	if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
 91		MutexUnlock(&proxyRenderer->mutex);
 92		return;
 93	}
 94	RingFIFOClear(&proxyRenderer->dirtyQueue);
 95	MutexUnlock(&proxyRenderer->mutex);
 96	ThreadJoin(proxyRenderer->thread);
 97	proxyRenderer->threadState = PROXY_THREAD_IDLE;
 98	ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
 99}
100
101static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) {
102	struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context;
103	while (!RingFIFOWrite(&proxyRenderer->dirtyQueue, data, length)) {
104		mLOG(GBA_VIDEO, DEBUG, "Can't write %"PRIz"u bytes. Proxy thread asleep?", length);
105		MutexLock(&proxyRenderer->mutex);
106		if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
107			mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
108			MutexUnlock(&proxyRenderer->mutex);
109			return false;
110		}
111		ConditionWake(&proxyRenderer->toThreadCond);
112		ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
113		MutexUnlock(&proxyRenderer->mutex);
114	}
115	return true;
116}
117
118static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block) {
119	struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context;
120	bool read = false;
121	while (true) {
122		read = RingFIFORead(&proxyRenderer->dirtyQueue, data, length);
123		if (!block || read) {
124			break;
125		}
126		mLOG(GBA_VIDEO, DEBUG, "Proxy thread can't read VRAM. CPU thread asleep?");
127		MutexLock(&proxyRenderer->mutex);
128		ConditionWake(&proxyRenderer->fromThreadCond);
129		ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
130		MutexUnlock(&proxyRenderer->mutex);
131	}
132	return read;
133}
134
135static void _lock(struct GBAVideoProxyRenderer* proxyRenderer) {
136	struct GBAVideoThreadProxyRenderer* threadProxy = (struct GBAVideoThreadProxyRenderer*) proxyRenderer;
137	MutexLock(&threadProxy->mutex);
138}
139
140static void _wait(struct GBAVideoProxyRenderer* proxyRenderer) {
141	struct GBAVideoThreadProxyRenderer* threadProxy = (struct GBAVideoThreadProxyRenderer*) proxyRenderer;
142	if (threadProxy->threadState == PROXY_THREAD_STOPPED) {
143		mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
144		_proxyThreadRecover(threadProxy);
145		return;
146	}
147	while (threadProxy->threadState == PROXY_THREAD_BUSY) {
148		ConditionWake(&threadProxy->toThreadCond);
149		ConditionWait(&threadProxy->fromThreadCond, &threadProxy->mutex);
150	}
151}
152
153static void _unlock(struct GBAVideoProxyRenderer* proxyRenderer) {
154	struct GBAVideoThreadProxyRenderer* threadProxy = (struct GBAVideoThreadProxyRenderer*) proxyRenderer;
155	MutexUnlock(&threadProxy->mutex);
156}
157
158static void _wake(struct GBAVideoProxyRenderer* proxyRenderer, int y) {
159	struct GBAVideoThreadProxyRenderer* threadProxy = (struct GBAVideoThreadProxyRenderer*) proxyRenderer;
160	if ((y & 15) == 15) {
161		ConditionWake(&threadProxy->toThreadCond);
162	}
163}
164
165static THREAD_ENTRY _proxyThread(void* renderer) {
166	struct GBAVideoThreadProxyRenderer* proxyRenderer = renderer;
167	ThreadSetName("Proxy Renderer Thread");
168
169	MutexLock(&proxyRenderer->mutex);
170	while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
171		ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
172		if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
173			break;
174		}
175		proxyRenderer->threadState = PROXY_THREAD_BUSY;
176		MutexUnlock(&proxyRenderer->mutex);
177		if (!mVideoLoggerRendererRun(&proxyRenderer->d.logger)) {
178			// FIFO was corrupted
179			proxyRenderer->threadState = PROXY_THREAD_STOPPED;
180			mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!");
181		}
182		MutexLock(&proxyRenderer->mutex);
183		ConditionWake(&proxyRenderer->fromThreadCond);
184		if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
185			proxyRenderer->threadState = PROXY_THREAD_IDLE;
186		}
187	}
188	MutexUnlock(&proxyRenderer->mutex);
189
190#ifdef _3DS
191	svcExitThread();
192#endif
193	return 0;
194}
195
196#endif