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 ConditionWake(&proxyRenderer->toThreadCond);
162 return value;
163}
164
165void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
166 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
167 int bit = 1 << (address >> 12);
168 if (proxyRenderer->vramDirtyBitmap & bit) {
169 return;
170 }
171 proxyRenderer->vramDirtyBitmap |= bit;
172}
173
174void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
175 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
176 struct GBAVideoDirtyInfo dirty = {
177 DIRTY_PALETTE,
178 address,
179 value,
180 0xDEADBEEF,
181 };
182 RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
183 ConditionWake(&proxyRenderer->toThreadCond);
184}
185
186void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
187 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
188 int bit = 1 << (oam & 31);
189 int base = oam >> 5;
190 if (proxyRenderer->oamDirtyBitmap[base] & bit) {
191 return;
192 }
193 proxyRenderer->oamDirtyBitmap[base] |= bit;
194 struct GBAVideoDirtyInfo dirty = {
195 DIRTY_OAM,
196 oam,
197 proxyRenderer->d.oam->raw[oam],
198 0xDEADBEEF,
199 };
200 RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
201 ConditionWake(&proxyRenderer->toThreadCond);
202}
203
204void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
205 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
206 if (proxyRenderer->vramDirtyBitmap) {
207 int bitmap = proxyRenderer->vramDirtyBitmap;
208 proxyRenderer->vramDirtyBitmap = 0;
209 int j;
210 for (j = 0; j < 24; ++j) {
211 if (!(bitmap & (1 << j))) {
212 continue;
213 }
214 struct GBAVideoDirtyInfo dirty = {
215 DIRTY_VRAM,
216 j * 0x1000,
217 0xABCD,
218 0xDEADBEEF,
219 };
220 RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
221 RingFIFOWrite(&proxyRenderer->dirtyQueue, &proxyRenderer->d.vram[j * 0x800], 0x1000);
222 }
223 }
224 struct GBAVideoDirtyInfo dirty = {
225 DIRTY_SCANLINE,
226 y,
227 0,
228 0xDEADBEEF,
229 };
230 RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
231 ConditionWake(&proxyRenderer->toThreadCond);
232}
233
234void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
235 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
236 MutexLock(&proxyRenderer->mutex);
237 // Insert an extra item into the queue to make sure it gets flushed
238 struct GBAVideoDirtyInfo dirty = {
239 DIRTY_FLUSH,
240 0,
241 0,
242 0xDEADBEEF,
243 };
244 RingFIFOWrite(&proxyRenderer->dirtyQueue, &dirty, sizeof(dirty));
245 do {
246 ConditionWake(&proxyRenderer->toThreadCond);
247 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
248 } while (proxyRenderer->threadState == PROXY_THREAD_BUSY);
249 proxyRenderer->backend->finishFrame(proxyRenderer->backend);
250 proxyRenderer->vramDirtyBitmap = 0;
251 memset(proxyRenderer->oamDirtyBitmap, 0, sizeof(proxyRenderer->oamDirtyBitmap));
252 MutexUnlock(&proxyRenderer->mutex);
253}
254
255void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels) {
256 // TODO
257}
258
259void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels) {
260 // TODO
261}
262
263static THREAD_ENTRY _proxyThread(void* renderer) {
264 struct GBAVideoThreadProxyRenderer* proxyRenderer = renderer;
265 ThreadSetName("Proxy Renderer Thread");
266
267 MutexLock(&proxyRenderer->mutex);
268 while (1) {
269 ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
270 if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
271 break;
272 }
273 proxyRenderer->threadState = PROXY_THREAD_BUSY;
274
275 MutexUnlock(&proxyRenderer->mutex);
276 struct GBAVideoDirtyInfo item;
277 while (RingFIFORead(&proxyRenderer->dirtyQueue, &item, sizeof(item))) {
278 switch (item.type) {
279 case DIRTY_REGISTER:
280 proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item.address, item.value);
281 break;
282 case DIRTY_PALETTE:
283 proxyRenderer->paletteProxy[item.address >> 1] = item.value;
284 proxyRenderer->backend->writePalette(proxyRenderer->backend, item.address, item.value);
285 break;
286 case DIRTY_OAM:
287 proxyRenderer->oamProxy.raw[item.address] = item.value;
288 proxyRenderer->backend->writeOAM(proxyRenderer->backend, item.address);
289 break;
290 case DIRTY_VRAM:
291 while (!RingFIFORead(&proxyRenderer->dirtyQueue, &proxyRenderer->vramProxy[item.address >> 1], 0x1000));
292 proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item.address);
293 break;
294 case DIRTY_SCANLINE:
295 proxyRenderer->backend->drawScanline(proxyRenderer->backend, item.address);
296 break;
297 case DIRTY_FLUSH:
298 // This is only here to ensure the queue gets flushed
299 break;
300 default:
301 // FIFO was corrupted
302 proxyRenderer->threadState = PROXY_THREAD_STOPPED;
303 break;
304 }
305 }
306 MutexLock(&proxyRenderer->mutex);
307 ConditionWake(&proxyRenderer->fromThreadCond);
308 if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
309 proxyRenderer->threadState = PROXY_THREAD_IDLE;
310 } else {
311 break;
312 }
313 }
314 MutexUnlock(&proxyRenderer->mutex);
315
316#ifdef _3DS
317 svcExitThread();
318#endif
319 return 0;
320}
321
322#endif