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