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 memset(proxyRenderer->oamDirtyBitmap, 0, sizeof(proxyRenderer->oamDirtyBitmap));
84 proxyRenderer->threadState = PROXY_THREAD_IDLE;
85 ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
86}
87
88void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer) {
89 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
90 MutexLock(&proxyRenderer->mutex);
91 while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
92 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
93 }
94 int i;
95 for (i = 0; i < 128; ++i) {
96 proxyRenderer->oamProxy.raw[i * 4] = 0x0200;
97 proxyRenderer->oamProxy.raw[i * 4 + 1] = 0x0000;
98 proxyRenderer->oamProxy.raw[i * 4 + 2] = 0x0000;
99 proxyRenderer->oamProxy.raw[i * 4 + 3] = 0x0000;
100 }
101 proxyRenderer->backend->reset(proxyRenderer->backend);
102 MutexUnlock(&proxyRenderer->mutex);
103}
104
105void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
106 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
107 bool waiting = false;
108 MutexLock(&proxyRenderer->mutex);
109 while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
110 ConditionWake(&proxyRenderer->toThreadCond);
111 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
112 }
113 if (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
114 proxyRenderer->threadState = PROXY_THREAD_STOPPED;
115 ConditionWake(&proxyRenderer->toThreadCond);
116 waiting = true;
117 }
118 MutexUnlock(&proxyRenderer->mutex);
119 if (waiting) {
120 ThreadJoin(proxyRenderer->thread);
121 }
122 ConditionDeinit(&proxyRenderer->fromThreadCond);
123 ConditionDeinit(&proxyRenderer->toThreadCond);
124 MutexDeinit(&proxyRenderer->mutex);
125 proxyRenderer->backend->deinit(proxyRenderer->backend);
126
127 mappedMemoryFree(proxyRenderer->vramProxy, SIZE_VRAM);
128}
129
130uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
131 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
132 switch (address) {
133 case REG_BG0CNT:
134 case REG_BG1CNT:
135 case REG_BG2CNT:
136 case REG_BG3CNT:
137 value &= 0xFFCF;
138 break;
139 case REG_BG0HOFS:
140 case REG_BG0VOFS:
141 case REG_BG1HOFS:
142 case REG_BG1VOFS:
143 case REG_BG2HOFS:
144 case REG_BG2VOFS:
145 case REG_BG3HOFS:
146 case REG_BG3VOFS:
147 value &= 0x01FF;
148 break;
149 }
150 if (address > REG_BLDY) {
151 return value;
152 }
153
154 struct GBAVideoDirtyInfo dirty = {
155 DIRTY_REGISTER,
156 address,
157 value,
158 0xDEADBEEF,
159 };
160 RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
161 return value;
162}
163
164void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
165 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
166 int bit = 1 << (address >> 12);
167 if (proxyRenderer->vramDirtyBitmap & bit) {
168 return;
169 }
170 proxyRenderer->vramDirtyBitmap |= bit;
171}
172
173void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
174 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
175 struct GBAVideoDirtyInfo dirty = {
176 DIRTY_PALETTE,
177 address,
178 value,
179 0xDEADBEEF,
180 };
181 RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
182}
183
184void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
185 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
186 int bit = 1 << (oam & 31);
187 int base = oam >> 5;
188 if (proxyRenderer->oamDirtyBitmap[base] & bit) {
189 return;
190 }
191 proxyRenderer->oamDirtyBitmap[base] |= bit;
192 struct GBAVideoDirtyInfo dirty = {
193 DIRTY_OAM,
194 oam,
195 proxyRenderer->d.oam->raw[oam],
196 0xDEADBEEF,
197 };
198 RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
199}
200
201void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
202 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
203 if (proxyRenderer->vramDirtyBitmap) {
204 int bitmap = proxyRenderer->vramDirtyBitmap;
205 proxyRenderer->vramDirtyBitmap = 0;
206 int j;
207 for (j = 0; j < 24; ++j) {
208 if (!(bitmap & (1 << j))) {
209 continue;
210 }
211 struct GBAVideoDirtyInfo dirty = {
212 DIRTY_VRAM,
213 j * 0x1000,
214 0xABCD,
215 0xDEADBEEF,
216 };
217 RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
218 RingFIFOWrite(&proxyRenderer->dirtyQueue, &proxyRenderer->d.vram[j * 0x800], 0x1000);
219 }
220 }
221 struct GBAVideoDirtyInfo dirty = {
222 DIRTY_SCANLINE,
223 y,
224 0,
225 0xDEADBEEF,
226 };
227 RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
228 if (!(y & 15)) {
229 ConditionWake(&proxyRenderer->toThreadCond);
230 }
231}
232
233void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
234 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
235 MutexLock(&proxyRenderer->mutex);
236 // Insert an extra item into the queue to make sure it gets flushed
237 struct GBAVideoDirtyInfo dirty = {
238 DIRTY_FLUSH,
239 0,
240 0,
241 0xDEADBEEF,
242 };
243 RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
244 do {
245 ConditionWake(&proxyRenderer->toThreadCond);
246 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
247 } while (proxyRenderer->threadState == PROXY_THREAD_BUSY);
248 proxyRenderer->backend->finishFrame(proxyRenderer->backend);
249 proxyRenderer->vramDirtyBitmap = 0;
250 memset(proxyRenderer->oamDirtyBitmap, 0, sizeof(proxyRenderer->oamDirtyBitmap));
251 MutexUnlock(&proxyRenderer->mutex);
252}
253
254void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels) {
255 // TODO
256}
257
258void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels) {
259 // TODO
260}
261
262static THREAD_ENTRY _proxyThread(void* renderer) {
263 struct GBAVideoThreadProxyRenderer* proxyRenderer = renderer;
264 ThreadSetName("Proxy Renderer Thread");
265
266 MutexLock(&proxyRenderer->mutex);
267 while (1) {
268 ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
269 if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
270 break;
271 }
272 proxyRenderer->threadState = PROXY_THREAD_BUSY;
273
274 MutexUnlock(&proxyRenderer->mutex);
275 struct GBAVideoDirtyInfo item;
276 while (RingFIFORead(&proxyRenderer->dirtyQueue, &item, sizeof(item))) {
277 switch (item.type) {
278 case DIRTY_REGISTER:
279 proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item.address, item.value);
280 break;
281 case DIRTY_PALETTE:
282 proxyRenderer->paletteProxy[item.address >> 1] = item.value;
283 proxyRenderer->backend->writePalette(proxyRenderer->backend, item.address, item.value);
284 break;
285 case DIRTY_OAM:
286 proxyRenderer->oamProxy.raw[item.address] = item.value;
287 proxyRenderer->backend->writeOAM(proxyRenderer->backend, item.address);
288 break;
289 case DIRTY_VRAM:
290 while (!RingFIFORead(&proxyRenderer->dirtyQueue, &proxyRenderer->vramProxy[item.address >> 1], 0x1000));
291 proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item.address);
292 break;
293 case DIRTY_SCANLINE:
294 proxyRenderer->backend->drawScanline(proxyRenderer->backend, item.address);
295 break;
296 case DIRTY_FLUSH:
297 // This is only here to ensure the queue gets flushed
298 break;
299 default:
300 // FIFO was corrupted
301 proxyRenderer->threadState = PROXY_THREAD_STOPPED;
302 break;
303 }
304 }
305 MutexLock(&proxyRenderer->mutex);
306 ConditionWake(&proxyRenderer->fromThreadCond);
307 if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
308 proxyRenderer->threadState = PROXY_THREAD_IDLE;
309 } else {
310 break;
311 }
312 }
313 MutexUnlock(&proxyRenderer->mutex);
314
315#ifdef _3DS
316 svcExitThread();
317#endif
318 return 0;
319}
320
321#endif