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