all repos — mgba @ 1a04532d4b00a33fa6adef638290a2f9632badfb

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