1 /* Copyright (c) 2013-2017 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 <mgba/internal/gb/renderers/proxy.h>
7 
8 #include <mgba/core/cache-set.h>
9 #include <mgba/internal/gb/gb.h>
10 #include <mgba/internal/gb/io.h>
11 
12 #define BUFFER_OAM 1
13 #define BUFFER_SGB 2
14 
15 static void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model, bool borders);
16 static void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer);
17 static uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
18 static void GBVideoProxyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data);
19 static void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
20 static void GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam);
21 static void GBVideoProxyRendererWritePalette(struct GBVideoRenderer* renderer, int address, uint16_t value);
22 static void GBVideoProxyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax);
23 static void GBVideoProxyRendererFinishScanline(struct GBVideoRenderer* renderer, int y);
24 static void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer);
25 static void GBVideoProxyRendererEnableSGBBorder(struct GBVideoRenderer* renderer, bool enable);
26 static void GBVideoProxyRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels);
27 static void GBVideoProxyRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels);
28 
29 static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet);
30 static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address);
31 
GBVideoProxyRendererCreate(struct GBVideoProxyRenderer * renderer,struct GBVideoRenderer * backend)32 void GBVideoProxyRendererCreate(struct GBVideoProxyRenderer* renderer, struct GBVideoRenderer* backend) {
33 	renderer->d.init = GBVideoProxyRendererInit;
34 	renderer->d.deinit = GBVideoProxyRendererDeinit;
35 	renderer->d.writeVideoRegister = GBVideoProxyRendererWriteVideoRegister;
36 	renderer->d.writeSGBPacket = GBVideoProxyRendererWriteSGBPacket;
37 	renderer->d.writeVRAM = GBVideoProxyRendererWriteVRAM;
38 	renderer->d.writeOAM = GBVideoProxyRendererWriteOAM;
39 	renderer->d.writePalette = GBVideoProxyRendererWritePalette;
40 	renderer->d.drawRange = GBVideoProxyRendererDrawRange;
41 	renderer->d.finishScanline = GBVideoProxyRendererFinishScanline;
42 	renderer->d.finishFrame = GBVideoProxyRendererFinishFrame;
43 	renderer->d.enableSGBBorder = GBVideoProxyRendererEnableSGBBorder;
44 	renderer->d.getPixels = GBVideoProxyRendererGetPixels;
45 	renderer->d.putPixels = GBVideoProxyRendererPutPixels;
46 
47 	renderer->d.disableBG = false;
48 	renderer->d.disableWIN = false;
49 	renderer->d.disableOBJ = false;
50 
51 	renderer->logger->context = renderer;
52 	renderer->logger->parsePacket = _parsePacket;
53 	renderer->logger->vramBlock = _vramBlock;
54 	renderer->logger->paletteSize = 0;
55 	renderer->logger->vramSize = GB_SIZE_VRAM;
56 	renderer->logger->oamSize = GB_SIZE_OAM;
57 
58 	renderer->backend = backend;
59 }
60 
_init(struct GBVideoProxyRenderer * proxyRenderer)61 static void _init(struct GBVideoProxyRenderer* proxyRenderer) {
62 	mVideoLoggerRendererInit(proxyRenderer->logger);
63 
64 	if (proxyRenderer->logger->block) {
65 		proxyRenderer->backend->vram = (uint8_t*) proxyRenderer->logger->vram;
66 		proxyRenderer->backend->oam = (union GBOAM*) proxyRenderer->logger->oam;
67 		proxyRenderer->backend->cache = NULL;
68 	}
69 }
70 
_reset(struct GBVideoProxyRenderer * proxyRenderer)71 static void _reset(struct GBVideoProxyRenderer* proxyRenderer) {
72 	memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, GB_SIZE_OAM);
73 	memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, GB_SIZE_VRAM);
74 
75 	proxyRenderer->oamMax = 0;
76 
77 	mVideoLoggerRendererReset(proxyRenderer->logger);
78 }
79 
GBVideoProxyRendererShim(struct GBVideo * video,struct GBVideoProxyRenderer * renderer)80 void GBVideoProxyRendererShim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer) {
81 	if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) {
82 		return;
83 	}
84 	renderer->backend = video->renderer;
85 	video->renderer = &renderer->d;
86 	renderer->d.cache = renderer->backend->cache;
87 	renderer->d.vram = video->vram;
88 	renderer->d.oam = &video->oam;
89 	_init(renderer);
90 	_reset(renderer);
91 }
92 
GBVideoProxyRendererUnshim(struct GBVideo * video,struct GBVideoProxyRenderer * renderer)93 void GBVideoProxyRendererUnshim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer) {
94 	if (video->renderer != &renderer->d) {
95 		return;
96 	}
97 	renderer->backend->cache = video->renderer->cache;
98 	video->renderer = renderer->backend;
99 	renderer->backend->vram = video->vram;
100 	renderer->backend->oam = &video->oam;
101 
102 	mVideoLoggerRendererDeinit(renderer->logger);
103 }
104 
GBVideoProxyRendererInit(struct GBVideoRenderer * renderer,enum GBModel model,bool borders)105 void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model, bool borders) {
106 	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
107 
108 	_init(proxyRenderer);
109 
110 	proxyRenderer->backend->init(proxyRenderer->backend, model, borders);
111 }
112 
GBVideoProxyRendererDeinit(struct GBVideoRenderer * renderer)113 void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer) {
114 	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
115 
116 	proxyRenderer->backend->deinit(proxyRenderer->backend);
117 
118 	mVideoLoggerRendererDeinit(proxyRenderer->logger);
119 }
120 
_parsePacket(struct mVideoLogger * logger,const struct mVideoLoggerDirtyInfo * item)121 static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) {
122 	struct GBVideoProxyRenderer* proxyRenderer = logger->context;
123 	uint8_t sgbPacket[16];
124 	switch (item->type) {
125 	case DIRTY_REGISTER:
126 		proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
127 		break;
128 	case DIRTY_PALETTE:
129 		if (item->address < 64) {
130 			proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
131 		}
132 		break;
133 	case DIRTY_OAM:
134 		if (item->address < GB_SIZE_OAM) {
135 			((uint8_t*) logger->oam)[item->address] = item->value;
136 			proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
137 		}
138 		break;
139 	case DIRTY_VRAM:
140 		if (item->address <= GB_SIZE_VRAM - 0x1000) {
141 			logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true);
142 			proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address);
143 		}
144 		break;
145 	case DIRTY_SCANLINE:
146 		proxyRenderer->backend->disableBG = proxyRenderer->d.disableBG;
147 		proxyRenderer->backend->disableWIN = proxyRenderer->d.disableWIN;
148 		proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ;
149 		if (item->address < GB_VIDEO_VERTICAL_PIXELS) {
150 			proxyRenderer->backend->finishScanline(proxyRenderer->backend, item->address);
151 		}
152 		break;
153 	case DIRTY_RANGE:
154 		if (item->value < item->value2 && item->value2 <= GB_VIDEO_HORIZONTAL_PIXELS && item->address < GB_VIDEO_VERTICAL_PIXELS) {
155 			proxyRenderer->backend->drawRange(proxyRenderer->backend, item->value, item->value2, item->address, proxyRenderer->objThisLine, proxyRenderer->oamMax);
156 		}
157 		break;
158 	case DIRTY_FRAME:
159 		proxyRenderer->backend->finishFrame(proxyRenderer->backend);
160 		break;
161 	case DIRTY_BUFFER:
162 		switch (item->address) {
163 		case BUFFER_OAM:
164 			proxyRenderer->oamMax = item->value2 / sizeof(struct GBObj);
165 			if (proxyRenderer->oamMax > 40) {
166 				proxyRenderer->oamMax = 0;
167 				return false;
168 			}
169 			logger->readData(logger, &proxyRenderer->objThisLine, item->value2, true);
170 			break;
171 		case BUFFER_SGB:
172 			logger->readData(logger, sgbPacket, 16, true);
173 			proxyRenderer->backend->writeSGBPacket(proxyRenderer->backend, sgbPacket);
174 			break;
175 		}
176 		break;
177 	case DIRTY_FLUSH:
178 		return false;
179 	default:
180 		return false;
181 	}
182 	return true;
183 }
184 
_vramBlock(struct mVideoLogger * logger,uint32_t address)185 static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address) {
186 	struct GBVideoProxyRenderer* proxyRenderer = logger->context;
187 	return (uint16_t*) &proxyRenderer->d.vram[address];
188 }
189 
GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer * renderer,uint16_t address,uint8_t value)190 uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {
191 	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
192 
193 	mVideoLoggerRendererWriteVideoRegister(proxyRenderer->logger, address, value);
194 	if (!proxyRenderer->logger->block) {
195 		proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, value);
196 	}
197 	return value;
198 }
199 
GBVideoProxyRendererWriteSGBPacket(struct GBVideoRenderer * renderer,uint8_t * data)200 void GBVideoProxyRendererWriteSGBPacket(struct GBVideoRenderer* renderer, uint8_t* data) {
201 	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
202 	if (!proxyRenderer->logger->block) {
203 		proxyRenderer->backend->writeSGBPacket(proxyRenderer->backend, data);
204 	}
205 	mVideoLoggerWriteBuffer(proxyRenderer->logger, BUFFER_SGB, 0, 16, data);
206 }
207 
GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer * renderer,uint16_t address)208 void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
209 	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
210 	mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address);
211 	if (!proxyRenderer->logger->block) {
212 		proxyRenderer->backend->writeVRAM(proxyRenderer->backend, address);
213 	}
214 	if (renderer->cache) {
215 		mCacheSetWriteVRAM(renderer->cache, address);
216 	}
217 }
218 
GBVideoProxyRendererWritePalette(struct GBVideoRenderer * renderer,int address,uint16_t value)219 void GBVideoProxyRendererWritePalette(struct GBVideoRenderer* renderer, int address, uint16_t value) {
220 	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
221 	mVideoLoggerRendererWritePalette(proxyRenderer->logger, address, value);
222 	if (!proxyRenderer->logger->block) {
223 		proxyRenderer->backend->writePalette(proxyRenderer->backend, address, value);
224 	}
225 	if (renderer->cache) {
226 		mCacheSetWritePalette(renderer->cache, address, mColorFrom555(value));
227 	}
228 }
229 
GBVideoProxyRendererWriteOAM(struct GBVideoRenderer * renderer,uint16_t oam)230 void GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam) {
231 	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
232 	if (!proxyRenderer->logger->block) {
233 		proxyRenderer->backend->writeOAM(proxyRenderer->backend, oam);
234 	}
235 	mVideoLoggerRendererWriteOAM(proxyRenderer->logger, oam, ((uint8_t*) proxyRenderer->d.oam->raw)[oam]);
236 }
237 
GBVideoProxyRendererDrawRange(struct GBVideoRenderer * renderer,int startX,int endX,int y,struct GBObj * obj,size_t oamMax)238 void GBVideoProxyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax) {
239 	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
240 	if (!proxyRenderer->logger->block) {
241 		proxyRenderer->backend->disableBG = proxyRenderer->d.disableBG;
242 		proxyRenderer->backend->disableWIN = proxyRenderer->d.disableWIN;
243 		proxyRenderer->backend->disableOBJ = proxyRenderer->d.disableOBJ;
244 		proxyRenderer->backend->drawRange(proxyRenderer->backend, startX, endX, y, obj, oamMax);
245 	}
246 	mVideoLoggerWriteBuffer(proxyRenderer->logger, BUFFER_OAM, 0, oamMax * sizeof(*obj), obj);
247 	mVideoLoggerRendererDrawRange(proxyRenderer->logger, startX, endX, y);
248 }
249 
GBVideoProxyRendererFinishScanline(struct GBVideoRenderer * renderer,int y)250 void GBVideoProxyRendererFinishScanline(struct GBVideoRenderer* renderer, int y) {
251 	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
252 	if (!proxyRenderer->logger->block) {
253 		proxyRenderer->backend->finishScanline(proxyRenderer->backend, y);
254 	}
255 	mVideoLoggerRendererDrawScanline(proxyRenderer->logger, y);
256 	if (proxyRenderer->logger->block && proxyRenderer->logger->wake) {
257 		proxyRenderer->logger->wake(proxyRenderer->logger, y);
258 	}
259 }
260 
GBVideoProxyRendererFinishFrame(struct GBVideoRenderer * renderer)261 void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer) {
262 	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
263 	if (!proxyRenderer->logger->block) {
264 		proxyRenderer->backend->finishFrame(proxyRenderer->backend);
265 	}
266 	mVideoLoggerRendererFinishFrame(proxyRenderer->logger);
267 	mVideoLoggerRendererFlush(proxyRenderer->logger);
268 }
269 
GBVideoProxyRendererEnableSGBBorder(struct GBVideoRenderer * renderer,bool enable)270 static void GBVideoProxyRendererEnableSGBBorder(struct GBVideoRenderer* renderer, bool enable) {
271 	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
272 	if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
273 		// Insert an extra item into the queue to make sure it gets flushed
274 		mVideoLoggerRendererFlush(proxyRenderer->logger);
275 		proxyRenderer->logger->wait(proxyRenderer->logger);
276 	}
277 	proxyRenderer->backend->enableSGBBorder(proxyRenderer->backend, enable);
278 }
279 
GBVideoProxyRendererGetPixels(struct GBVideoRenderer * renderer,size_t * stride,const void ** pixels)280 static void GBVideoProxyRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels) {
281 	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
282 	if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
283 		proxyRenderer->logger->lock(proxyRenderer->logger);
284 		// Insert an extra item into the queue to make sure it gets flushed
285 		mVideoLoggerRendererFlush(proxyRenderer->logger);
286 		proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_GET_PIXELS);
287 		mVideoLoggerRendererFlush(proxyRenderer->logger);
288 		proxyRenderer->logger->unlock(proxyRenderer->logger);
289 		*pixels = proxyRenderer->logger->pixelBuffer;
290 		*stride = proxyRenderer->logger->pixelStride;
291 	} else {
292 		proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
293 	}
294 }
295 
GBVideoProxyRendererPutPixels(struct GBVideoRenderer * renderer,size_t stride,const void * pixels)296 static void GBVideoProxyRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels) {
297 	struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
298 	if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
299 		proxyRenderer->logger->lock(proxyRenderer->logger);
300 	}
301 	proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
302 	if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
303 		proxyRenderer->logger->unlock(proxyRenderer->logger);
304 	}
305 }
306