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#include <mgba-util/memory.h>
13
14#ifndef DISABLE_THREADING
15
16static void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer);
17static void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer);
18static void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer);
19static uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
20static void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
21static void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
22static void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
23static void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
24static void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer);
25static void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels);
26static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
27
28static THREAD_ENTRY _proxyThread(void* renderer);
29
30static bool _writeData(struct mVideoProxy* proxy, const void* data, size_t length);
31static bool _readData(struct mVideoProxy* proxy, void* data, size_t length, bool block);
32static bool _parsePacket(struct mVideoProxy* proxy, const struct mVideoProxyDirtyInfo* packet);
33static uint16_t* _vramBlock(struct mVideoProxy* proxy, uint32_t address);
34
35void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) {
36 renderer->d.init = GBAVideoThreadProxyRendererInit;
37 renderer->d.reset = GBAVideoThreadProxyRendererReset;
38 renderer->d.deinit = GBAVideoThreadProxyRendererDeinit;
39 renderer->d.writeVideoRegister = GBAVideoThreadProxyRendererWriteVideoRegister;
40 renderer->d.writeVRAM = GBAVideoThreadProxyRendererWriteVRAM;
41 renderer->d.writeOAM = GBAVideoThreadProxyRendererWriteOAM;
42 renderer->d.writePalette = GBAVideoThreadProxyRendererWritePalette;
43 renderer->d.drawScanline = GBAVideoThreadProxyRendererDrawScanline;
44 renderer->d.finishFrame = GBAVideoThreadProxyRendererFinishFrame;
45 renderer->d.getPixels = GBAVideoThreadProxyRendererGetPixels;
46 renderer->d.putPixels = GBAVideoThreadProxyRendererPutPixels;
47
48 renderer->d.disableBG[0] = false;
49 renderer->d.disableBG[1] = false;
50 renderer->d.disableBG[2] = false;
51 renderer->d.disableBG[3] = false;
52 renderer->d.disableOBJ = false;
53
54 renderer->proxy.context = renderer;
55 renderer->proxy.writeData = _writeData;
56 renderer->proxy.readData = _readData;
57 renderer->proxy.parsePacket = _parsePacket;
58 renderer->proxy.vramBlock = _vramBlock;
59 renderer->proxy.paletteSize = SIZE_PALETTE_RAM;
60 renderer->proxy.vramSize = SIZE_VRAM;
61 renderer->proxy.oamSize = SIZE_OAM;
62
63 renderer->backend = backend;
64}
65
66void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer) {
67 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
68 ConditionInit(&proxyRenderer->fromThreadCond);
69 ConditionInit(&proxyRenderer->toThreadCond);
70 MutexInit(&proxyRenderer->mutex);
71 RingFIFOInit(&proxyRenderer->dirtyQueue, 0x40000);
72
73 mVideoProxyRendererInit(&proxyRenderer->proxy);
74
75 proxyRenderer->backend->palette = proxyRenderer->proxy.palette;
76 proxyRenderer->backend->vram = proxyRenderer->proxy.vram;
77 proxyRenderer->backend->oam = (union GBAOAM*) proxyRenderer->proxy.oam;
78 proxyRenderer->backend->cache = NULL;
79
80 proxyRenderer->backend->init(proxyRenderer->backend);
81
82 proxyRenderer->threadState = PROXY_THREAD_IDLE;
83 ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
84}
85
86void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer) {
87 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
88 MutexLock(&proxyRenderer->mutex);
89 while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
90 ConditionWake(&proxyRenderer->toThreadCond);
91 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
92 }
93 memcpy(proxyRenderer->proxy.oam, &renderer->oam->raw, SIZE_OAM);
94 memcpy(proxyRenderer->proxy.palette, renderer->palette, SIZE_PALETTE_RAM);
95 memcpy(proxyRenderer->proxy.vram, renderer->vram, SIZE_VRAM);
96
97 mVideoProxyRendererReset(&proxyRenderer->proxy);
98
99 proxyRenderer->backend->reset(proxyRenderer->backend);
100 MutexUnlock(&proxyRenderer->mutex);
101}
102
103void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
104 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
105 bool waiting = false;
106 MutexLock(&proxyRenderer->mutex);
107 while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
108 ConditionWake(&proxyRenderer->toThreadCond);
109 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
110 }
111 if (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
112 proxyRenderer->threadState = PROXY_THREAD_STOPPED;
113 ConditionWake(&proxyRenderer->toThreadCond);
114 waiting = true;
115 }
116 MutexUnlock(&proxyRenderer->mutex);
117 if (waiting) {
118 ThreadJoin(proxyRenderer->thread);
119 }
120 ConditionDeinit(&proxyRenderer->fromThreadCond);
121 ConditionDeinit(&proxyRenderer->toThreadCond);
122 MutexDeinit(&proxyRenderer->mutex);
123 proxyRenderer->backend->deinit(proxyRenderer->backend);
124
125 mVideoProxyRendererDeinit(&proxyRenderer->proxy);
126}
127
128void _proxyThreadRecover(struct GBAVideoThreadProxyRenderer* proxyRenderer) {
129 MutexLock(&proxyRenderer->mutex);
130 if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
131 MutexUnlock(&proxyRenderer->mutex);
132 return;
133 }
134 RingFIFOClear(&proxyRenderer->dirtyQueue);
135 MutexUnlock(&proxyRenderer->mutex);
136 ThreadJoin(proxyRenderer->thread);
137 proxyRenderer->threadState = PROXY_THREAD_IDLE;
138 ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
139}
140
141static bool _writeData(struct mVideoProxy* proxy, const void* data, size_t length) {
142 struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context;
143 while (!RingFIFOWrite(&proxyRenderer->dirtyQueue, data, length)) {
144 mLOG(GBA_VIDEO, DEBUG, "Can't write %"PRIz"u bytes. Proxy thread asleep?", length);
145 MutexLock(&proxyRenderer->mutex);
146 if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
147 mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
148 MutexUnlock(&proxyRenderer->mutex);
149 return false;
150 }
151 ConditionWake(&proxyRenderer->toThreadCond);
152 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
153 MutexUnlock(&proxyRenderer->mutex);
154 }
155 return true;
156}
157
158static bool _readData(struct mVideoProxy* proxy, void* data, size_t length, bool block) {
159 struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context;
160 bool read = false;
161 while (true) {
162 read = RingFIFORead(&proxyRenderer->dirtyQueue, data, length);
163 if (!block || read) {
164 break;
165 }
166 mLOG(GBA_VIDEO, DEBUG, "Proxy thread can't read VRAM. CPU thread asleep?");
167 MutexLock(&proxyRenderer->mutex);
168 ConditionWake(&proxyRenderer->fromThreadCond);
169 ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
170 MutexUnlock(&proxyRenderer->mutex);
171 }
172 return read;
173}
174
175static bool _parsePacket(struct mVideoProxy* proxy, const struct mVideoProxyDirtyInfo* item) {
176 struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context;
177 switch (item->type) {
178 case DIRTY_REGISTER:
179 proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
180 break;
181 case DIRTY_PALETTE:
182 proxy->palette[item->address >> 1] = item->value;
183 proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
184 break;
185 case DIRTY_OAM:
186 proxy->oam[item->address] = item->value;
187 proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
188 break;
189 case DIRTY_VRAM:
190 proxy->readData(proxy, &proxy->vram[item->address >> 1], 0x1000, true);
191 proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address);
192 break;
193 case DIRTY_SCANLINE:
194 proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address);
195 break;
196 case DIRTY_FLUSH:
197 return false;
198 default:
199 return false;
200 }
201 return true;
202}
203
204static uint16_t* _vramBlock(struct mVideoProxy* proxy, uint32_t address) {
205 struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context;
206 return &proxyRenderer->d.vram[address >> 1];
207}
208
209uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
210 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
211 switch (address) {
212 case REG_BG0CNT:
213 case REG_BG1CNT:
214 case REG_BG2CNT:
215 case REG_BG3CNT:
216 value &= 0xFFCF;
217 break;
218 case REG_BG0HOFS:
219 case REG_BG0VOFS:
220 case REG_BG1HOFS:
221 case REG_BG1VOFS:
222 case REG_BG2HOFS:
223 case REG_BG2VOFS:
224 case REG_BG3HOFS:
225 case REG_BG3VOFS:
226 value &= 0x01FF;
227 break;
228 }
229 if (address > REG_BLDY) {
230 return value;
231 }
232
233 mVideoProxyRendererWriteVideoRegister(&proxyRenderer->proxy, address, value);
234 return value;
235}
236
237void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
238 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
239 mVideoProxyRendererWriteVRAM(&proxyRenderer->proxy, address);
240 if (renderer->cache) {
241 mTileCacheWriteVRAM(renderer->cache, address);
242 }
243}
244
245void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
246 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
247 mVideoProxyRendererWritePalette(&proxyRenderer->proxy, address, value);
248 if (renderer->cache) {
249 mTileCacheWritePalette(renderer->cache, address);
250 }
251}
252
253void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
254 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
255 mVideoProxyRendererWriteOAM(&proxyRenderer->proxy, oam, proxyRenderer->d.oam->raw[oam]);
256}
257
258void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
259 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
260 mVideoProxyRendererDrawScanline(&proxyRenderer->proxy, y);
261 if ((y & 15) == 15) {
262 ConditionWake(&proxyRenderer->toThreadCond);
263 }
264}
265
266void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
267 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
268 if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
269 mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
270 _proxyThreadRecover(proxyRenderer);
271 return;
272 }
273 MutexLock(&proxyRenderer->mutex);
274 // Insert an extra item into the queue to make sure it gets flushed
275 mVideoProxyRendererFlush(&proxyRenderer->proxy);
276 do {
277 ConditionWake(&proxyRenderer->toThreadCond);
278 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
279 } while (proxyRenderer->threadState == PROXY_THREAD_BUSY);
280 proxyRenderer->backend->finishFrame(proxyRenderer->backend);
281 MutexUnlock(&proxyRenderer->mutex);
282}
283
284static void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
285 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
286 MutexLock(&proxyRenderer->mutex);
287 // Insert an extra item into the queue to make sure it gets flushed
288 mVideoProxyRendererFlush(&proxyRenderer->proxy);
289 while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
290 ConditionWake(&proxyRenderer->toThreadCond);
291 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
292 }
293 proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
294 MutexUnlock(&proxyRenderer->mutex);
295}
296
297static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
298 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
299 MutexLock(&proxyRenderer->mutex);
300 // Insert an extra item into the queue to make sure it gets flushed
301 mVideoProxyRendererFlush(&proxyRenderer->proxy);
302 while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
303 ConditionWake(&proxyRenderer->toThreadCond);
304 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
305 }
306 proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
307 MutexUnlock(&proxyRenderer->mutex);
308}
309
310static THREAD_ENTRY _proxyThread(void* renderer) {
311 struct GBAVideoThreadProxyRenderer* proxyRenderer = renderer;
312 ThreadSetName("Proxy Renderer Thread");
313
314 MutexLock(&proxyRenderer->mutex);
315 while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
316 ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
317 if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
318 break;
319 }
320 proxyRenderer->threadState = PROXY_THREAD_BUSY;
321 MutexUnlock(&proxyRenderer->mutex);
322 if (!mVideoProxyRendererRun(&proxyRenderer->proxy)) {
323 // FIFO was corrupted
324 proxyRenderer->threadState = PROXY_THREAD_STOPPED;
325 mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!");
326 }
327 MutexLock(&proxyRenderer->mutex);
328 ConditionWake(&proxyRenderer->fromThreadCond);
329 if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
330 proxyRenderer->threadState = PROXY_THREAD_IDLE;
331 }
332 }
333 MutexUnlock(&proxyRenderer->mutex);
334
335#ifdef _3DS
336 svcExitThread();
337#endif
338 return 0;
339}
340
341#endif