all repos — mgba @ 3e0675f539218973801f40d1abcacc2ffbf89e6c

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