all repos — mgba @ 8a6ea929d2b905e51aa21ccf460e9fb2696da0ea

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