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 memset(proxyRenderer->paletteDirtyBitmap, 0, sizeof(proxyRenderer->paletteDirtyBitmap));
68 memset(proxyRenderer->regDirtyBitmap, 0, sizeof(proxyRenderer->regDirtyBitmap));
69 proxyRenderer->threadState = PROXY_THREAD_IDLE;
70 ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
71}
72
73void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer) {
74 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
75 MutexLock(&proxyRenderer->mutex);
76 while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
77 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
78 }
79 int i;
80 for (i = 0; i < 128; ++i) {
81 proxyRenderer->oamProxy.raw[i * 4] = 0x0200;
82 proxyRenderer->oamProxy.raw[i * 4 + 1] = 0x0000;
83 proxyRenderer->oamProxy.raw[i * 4 + 2] = 0x0000;
84 proxyRenderer->oamProxy.raw[i * 4 + 3] = 0x0000;
85 }
86 proxyRenderer->backend->reset(proxyRenderer->backend);
87 MutexUnlock(&proxyRenderer->mutex);
88}
89
90void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
91 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
92 bool waiting = false;
93 MutexLock(&proxyRenderer->mutex);
94 while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
95 ConditionWake(&proxyRenderer->toThreadCond);
96 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
97 }
98 if (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
99 proxyRenderer->threadState = PROXY_THREAD_STOPPED;
100 ConditionWake(&proxyRenderer->toThreadCond);
101 waiting = true;
102 }
103 MutexUnlock(&proxyRenderer->mutex);
104 if (waiting) {
105 ThreadJoin(proxyRenderer->thread);
106 }
107 ConditionDeinit(&proxyRenderer->fromThreadCond);
108 ConditionDeinit(&proxyRenderer->toThreadCond);
109 MutexDeinit(&proxyRenderer->mutex);
110 proxyRenderer->backend->deinit(proxyRenderer->backend);
111
112 mappedMemoryFree(proxyRenderer->vramProxy, SIZE_VRAM);
113}
114
115uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
116 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
117 switch (address) {
118 case REG_BG0CNT:
119 case REG_BG1CNT:
120 case REG_BG2CNT:
121 case REG_BG3CNT:
122 value &= 0xFFCF;
123 break;
124 case REG_BG0HOFS:
125 case REG_BG0VOFS:
126 case REG_BG1HOFS:
127 case REG_BG1VOFS:
128 case REG_BG2HOFS:
129 case REG_BG2VOFS:
130 case REG_BG3HOFS:
131 case REG_BG3VOFS:
132 value &= 0x01FF;
133 break;
134 }
135 if (address > REG_BLDY) {
136 return value;
137 }
138 proxyRenderer->regProxy[address >> 1] = value;
139 int bit = 1 << ((address >> 1) & 31);
140 int base = address >> 6;
141 if (proxyRenderer->regDirtyBitmap[base] & bit) {
142 return value;
143 }
144 proxyRenderer->regDirtyBitmap[base] |= bit;
145
146 struct GBAVideoDirtyInfo dirty = {
147 DIRTY_REGISTER,
148 address
149 };
150 *GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
151 return value;
152}
153
154void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
155 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
156 int bit = 1 << (address >> 12);
157 if (proxyRenderer->vramDirtyBitmap & bit) {
158 return;
159 }
160 proxyRenderer->vramDirtyBitmap |= bit;
161 struct GBAVideoDirtyInfo dirty = {
162 DIRTY_VRAM,
163 address
164 };
165 *GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
166}
167
168void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
169 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
170 proxyRenderer->paletteProxy[address >> 1] = value;
171 int bit = 1 << ((address >> 1) & 31);
172 int base = address >> 6;
173 if (proxyRenderer->paletteDirtyBitmap[base] & bit) {
174 return;
175 }
176 proxyRenderer->paletteDirtyBitmap[base] |= bit;
177 struct GBAVideoDirtyInfo dirty = {
178 DIRTY_PALETTE,
179 address
180 };
181 *GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
182}
183
184void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
185 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
186 proxyRenderer->oamProxy.raw[oam] = proxyRenderer->d.oam->raw[oam];
187 int bit = 1 << (oam & 31);
188 int base = oam >> 5;
189 if (proxyRenderer->oamDirtyBitmap[base] & bit) {
190 return;
191 }
192 proxyRenderer->oamDirtyBitmap[base] |= bit;
193 struct GBAVideoDirtyInfo dirty = {
194 DIRTY_OAM,
195 oam
196 };
197 *GBAVideoDirtyQueueAppend(&proxyRenderer->dirtyQueue) = dirty;
198}
199
200void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
201 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
202 MutexLock(&proxyRenderer->mutex);
203 while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
204 ConditionWake(&proxyRenderer->toThreadCond);
205 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
206 }
207 proxyRenderer->y = y;
208 ConditionWake(&proxyRenderer->toThreadCond);
209 while (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
210 ConditionWake(&proxyRenderer->toThreadCond);
211 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
212 }
213 MutexUnlock(&proxyRenderer->mutex);
214}
215
216void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
217 struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
218 MutexLock(&proxyRenderer->mutex);
219 while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
220 ConditionWake(&proxyRenderer->toThreadCond);
221 ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
222 }
223 proxyRenderer->backend->finishFrame(proxyRenderer->backend);
224 MutexUnlock(&proxyRenderer->mutex);
225}
226
227void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels) {
228 // TODO
229}
230
231void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels) {
232 // TODO
233}
234
235
236static THREAD_ENTRY _proxyThread(void* renderer) {
237 struct GBAVideoThreadProxyRenderer* proxyRenderer = renderer;
238 ThreadSetName("Proxy Renderer Thread");
239
240 MutexLock(&proxyRenderer->mutex);
241 while (1) {
242 ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
243 if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
244 break;
245 }
246 proxyRenderer->threadState = PROXY_THREAD_BUSY;
247 proxyRenderer->vramDirtyBitmap = 0;
248 memset(proxyRenderer->oamDirtyBitmap, 0, sizeof(proxyRenderer->oamDirtyBitmap));
249 memset(proxyRenderer->paletteDirtyBitmap, 0, sizeof(proxyRenderer->paletteDirtyBitmap));
250 memset(proxyRenderer->regDirtyBitmap, 0, sizeof(proxyRenderer->regDirtyBitmap));
251 size_t queueSize = GBAVideoDirtyQueueSize(&proxyRenderer->dirtyQueue);
252 struct GBAVideoDirtyInfo* queue = malloc(queueSize * sizeof(struct GBAVideoDirtyInfo));
253 memcpy(queue, GBAVideoDirtyQueueGetPointer(&proxyRenderer->dirtyQueue, 0), queueSize * sizeof(struct GBAVideoDirtyInfo));
254 GBAVideoDirtyQueueClear(&proxyRenderer->dirtyQueue);
255 size_t i;
256 for (i = 0; i < queueSize; ++i) {
257 switch (queue[i].type) {
258 case DIRTY_REGISTER:
259 proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, queue[i].address, proxyRenderer->regProxy[queue[i].address >> 1]);
260 break;
261 case DIRTY_VRAM:
262 proxyRenderer->backend->writeVRAM(proxyRenderer->backend, queue[i].address);
263 memcpy(&proxyRenderer->vramProxy[(queue[i].address & ~0xFFF) >> 1], &proxyRenderer->d.vram[(queue[i].address & ~0xFFF) >> 1], 0x1000);
264 break;
265 case DIRTY_PALETTE:
266 proxyRenderer->backend->writePalette(proxyRenderer->backend, queue[i].address, proxyRenderer->paletteProxy[queue[i].address >> 1]);
267 break;
268 case DIRTY_OAM:
269 proxyRenderer->backend->writeOAM(proxyRenderer->backend, queue[i].address);
270 break;
271 }
272 }
273 free(queue);
274 ConditionWake(&proxyRenderer->fromThreadCond);
275 ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
276 MutexUnlock(&proxyRenderer->mutex);
277
278 proxyRenderer->backend->drawScanline(proxyRenderer->backend, proxyRenderer->y);
279
280 MutexLock(&proxyRenderer->mutex);
281 proxyRenderer->threadState = PROXY_THREAD_IDLE;
282 ConditionWake(&proxyRenderer->fromThreadCond);
283 }
284 MutexUnlock(&proxyRenderer->mutex);
285
286 return 0;
287}