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
12#ifndef DISABLE_THREADING
13
14enum GBAVideoDirtyType {
15 DIRTY_DUMMY = 0,
16 DIRTY_REGISTER,
17 DIRTY_OAM,
18 DIRTY_PALETTE,
19 DIRTY_VRAM,
20 DIRTY_SCANLINE,
21 DIRTY_FLUSH
22};
23
24struct GBAVideoDirtyInfo {
25 enum GBAVideoDirtyType type;
26 uint32_t address;
27 uint16_t value;
28 uint32_t padding;
29};
30
31static void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer);
32static void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer);
33static void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer);
34static uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
35static void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
36static void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
37static void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
38static void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
39static void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer);
40static void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels);
41static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels);
42
43static THREAD_ENTRY _proxyThread(void* renderer);
44
45void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) {
46 renderer->d.init = GBAVideoThreadProxyRendererInit;
47 renderer->d.reset = GBAVideoThreadProxyRendererReset;
48 renderer->d.deinit = GBAVideoThreadProxyRendererDeinit;
49 renderer->d.writeVideoRegister = GBAVideoThreadProxyRendererWriteVideoRegister;
50 renderer->d.writeVRAM = GBAVideoThreadProxyRendererWriteVRAM;
51 renderer->d.writeOAM = GBAVideoThreadProxyRendererWriteOAM;
52 renderer->d.writePalette = GBAVideoThreadProxyRendererWritePalette;
53 renderer->d.drawScanline = GBAVideoThreadProxyRendererDrawScanline;
54 renderer->d.finishFrame = GBAVideoThreadProxyRendererFinishFrame;
55 renderer->d.getPixels = GBAVideoThreadProxyRendererGetPixels;
56 renderer->d.putPixels = GBAVideoThreadProxyRendererPutPixels;
57
58 renderer->d.disableBG[0] = false;
59 renderer->d.disableBG[1] = false;
60 renderer->d.disableBG[2] = false;
61 renderer->d.disableBG[3] = false;
62 renderer->d.disableOBJ = false;
63
64 renderer->backend = backend;
65}
66
67void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer) {
68 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
69 ConditionInit(&proxyRenderer->fromThreadCond);
70 ConditionInit(&proxyRenderer->toThreadCond);
71 MutexInit(&proxyRenderer->mutex);
72 RingFIFOInit(&proxyRenderer->dirtyQueue, 0x40000, 0x1000);
73 proxyRenderer->threadState = PROXY_THREAD_STOPPED;
74
75 proxyRenderer->vramProxy = anonymousMemoryMap(SIZE_VRAM);
76 proxyRenderer->backend->palette = proxyRenderer->paletteProxy;
77 proxyRenderer->backend->vram = proxyRenderer->vramProxy;
78 proxyRenderer->backend->oam = &proxyRenderer->oamProxy;
79
80 proxyRenderer->backend->init(proxyRenderer->backend);
81
82 proxyRenderer->vramDirtyBitmap = 0;
83 proxyRenderer->threadState = PROXY_THREAD_IDLE;
84 ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
85}
86
87void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer) {
88 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
89 MutexLock(&proxyRenderer->mutex);
90 while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
91 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
92 }
93 memcpy(&proxyRenderer->oamProxy.raw, &renderer->oam->raw, SIZE_OAM);
94 memcpy(proxyRenderer->paletteProxy, renderer->palette, SIZE_PALETTE_RAM);
95 memcpy(proxyRenderer->vramProxy, renderer->vram, SIZE_VRAM);
96 proxyRenderer->backend->reset(proxyRenderer->backend);
97 MutexUnlock(&proxyRenderer->mutex);
98}
99
100void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
101 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
102 bool waiting = false;
103 MutexLock(&proxyRenderer->mutex);
104 while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
105 ConditionWake(&proxyRenderer->toThreadCond);
106 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
107 }
108 if (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
109 proxyRenderer->threadState = PROXY_THREAD_STOPPED;
110 ConditionWake(&proxyRenderer->toThreadCond);
111 waiting = true;
112 }
113 MutexUnlock(&proxyRenderer->mutex);
114 if (waiting) {
115 ThreadJoin(proxyRenderer->thread);
116 }
117 ConditionDeinit(&proxyRenderer->fromThreadCond);
118 ConditionDeinit(&proxyRenderer->toThreadCond);
119 MutexDeinit(&proxyRenderer->mutex);
120 proxyRenderer->backend->deinit(proxyRenderer->backend);
121
122 mappedMemoryFree(proxyRenderer->vramProxy, SIZE_VRAM);
123}
124
125uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
126 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
127 switch (address) {
128 case REG_BG0CNT:
129 case REG_BG1CNT:
130 case REG_BG2CNT:
131 case REG_BG3CNT:
132 value &= 0xFFCF;
133 break;
134 case REG_BG0HOFS:
135 case REG_BG0VOFS:
136 case REG_BG1HOFS:
137 case REG_BG1VOFS:
138 case REG_BG2HOFS:
139 case REG_BG2VOFS:
140 case REG_BG3HOFS:
141 case REG_BG3VOFS:
142 value &= 0x01FF;
143 break;
144 }
145 if (address > REG_BLDY) {
146 return value;
147 }
148
149 struct GBAVideoDirtyInfo dirty = {
150 DIRTY_REGISTER,
151 address,
152 value,
153 0xDEADBEEF,
154 };
155 RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
156 return value;
157}
158
159void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
160 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
161 int bit = 1 << (address >> 12);
162 if (proxyRenderer->vramDirtyBitmap & bit) {
163 return;
164 }
165 proxyRenderer->vramDirtyBitmap |= bit;
166}
167
168void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
169 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
170 struct GBAVideoDirtyInfo dirty = {
171 DIRTY_PALETTE,
172 address,
173 value,
174 0xDEADBEEF,
175 };
176 RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
177}
178
179void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
180 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
181 struct GBAVideoDirtyInfo dirty = {
182 DIRTY_OAM,
183 oam,
184 proxyRenderer->d.oam->raw[oam],
185 0xDEADBEEF,
186 };
187 RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
188}
189
190void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
191 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
192 if (proxyRenderer->vramDirtyBitmap) {
193 int bitmap = proxyRenderer->vramDirtyBitmap;
194 proxyRenderer->vramDirtyBitmap = 0;
195 int j;
196 for (j = 0; j < 24; ++j) {
197 if (!(bitmap & (1 << j))) {
198 continue;
199 }
200 struct GBAVideoDirtyInfo dirty = {
201 DIRTY_VRAM,
202 j * 0x1000,
203 0xABCD,
204 0xDEADBEEF,
205 };
206 RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
207 RingFIFOWrite(&proxyRenderer->dirtyQueue, &proxyRenderer->d.vram[j * 0x800], 0x1000);
208 }
209 }
210 struct GBAVideoDirtyInfo dirty = {
211 DIRTY_SCANLINE,
212 y,
213 0,
214 0xDEADBEEF,
215 };
216 RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
217 if ((y & 15) == 15) {
218 ConditionWake(&proxyRenderer->toThreadCond);
219 }
220}
221
222void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
223 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
224 MutexLock(&proxyRenderer->mutex);
225 // Insert an extra item into the queue to make sure it gets flushed
226 struct GBAVideoDirtyInfo dirty = {
227 DIRTY_FLUSH,
228 0,
229 0,
230 0xDEADBEEF,
231 };
232 RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
233 do {
234 ConditionWake(&proxyRenderer->toThreadCond);
235 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
236 } while (proxyRenderer->threadState == PROXY_THREAD_BUSY);
237 proxyRenderer->backend->finishFrame(proxyRenderer->backend);
238 proxyRenderer->vramDirtyBitmap = 0;
239 MutexUnlock(&proxyRenderer->mutex);
240}
241
242void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels) {
243 // TODO
244}
245
246void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels) {
247 // TODO
248}
249
250static THREAD_ENTRY _proxyThread(void* renderer) {
251 struct GBAVideoThreadProxyRenderer* proxyRenderer = renderer;
252 ThreadSetName("Proxy Renderer Thread");
253
254 MutexLock(&proxyRenderer->mutex);
255 while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
256 ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
257 if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
258 break;
259 }
260 proxyRenderer->threadState = PROXY_THREAD_BUSY;
261
262 MutexUnlock(&proxyRenderer->mutex);
263 struct GBAVideoDirtyInfo item;
264 while (RingFIFORead(&proxyRenderer->dirtyQueue, &item, sizeof(item))) {
265 switch (item.type) {
266 case DIRTY_REGISTER:
267 proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item.address, item.value);
268 break;
269 case DIRTY_PALETTE:
270 proxyRenderer->paletteProxy[item.address >> 1] = item.value;
271 proxyRenderer->backend->writePalette(proxyRenderer->backend, item.address, item.value);
272 break;
273 case DIRTY_OAM:
274 proxyRenderer->oamProxy.raw[item.address] = item.value;
275 proxyRenderer->backend->writeOAM(proxyRenderer->backend, item.address);
276 break;
277 case DIRTY_VRAM:
278 while (!RingFIFORead(&proxyRenderer->dirtyQueue, &proxyRenderer->vramProxy[item.address >> 1], 0x1000));
279 proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item.address);
280 break;
281 case DIRTY_SCANLINE:
282 proxyRenderer->backend->drawScanline(proxyRenderer->backend, item.address);
283 break;
284 case DIRTY_FLUSH:
285 // This is only here to ensure the queue gets flushed
286 break;
287 default:
288 // FIFO was corrupted
289 proxyRenderer->threadState = PROXY_THREAD_STOPPED;
290 break;
291 }
292 }
293 MutexLock(&proxyRenderer->mutex);
294 ConditionWake(&proxyRenderer->fromThreadCond);
295 if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
296 proxyRenderer->threadState = PROXY_THREAD_IDLE;
297 }
298 }
299 MutexUnlock(&proxyRenderer->mutex);
300
301#ifdef _3DS
302 svcExitThread();
303#endif
304 return 0;
305}
306
307#endif