2017-04-14 05:13:26 +00:00
|
|
|
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
2015-07-29 06:56:45 +00:00
|
|
|
*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2017-04-27 01:41:45 +00:00
|
|
|
#include <mgba/feature/thread-proxy.h>
|
2015-07-29 06:56:45 +00:00
|
|
|
|
2016-12-31 01:00:22 +00:00
|
|
|
#include <mgba/core/tile-cache.h>
|
|
|
|
#include <mgba/internal/gba/gba.h>
|
2015-07-29 06:56:45 +00:00
|
|
|
|
2015-08-30 03:08:00 +00:00
|
|
|
#ifndef DISABLE_THREADING
|
|
|
|
|
2017-04-17 10:48:54 +00:00
|
|
|
static void mVideoThreadProxyInit(struct mVideoLogger* logger);
|
|
|
|
static void mVideoThreadProxyReset(struct mVideoLogger* logger);
|
|
|
|
static void mVideoThreadProxyDeinit(struct mVideoLogger* logger);
|
2015-07-29 06:56:45 +00:00
|
|
|
|
|
|
|
static THREAD_ENTRY _proxyThread(void* renderer);
|
|
|
|
|
2017-04-14 21:05:29 +00:00
|
|
|
static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length);
|
|
|
|
static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block);
|
|
|
|
|
2017-04-17 09:21:02 +00:00
|
|
|
static void _lock(struct mVideoLogger* logger);
|
|
|
|
static void _unlock(struct mVideoLogger* logger);
|
|
|
|
static void _wait(struct mVideoLogger* logger);
|
|
|
|
static void _wake(struct mVideoLogger* logger, int y);
|
2017-04-14 00:15:16 +00:00
|
|
|
|
2017-04-17 10:48:54 +00:00
|
|
|
void mVideoThreadProxyCreate(struct mVideoThreadProxy* renderer) {
|
|
|
|
mVideoLoggerRendererCreate(&renderer->d, false);
|
|
|
|
renderer->d.block = true;
|
|
|
|
|
|
|
|
renderer->d.init = mVideoThreadProxyInit;
|
|
|
|
renderer->d.reset = mVideoThreadProxyReset;
|
|
|
|
renderer->d.deinit = mVideoThreadProxyDeinit;
|
|
|
|
renderer->d.lock = _lock;
|
|
|
|
renderer->d.unlock = _unlock;
|
|
|
|
renderer->d.wait = _wait;
|
|
|
|
renderer->d.wake = _wake;
|
|
|
|
|
|
|
|
renderer->d.writeData = _writeData;
|
|
|
|
renderer->d.readData = _readData;
|
2015-07-29 06:56:45 +00:00
|
|
|
}
|
|
|
|
|
2017-04-17 10:48:54 +00:00
|
|
|
void mVideoThreadProxyInit(struct mVideoLogger* logger) {
|
|
|
|
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
|
2015-07-29 06:56:45 +00:00
|
|
|
ConditionInit(&proxyRenderer->fromThreadCond);
|
|
|
|
ConditionInit(&proxyRenderer->toThreadCond);
|
|
|
|
MutexInit(&proxyRenderer->mutex);
|
2016-08-15 02:40:24 +00:00
|
|
|
RingFIFOInit(&proxyRenderer->dirtyQueue, 0x40000);
|
2015-07-29 06:56:45 +00:00
|
|
|
|
|
|
|
proxyRenderer->threadState = PROXY_THREAD_IDLE;
|
|
|
|
ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
|
|
|
|
}
|
|
|
|
|
2017-04-17 10:48:54 +00:00
|
|
|
void mVideoThreadProxyReset(struct mVideoLogger* logger) {
|
|
|
|
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
|
2015-07-29 06:56:45 +00:00
|
|
|
MutexLock(&proxyRenderer->mutex);
|
|
|
|
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
|
2016-08-10 06:34:36 +00:00
|
|
|
ConditionWake(&proxyRenderer->toThreadCond);
|
2015-07-29 06:56:45 +00:00
|
|
|
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
|
|
|
}
|
|
|
|
MutexUnlock(&proxyRenderer->mutex);
|
|
|
|
}
|
|
|
|
|
2017-04-17 10:48:54 +00:00
|
|
|
void mVideoThreadProxyDeinit(struct mVideoLogger* logger) {
|
|
|
|
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
|
2015-07-29 06:56:45 +00:00
|
|
|
bool waiting = false;
|
|
|
|
MutexLock(&proxyRenderer->mutex);
|
|
|
|
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
|
|
|
|
ConditionWake(&proxyRenderer->toThreadCond);
|
|
|
|
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
|
|
|
}
|
|
|
|
if (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
|
|
|
|
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
|
|
|
|
ConditionWake(&proxyRenderer->toThreadCond);
|
|
|
|
waiting = true;
|
|
|
|
}
|
|
|
|
MutexUnlock(&proxyRenderer->mutex);
|
|
|
|
if (waiting) {
|
|
|
|
ThreadJoin(proxyRenderer->thread);
|
|
|
|
}
|
|
|
|
ConditionDeinit(&proxyRenderer->fromThreadCond);
|
|
|
|
ConditionDeinit(&proxyRenderer->toThreadCond);
|
|
|
|
MutexDeinit(&proxyRenderer->mutex);
|
|
|
|
}
|
|
|
|
|
2017-04-17 10:48:54 +00:00
|
|
|
void _proxyThreadRecover(struct mVideoThreadProxy* proxyRenderer) {
|
2016-08-18 05:17:20 +00:00
|
|
|
MutexLock(&proxyRenderer->mutex);
|
2016-08-18 07:08:04 +00:00
|
|
|
if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
|
2016-08-18 05:17:20 +00:00
|
|
|
MutexUnlock(&proxyRenderer->mutex);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
RingFIFOClear(&proxyRenderer->dirtyQueue);
|
|
|
|
MutexUnlock(&proxyRenderer->mutex);
|
|
|
|
ThreadJoin(proxyRenderer->thread);
|
|
|
|
proxyRenderer->threadState = PROXY_THREAD_IDLE;
|
|
|
|
ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
|
|
|
|
}
|
|
|
|
|
2017-04-14 21:05:29 +00:00
|
|
|
static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) {
|
2017-04-17 10:48:54 +00:00
|
|
|
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
|
2016-08-15 02:40:24 +00:00
|
|
|
while (!RingFIFOWrite(&proxyRenderer->dirtyQueue, data, length)) {
|
2016-09-17 11:34:02 +00:00
|
|
|
mLOG(GBA_VIDEO, DEBUG, "Can't write %"PRIz"u bytes. Proxy thread asleep?", length);
|
2016-08-15 02:40:24 +00:00
|
|
|
MutexLock(&proxyRenderer->mutex);
|
|
|
|
if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
|
|
|
|
mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
|
|
|
|
MutexUnlock(&proxyRenderer->mutex);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ConditionWake(&proxyRenderer->toThreadCond);
|
|
|
|
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
|
|
|
MutexUnlock(&proxyRenderer->mutex);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-04-14 21:05:29 +00:00
|
|
|
static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block) {
|
2017-04-17 10:48:54 +00:00
|
|
|
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
|
2017-04-14 05:13:26 +00:00
|
|
|
bool read = false;
|
|
|
|
while (true) {
|
|
|
|
read = RingFIFORead(&proxyRenderer->dirtyQueue, data, length);
|
|
|
|
if (!block || read) {
|
|
|
|
break;
|
|
|
|
}
|
2018-09-23 20:26:52 +00:00
|
|
|
mLOG(GBA_VIDEO, DEBUG, "Can't read %"PRIz"u bytes. CPU thread asleep?", length);
|
2017-04-14 05:13:26 +00:00
|
|
|
MutexLock(&proxyRenderer->mutex);
|
|
|
|
ConditionWake(&proxyRenderer->fromThreadCond);
|
|
|
|
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
|
|
|
|
MutexUnlock(&proxyRenderer->mutex);
|
|
|
|
}
|
|
|
|
return read;
|
|
|
|
}
|
|
|
|
|
2017-04-17 09:21:02 +00:00
|
|
|
static void _lock(struct mVideoLogger* logger) {
|
2017-04-17 10:48:54 +00:00
|
|
|
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
|
2017-04-17 09:21:02 +00:00
|
|
|
MutexLock(&proxyRenderer->mutex);
|
2015-07-29 06:56:45 +00:00
|
|
|
}
|
|
|
|
|
2017-04-17 09:21:02 +00:00
|
|
|
static void _wait(struct mVideoLogger* logger) {
|
2017-04-17 10:48:54 +00:00
|
|
|
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
|
2017-04-17 09:21:02 +00:00
|
|
|
if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
|
2016-08-15 02:40:24 +00:00
|
|
|
mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
|
2017-04-17 09:21:02 +00:00
|
|
|
_proxyThreadRecover(proxyRenderer);
|
2016-08-14 11:20:37 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-09-23 20:26:52 +00:00
|
|
|
while (RingFIFOSize(&proxyRenderer->dirtyQueue)) {
|
2017-04-17 09:21:02 +00:00
|
|
|
ConditionWake(&proxyRenderer->toThreadCond);
|
|
|
|
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
2017-04-14 21:05:29 +00:00
|
|
|
}
|
2015-07-29 06:56:45 +00:00
|
|
|
}
|
|
|
|
|
2017-04-17 09:21:02 +00:00
|
|
|
static void _unlock(struct mVideoLogger* logger) {
|
2017-04-17 10:48:54 +00:00
|
|
|
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
|
2017-04-17 09:21:02 +00:00
|
|
|
MutexUnlock(&proxyRenderer->mutex);
|
2015-07-29 06:56:45 +00:00
|
|
|
}
|
|
|
|
|
2017-04-17 09:21:02 +00:00
|
|
|
static void _wake(struct mVideoLogger* logger, int y) {
|
2017-04-17 10:48:54 +00:00
|
|
|
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
|
2017-04-14 21:05:29 +00:00
|
|
|
if ((y & 15) == 15) {
|
2017-04-17 09:21:02 +00:00
|
|
|
ConditionWake(&proxyRenderer->toThreadCond);
|
2016-08-06 17:28:30 +00:00
|
|
|
}
|
2015-07-29 06:56:45 +00:00
|
|
|
}
|
|
|
|
|
2017-04-17 10:48:54 +00:00
|
|
|
static THREAD_ENTRY _proxyThread(void* logger) {
|
|
|
|
struct mVideoThreadProxy* proxyRenderer = logger;
|
2015-07-29 06:56:45 +00:00
|
|
|
ThreadSetName("Proxy Renderer Thread");
|
|
|
|
|
|
|
|
MutexLock(&proxyRenderer->mutex);
|
2016-08-02 02:09:46 +00:00
|
|
|
while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
|
2015-07-29 06:56:45 +00:00
|
|
|
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
|
|
|
|
if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
|
|
|
|
break;
|
|
|
|
}
|
2017-04-14 05:13:26 +00:00
|
|
|
proxyRenderer->threadState = PROXY_THREAD_BUSY;
|
|
|
|
MutexUnlock(&proxyRenderer->mutex);
|
2017-04-17 10:48:54 +00:00
|
|
|
if (!mVideoLoggerRendererRun(&proxyRenderer->d, false)) {
|
2017-04-14 05:13:26 +00:00
|
|
|
// FIFO was corrupted
|
|
|
|
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
|
|
|
|
mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!");
|
2015-07-29 06:56:45 +00:00
|
|
|
}
|
2017-04-14 05:13:26 +00:00
|
|
|
MutexLock(&proxyRenderer->mutex);
|
2015-07-29 06:56:45 +00:00
|
|
|
ConditionWake(&proxyRenderer->fromThreadCond);
|
2015-09-04 08:48:24 +00:00
|
|
|
if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
|
|
|
|
proxyRenderer->threadState = PROXY_THREAD_IDLE;
|
|
|
|
}
|
2015-07-29 06:56:45 +00:00
|
|
|
}
|
|
|
|
MutexUnlock(&proxyRenderer->mutex);
|
|
|
|
|
2015-09-04 08:48:24 +00:00
|
|
|
#ifdef _3DS
|
|
|
|
svcExitThread();
|
|
|
|
#endif
|
2015-07-29 06:56:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2015-08-30 03:08:00 +00:00
|
|
|
|
|
|
|
#endif
|