all repos — mgba @ 18e6acaf727d5fd69d2c8866c897121c020c8ccf

mGBA Game Boy Advance Emulator

src/gba/renderers/gl.c (view raw)

  1/* Copyright (c) 2013-2019 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/gba/renderers/gl.h>
  7
  8#include <mgba/core/cache-set.h>
  9#include <mgba/internal/arm/macros.h>
 10#include <mgba/internal/gba/io.h>
 11#include <mgba/internal/gba/renderers/cache-set.h>
 12
 13static void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer);
 14static void GBAVideoGLRendererDeinit(struct GBAVideoRenderer* renderer);
 15static void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer);
 16static void GBAVideoGLRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
 17static void GBAVideoGLRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
 18static void GBAVideoGLRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
 19static uint16_t GBAVideoGLRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
 20static void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
 21static void GBAVideoGLRendererFinishFrame(struct GBAVideoRenderer* renderer);
 22static void GBAVideoGLRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels);
 23static void GBAVideoGLRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
 24
 25static void GBAVideoGLRendererUpdateDISPCNT(struct GBAVideoGLRenderer* renderer);
 26static void GBAVideoGLRendererWriteBGCNT(struct GBAVideoGLBackground* bg, uint16_t value);
 27static void GBAVideoGLRendererWriteBGX_LO(struct GBAVideoGLBackground* bg, uint16_t value);
 28static void GBAVideoGLRendererWriteBGX_HI(struct GBAVideoGLBackground* bg, uint16_t value);
 29static void GBAVideoGLRendererWriteBGY_LO(struct GBAVideoGLBackground* bg, uint16_t value);
 30static void GBAVideoGLRendererWriteBGY_HI(struct GBAVideoGLBackground* bg, uint16_t value);
 31static void GBAVideoGLRendererWriteBLDCNT(struct GBAVideoGLRenderer* renderer, uint16_t value);
 32
 33static void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y);
 34static void GBAVideoGLRendererDrawBackgroundMode2(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y);
 35static void GBAVideoGLRendererDrawBackgroundMode3(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y);
 36static void GBAVideoGLRendererDrawBackgroundMode4(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y);
 37static void GBAVideoGLRendererDrawBackgroundMode5(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y);
 38
 39#define TEST_LAYER_ENABLED(X) !renderer->disableBG[X] && glRenderer->bg[X].enabled == 4 && glRenderer->bg[X].priority == priority
 40
 41static const GLchar* const _gl3Header =
 42	"#version 130\n";
 43
 44static const char* const _vertexShader =
 45	"attribute vec2 position;\n"
 46	"uniform ivec3 loc;\n"
 47	"const ivec3 maxPos = ivec3(240, 160, 32);\n"
 48	"varying vec2 texCoord;\n"
 49
 50	"void main() {\n"
 51	"	vec2 local = (position * loc.x + vec2(0, loc.y)) / vec2(1., maxPos.y);\n"
 52	"	gl_Position = vec4(local * 2. - 1., loc.z / float(maxPos.z), 1.);\n"
 53	"	texCoord = local * maxPos.xy;\n"
 54	"}";
 55
 56static const char* const _renderTile16 =
 57	"vec4 renderTile(int tile, int paletteId, ivec2 localCoord) {\n"
 58	"	int address = charBase + tile * 16 + (localCoord.x >> 2) + (localCoord.y << 1);\n"
 59	"	vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
 60	"	int entry = int(halfrow[3 - (localCoord.x & 3)] * 15.9);\n"
 61	"	vec4 color = texelFetch(palette, ivec2(entry, paletteId), 0);\n"
 62	"	if (entry == 0) {\n"
 63	"		color.a = 0;\n"
 64	"	} else {\n"
 65	"		color.a = 1;\n"
 66	"	}\n"
 67	"	return color;\n"
 68	"}";
 69
 70static const char* const _renderTile256 =
 71	"vec4 renderTile(int tile, int paletteId, ivec2 localCoord) {\n"
 72	"	int address = charBase + tile * 32 + (localCoord.x >> 1) + (localCoord.y << 2);\n"
 73	"	vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
 74	"	int entry = int(halfrow[3 - 2 * (localCoord.x & 1)] * 15.9);\n"
 75	"	int pal2 = int(halfrow[2 - 2 * (localCoord.x & 1)] * 15.9);\n"
 76	"	vec4 color = texelFetch(palette, ivec2(entry, pal2 + (paletteId & 16)), 0);\n"
 77	"	if (pal2 > 0 || entry > 0) {\n"
 78	"		color.a = 1.;\n"
 79	"	} else {\n"
 80	"		color.a = 0.;\n"
 81	"	}\n"
 82	"	return color;\n"
 83	"}";
 84
 85static const char* const _renderMode0 =
 86	"varying vec2 texCoord;\n"
 87	"uniform sampler2D vram;\n"
 88	"uniform sampler2D palette;\n"
 89	"uniform int screenBase;\n"
 90	"uniform int charBase;\n"
 91	"uniform int size;\n"
 92	"uniform ivec2 offset;\n"
 93
 94	"vec4 renderTile(int tile, int paletteId, ivec2 localCoord);\n"
 95
 96	"void main() {\n"
 97	"	ivec2 coord = ivec2(texCoord) + offset;\n"
 98	"	if ((size & 1) == 1) {\n"
 99	"		coord.y += coord.x & 256;\n"
100	"	}\n"
101	"	coord.x &= 255;\n"
102	"	int mapAddress = screenBase + (coord.x >> 3) + (coord.y >> 3) * 32;\n"
103	"	vec4 map = texelFetch(vram, ivec2(mapAddress & 255, mapAddress >> 8), 0);\n"
104	"	int flags = int(map.g * 15.9);\n"
105	"	if ((flags & 4) == 4) {\n"
106	"		coord.x ^= 7;\n"
107	"	}\n"
108	"	if ((flags & 8) == 8) {\n"
109	"		coord.y ^= 7;\n"
110	"	}\n"
111	"	int tile = int(map.a * 15.9) + int(map.b * 15.9) * 16 + (flags & 0x3) * 256;\n"
112	"	gl_FragColor = renderTile(tile, int(map.r * 15.9), coord & 7);\n"
113	"}";
114
115static const char* const _fetchTileOverflow =
116	"vec4 fetchTile(ivec2 coord) {\n"
117	"	int sizeAdjusted = (0x8000 << size) - 1;\n"
118	"	coord &= sizeAdjusted;\n"
119	"	return renderTile(coord);\n"
120	"}";
121
122static const char* const _fetchTileNoOverflow =
123	"vec4 fetchTile(ivec2 coord) {\n"
124	"	int sizeAdjusted = (0x8000 << size) - 1;\n"
125	"	ivec2 outerCoord = coord & ~sizeAdjusted;\n"
126	"	if ((outerCoord.x | outerCoord.y) != 0) {\n"
127	"		vec4 color = texelFetch(palette, ivec2(0, 0), 0);\n"
128	"		color.a = 0;\n"
129	"		return color;\n"
130	"	}\n"
131	"	return renderTile(coord);\n"
132	"}";
133
134static const char* const _renderMode2 =
135	"varying vec2 texCoord;\n"
136	"uniform sampler2D vram;\n"
137	"uniform sampler2D palette;\n"
138	"uniform int screenBase;\n"
139	"uniform int charBase;\n"
140	"uniform int size;\n"
141	"uniform ivec2 offset;\n"
142	"uniform ivec2 oldOffset;\n"
143	"uniform mat2x2 transform;\n"
144	"uniform mat2x2 oldTransform;\n"
145
146	"vec4 fetchTile(ivec2 coord);\n"
147
148	"vec4 renderTile(ivec2 coord) {\n"
149	"	int map = (coord.x >> 11) + (((coord.y >> 7) & 0x7F0) << size);\n"
150	"	int mapAddress = screenBase + (map >> 1);\n"
151	"	vec4 twomaps = texelFetch(vram, ivec2(mapAddress & 255, mapAddress >> 8), 0);\n"
152	"	int tile = int(twomaps[3 - 2 * (map & 1)] * 15.9) + int(twomaps[2 - 2 * (map & 1)] * 15.9) * 16;\n"
153	"	int address = charBase + tile * 32 + ((coord.x >> 9) & 3) + ((coord.y >> 6) & 0x1C);\n"
154	"	vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
155	"	int entry = int(halfrow[3 - ((coord.x >> 7) & 2)] * 15.9);\n"
156	"	int pal2 = int(halfrow[2 - ((coord.x >> 7) & 2)] * 15.9);\n"
157	"	vec4 color = texelFetch(palette, ivec2(entry, pal2), 0);\n"
158	"	if (pal2 > 0 || entry > 0) {\n"
159	"		color.a = 1.;\n"
160	"	} else {\n"
161	"		color.a = 0.;\n"
162	"	}\n"
163	"	return color;\n"
164	"}"
165
166	"void main() {\n"
167	"	vec2 newCoord = transform[0] * texCoord.x + offset;\n"
168	"	vec2 oldCoord = oldTransform[0] * texCoord.x + oldOffset;\n"
169	"	gl_FragColor = fetchTile(ivec2(newCoord * fract(texCoord.y) + oldCoord * (1. - fract(texCoord.y))));\n"
170	"}";
171
172static const char* const _composite =
173	"varying vec2 texCoord;\n"
174	"uniform sampler2D layer;\n"
175	"uniform int scale;\n"
176
177	"void main() {\n"
178	"	vec4 color = texelFetch(layer, ivec2(texCoord * scale), 0);\n"
179	"	if (color.a == 0) {\n"
180	"		discard;\n"
181	"	}\n"
182	"	gl_FragColor = color;\n"
183	"}";
184
185static const GLint _vertices[] = {
186	0, 0,
187	0, 1,
188	1, 1,
189	1, 0,
190};
191
192void GBAVideoGLRendererCreate(struct GBAVideoGLRenderer* renderer) {
193	renderer->d.init = GBAVideoGLRendererInit;
194	renderer->d.reset = GBAVideoGLRendererReset;
195	renderer->d.deinit = GBAVideoGLRendererDeinit;
196	renderer->d.writeVideoRegister = GBAVideoGLRendererWriteVideoRegister;
197	renderer->d.writeVRAM = GBAVideoGLRendererWriteVRAM;
198	renderer->d.writeOAM = GBAVideoGLRendererWriteOAM;
199	renderer->d.writePalette = GBAVideoGLRendererWritePalette;
200	renderer->d.drawScanline = GBAVideoGLRendererDrawScanline;
201	renderer->d.finishFrame = GBAVideoGLRendererFinishFrame;
202	renderer->d.getPixels = GBAVideoGLRendererGetPixels;
203	renderer->d.putPixels = GBAVideoGLRendererPutPixels;
204
205	renderer->d.disableBG[0] = false;
206	renderer->d.disableBG[1] = false;
207	renderer->d.disableBG[2] = false;
208	renderer->d.disableBG[3] = false;
209	renderer->d.disableOBJ = false;
210
211	renderer->scale = 1;
212}
213
214void _compileBackground(struct GBAVideoGLRenderer* glRenderer, GLuint program, const char** shaderBuffer, int shaderBufferLines, GLuint vs, char* log) {
215	GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
216	glAttachShader(program, vs);
217	glAttachShader(program, fs);
218	glShaderSource(fs, shaderBufferLines, shaderBuffer, 0);
219	glCompileShader(fs);
220	glGetShaderInfoLog(fs, 1024, 0, log);
221	if (log[0]) {
222		mLOG(GBA_VIDEO, ERROR, "Fragment shader compilation failure: %s", log);
223	}
224	glLinkProgram(program);
225	glGetProgramInfoLog(program, 1024, 0, log);
226	if (log[0]) {
227		mLOG(GBA_VIDEO, ERROR, "Program link failure: %s", log);
228	}
229	glDeleteShader(fs);
230}
231
232void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) {
233	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
234	glGenFramebuffers(2, glRenderer->fbo);
235	glGenTextures(3, glRenderer->layers);
236
237	glGenTextures(1, &glRenderer->paletteTex);
238	glBindTexture(GL_TEXTURE_2D, glRenderer->paletteTex);
239	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
240	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
241
242	glGenTextures(1, &glRenderer->vramTex);
243	glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex);
244	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
245	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
246	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, 256, 192, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 0);
247
248	glGenTextures(1, &glRenderer->oamTex);
249	glBindTexture(GL_TEXTURE_2D, glRenderer->oamTex);
250	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
251	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
252
253	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[1]);
254	glBindTexture(GL_TEXTURE_2D, glRenderer->outputTex);
255	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
256	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
257	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
258	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
259	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
260	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glRenderer->outputTex, 0);
261
262	glBindTexture(GL_TEXTURE_2D, glRenderer->layers[2]);
263	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
264	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
265	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
266	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
267	glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, 0);
268	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, glRenderer->layers[2], 0);
269
270	glBindFramebuffer(GL_FRAMEBUFFER, 0);
271
272	int i;
273	for (i = 0; i < 4; ++i) {
274		glRenderer->bg[i].index = i;
275		glGenFramebuffers(1, &glRenderer->bg[i].fbo);
276		glGenTextures(1, &glRenderer->bg[i].tex);
277		glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->bg[i].fbo);
278		glBindTexture(GL_TEXTURE_2D, glRenderer->bg[i].tex);
279		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
280		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
281		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
282		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
283		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
284		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glRenderer->bg[i].tex, 0);
285		glBindFramebuffer(GL_FRAMEBUFFER, 0);
286	}
287
288	glRenderer->compositeProgram = glCreateProgram();
289	glRenderer->objProgram = glCreateProgram();
290	glRenderer->bgProgram[0] = glCreateProgram();
291	glRenderer->bgProgram[1] = glCreateProgram();
292	glRenderer->bgProgram[2] = glCreateProgram();
293	glRenderer->bgProgram[3] = glCreateProgram();
294	glRenderer->bgProgram[4] = glCreateProgram();
295	glRenderer->bgProgram[5] = glCreateProgram();
296
297	char log[1024];
298	const GLchar* shaderBuffer[8];
299	shaderBuffer[0] = _gl3Header;
300
301	GLuint vs = glCreateShader(GL_VERTEX_SHADER);
302	shaderBuffer[1] = _vertexShader;
303	glShaderSource(vs, 2, shaderBuffer, 0);
304	glCompileShader(vs);
305	glGetShaderInfoLog(vs, 1024, 0, log);
306	if (log[0]) {
307		mLOG(GBA_VIDEO, ERROR, "Vertex shader compilation failure: %s", log);
308	}
309
310	shaderBuffer[1] = _renderMode0;
311
312	shaderBuffer[2] = _renderTile16;
313	_compileBackground(glRenderer, glRenderer->bgProgram[0], shaderBuffer, 3, vs, log);
314
315	shaderBuffer[2] = _renderTile256;
316	_compileBackground(glRenderer, glRenderer->bgProgram[1], shaderBuffer, 3, vs, log);
317
318	shaderBuffer[1] = _renderMode2;
319
320	shaderBuffer[2] = _fetchTileOverflow;
321	_compileBackground(glRenderer, glRenderer->bgProgram[2], shaderBuffer, 3, vs, log);
322
323	shaderBuffer[2] = _fetchTileNoOverflow;
324	_compileBackground(glRenderer, glRenderer->bgProgram[3], shaderBuffer, 3, vs, log);
325
326	shaderBuffer[1] = _composite;
327	_compileBackground(glRenderer, glRenderer->compositeProgram, shaderBuffer, 2, vs, log);
328
329	glDeleteShader(vs);
330
331	GBAVideoGLRendererReset(renderer);
332}
333
334void GBAVideoGLRendererDeinit(struct GBAVideoRenderer* renderer) {
335	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
336	glDeleteFramebuffers(2, glRenderer->fbo);
337	glDeleteTextures(3, glRenderer->layers);
338	glDeleteTextures(1, &glRenderer->paletteTex);
339	glDeleteTextures(1, &glRenderer->vramTex);
340	glDeleteTextures(1, &glRenderer->oamTex);
341}
342
343void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer) {
344	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
345
346	glRenderer->paletteDirty = true;
347	glRenderer->vramDirty = 0xFFFFFF;
348}
349
350void GBAVideoGLRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
351	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
352	glRenderer->vramDirty |= 1 << (address >> 12);
353}
354
355void GBAVideoGLRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
356	UNUSED(oam);
357	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
358	glRenderer->oamDirty = true;
359}
360
361void GBAVideoGLRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
362	UNUSED(address);
363	UNUSED(value);
364	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
365	glRenderer->paletteDirty = true;
366}
367
368uint16_t GBAVideoGLRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
369	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
370	if (renderer->cache) {
371		GBAVideoCacheWriteVideoRegister(renderer->cache, address, value);
372	}
373
374	switch (address) {
375	case REG_DISPCNT:
376		value &= 0xFFF7;
377		glRenderer->dispcnt = value;
378		GBAVideoGLRendererUpdateDISPCNT(glRenderer);
379		break;
380	case REG_BG0CNT:
381		value &= 0xDFFF;
382		GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[0], value);
383		break;
384	case REG_BG1CNT:
385		value &= 0xDFFF;
386		GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[1], value);
387		break;
388	case REG_BG2CNT:
389		value &= 0xFFFF;
390		GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[2], value);
391		break;
392	case REG_BG3CNT:
393		value &= 0xFFFF;
394		GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[3], value);
395		break;
396	case REG_BG0HOFS:
397		value &= 0x01FF;
398		glRenderer->bg[0].x = value;
399		break;
400	case REG_BG0VOFS:
401		value &= 0x01FF;
402		glRenderer->bg[0].y = value;
403		break;
404	case REG_BG1HOFS:
405		value &= 0x01FF;
406		glRenderer->bg[1].x = value;
407		break;
408	case REG_BG1VOFS:
409		value &= 0x01FF;
410		glRenderer->bg[1].y = value;
411		break;
412	case REG_BG2HOFS:
413		value &= 0x01FF;
414		glRenderer->bg[2].x = value;
415		break;
416	case REG_BG2VOFS:
417		value &= 0x01FF;
418		glRenderer->bg[2].y = value;
419		break;
420	case REG_BG3HOFS:
421		value &= 0x01FF;
422		glRenderer->bg[3].x = value;
423		break;
424	case REG_BG3VOFS:
425		value &= 0x01FF;
426		glRenderer->bg[3].y = value;
427		break;
428	case REG_BG2PA:
429		glRenderer->bg[2].dx = value;
430		break;
431	case REG_BG2PB:
432		glRenderer->bg[2].dmx = value;
433		break;
434	case REG_BG2PC:
435		glRenderer->bg[2].dy = value;
436		break;
437	case REG_BG2PD:
438		glRenderer->bg[2].dmy = value;
439		break;
440	case REG_BG2X_LO:
441		GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[2], value);
442		break;
443	case REG_BG2X_HI:
444		GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[2], value);
445		break;
446	case REG_BG2Y_LO:
447		GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[2], value);
448		break;
449	case REG_BG2Y_HI:
450		GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[2], value);
451		break;
452	case REG_BG3PA:
453		glRenderer->bg[3].dx = value;
454		break;
455	case REG_BG3PB:
456		glRenderer->bg[3].dmx = value;
457		break;
458	case REG_BG3PC:
459		glRenderer->bg[3].dy = value;
460		break;
461	case REG_BG3PD:
462		glRenderer->bg[3].dmy = value;
463		break;
464	case REG_BG3X_LO:
465		GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[3], value);
466		break;
467	case REG_BG3X_HI:
468		GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[3], value);
469		break;
470	case REG_BG3Y_LO:
471		GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[3], value);
472		break;
473	case REG_BG3Y_HI:
474		GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[3], value);
475		break;
476	case REG_BLDCNT:
477		GBAVideoGLRendererWriteBLDCNT(glRenderer, value);
478		value &= 0x3FFF;
479		break;
480	case REG_BLDALPHA:
481		glRenderer->blda = value & 0x1F;
482		if (glRenderer->blda > 0x10) {
483			glRenderer->blda = 0x10;
484		}
485		glRenderer->bldb = (value >> 8) & 0x1F;
486		if (glRenderer->bldb > 0x10) {
487			glRenderer->bldb = 0x10;
488		}
489		value &= 0x1F1F;
490		break;
491	case REG_BLDY:
492		value &= 0x1F;
493		if (value > 0x10) {
494			value = 0x10;
495		}
496		if (glRenderer->bldy != value) {
497			glRenderer->bldy = value;
498		}
499		break;
500	case REG_WIN0H:
501		/*glRenderer->winN[0].h.end = value;
502		glRenderer->winN[0].h.start = value >> 8;
503		if (glRenderer->winN[0].h.start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[0].h.start > glRenderer->winN[0].h.end) {
504			glRenderer->winN[0].h.start = 0;
505		}
506		if (glRenderer->winN[0].h.end > GBA_VIDEO_HORIZONTAL_PIXELS) {
507			glRenderer->winN[0].h.end = GBA_VIDEO_HORIZONTAL_PIXELS;
508			if (glRenderer->winN[0].h.start > GBA_VIDEO_HORIZONTAL_PIXELS) {
509				glRenderer->winN[0].h.start = GBA_VIDEO_HORIZONTAL_PIXELS;
510			}
511		}*/
512		break;
513	case REG_WIN1H:
514		/*glRenderer->winN[1].h.end = value;
515		glRenderer->winN[1].h.start = value >> 8;
516		if (glRenderer->winN[1].h.start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[1].h.start > glRenderer->winN[1].h.end) {
517			glRenderer->winN[1].h.start = 0;
518		}
519		if (glRenderer->winN[1].h.end > GBA_VIDEO_HORIZONTAL_PIXELS) {
520			glRenderer->winN[1].h.end = GBA_VIDEO_HORIZONTAL_PIXELS;
521			if (glRenderer->winN[1].h.start > GBA_VIDEO_HORIZONTAL_PIXELS) {
522				glRenderer->winN[1].h.start = GBA_VIDEO_HORIZONTAL_PIXELS;
523			}
524		}*/
525		break;
526	case REG_WIN0V:
527		/*glRenderer->winN[0].v.end = value;
528		glRenderer->winN[0].v.start = value >> 8;
529		if (glRenderer->winN[0].v.start > GBA_VIDEO_VERTICAL_PIXELS && glRenderer->winN[0].v.start > glRenderer->winN[0].v.end) {
530			glRenderer->winN[0].v.start = 0;
531		}
532		if (glRenderer->winN[0].v.end > GBA_VIDEO_VERTICAL_PIXELS) {
533			glRenderer->winN[0].v.end = GBA_VIDEO_VERTICAL_PIXELS;
534			if (glRenderer->winN[0].v.start > GBA_VIDEO_VERTICAL_PIXELS) {
535				glRenderer->winN[0].v.start = GBA_VIDEO_VERTICAL_PIXELS;
536			}
537		}*/
538		break;
539	case REG_WIN1V:
540		/*glRenderer->winN[1].v.end = value;
541		glRenderer->winN[1].v.start = value >> 8;
542		if (glRenderer->winN[1].v.start > GBA_VIDEO_VERTICAL_PIXELS && glRenderer->winN[1].v.start > glRenderer->winN[1].v.end) {
543			glRenderer->winN[1].v.start = 0;
544		}
545		if (glRenderer->winN[1].v.end > GBA_VIDEO_VERTICAL_PIXELS) {
546			glRenderer->winN[1].v.end = GBA_VIDEO_VERTICAL_PIXELS;
547			if (glRenderer->winN[1].v.start > GBA_VIDEO_VERTICAL_PIXELS) {
548				glRenderer->winN[1].v.start = GBA_VIDEO_VERTICAL_PIXELS;
549			}
550		}*/
551		break;
552	case REG_WININ:
553		value &= 0x3F3F;
554		//glRenderer->winN[0].control.packed = value;
555		//glRenderer->winN[1].control.packed = value >> 8;
556		break;
557	case REG_WINOUT:
558		value &= 0x3F3F;
559		//glRenderer->winout.packed = value;
560		//glRenderer->objwin.packed = value >> 8;
561		break;
562	case REG_MOSAIC:
563		glRenderer->mosaic = value;
564		break;
565	case REG_GREENSWP:
566		mLOG(GBA_VIDEO, STUB, "Stub video register write: 0x%03X", address);
567		break;
568	default:
569		mLOG(GBA_VIDEO, GAME_ERROR, "Invalid video register: 0x%03X", address);
570	}
571	return value;
572}
573
574void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
575	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
576	if (glRenderer->paletteDirty) {
577		glBindTexture(GL_TEXTURE_2D, glRenderer->paletteTex);
578		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, 16, 32, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, glRenderer->d.palette);
579		glRenderer->paletteDirty = false;
580	}
581	if (glRenderer->oamDirty) {
582		glBindTexture(GL_TEXTURE_2D, glRenderer->oamTex);
583		glTexImage2D(GL_TEXTURE_2D, 0, GL_R16UI, 4, 128, 0, GL_RED_INTEGER, GL_UNSIGNED_SHORT, glRenderer->d.oam);
584		glRenderer->oamDirty = false;
585	}
586	int i;
587	for (i = 0; i < 24; ++i) {
588		if (!(glRenderer->vramDirty & (1 << i))) {
589			continue;
590		}
591		// TODO: PBOs
592		glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex);
593		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 8 * i, 256, 8, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, &glRenderer->d.vram[2048 * i]);
594	}
595	glRenderer->vramDirty = 0;
596
597	uint32_t backdrop = M_RGB5_TO_RGB8(renderer->palette[0]);
598	glClearColor(((backdrop >> 16) & 0xFF) / 256., ((backdrop >> 8) & 0xFF) / 256., (backdrop & 0xFF) / 256., 0.f);
599	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[1]);
600	glEnable(GL_SCISSOR_TEST);
601	glScissor(0, y * glRenderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, glRenderer->scale);
602	glClear(GL_COLOR_BUFFER_BIT);
603	glDisable(GL_SCISSOR_TEST);
604	if (y == 0) {
605		glClearDepthf(1);
606		glClear(GL_DEPTH_BUFFER_BIT);
607	}
608	glBindFramebuffer(GL_FRAMEBUFFER, 0);
609
610	unsigned priority;
611	for (priority = 0; priority < 4; ++priority) {
612		if (TEST_LAYER_ENABLED(0) && GBARegisterDISPCNTGetMode(glRenderer->dispcnt) < 2) {
613			GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[0], y);
614		}
615		if (TEST_LAYER_ENABLED(1) && GBARegisterDISPCNTGetMode(glRenderer->dispcnt) < 2) {
616			GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[1], y);
617		}
618		if (TEST_LAYER_ENABLED(2)) {
619			switch (GBARegisterDISPCNTGetMode(glRenderer->dispcnt)) {
620			case 0:
621				GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[2], y);
622				break;
623			case 1:
624			case 2:
625				GBAVideoGLRendererDrawBackgroundMode2(glRenderer, &glRenderer->bg[2], y);
626				break;
627			case 3:
628				//GBAVideoGLRendererDrawBackgroundMode3(glRenderer, &glRenderer->bg[2], y);
629				break;
630			case 4:
631				//GBAVideoGLRendererDrawBackgroundMode4(glRenderer, &glRenderer->bg[2], y);
632				break;
633			case 5:
634				//GBAVideoGLRendererDrawBackgroundMode5(glRenderer, &glRenderer->bg[2], y);
635				break;
636			}
637		}
638		if (TEST_LAYER_ENABLED(3)) {
639			switch (GBARegisterDISPCNTGetMode(glRenderer->dispcnt)) {
640			case 0:
641				GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[3], y);
642				break;
643			case 2:
644				GBAVideoGLRendererDrawBackgroundMode2(glRenderer, &glRenderer->bg[3], y);
645				break;
646			}
647		}
648	}
649	if (GBARegisterDISPCNTGetMode(glRenderer->dispcnt) != 0) {
650		glRenderer->bg[2].lastDx = glRenderer->bg[2].dx;
651		glRenderer->bg[2].lastDy = glRenderer->bg[2].dy;
652		glRenderer->bg[2].lastDmx = glRenderer->bg[2].dmx;
653		glRenderer->bg[2].lastDmy = glRenderer->bg[2].dmy;
654		glRenderer->bg[2].lastSx = glRenderer->bg[2].sx;
655		glRenderer->bg[2].lastSy = glRenderer->bg[2].sy;
656		glRenderer->bg[2].lastSx = glRenderer->bg[2].sx;
657		glRenderer->bg[2].lastSy = glRenderer->bg[2].sy;
658		glRenderer->bg[2].sx += glRenderer->bg[2].dmx;
659		glRenderer->bg[2].sy += glRenderer->bg[2].dmy;
660		glRenderer->bg[3].lastDx = glRenderer->bg[3].dx;
661		glRenderer->bg[3].lastDy = glRenderer->bg[3].dy;
662		glRenderer->bg[3].lastDmx = glRenderer->bg[3].dmx;
663		glRenderer->bg[3].lastDmy = glRenderer->bg[3].dmy;
664		glRenderer->bg[3].lastSx = glRenderer->bg[3].sx;
665		glRenderer->bg[3].lastSy = glRenderer->bg[3].sy;
666		glRenderer->bg[3].sx += glRenderer->bg[3].dmx;
667		glRenderer->bg[3].sy += glRenderer->bg[3].dmy;
668	}
669}
670
671void GBAVideoGLRendererFinishFrame(struct GBAVideoRenderer* renderer) {
672	UNUSED(renderer);
673	glFlush();
674}
675
676void GBAVideoGLRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
677
678}
679
680void GBAVideoGLRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
681
682}
683
684static void _enableBg(struct GBAVideoGLRenderer* renderer, int bg, bool active) {
685	int wasActive = renderer->bg[bg].enabled;
686	if (!active) {
687		renderer->bg[bg].enabled = 0;
688	} else if (!wasActive && active) {
689		/*if (renderer->nextY == 0 || GBARegisterDISPCNTGetMode(renderer->dispcnt) > 2) {
690			// TODO: Investigate in more depth how switching background works in different modes
691			renderer->bg[bg].enabled = 4;
692		} else {
693			renderer->bg[bg].enabled = 1;
694		}*/
695		renderer->bg[bg].enabled = 4;
696	}
697}
698
699static void GBAVideoGLRendererUpdateDISPCNT(struct GBAVideoGLRenderer* renderer) {
700	_enableBg(renderer, 0, GBARegisterDISPCNTGetBg0Enable(renderer->dispcnt));
701	_enableBg(renderer, 1, GBARegisterDISPCNTGetBg1Enable(renderer->dispcnt));
702	_enableBg(renderer, 2, GBARegisterDISPCNTGetBg2Enable(renderer->dispcnt));
703	_enableBg(renderer, 3, GBARegisterDISPCNTGetBg3Enable(renderer->dispcnt));
704}
705
706static void GBAVideoGLRendererWriteBGCNT(struct GBAVideoGLBackground* bg, uint16_t value) {
707	bg->priority = GBARegisterBGCNTGetPriority(value);
708	bg->charBase = GBARegisterBGCNTGetCharBase(value) << 13;
709	bg->mosaic = GBARegisterBGCNTGetMosaic(value);
710	bg->multipalette = GBARegisterBGCNTGet256Color(value);
711	bg->screenBase = GBARegisterBGCNTGetScreenBase(value) << 10;
712	bg->overflow = GBARegisterBGCNTGetOverflow(value);
713	bg->size = GBARegisterBGCNTGetSize(value);
714}
715
716static void GBAVideoGLRendererWriteBGX_LO(struct GBAVideoGLBackground* bg, uint16_t value) {
717	bg->refx = (bg->refx & 0xFFFF0000) | value;
718	bg->sx = bg->refx;
719}
720
721static void GBAVideoGLRendererWriteBGX_HI(struct GBAVideoGLBackground* bg, uint16_t value) {
722	bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
723	bg->refx <<= 4;
724	bg->refx >>= 4;
725	bg->sx = bg->refx;
726}
727
728static void GBAVideoGLRendererWriteBGY_LO(struct GBAVideoGLBackground* bg, uint16_t value) {
729	bg->refy = (bg->refy & 0xFFFF0000) | value;
730	bg->sy = bg->refy;
731}
732
733static void GBAVideoGLRendererWriteBGY_HI(struct GBAVideoGLBackground* bg, uint16_t value) {
734	bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
735	bg->refy <<= 4;
736	bg->refy >>= 4;
737	bg->sy = bg->refy;
738}
739
740static void GBAVideoGLRendererWriteBLDCNT(struct GBAVideoGLRenderer* renderer, uint16_t value) {
741	renderer->bg[0].target1 = GBARegisterBLDCNTGetTarget1Bg0(value);
742	renderer->bg[1].target1 = GBARegisterBLDCNTGetTarget1Bg1(value);
743	renderer->bg[2].target1 = GBARegisterBLDCNTGetTarget1Bg2(value);
744	renderer->bg[3].target1 = GBARegisterBLDCNTGetTarget1Bg3(value);
745	renderer->bg[0].target2 = GBARegisterBLDCNTGetTarget2Bg0(value);
746	renderer->bg[1].target2 = GBARegisterBLDCNTGetTarget2Bg1(value);
747	renderer->bg[2].target2 = GBARegisterBLDCNTGetTarget2Bg2(value);
748	renderer->bg[3].target2 = GBARegisterBLDCNTGetTarget2Bg3(value);
749
750	renderer->blendEffect = GBARegisterBLDCNTGetEffect(value);
751	renderer->target1Obj = GBARegisterBLDCNTGetTarget1Obj(value);
752	renderer->target1Bd = GBARegisterBLDCNTGetTarget1Bd(value);
753	renderer->target2Obj = GBARegisterBLDCNTGetTarget2Obj(value);
754	renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value);
755}
756
757static void _compositeLayer(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
758	if ((y & 0x1F) != 0x1F) {
759		return;
760	}
761	glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[1]);
762	glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
763	glScissor(0, (y * renderer->scale) % (0x20 * renderer->scale), GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, 0x20 * renderer->scale);
764	glDepthFunc(GL_LESS);
765	glEnable(GL_DEPTH_TEST);
766	glUseProgram(renderer->compositeProgram);
767	glActiveTexture(GL_TEXTURE0);
768	glBindTexture(GL_TEXTURE_2D, background->tex);
769	glUniform3i(0, 0x20, y & ~0x1F, (background->priority << 3) + (background->index << 1) + 1);
770	glUniform1i(1, 0);
771	glUniform1i(2, renderer->scale);
772	glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, _vertices);
773	glEnableVertexAttribArray(0);
774	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
775	glDisable(GL_DEPTH_TEST);
776}
777
778void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
779	int inY = y + background->y;
780	int yBase = inY & 0xFF;
781	if (background->size == 2) {
782		yBase += inY & 0x100;
783	} else if (background->size == 3) {
784		yBase += (inY & 0x100) << 1;
785	}
786	glBindFramebuffer(GL_FRAMEBUFFER, background->fbo);
787	glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
788	glScissor(0, y * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->scale);
789	glUseProgram(renderer->bgProgram[background->multipalette ? 1 : 0]);
790	glActiveTexture(GL_TEXTURE0);
791	glBindTexture(GL_TEXTURE_2D, renderer->vramTex);
792	glActiveTexture(GL_TEXTURE0 + 1);
793	glBindTexture(GL_TEXTURE_2D, renderer->paletteTex);
794	glUniform3i(0, 1, y, 0);
795	glUniform1i(1, 0);
796	glUniform1i(2, 1);
797	glUniform1i(3, background->screenBase);
798	glUniform1i(4, background->charBase);
799	glUniform1i(5, background->size);
800	glUniform2i(6, background->x, yBase - y);
801	glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, _vertices);
802	glEnableVertexAttribArray(0);
803	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
804
805	_compositeLayer(renderer, background, y);
806
807	glBindFramebuffer(GL_FRAMEBUFFER, 0);
808}
809
810void GBAVideoGLRendererDrawBackgroundMode2(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
811	glBindFramebuffer(GL_FRAMEBUFFER, background->fbo);
812	glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
813	glScissor(0, y * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->scale);
814	glUseProgram(renderer->bgProgram[background->overflow ? 2 : 3]);
815	glActiveTexture(GL_TEXTURE0);
816	glBindTexture(GL_TEXTURE_2D, renderer->vramTex);
817	glActiveTexture(GL_TEXTURE0 + 1);
818	glBindTexture(GL_TEXTURE_2D, renderer->paletteTex);
819	glUniform3i(0, 1, y, 0);
820	glUniform1i(1, 0);
821	glUniform1i(2, 1);
822	glUniform1i(3, background->screenBase);
823	glUniform1i(4, background->charBase);
824	glUniform1i(5, background->size);
825	glUniform2i(6, background->sx, background->sy);
826	glUniform2i(7, background->lastSx, background->lastSy);
827	glUniformMatrix2fv(8, 1, GL_FALSE, (GLfloat[]) { background->dx, background->dy, background->dmx, background->dmy });
828	glUniformMatrix2fv(9, 1, GL_FALSE, (GLfloat[]) { background->lastDx, background->lastDy, background->lastDmx, background->lastDmy });
829	glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, _vertices);
830	glEnableVertexAttribArray(0);
831	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
832
833	_compositeLayer(renderer, background, y);
834
835	glBindFramebuffer(GL_FRAMEBUFFER, 0);
836}