all repos — mgba @ 5cc4c752e59bdedb40feb64b9818d3d5baac6cd8

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#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
   9
  10#include <mgba/core/cache-set.h>
  11#include <mgba/internal/arm/macros.h>
  12#include <mgba/internal/gba/io.h>
  13#include <mgba/internal/gba/renderers/cache-set.h>
  14#include <mgba-util/memory.h>
  15
  16#define FLAG_CONST "const vec4 flagCoeff = vec4(64., 32., 16., 1.);\n"
  17
  18static void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer);
  19static void GBAVideoGLRendererDeinit(struct GBAVideoRenderer* renderer);
  20static void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer);
  21static void GBAVideoGLRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
  22static void GBAVideoGLRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
  23static void GBAVideoGLRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
  24static uint16_t GBAVideoGLRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
  25static void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
  26static void GBAVideoGLRendererFinishFrame(struct GBAVideoRenderer* renderer);
  27static void GBAVideoGLRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels);
  28static void GBAVideoGLRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
  29
  30static void GBAVideoGLRendererUpdateDISPCNT(struct GBAVideoGLRenderer* renderer);
  31static void GBAVideoGLRendererWriteBGCNT(struct GBAVideoGLBackground* bg, uint16_t value);
  32static void GBAVideoGLRendererWriteBGX_LO(struct GBAVideoGLBackground* bg, uint16_t value);
  33static void GBAVideoGLRendererWriteBGX_HI(struct GBAVideoGLBackground* bg, uint16_t value);
  34static void GBAVideoGLRendererWriteBGY_LO(struct GBAVideoGLBackground* bg, uint16_t value);
  35static void GBAVideoGLRendererWriteBGY_HI(struct GBAVideoGLBackground* bg, uint16_t value);
  36static void GBAVideoGLRendererWriteBLDCNT(struct GBAVideoGLRenderer* renderer, uint16_t value);
  37
  38static void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GBAObj* sprite, int y, int spriteY);
  39static void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y);
  40static void GBAVideoGLRendererDrawBackgroundMode2(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y);
  41static void GBAVideoGLRendererDrawBackgroundMode3(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y);
  42static void GBAVideoGLRendererDrawBackgroundMode4(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y);
  43static void GBAVideoGLRendererDrawBackgroundMode5(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y);
  44static void GBAVideoGLRendererDrawWindow(struct GBAVideoGLRenderer* renderer, int y);
  45
  46static void _cleanRegister(struct GBAVideoGLRenderer* renderer, int address, uint16_t value);
  47static void _drawScanlines(struct GBAVideoGLRenderer* renderer, int lastY);
  48static void _finalizeLayers(struct GBAVideoGLRenderer* renderer);
  49
  50#define TEST_LAYER_ENABLED(X) !glRenderer->d.disableBG[X] && glRenderer->bg[X].enabled == 4
  51
  52struct GBAVideoGLUniform {
  53	const char* name;
  54	int type;
  55};
  56
  57static const GLchar* const _gl3Header =
  58	"#version 130\n";
  59
  60static const char* const _vertexShader =
  61	"in vec2 position;\n"
  62	"uniform ivec2 loc;\n"
  63	"uniform ivec2 maxPos;\n"
  64	"out vec2 texCoord;\n"
  65
  66	"void main() {\n"
  67	"	vec2 local = vec2(position.x, float(position.y * loc.x + loc.y) / abs(maxPos.y));\n"
  68	"	gl_Position = vec4((local * 2. - 1.) * sign(maxPos), 0., 1.);\n"
  69	"	texCoord = local * abs(maxPos);\n"
  70	"}";
  71
  72static const char* const _renderTile16 =
  73	"vec4 renderTile(int tile, int paletteId, ivec2 localCoord) {\n"
  74	"	int address = charBase + tile * 16 + (localCoord.x >> 2) + (localCoord.y << 1);\n"
  75	"	vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
  76	"	int entry = int(halfrow[3 - (localCoord.x & 3)] * 15.9);\n"
  77	"	vec4 color = texelFetch(palette, ivec2(entry, paletteId), 0);\n"
  78	"	if (entry == 0) {\n"
  79	"		discard;\n"
  80	"	}\n"
  81	"	color.a = 1;\n"
  82	"	return color;\n"
  83	"}";
  84
  85static const char* const _renderTile256 =
  86	"vec4 renderTile(int tile, int paletteId, ivec2 localCoord) {\n"
  87	"	int address = charBase + tile * 32 + (localCoord.x >> 1) + (localCoord.y << 2);\n"
  88	"	vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
  89	"	int entry = int(halfrow[3 - 2 * (localCoord.x & 1)] * 15.9);\n"
  90	"	int pal2 = int(halfrow[2 - 2 * (localCoord.x & 1)] * 15.9);\n"
  91	"	vec4 color = texelFetch(palette, ivec2(entry, pal2 + (paletteId & 16)), 0);\n"
  92	"	if ((pal2 | entry) == 0) {\n"
  93	"		discard;\n"
  94	"	}\n"
  95	"	color.a = 1.;\n"
  96	"	return color;\n"
  97	"}";
  98
  99static const struct GBAVideoGLUniform _uniformsMode0[] = {
 100	{ "loc", GBA_GL_VS_LOC, },
 101	{ "maxPos", GBA_GL_VS_MAXPOS, },
 102	{ "vram", GBA_GL_BG_VRAM, },
 103	{ "palette", GBA_GL_BG_PALETTE, },
 104	{ "screenBase", GBA_GL_BG_SCREENBASE, },
 105	{ "charBase", GBA_GL_BG_CHARBASE, },
 106	{ "size", GBA_GL_BG_SIZE, },
 107	{ "offset", GBA_GL_BG_OFFSET, },
 108	{ "inflags", GBA_GL_BG_INFLAGS, },
 109	{ "mosaic", GBA_GL_BG_MOSAIC, },
 110	{ 0 }
 111};
 112
 113static const char* const _renderMode0 =
 114	"in vec2 texCoord;\n"
 115	"uniform sampler2D vram;\n"
 116	"uniform sampler2D palette;\n"
 117	"uniform int screenBase;\n"
 118	"uniform int charBase;\n"
 119	"uniform int size;\n"
 120	"uniform ivec2 offset;\n"
 121	"uniform ivec4 inflags;\n"
 122	"uniform ivec2 mosaic;\n"
 123	"out vec4 color;\n"
 124	"out vec4 flags;\n"
 125	FLAG_CONST
 126
 127	"vec4 renderTile(int tile, int paletteId, ivec2 localCoord);\n"
 128
 129	"void main() {\n"
 130	"	ivec2 coord = ivec2(texCoord);\n"
 131	"	if (mosaic.x > 1) {\n"
 132	"		coord.x -= int(mod(coord.x, mosaic.x));\n"
 133	"	}\n"
 134	"	if (mosaic.y > 1) {\n"
 135	"		coord.y -= int(mod(coord.y, mosaic.y));\n"
 136	"	}\n"
 137	"	coord += offset;\n"
 138	"	if ((size & 1) == 1) {\n"
 139	"		coord.y += coord.x & 256;\n"
 140	"	}\n"
 141	"	coord.x &= 255;\n"
 142	"	int mapAddress = screenBase + (coord.x >> 3) + (coord.y >> 3) * 32;\n"
 143	"	vec4 map = texelFetch(vram, ivec2(mapAddress & 255, mapAddress >> 8), 0);\n"
 144	"	int tileFlags = int(map.g * 15.9);\n"
 145	"	if ((tileFlags & 4) == 4) {\n"
 146	"		coord.x ^= 7;\n"
 147	"	}\n"
 148	"	if ((tileFlags & 8) == 8) {\n"
 149	"		coord.y ^= 7;\n"
 150	"	}\n"
 151	"	int tile = int(map.a * 15.9) + int(map.b * 15.9) * 16 + (tileFlags & 0x3) * 256;\n"
 152	"	color = renderTile(tile, int(map.r * 15.9), coord & 7);\n"
 153	"	flags = inflags / flagCoeff;\n"
 154	"}";
 155
 156static const char* const _fetchTileOverflow =
 157	"vec4 fetchTile(ivec2 coord) {\n"
 158	"	int sizeAdjusted = (0x8000 << size) - 1;\n"
 159	"	coord &= sizeAdjusted;\n"
 160	"	return renderTile(coord);\n"
 161	"}";
 162
 163static const char* const _fetchTileNoOverflow =
 164	"vec4 fetchTile(ivec2 coord) {\n"
 165	"	int sizeAdjusted = (0x8000 << size) - 1;\n"
 166	"	ivec2 outerCoord = coord & ~sizeAdjusted;\n"
 167	"	if ((outerCoord.x | outerCoord.y) != 0) {\n"
 168	"		discard;\n"
 169	"	}\n"
 170	"	return renderTile(coord);\n"
 171	"}";
 172
 173static const struct GBAVideoGLUniform _uniformsMode2[] = {
 174	{ "loc", GBA_GL_VS_LOC, },
 175	{ "maxPos", GBA_GL_VS_MAXPOS, },
 176	{ "vram", GBA_GL_BG_VRAM, },
 177	{ "palette", GBA_GL_BG_PALETTE, },
 178	{ "screenBase", GBA_GL_BG_SCREENBASE, },
 179	{ "charBase", GBA_GL_BG_CHARBASE, },
 180	{ "size", GBA_GL_BG_SIZE, },
 181	{ "inflags", GBA_GL_BG_INFLAGS, },
 182	{ "offset", GBA_GL_BG_OFFSET, },
 183	{ "transform", GBA_GL_BG_TRANSFORM, },
 184	{ "mosaic", GBA_GL_BG_MOSAIC, },
 185	{ 0 }
 186};
 187
 188static const char* const _interpolate =
 189	"vec2 interpolate(ivec2 arr[4], float x) {\n"
 190	"	float x1m = 1. - x;\n"
 191	"	return x1m * x1m * x1m * arr[0] +"
 192		"  3 * x1m * x1m * x   * arr[1] +"
 193		"  3 * x1m * x   * x   * arr[2] +"
 194		"      x   * x   * x   * arr[3];\n"
 195	"}\n"
 196
 197	"void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]) {\n"
 198	"	mat[2] = texelFetch(transform, ivec2(0, y), 0).xz;\n"
 199	"	ivec4 splitOffset[4];\n"
 200	"	splitOffset[2] = texelFetch(transform, ivec2(1, y), 0);\n"
 201
 202	"	mat[0] = texelFetch(transform, ivec2(0, y - 2), 0).xz;\n"
 203	"	mat[1] = texelFetch(transform, ivec2(0, y - 1), 0).xz;\n"
 204	"	mat[3] = texelFetch(transform, ivec2(0, y + 1), 0).xz;\n"
 205
 206	"	splitOffset[0] = texelFetch(transform, ivec2(1, y - 2), 0);\n"
 207	"	splitOffset[1] = texelFetch(transform, ivec2(1, y - 1), 0);\n"
 208	"	splitOffset[3] = texelFetch(transform, ivec2(1, y + 1), 0);\n"
 209
 210	"	aff[0] = (splitOffset[0].xz & 0xFFFF) + (splitOffset[0].yw << 16);\n"
 211	"	aff[1] = (splitOffset[1].xz & 0xFFFF) + (splitOffset[1].yw << 16);\n"
 212	"	aff[2] = (splitOffset[2].xz & 0xFFFF) + (splitOffset[2].yw << 16);\n"
 213	"	aff[3] = (splitOffset[3].xz & 0xFFFF) + (splitOffset[3].yw << 16);\n"
 214	"}\n";
 215
 216static const char* const _renderMode2 =
 217	"in vec2 texCoord;\n"
 218	"uniform sampler2D vram;\n"
 219	"uniform sampler2D palette;\n"
 220	"uniform int screenBase;\n"
 221	"uniform int charBase;\n"
 222	"uniform int size;\n"
 223	"uniform ivec4 inflags;\n"
 224	"uniform isampler2D transform;\n"
 225	"uniform ivec2 mosaic;\n"
 226	"out vec4 color;\n"
 227	"out vec4 flags;\n"
 228	FLAG_CONST
 229	"precision highp float;\n"
 230	"precision highp int;\n"
 231
 232	"vec4 fetchTile(ivec2 coord);\n"
 233	"vec2 interpolate(ivec2 arr[4], float x);\n"
 234	"void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n"
 235
 236	"vec4 renderTile(ivec2 coord) {\n"
 237	"	int map = (coord.x >> 11) + (((coord.y >> 7) & 0x7F0) << size);\n"
 238	"	int mapAddress = screenBase + (map >> 1);\n"
 239	"	vec4 twomaps = texelFetch(vram, ivec2(mapAddress & 255, mapAddress >> 8), 0);\n"
 240	"	int tile = int(twomaps[3 - 2 * (map & 1)] * 15.9) + int(twomaps[2 - 2 * (map & 1)] * 15.9) * 16;\n"
 241	"	int address = charBase + tile * 32 + ((coord.x >> 9) & 3) + ((coord.y >> 6) & 0x1C);\n"
 242	"	vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
 243	"	int entry = int(halfrow[3 - ((coord.x >> 7) & 2)] * 15.9);\n"
 244	"	int pal2 = int(halfrow[2 - ((coord.x >> 7) & 2)] * 15.9);\n"
 245	"	vec4 color = texelFetch(palette, ivec2(entry, pal2), 0);\n"
 246	"	if ((pal2 | entry) == 0) {\n"
 247	"		discard;\n"
 248	"	}\n"
 249	"	color.a = 1.;\n"
 250	"	return color;\n"
 251	"}\n"
 252
 253	"void main() {\n"
 254	"	ivec2 mat[4];\n"
 255	"	ivec2 offset[4];\n"
 256	"	loadAffine(int(texCoord.y), mat, offset);\n"
 257	"	vec2 coord = texCoord;\n"
 258	"	if (mosaic.x > 1) {\n"
 259	"		coord.x -= mod(coord.x, mosaic.x);\n"
 260	"	}\n"
 261	"	if (mosaic.y > 1) {\n"
 262	"		coord.y -= mod(coord.y, mosaic.y);\n"
 263	"	}\n"
 264	"	float y = fract(coord.y);\n"
 265	"	float lin = 0.5 + y * 0.25;\n"
 266	"	vec2 mixedTransform = interpolate(mat, lin);\n"
 267	"	vec2 mixedOffset = interpolate(offset, lin);\n"
 268	"	color = fetchTile(ivec2(mixedTransform * coord.x + mixedOffset));\n"
 269	"	flags = inflags / flagCoeff;\n"
 270	"}";
 271
 272static const struct GBAVideoGLUniform _uniformsMode35[] = {
 273	{ "loc", GBA_GL_VS_LOC, },
 274	{ "maxPos", GBA_GL_VS_MAXPOS, },
 275	{ "vram", GBA_GL_BG_VRAM, },
 276	{ "charBase", GBA_GL_BG_CHARBASE, },
 277	{ "size", GBA_GL_BG_SIZE, },
 278	{ "inflags", GBA_GL_BG_INFLAGS, },
 279	{ "offset", GBA_GL_BG_OFFSET, },
 280	{ "transform", GBA_GL_BG_TRANSFORM, },
 281	{ "mosaic", GBA_GL_BG_MOSAIC, },
 282	{ 0 }
 283};
 284
 285static const char* const _renderMode35 =
 286	"in vec2 texCoord;\n"
 287	"uniform sampler2D vram;\n"
 288	"uniform int charBase;\n"
 289	"uniform ivec2 size;\n"
 290	"uniform ivec4 inflags;\n"
 291	"uniform isampler2D transform;\n"
 292	"uniform ivec2 mosaic;\n"
 293	"out vec4 color;\n"
 294	"out vec4 flags;\n"
 295	FLAG_CONST
 296	"precision highp float;\n"
 297	"precision highp int;\n"
 298
 299	"vec2 interpolate(ivec2 arr[4], float x);\n"
 300	"void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n"
 301
 302	"void main() {\n"
 303	"	ivec2 mat[4];\n"
 304	"	ivec2 offset[4];\n"
 305	"	loadAffine(int(texCoord.y), mat, offset);\n"
 306	"	vec2 incoord = texCoord;\n"
 307	"	if (mosaic.x > 1) {\n"
 308	"		incoord.x -= mod(incoord.x, mosaic.x);\n"
 309	"	}\n"
 310	"	if (mosaic.y > 1) {\n"
 311	"		incoord.y -= mod(incoord.y, mosaic.y);\n"
 312	"	}\n"
 313	"	float y = fract(incoord.y);\n"
 314	"	float lin = 0.5 + y * 0.25;\n"
 315	"	vec2 mixedTransform = interpolate(mat, lin);\n"
 316	"	vec2 mixedOffset = interpolate(offset, lin);\n"
 317	"	ivec2 coord = ivec2(mixedTransform * incoord.x + mixedOffset);\n"
 318	"	if (coord.x < 0 || coord.x >= (size.x << 8)) {\n"
 319	"		discard;\n"
 320	"	}\n"
 321	"	if (coord.y < 0 || coord.y >= (size.y << 8)) {\n"
 322	"		discard;\n"
 323	"	}\n"
 324	"	int address = charBase + (coord.x >> 8) + (coord.y >> 8) * size.x;\n"
 325	"	ivec4 entry = ivec4(texelFetch(vram, ivec2(address & 255, address >> 8), 0) * 15.9);\n"
 326	"	int sixteen = (entry.x << 12) | (entry.y << 8) | (entry.z << 4) | entry.w;\n"
 327	"	color = vec4((sixteen & 0x1F) / 31., ((sixteen >> 5) & 0x1F) / 31., ((sixteen >> 10) & 0x1F) / 31., 1.);\n"
 328	"	flags = inflags / flagCoeff;\n"
 329	"}";
 330
 331static const struct GBAVideoGLUniform _uniformsMode4[] = {
 332	{ "loc", GBA_GL_VS_LOC, },
 333	{ "maxPos", GBA_GL_VS_MAXPOS, },
 334	{ "vram", GBA_GL_BG_VRAM, },
 335	{ "palette", GBA_GL_BG_PALETTE, },
 336	{ "charBase", GBA_GL_BG_CHARBASE, },
 337	{ "size", GBA_GL_BG_SIZE, },
 338	{ "inflags", GBA_GL_BG_INFLAGS, },
 339	{ "offset", GBA_GL_BG_OFFSET, },
 340	{ "transform", GBA_GL_BG_TRANSFORM, },
 341	{ "mosaic", GBA_GL_BG_MOSAIC, },
 342	{ 0 }
 343};
 344
 345static const char* const _renderMode4 =
 346	"in vec2 texCoord;\n"
 347	"uniform sampler2D vram;\n"
 348	"uniform sampler2D palette;\n"
 349	"uniform int charBase;\n"
 350	"uniform ivec2 size;\n"
 351	"uniform ivec4 inflags;\n"
 352	"uniform isampler2D transform;\n"
 353	"uniform ivec2 mosaic;\n"
 354	"out vec4 color;\n"
 355	"out vec4 flags;\n"
 356	FLAG_CONST
 357	"precision highp float;\n"
 358	"precision highp int;\n"
 359
 360	"vec2 interpolate(ivec2 arr[4], float x);\n"
 361	"void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n"
 362
 363	"void main() {\n"
 364	"	ivec2 mat[4];\n"
 365	"	ivec2 offset[4];\n"
 366	"	loadAffine(int(texCoord.y), mat, offset);\n"
 367	"	vec2 incoord = texCoord;\n"
 368	"	if (mosaic.x > 1) {\n"
 369	"		incoord.x -= mod(incoord.x, mosaic.x);\n"
 370	"	}\n"
 371	"	if (mosaic.y > 1) {\n"
 372	"		incoord.y -= mod(incoord.y, mosaic.y);\n"
 373	"	}\n"
 374	"	float y = fract(incoord.y);\n"
 375	"	float lin = 0.5 + y * 0.25;\n"
 376	"	vec2 mixedTransform = interpolate(mat, lin);\n"
 377	"	vec2 mixedOffset = interpolate(offset, lin);\n"
 378	"	ivec2 coord = ivec2(mixedTransform * incoord.x + mixedOffset);\n"
 379	"	if (coord.x < 0 || coord.x >= (size.x << 8)) {\n"
 380	"		discard;\n"
 381	"	}\n"
 382	"	if (coord.y < 0 || coord.y >= (size.y << 8)) {\n"
 383	"		discard;\n"
 384	"	}\n"
 385	"	int address = charBase + (coord.x >> 8) + (coord.y >> 8) * size.x;\n"
 386	"	vec4 twoEntries = texelFetch(vram, ivec2((address >> 1) & 255, address >> 9), 0);\n"
 387	"	ivec2 entry = ivec2(twoEntries[3 - 2 * (address & 1)] * 15.9, twoEntries[2 - 2 * (address & 1)] * 15.9);\n"
 388	"	color = texelFetch(palette, entry, 0);\n"
 389	"	color.a = 1;\n"
 390	"	flags = inflags / flagCoeff;\n"
 391	"}";
 392
 393static const struct GBAVideoGLUniform _uniformsObj[] = {
 394	{ "loc", GBA_GL_VS_LOC, },
 395	{ "maxPos", GBA_GL_VS_MAXPOS, },
 396	{ "vram", GBA_GL_OBJ_VRAM, },
 397	{ "palette", GBA_GL_OBJ_PALETTE, },
 398	{ "charBase", GBA_GL_OBJ_CHARBASE, },
 399	{ "stride", GBA_GL_OBJ_STRIDE, },
 400	{ "localPalette", GBA_GL_OBJ_LOCALPALETTE, },
 401	{ "inflags", GBA_GL_OBJ_INFLAGS, },
 402	{ "transform", GBA_GL_OBJ_TRANSFORM, },
 403	{ "dims", GBA_GL_OBJ_DIMS, },
 404	{ "objwin", GBA_GL_OBJ_OBJWIN, },
 405	{ "mosaic", GBA_GL_OBJ_MOSAIC, },
 406	{ 0 }
 407};
 408
 409static const char* const _renderObj =
 410	"in vec2 texCoord;\n"
 411	"uniform sampler2D vram;\n"
 412	"uniform sampler2D palette;\n"
 413	"uniform int charBase;\n"
 414	"uniform int stride;\n"
 415	"uniform int localPalette;\n"
 416	"uniform ivec4 inflags;\n"
 417	"uniform mat2x2 transform;\n"
 418	"uniform ivec4 dims;\n"
 419	"uniform vec4 objwin;\n"
 420	"uniform ivec4 mosaic;\n"
 421	"out vec4 color;\n"
 422	"out vec4 flags;\n"
 423	"out vec3 window;\n"
 424	FLAG_CONST
 425
 426	"vec4 renderTile(int tile, int paletteId, ivec2 localCoord);\n"
 427
 428	"void main() {\n"
 429	"	vec2 incoord = texCoord;\n"
 430	"	if (mosaic.x > 1) {\n"
 431	"		int x = int(incoord.x);\n"
 432	"		incoord.x = clamp(x - int(mod(mosaic.z + x, mosaic.x)), 0, dims.z - 1);\n"
 433	"	} else if (mosaic.x < -1) {\n"
 434	"		int x = dims.z - int(incoord.x) - 1;\n"
 435	"		incoord.x = clamp(dims.z - x + int(mod(mosaic.z + x, -mosaic.x)) - 1, 0, dims.z - 1);\n"
 436	"	}\n"
 437	"	if (mosaic.y > 1) {\n"
 438	"		int y = int(incoord.y);\n"
 439	"		incoord.y = clamp(y - int(mod(mosaic.w + y, mosaic.y)), 0, dims.w - 1);\n"
 440	"	}\n"
 441	"	ivec2 coord = ivec2(transform * (incoord - dims.zw / 2) + dims.xy / 2);\n"
 442	"	if ((coord & ~(dims.xy - 1)) != ivec2(0, 0)) {\n"
 443	"		discard;\n"
 444	"	}\n"
 445	"	vec4 pix = renderTile((coord.x >> 3) + (coord.y >> 3) * stride, 16 + localPalette, coord & 7);\n"
 446	"	if (objwin.x > 0) {\n"
 447	"		pix.a = 0;\n"
 448	"	}\n"
 449	"	color = pix;\n"
 450	"	flags = inflags / flagCoeff;\n"
 451	"	window = objwin.yzw;\n"
 452	"}";
 453
 454static const struct GBAVideoGLUniform _uniformsFinalize[] = {
 455	{ "loc", GBA_GL_VS_LOC, },
 456	{ "maxPos", GBA_GL_VS_MAXPOS, },
 457	{ "scale", GBA_GL_FINALIZE_SCALE, },
 458	{ "layers", GBA_GL_FINALIZE_LAYERS, },
 459	{ "flags", GBA_GL_FINALIZE_FLAGS, },
 460	{ "window", GBA_GL_FINALIZE_WINDOW, },
 461	{ "backdrop", GBA_GL_FINALIZE_BACKDROP, },
 462	{ "backdropFlags", GBA_GL_FINALIZE_BACKDROPFLAGS, },
 463	{ 0 }
 464};
 465
 466static const char* const _finalize =
 467	"in vec2 texCoord;\n"
 468	"uniform int scale;\n"
 469	"uniform sampler2D layers[5];\n"
 470	"uniform sampler2D flags[5];\n"
 471	"uniform sampler2D window;\n"
 472	"uniform sampler2D backdrop;\n"
 473	"uniform sampler2D backdropFlags;\n"
 474	FLAG_CONST
 475	"out vec4 color;\n"
 476
 477	"void composite(vec4 pixel, ivec4 flags, inout vec4 topPixel, inout ivec4 topFlags, inout vec4 bottomPixel, inout ivec4 bottomFlags) {\n"
 478	"	if (pixel.a == 0) {\n"
 479	"		return;\n"
 480	"	}\n"
 481	"	if (flags.x >= topFlags.x) {\n"
 482	"		if (flags.x >= bottomFlags.x) {\n"
 483	"			return;\n"
 484	"		}\n"
 485	"		bottomFlags = flags;\n"
 486	"		bottomPixel = pixel;\n"
 487	"	} else {\n"
 488	"		bottomFlags = topFlags;\n"
 489	"		topFlags = flags;\n"
 490	"		bottomPixel = topPixel;\n"
 491	"		topPixel = pixel;\n"
 492	"	}\n"
 493	"}\n"
 494
 495	"void main() {\n"
 496	"	vec4 topPixel = texelFetch(backdrop, ivec2(0, texCoord.y), 0);\n"
 497	"	vec4 bottomPixel = topPixel;\n"
 498	"	ivec4 topFlags = ivec4(texelFetch(backdropFlags, ivec2(0, texCoord.y), 0) * flagCoeff);\n"
 499	"	ivec4 bottomFlags = topFlags;\n"
 500	"	vec4 windowFlags = texelFetch(window, ivec2(texCoord * scale), 0);\n"
 501	"	int layerWindow = int(windowFlags.x * 128);\n"
 502	"	if ((layerWindow & 1) == 0) {\n"
 503	"		vec4 pix = texelFetch(layers[0], ivec2(texCoord * scale), 0);\n"
 504	"		ivec4 inflags = ivec4(texelFetch(flags[0], ivec2(texCoord * scale), 0).xyz * flagCoeff.xyz, 0);\n"
 505	"		composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n"
 506	"	}\n"
 507	"	if ((layerWindow & 2) == 0) {\n"
 508	"		vec4 pix = texelFetch(layers[1], ivec2(texCoord * scale), 0);\n"
 509	"		ivec4 inflags = ivec4(texelFetch(flags[1], ivec2(texCoord * scale), 0).xyz * flagCoeff.xyz, 0);\n"
 510	"		composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n"
 511	"	}\n"
 512	"	if ((layerWindow & 4) == 0) {\n"
 513	"		vec4 pix = texelFetch(layers[2], ivec2(texCoord * scale), 0);\n"
 514	"		ivec4 inflags = ivec4(texelFetch(flags[2], ivec2(texCoord * scale), 0).xyz * flagCoeff.xyz, 0);\n"
 515	"		composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n"
 516	"	}\n"
 517	"	if ((layerWindow & 8) == 0) {\n"
 518	"		vec4 pix = texelFetch(layers[3], ivec2(texCoord * scale), 0);\n"
 519	"		ivec4 inflags = ivec4(texelFetch(flags[3], ivec2(texCoord * scale), 0).xyz * flagCoeff.xyz, 0);\n"
 520	"		composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n"
 521	"	}\n"
 522	"	if ((layerWindow & 16) == 0) {\n"
 523	"		vec4 pix = texelFetch(layers[4], ivec2(texCoord * scale), 0);\n"
 524	"		ivec4 inflags = ivec4(texelFetch(flags[4], ivec2(texCoord * scale), 0) * flagCoeff);\n"
 525	"		composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n"
 526	"	}\n"
 527	"	if ((layerWindow & 32) != 0) {\n"
 528	"		topFlags.y &= ~1;\n"
 529	"	}\n"
 530	"	if (((topFlags.y & 13) == 5 || topFlags.w > 0) && (bottomFlags.y & 2) == 2) {\n"
 531	"		topPixel *= topFlags.z / 16.;\n"
 532	"		topPixel += bottomPixel * windowFlags.y;\n"
 533	"	} else if ((topFlags.y & 13) == 9) {\n"
 534	"		topPixel += (1. - topPixel) * windowFlags.z;\n"
 535	"	} else if ((topFlags.y & 13) == 13) {\n"
 536	"		topPixel -= topPixel * windowFlags.z;\n"
 537	"	}\n"
 538	"	color = topPixel;\n"
 539	"}";
 540
 541static const GLint _vertices[] = {
 542	0, 0,
 543	0, 1,
 544	1, 1,
 545	1, 0,
 546};
 547
 548void GBAVideoGLRendererCreate(struct GBAVideoGLRenderer* renderer) {
 549	renderer->d.init = GBAVideoGLRendererInit;
 550	renderer->d.reset = GBAVideoGLRendererReset;
 551	renderer->d.deinit = GBAVideoGLRendererDeinit;
 552	renderer->d.writeVideoRegister = GBAVideoGLRendererWriteVideoRegister;
 553	renderer->d.writeVRAM = GBAVideoGLRendererWriteVRAM;
 554	renderer->d.writeOAM = GBAVideoGLRendererWriteOAM;
 555	renderer->d.writePalette = GBAVideoGLRendererWritePalette;
 556	renderer->d.drawScanline = GBAVideoGLRendererDrawScanline;
 557	renderer->d.finishFrame = GBAVideoGLRendererFinishFrame;
 558	renderer->d.getPixels = GBAVideoGLRendererGetPixels;
 559	renderer->d.putPixels = GBAVideoGLRendererPutPixels;
 560
 561	renderer->d.disableBG[0] = false;
 562	renderer->d.disableBG[1] = false;
 563	renderer->d.disableBG[2] = false;
 564	renderer->d.disableBG[3] = false;
 565	renderer->d.disableOBJ = false;
 566
 567	renderer->scale = 1;
 568}
 569
 570static void _compileShader(struct GBAVideoGLRenderer* glRenderer, struct GBAVideoGLShader* shader, const char** shaderBuffer, int shaderBufferLines, GLuint vs, const struct GBAVideoGLUniform* uniforms, char* log) {
 571	GLuint program = glCreateProgram();
 572	shader->program = program;
 573
 574	GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
 575	glAttachShader(program, vs);
 576	glAttachShader(program, fs);
 577	glShaderSource(fs, shaderBufferLines, shaderBuffer, 0);
 578	glCompileShader(fs);
 579	glGetShaderInfoLog(fs, 1024, 0, log);
 580	if (log[0]) {
 581		mLOG(GBA_VIDEO, ERROR, "Fragment shader compilation failure: %s", log);
 582	}
 583	glLinkProgram(program);
 584	glGetProgramInfoLog(program, 1024, 0, log);
 585	if (log[0]) {
 586		mLOG(GBA_VIDEO, ERROR, "Program link failure: %s", log);
 587	}
 588	glDeleteShader(fs);
 589#ifndef BUILD_GLES3
 590	glBindFragDataLocation(program, 0, "color");
 591	glBindFragDataLocation(program, 1, "flags");
 592#endif
 593
 594	glGenVertexArrays(1, &shader->vao);
 595	glBindVertexArray(shader->vao);
 596	glBindBuffer(GL_ARRAY_BUFFER, glRenderer->vbo);
 597	GLuint positionLocation = glGetAttribLocation(program, "position");
 598	glEnableVertexAttribArray(positionLocation);
 599	glVertexAttribPointer(positionLocation, 2, GL_INT, GL_FALSE, 0, NULL);
 600
 601	size_t i;
 602	for (i = 0; uniforms[i].name; ++i) {
 603		shader->uniforms[uniforms[i].type] = glGetUniformLocation(program, uniforms[i].name);
 604	}
 605}
 606
 607static void _deleteShader(struct GBAVideoGLShader* shader) {
 608	glDeleteProgram(shader->program);
 609	glDeleteVertexArrays(1, &shader->vao);
 610}
 611
 612static void _initFramebufferTexture(GLuint tex, GLenum format, GLenum attachment, int scale) {
 613	glBindTexture(GL_TEXTURE_2D, tex);
 614	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 615	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 616	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 617	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 618	glTexImage2D(GL_TEXTURE_2D, 0, format, scale > 0 ? GBA_VIDEO_HORIZONTAL_PIXELS * scale : 1, GBA_VIDEO_VERTICAL_PIXELS * (scale > 0 ? scale : 1), 0, format, GL_UNSIGNED_BYTE, 0);
 619	glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0);	
 620}
 621
 622void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) {
 623	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 624	glRenderer->temporaryBuffer = NULL;
 625
 626	glGenFramebuffers(GBA_GL_FBO_MAX, glRenderer->fbo);
 627	glGenTextures(GBA_GL_TEX_MAX, glRenderer->layers);
 628
 629	glGenTextures(1, &glRenderer->paletteTex);
 630	glBindTexture(GL_TEXTURE_2D, glRenderer->paletteTex);
 631	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 632	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 633
 634	glGenTextures(1, &glRenderer->vramTex);
 635	glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex);
 636	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 637	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 638	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, 256, 192, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 0);
 639
 640	glBindTexture(GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_AFFINE_2]);
 641	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 642	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 643	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 644	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 645	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16I, 2, GBA_VIDEO_VERTICAL_PIXELS, 0, GL_RGBA_INTEGER, GL_SHORT, NULL);
 646	glBindTexture(GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_AFFINE_3]);
 647	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 648	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 649	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 650	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 651	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16I, 2, GBA_VIDEO_VERTICAL_PIXELS, 0, GL_RGBA_INTEGER, GL_SHORT, NULL);
 652
 653	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OBJ]);
 654	_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_OBJ_COLOR], GL_RGBA, GL_COLOR_ATTACHMENT0, glRenderer->scale);
 655	_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_OBJ_FLAGS], GL_RGBA, GL_COLOR_ATTACHMENT1, glRenderer->scale);
 656	_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_WINDOW], GL_RGBA, GL_COLOR_ATTACHMENT2, glRenderer->scale);
 657
 658	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_BACKDROP]);
 659	_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_BACKDROP_COLOR], GL_RGB, GL_COLOR_ATTACHMENT0, 0);
 660	_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_BACKDROP_FLAGS], GL_RGBA, GL_COLOR_ATTACHMENT1, 0);
 661
 662	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_WINDOW]);
 663	_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_WINDOW], GL_RGB, GL_COLOR_ATTACHMENT0, glRenderer->scale);
 664
 665	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OUTPUT]);
 666	_initFramebufferTexture(glRenderer->outputTex, GL_RGB, GL_COLOR_ATTACHMENT0, glRenderer->scale);
 667
 668	glBindFramebuffer(GL_FRAMEBUFFER, 0);
 669
 670	glGenBuffers(1, &glRenderer->vbo);
 671	glBindBuffer(GL_ARRAY_BUFFER, glRenderer->vbo);
 672	glBufferData(GL_ARRAY_BUFFER, sizeof(_vertices), _vertices, GL_STATIC_DRAW);
 673
 674	int i;
 675	for (i = 0; i < 4; ++i) {
 676		struct GBAVideoGLBackground* bg = &glRenderer->bg[i];
 677		bg->index = i;
 678		bg->enabled = 0;
 679		bg->priority = 0;
 680		bg->charBase = 0;
 681		bg->mosaic = 0;
 682		bg->multipalette = 0;
 683		bg->screenBase = 0;
 684		bg->overflow = 0;
 685		bg->size = 0;
 686		bg->target1 = 0;
 687		bg->target2 = 0;
 688		bg->x = 0;
 689		bg->y = 0;
 690		bg->refx = 0;
 691		bg->refy = 0;
 692		bg->affine.dx = 256;
 693		bg->affine.dmx = 0;
 694		bg->affine.dy = 0;
 695		bg->affine.dmy = 256;
 696		bg->affine.sx = 0;
 697		bg->affine.sy = 0;
 698		glGenFramebuffers(1, &bg->fbo);
 699		glGenTextures(1, &bg->tex);
 700		glGenTextures(1, &bg->flags);
 701		glBindFramebuffer(GL_FRAMEBUFFER, bg->fbo);
 702		_initFramebufferTexture(bg->tex, GL_RGBA, GL_COLOR_ATTACHMENT0, glRenderer->scale);
 703		_initFramebufferTexture(bg->flags, GL_RGB, GL_COLOR_ATTACHMENT1, glRenderer->scale);
 704		glBindFramebuffer(GL_FRAMEBUFFER, 0);
 705	}
 706
 707	char log[1024];
 708	const GLchar* shaderBuffer[8];
 709	shaderBuffer[0] = _gl3Header;
 710
 711	GLuint vs = glCreateShader(GL_VERTEX_SHADER);
 712	shaderBuffer[1] = _vertexShader;
 713	glShaderSource(vs, 2, shaderBuffer, 0);
 714	glCompileShader(vs);
 715	glGetShaderInfoLog(vs, 1024, 0, log);
 716	if (log[0]) {
 717		mLOG(GBA_VIDEO, ERROR, "Vertex shader compilation failure: %s", log);
 718	}
 719
 720	shaderBuffer[1] = _renderMode0;
 721
 722	shaderBuffer[2] = _renderTile16;
 723	_compileShader(glRenderer, &glRenderer->bgShader[0], shaderBuffer, 3, vs, _uniformsMode0, log);
 724
 725	shaderBuffer[2] = _renderTile256;
 726	_compileShader(glRenderer, &glRenderer->bgShader[1], shaderBuffer, 3, vs, _uniformsMode0, log);
 727
 728	shaderBuffer[1] = _renderMode2;
 729	shaderBuffer[2] = _interpolate;
 730
 731	shaderBuffer[3] = _fetchTileOverflow;
 732	_compileShader(glRenderer, &glRenderer->bgShader[2], shaderBuffer, 4, vs, _uniformsMode2, log);
 733
 734	shaderBuffer[3] = _fetchTileNoOverflow;
 735	_compileShader(glRenderer, &glRenderer->bgShader[3], shaderBuffer, 4, vs, _uniformsMode2, log);
 736
 737	shaderBuffer[1] = _renderMode4;
 738	shaderBuffer[2] = _interpolate;
 739	_compileShader(glRenderer, &glRenderer->bgShader[4], shaderBuffer, 3, vs, _uniformsMode4, log);
 740
 741	shaderBuffer[1] = _renderMode35;
 742	shaderBuffer[2] = _interpolate;
 743	_compileShader(glRenderer, &glRenderer->bgShader[5], shaderBuffer, 3, vs, _uniformsMode35, log);
 744
 745	shaderBuffer[1] = _renderObj;
 746
 747	shaderBuffer[2] = _renderTile16;
 748	_compileShader(glRenderer, &glRenderer->objShader[0], shaderBuffer, 3, vs, _uniformsObj, log);
 749#ifndef BUILD_GLES3
 750	glBindFragDataLocation(glRenderer->objShader[0].program, 2, "window");
 751#endif
 752
 753	shaderBuffer[2] = _renderTile256;
 754	_compileShader(glRenderer, &glRenderer->objShader[1], shaderBuffer, 3, vs, _uniformsObj, log);
 755#ifndef BUILD_GLES3
 756	glBindFragDataLocation(glRenderer->objShader[1].program, 2, "window");
 757#endif
 758
 759	shaderBuffer[1] = _finalize;
 760	_compileShader(glRenderer, &glRenderer->finalizeShader, shaderBuffer, 2, vs, _uniformsFinalize, log);
 761
 762	glBindVertexArray(0);
 763	glDeleteShader(vs);
 764
 765	GBAVideoGLRendererReset(renderer);
 766}
 767
 768void GBAVideoGLRendererDeinit(struct GBAVideoRenderer* renderer) {
 769	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 770	if (glRenderer->temporaryBuffer) {
 771		mappedMemoryFree(glRenderer->temporaryBuffer, GBA_VIDEO_HORIZONTAL_PIXELS * GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale * glRenderer->scale);
 772	}
 773	glDeleteFramebuffers(GBA_GL_FBO_MAX, glRenderer->fbo);
 774	glDeleteTextures(GBA_GL_TEX_MAX, glRenderer->layers);
 775	glDeleteTextures(1, &glRenderer->paletteTex);
 776	glDeleteTextures(1, &glRenderer->vramTex);
 777
 778	_deleteShader(&glRenderer->bgShader[0]);
 779	_deleteShader(&glRenderer->bgShader[1]);
 780	_deleteShader(&glRenderer->bgShader[2]);
 781	_deleteShader(&glRenderer->bgShader[3]);
 782	_deleteShader(&glRenderer->objShader[0]);
 783	_deleteShader(&glRenderer->objShader[1]);
 784	_deleteShader(&glRenderer->finalizeShader);
 785
 786	int i;
 787	for (i = 0; i < 4; ++i) {
 788		struct GBAVideoGLBackground* bg = &glRenderer->bg[i];
 789		glDeleteFramebuffers(1, &bg->fbo);
 790		glDeleteTextures(1, &bg->tex);
 791		glDeleteTextures(1, &bg->flags);
 792	}
 793}
 794
 795void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer) {
 796	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 797
 798	glRenderer->paletteDirty = true;
 799	glRenderer->vramDirty = 0xFFFFFF;
 800	glRenderer->firstAffine = -1;
 801	glRenderer->firstY = -1;
 802	glRenderer->dispcnt = 0;
 803	glRenderer->mosaic = 0;
 804}
 805
 806void GBAVideoGLRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
 807	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 808	glRenderer->vramDirty |= 1 << (address >> 12);
 809}
 810
 811void GBAVideoGLRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
 812	UNUSED(oam);
 813	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 814	glRenderer->oamDirty = true;
 815}
 816
 817void GBAVideoGLRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
 818	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 819#ifdef BUILD_GLES3
 820	glRenderer->shadowPalette[address >> 1] = (value & 0x3F) | ((value & 0x7FE0) << 1);
 821#else
 822	UNUSED(address);
 823	UNUSED(value);
 824#endif
 825	glRenderer->paletteDirty = true;
 826}
 827
 828uint16_t GBAVideoGLRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
 829	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 830	if (renderer->cache) {
 831		GBAVideoCacheWriteVideoRegister(renderer->cache, address, value);
 832	}
 833
 834	bool dirty = true;
 835	switch (address) {
 836	case REG_DISPCNT:
 837		value &= 0xFFF7;
 838		break;
 839	case REG_BG0CNT:
 840	case REG_BG1CNT:
 841		value &= 0xDFFF;
 842		break;
 843	case REG_BG0HOFS:
 844	case REG_BG0VOFS:
 845	case REG_BG1HOFS:
 846	case REG_BG1VOFS:
 847	case REG_BG2HOFS:
 848	case REG_BG2VOFS:
 849	case REG_BG3HOFS:
 850	case REG_BG3VOFS:
 851		value &= 0x01FF;
 852		break;
 853	case REG_BG2PA:
 854		glRenderer->bg[2].affine.dx = value;
 855		dirty = false;
 856		break;
 857	case REG_BG2PB:
 858		glRenderer->bg[2].affine.dmx = value;
 859		dirty = false;
 860		break;
 861	case REG_BG2PC:
 862		glRenderer->bg[2].affine.dy = value;
 863		dirty = false;
 864		break;
 865	case REG_BG2PD:
 866		glRenderer->bg[2].affine.dmy = value;
 867		dirty = false;
 868		break;
 869	case REG_BG2X_LO:
 870		GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[2], value);
 871		dirty = false;
 872		break;
 873	case REG_BG2X_HI:
 874		GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[2], value);
 875		dirty = false;
 876		break;
 877	case REG_BG2Y_LO:
 878		GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[2], value);
 879		dirty = false;
 880		break;
 881	case REG_BG2Y_HI:
 882		GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[2], value);
 883		dirty = false;
 884		break;
 885	case REG_BG3PA:
 886		glRenderer->bg[3].affine.dx = value;
 887		dirty = false;
 888		break;
 889	case REG_BG3PB:
 890		glRenderer->bg[3].affine.dmx = value;
 891		dirty = false;
 892		break;
 893	case REG_BG3PC:
 894		glRenderer->bg[3].affine.dy = value;
 895		dirty = false;
 896		break;
 897	case REG_BG3PD:
 898		glRenderer->bg[3].affine.dmy = value;
 899		dirty = false;
 900		break;
 901	case REG_BG3X_LO:
 902		GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[3], value);
 903		dirty = false;
 904		break;
 905	case REG_BG3X_HI:
 906		GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[3], value);
 907		dirty = false;
 908		break;
 909	case REG_BG3Y_LO:
 910		GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[3], value);
 911		dirty = false;
 912		break;
 913	case REG_BG3Y_HI:
 914		GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[3], value);
 915		dirty = false;
 916		break;
 917	case REG_BLDALPHA:
 918		value &= 0x1F1F;
 919		break;
 920	case REG_BLDY:
 921		value &= 0x1F;
 922		if (value > 0x10) {
 923			value = 0x10;
 924		}
 925		break;
 926	case REG_WININ:
 927		value &= 0x3F3F;
 928		break;
 929	case REG_WINOUT:
 930		value &= 0x3F3F;
 931		break;
 932	default:
 933		break;
 934	}
 935	if (glRenderer->shadowRegs[address >> 1] == value) {
 936		dirty = false;
 937	} else {
 938		glRenderer->shadowRegs[address >> 1] = value;
 939	}
 940	if (dirty) {
 941		glRenderer->regsDirty |= 1ULL << (address >> 1);
 942	}
 943	return value;
 944}
 945
 946void _cleanRegister(struct GBAVideoGLRenderer* glRenderer, int address, uint16_t value) {
 947	switch (address) {
 948	case REG_DISPCNT:
 949		glRenderer->dispcnt = value;
 950		GBAVideoGLRendererUpdateDISPCNT(glRenderer);
 951		break;
 952	case REG_BG0CNT:
 953		GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[0], value);
 954		break;
 955	case REG_BG1CNT:
 956		GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[1], value);
 957		break;
 958	case REG_BG2CNT:
 959		GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[2], value);
 960		break;
 961	case REG_BG3CNT:
 962		GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[3], value);
 963		break;
 964	case REG_BG0HOFS:
 965		glRenderer->bg[0].x = value;
 966		break;
 967	case REG_BG0VOFS:
 968		glRenderer->bg[0].y = value;
 969		break;
 970	case REG_BG1HOFS:
 971		glRenderer->bg[1].x = value;
 972		break;
 973	case REG_BG1VOFS:
 974		glRenderer->bg[1].y = value;
 975		break;
 976	case REG_BG2HOFS:
 977		glRenderer->bg[2].x = value;
 978		break;
 979	case REG_BG2VOFS:
 980		glRenderer->bg[2].y = value;
 981		break;
 982	case REG_BG3HOFS:
 983		glRenderer->bg[3].x = value;
 984		break;
 985	case REG_BG3VOFS:
 986		glRenderer->bg[3].y = value;
 987		break;
 988	case REG_BLDCNT:
 989		GBAVideoGLRendererWriteBLDCNT(glRenderer, value);
 990		value &= 0x3FFF;
 991		break;
 992	case REG_BLDALPHA:
 993		glRenderer->blda = value & 0x1F;
 994		if (glRenderer->blda > 0x10) {
 995			glRenderer->blda = 0x10;
 996		}
 997		glRenderer->bldb = (value >> 8) & 0x1F;
 998		if (glRenderer->bldb > 0x10) {
 999			glRenderer->bldb = 0x10;
1000		}
1001		value &= 0x1F1F;
1002		break;
1003	case REG_BLDY:
1004		glRenderer->bldy = value;
1005		break;
1006	case REG_WIN0H:
1007		glRenderer->winN[0].h[0].end = value;
1008		glRenderer->winN[0].h[0].start = value >> 8;
1009		if (glRenderer->winN[0].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[0].h[0].start > glRenderer->winN[0].h[0].end) {
1010			glRenderer->winN[0].h[0].start = 0;
1011		}
1012		if (glRenderer->winN[0].h[0].end > GBA_VIDEO_HORIZONTAL_PIXELS) {
1013			glRenderer->winN[0].h[0].end = GBA_VIDEO_HORIZONTAL_PIXELS;
1014			if (glRenderer->winN[0].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS) {
1015				glRenderer->winN[0].h[0].start = GBA_VIDEO_HORIZONTAL_PIXELS;
1016			}
1017		}
1018		break;
1019	case REG_WIN1H:
1020		glRenderer->winN[1].h[0].end = value;
1021		glRenderer->winN[1].h[0].start = value >> 8;
1022		if (glRenderer->winN[1].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[1].h[0].start > glRenderer->winN[1].h[0].end) {
1023			glRenderer->winN[1].h[0].start = 0;
1024		}
1025		if (glRenderer->winN[1].h[0].end > GBA_VIDEO_HORIZONTAL_PIXELS) {
1026			glRenderer->winN[1].h[0].end = GBA_VIDEO_HORIZONTAL_PIXELS;
1027			if (glRenderer->winN[1].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS) {
1028				glRenderer->winN[1].h[0].start = GBA_VIDEO_HORIZONTAL_PIXELS;
1029			}
1030		}
1031		break;
1032	case REG_WIN0V:
1033		glRenderer->winN[0].v.end = value;
1034		glRenderer->winN[0].v.start = value >> 8;
1035		if (glRenderer->winN[0].v.start > GBA_VIDEO_VERTICAL_PIXELS && glRenderer->winN[0].v.start > glRenderer->winN[0].v.end) {
1036			glRenderer->winN[0].v.start = 0;
1037		}
1038		if (glRenderer->winN[0].v.end > GBA_VIDEO_VERTICAL_PIXELS) {
1039			glRenderer->winN[0].v.end = GBA_VIDEO_VERTICAL_PIXELS;
1040			if (glRenderer->winN[0].v.start > GBA_VIDEO_VERTICAL_PIXELS) {
1041				glRenderer->winN[0].v.start = GBA_VIDEO_VERTICAL_PIXELS;
1042			}
1043		}
1044		break;
1045	case REG_WIN1V:
1046		glRenderer->winN[1].v.end = value;
1047		glRenderer->winN[1].v.start = value >> 8;
1048		if (glRenderer->winN[1].v.start > GBA_VIDEO_VERTICAL_PIXELS && glRenderer->winN[1].v.start > glRenderer->winN[1].v.end) {
1049			glRenderer->winN[1].v.start = 0;
1050		}
1051		if (glRenderer->winN[1].v.end > GBA_VIDEO_VERTICAL_PIXELS) {
1052			glRenderer->winN[1].v.end = GBA_VIDEO_VERTICAL_PIXELS;
1053			if (glRenderer->winN[1].v.start > GBA_VIDEO_VERTICAL_PIXELS) {
1054				glRenderer->winN[1].v.start = GBA_VIDEO_VERTICAL_PIXELS;
1055			}
1056		}
1057		break;
1058	case REG_WININ:
1059		glRenderer->winN[0].control = value;
1060		glRenderer->winN[1].control = value >> 8;
1061		break;
1062	case REG_WINOUT:
1063		glRenderer->winout = value;
1064		glRenderer->objwin = value >> 8;
1065		break;
1066	case REG_MOSAIC:
1067		glRenderer->mosaic = value;
1068		break;
1069	default:
1070		break;
1071	}
1072}
1073
1074void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
1075	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
1076
1077	memcpy(&glRenderer->affine[0][y], &glRenderer->bg[2].affine, sizeof(struct GBAVideoGLAffine));
1078	memcpy(&glRenderer->affine[1][y], &glRenderer->bg[3].affine, sizeof(struct GBAVideoGLAffine));
1079	if (GBARegisterDISPCNTGetMode(glRenderer->dispcnt) != 0) {
1080		if (glRenderer->firstAffine < 0) {
1081			glRenderer->firstAffine = y;
1082		}
1083	} else {
1084		glRenderer->firstAffine = -1;
1085	}
1086
1087	if (glRenderer->paletteDirty || glRenderer->vramDirty || glRenderer->oamDirty || glRenderer->regsDirty) {
1088		if (glRenderer->firstY >= 0) {
1089			_drawScanlines(glRenderer, y - 1);
1090		}
1091	}
1092	if (glRenderer->firstY < 0) {
1093		glRenderer->firstY = y;
1094	}
1095
1096	memcpy(&glRenderer->winN[0].h[1], &glRenderer->winN[0].h[0], sizeof(struct GBAVideoWindowRegion));
1097	memcpy(&glRenderer->winN[1].h[1], &glRenderer->winN[1].h[0], sizeof(struct GBAVideoWindowRegion));
1098
1099	int i;
1100	for (i = 0; i < 0x30; ++i) {
1101		if (!(glRenderer->regsDirty & (1ULL << i))) {
1102			continue;
1103		}
1104		_cleanRegister(glRenderer, i << 1, glRenderer->shadowRegs[i]);
1105	}
1106	glRenderer->regsDirty = 0;
1107
1108	if (glRenderer->paletteDirty) {
1109		glBindTexture(GL_TEXTURE_2D, glRenderer->paletteTex);
1110#ifdef BUILD_GLES3
1111		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, 16, 32, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_6_5, glRenderer->shadowPalette);
1112#else
1113		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, 16, 32, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, glRenderer->d.palette);
1114#endif
1115		glRenderer->paletteDirty = false;
1116	}
1117
1118	int first = -1;
1119	glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex);
1120	for (i = 0; i < 25; ++i) {
1121		if (!(glRenderer->vramDirty & (1 << i))) {
1122			if (first >= 0) {
1123				glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 8 * first, 256, 8 * (i - first), GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, &glRenderer->d.vram[2048 * first]);
1124				first = -1;
1125			}
1126		} else if (first < 0) {
1127			first = i;
1128		}
1129	}
1130	glRenderer->vramDirty = 0;
1131
1132	if (glRenderer->oamDirty) {
1133		glRenderer->oamMax = GBAVideoRendererCleanOAM(glRenderer->d.oam->obj, glRenderer->sprites, 0);
1134		glRenderer->oamDirty = false;
1135	}
1136
1137	if (y == 0) {
1138		memcpy(&glRenderer->winN[0].h[1], &glRenderer->winN[0].h[0], sizeof(struct GBAVideoWindowRegion));
1139		memcpy(&glRenderer->winN[1].h[1], &glRenderer->winN[1].h[0], sizeof(struct GBAVideoWindowRegion));
1140
1141		glDisable(GL_SCISSOR_TEST);		
1142		glClearColor(0, 0, 0, 0);
1143		glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OBJ]);
1144		glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
1145		glClear(GL_COLOR_BUFFER_BIT);
1146
1147		for (i = 0; i < 4; ++i) {
1148			glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->bg[i].fbo);
1149			glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
1150			glClear(GL_COLOR_BUFFER_BIT);
1151		}
1152	}
1153
1154	if (GBARegisterDISPCNTGetMode(glRenderer->dispcnt) != 0) {
1155		glRenderer->bg[2].affine.sx += glRenderer->bg[2].affine.dmx;
1156		glRenderer->bg[2].affine.sy += glRenderer->bg[2].affine.dmy;
1157		glRenderer->bg[3].affine.sx += glRenderer->bg[3].affine.dmx;
1158		glRenderer->bg[3].affine.sy += glRenderer->bg[3].affine.dmy;
1159	}
1160}
1161
1162void _drawScanlines(struct GBAVideoGLRenderer* glRenderer, int y) {
1163	if (glRenderer->firstAffine >= 0) {
1164		glBindTexture(GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_AFFINE_2]);
1165		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16I, 2, GBA_VIDEO_VERTICAL_PIXELS, 0, GL_RGBA_INTEGER, GL_SHORT, glRenderer->affine[0]);
1166		glBindTexture(GL_TEXTURE_2D, glRenderer->layers[GBA_GL_TEX_AFFINE_3]);
1167		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16I, 2, GBA_VIDEO_VERTICAL_PIXELS, 0, GL_RGBA_INTEGER, GL_SHORT, glRenderer->affine[1]);
1168	}
1169	glEnable(GL_SCISSOR_TEST);
1170
1171	uint32_t backdrop = M_RGB5_TO_RGB8(glRenderer->d.palette[0]);
1172	glViewport(0, 0, 1, GBA_VIDEO_VERTICAL_PIXELS);
1173	glScissor(0, glRenderer->firstY, 1, y - glRenderer->firstY + 1);
1174	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_BACKDROP]);
1175	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1176	glClearColor(((backdrop >> 16) & 0xFF) / 256., ((backdrop >> 8) & 0xFF) / 256., (backdrop & 0xFF) / 256., 0.f);
1177	glClear(GL_COLOR_BUFFER_BIT);
1178	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT1 });
1179	glClearColor(1, (glRenderer->target1Bd | (glRenderer->target2Bd * 2) | (glRenderer->blendEffect * 4)) / 32.f, glRenderer->blda / 16.f, 0);
1180	glClear(GL_COLOR_BUFFER_BIT);
1181	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1182
1183	GBAVideoGLRendererDrawWindow(glRenderer, y);
1184	if (GBARegisterDISPCNTIsObjEnable(glRenderer->dispcnt) && !glRenderer->d.disableOBJ) {
1185		int i;
1186		for (i = glRenderer->oamMax; i--;) {
1187			struct GBAVideoRendererSprite* sprite = &glRenderer->sprites[i];
1188			if ((y < sprite->y && (sprite->endY - 256 < 0 || glRenderer->firstY >= sprite->endY - 256)) || glRenderer->firstY >= sprite->endY) {
1189				continue;
1190			}
1191
1192			GBAVideoGLRendererDrawSprite(glRenderer, &sprite->obj, y, sprite->y);
1193		}
1194	}
1195
1196	if (TEST_LAYER_ENABLED(0) && GBARegisterDISPCNTGetMode(glRenderer->dispcnt) < 2) {
1197		GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[0], y);
1198	}
1199	if (TEST_LAYER_ENABLED(1) && GBARegisterDISPCNTGetMode(glRenderer->dispcnt) < 2) {
1200		GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[1], y);
1201	}
1202	if (TEST_LAYER_ENABLED(2)) {
1203		switch (GBARegisterDISPCNTGetMode(glRenderer->dispcnt)) {
1204		case 0:
1205			GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[2], y);
1206			break;
1207		case 1:
1208		case 2:
1209			GBAVideoGLRendererDrawBackgroundMode2(glRenderer, &glRenderer->bg[2], y);
1210			break;
1211		case 3:
1212			GBAVideoGLRendererDrawBackgroundMode3(glRenderer, &glRenderer->bg[2], y);
1213			break;
1214		case 4:
1215			GBAVideoGLRendererDrawBackgroundMode4(glRenderer, &glRenderer->bg[2], y);
1216			break;
1217		case 5:
1218			GBAVideoGLRendererDrawBackgroundMode5(glRenderer, &glRenderer->bg[2], y);
1219			break;
1220		}
1221	}
1222	if (TEST_LAYER_ENABLED(3)) {
1223		switch (GBARegisterDISPCNTGetMode(glRenderer->dispcnt)) {
1224		case 0:
1225			GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[3], y);
1226			break;
1227		case 2:
1228			GBAVideoGLRendererDrawBackgroundMode2(glRenderer, &glRenderer->bg[3], y);
1229			break;
1230		}
1231	}
1232	glRenderer->firstY = -1;
1233}
1234
1235void GBAVideoGLRendererFinishFrame(struct GBAVideoRenderer* renderer) {
1236	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
1237	_drawScanlines(glRenderer, GBA_VIDEO_VERTICAL_PIXELS - 1);
1238	_finalizeLayers(glRenderer);
1239	glBindVertexArray(0);
1240	glRenderer->firstAffine = -1;
1241	glRenderer->firstY = -1;
1242	glRenderer->bg[2].affine.sx = glRenderer->bg[2].refx;
1243	glRenderer->bg[2].affine.sy = glRenderer->bg[2].refy;
1244	glRenderer->bg[3].affine.sx = glRenderer->bg[3].refx;
1245	glRenderer->bg[3].affine.sy = glRenderer->bg[3].refy;
1246}
1247
1248void GBAVideoGLRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
1249	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
1250	*stride = GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale;
1251	if (!glRenderer->temporaryBuffer) {
1252		glRenderer->temporaryBuffer = anonymousMemoryMap(GBA_VIDEO_HORIZONTAL_PIXELS * GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale * glRenderer->scale * BYTES_PER_PIXEL);
1253	}
1254	glFinish();
1255	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OUTPUT]);
1256	glPixelStorei(GL_PACK_ROW_LENGTH, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale);
1257	glPixelStorei(GL_PACK_ALIGNMENT, 1);
1258	glReadPixels(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale, GL_RGBA, GL_UNSIGNED_BYTE, (void*) glRenderer->temporaryBuffer);
1259	*pixels = glRenderer->temporaryBuffer;
1260}
1261
1262void GBAVideoGLRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
1263
1264}
1265
1266static void _enableBg(struct GBAVideoGLRenderer* renderer, int bg, bool active) {
1267	int wasActive = renderer->bg[bg].enabled;
1268	if (!active) {
1269		renderer->bg[bg].enabled = 0;
1270	} else if (!wasActive && active) {
1271		/*if (renderer->nextY == 0 || GBARegisterDISPCNTGetMode(renderer->dispcnt) > 2) {
1272			// TODO: Investigate in more depth how switching background works in different modes
1273			renderer->bg[bg].enabled = 4;
1274		} else {
1275			renderer->bg[bg].enabled = 1;
1276		}*/
1277		renderer->bg[bg].enabled = 4;
1278	}
1279}
1280
1281static void GBAVideoGLRendererUpdateDISPCNT(struct GBAVideoGLRenderer* renderer) {
1282	_enableBg(renderer, 0, GBARegisterDISPCNTGetBg0Enable(renderer->dispcnt));
1283	_enableBg(renderer, 1, GBARegisterDISPCNTGetBg1Enable(renderer->dispcnt));
1284	_enableBg(renderer, 2, GBARegisterDISPCNTGetBg2Enable(renderer->dispcnt));
1285	_enableBg(renderer, 3, GBARegisterDISPCNTGetBg3Enable(renderer->dispcnt));
1286}
1287
1288static void GBAVideoGLRendererWriteBGCNT(struct GBAVideoGLBackground* bg, uint16_t value) {
1289	bg->priority = GBARegisterBGCNTGetPriority(value);
1290	bg->charBase = GBARegisterBGCNTGetCharBase(value) << 13;
1291	bg->mosaic = GBARegisterBGCNTGetMosaic(value);
1292	bg->multipalette = GBARegisterBGCNTGet256Color(value);
1293	bg->screenBase = GBARegisterBGCNTGetScreenBase(value) << 10;
1294	bg->overflow = GBARegisterBGCNTGetOverflow(value);
1295	bg->size = GBARegisterBGCNTGetSize(value);
1296}
1297
1298static void GBAVideoGLRendererWriteBGX_LO(struct GBAVideoGLBackground* bg, uint16_t value) {
1299	bg->refx = (bg->refx & 0xFFFF0000) | value;
1300	bg->affine.sx = bg->refx;
1301}
1302
1303static void GBAVideoGLRendererWriteBGX_HI(struct GBAVideoGLBackground* bg, uint16_t value) {
1304	bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
1305	bg->refx <<= 4;
1306	bg->refx >>= 4;
1307	bg->affine.sx = bg->refx;
1308}
1309
1310static void GBAVideoGLRendererWriteBGY_LO(struct GBAVideoGLBackground* bg, uint16_t value) {
1311	bg->refy = (bg->refy & 0xFFFF0000) | value;
1312	bg->affine.sy = bg->refy;
1313}
1314
1315static void GBAVideoGLRendererWriteBGY_HI(struct GBAVideoGLBackground* bg, uint16_t value) {
1316	bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
1317	bg->refy <<= 4;
1318	bg->refy >>= 4;
1319	bg->affine.sy = bg->refy;
1320}
1321
1322static void GBAVideoGLRendererWriteBLDCNT(struct GBAVideoGLRenderer* renderer, uint16_t value) {
1323	renderer->bg[0].target1 = GBARegisterBLDCNTGetTarget1Bg0(value);
1324	renderer->bg[1].target1 = GBARegisterBLDCNTGetTarget1Bg1(value);
1325	renderer->bg[2].target1 = GBARegisterBLDCNTGetTarget1Bg2(value);
1326	renderer->bg[3].target1 = GBARegisterBLDCNTGetTarget1Bg3(value);
1327	renderer->bg[0].target2 = GBARegisterBLDCNTGetTarget2Bg0(value);
1328	renderer->bg[1].target2 = GBARegisterBLDCNTGetTarget2Bg1(value);
1329	renderer->bg[2].target2 = GBARegisterBLDCNTGetTarget2Bg2(value);
1330	renderer->bg[3].target2 = GBARegisterBLDCNTGetTarget2Bg3(value);
1331
1332	renderer->blendEffect = GBARegisterBLDCNTGetEffect(value);
1333	renderer->target1Obj = GBARegisterBLDCNTGetTarget1Obj(value);
1334	renderer->target1Bd = GBARegisterBLDCNTGetTarget1Bd(value);
1335	renderer->target2Obj = GBARegisterBLDCNTGetTarget2Obj(value);
1336	renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value);
1337}
1338
1339void _finalizeLayers(struct GBAVideoGLRenderer* renderer) {
1340	const GLuint* uniforms = renderer->finalizeShader.uniforms;
1341	glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_OUTPUT]);
1342	glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
1343	glScissor(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
1344	glUseProgram(renderer->finalizeShader.program);
1345	glBindVertexArray(renderer->finalizeShader.vao);
1346	glActiveTexture(GL_TEXTURE0);
1347	glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_WINDOW]);
1348	glActiveTexture(GL_TEXTURE0 + 1);
1349	glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_OBJ_COLOR]);
1350	glActiveTexture(GL_TEXTURE0 + 2);
1351	glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_OBJ_FLAGS]);
1352	glActiveTexture(GL_TEXTURE0 + 3);
1353	glBindTexture(GL_TEXTURE_2D, renderer->bg[0].tex);
1354	glActiveTexture(GL_TEXTURE0 + 4);
1355	glBindTexture(GL_TEXTURE_2D, renderer->bg[0].flags);
1356	glActiveTexture(GL_TEXTURE0 + 5);
1357	glBindTexture(GL_TEXTURE_2D, renderer->bg[1].tex);
1358	glActiveTexture(GL_TEXTURE0 + 6);
1359	glBindTexture(GL_TEXTURE_2D, renderer->bg[1].flags);
1360	glActiveTexture(GL_TEXTURE0 + 7);
1361	glBindTexture(GL_TEXTURE_2D, renderer->bg[2].tex);
1362	glActiveTexture(GL_TEXTURE0 + 8);
1363	glBindTexture(GL_TEXTURE_2D, renderer->bg[2].flags);
1364	glActiveTexture(GL_TEXTURE0 + 9);
1365	glBindTexture(GL_TEXTURE_2D, renderer->bg[3].tex);
1366	glActiveTexture(GL_TEXTURE0 + 10);
1367	glBindTexture(GL_TEXTURE_2D, renderer->bg[3].flags);
1368	glActiveTexture(GL_TEXTURE0 + 11);
1369	glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_BACKDROP_COLOR]);
1370	glActiveTexture(GL_TEXTURE0 + 12);
1371	glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_BACKDROP_FLAGS]);
1372
1373	glUniform2i(uniforms[GBA_GL_VS_LOC], GBA_VIDEO_VERTICAL_PIXELS, 0);
1374	glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
1375	glUniform1i(uniforms[GBA_GL_FINALIZE_SCALE], renderer->scale);
1376	glUniform1iv(uniforms[GBA_GL_FINALIZE_LAYERS], 5, (GLint[]) { 3, 5, 7, 9, 1 });
1377	glUniform1iv(uniforms[GBA_GL_FINALIZE_FLAGS], 5, (GLint[]) { 4, 6, 8, 10, 2 });
1378	glUniform1i(uniforms[GBA_GL_FINALIZE_WINDOW], 0);
1379	glUniform1i(uniforms[GBA_GL_FINALIZE_WINDOW], 0);
1380	glUniform1i(uniforms[GBA_GL_FINALIZE_BACKDROP], 11);
1381	glUniform1i(uniforms[GBA_GL_FINALIZE_BACKDROPFLAGS], 12);
1382	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1383	glBindFramebuffer(GL_FRAMEBUFFER, 0);
1384}
1385
1386void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GBAObj* sprite, int y, int spriteY) {
1387	int width = GBAVideoObjSizes[GBAObjAttributesAGetShape(sprite->a) * 4 + GBAObjAttributesBGetSize(sprite->b)][0];
1388	int height = GBAVideoObjSizes[GBAObjAttributesAGetShape(sprite->a) * 4 + GBAObjAttributesBGetSize(sprite->b)][1];
1389	int32_t x = (uint32_t) GBAObjAttributesBGetX(sprite->b) << 23;
1390	x >>= 23;
1391
1392	int align = GBAObjAttributesAIs256Color(sprite->a) && !GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt);
1393	unsigned charBase = (BASE_TILE >> 1) + (GBAObjAttributesCGetTile(sprite->c) & ~align) * 0x10;
1394	int stride = GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? (width >> 3) : (0x20 >> GBAObjAttributesAGet256Color(sprite->a));
1395
1396	if (spriteY + height >= 256) {
1397		spriteY -= 256;
1398	}
1399
1400	if (!GBAObjAttributesAIsTransformed(sprite->a) && GBAObjAttributesBIsVFlip(sprite->b)) {
1401		spriteY = (renderer->firstY - height) + (renderer->firstY - spriteY) + 1;
1402	}
1403
1404	int totalWidth = width;
1405	int totalHeight = height;
1406	if (GBAObjAttributesAIsTransformed(sprite->a) && GBAObjAttributesAIsDoubleSize(sprite->a)) {
1407		totalWidth <<= 1;
1408		totalHeight <<= 1;
1409	}
1410
1411	const struct GBAVideoGLShader* shader = &renderer->objShader[GBAObjAttributesAGet256Color(sprite->a)];
1412	const GLuint* uniforms = shader->uniforms;
1413	glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_OBJ]);
1414	glViewport(x * renderer->scale, spriteY * renderer->scale, totalWidth * renderer->scale, totalHeight * renderer->scale);
1415	glScissor(x * renderer->scale, renderer->firstY * renderer->scale, totalWidth * renderer->scale, (y - renderer->firstY + 1) * renderer->scale);
1416	glUseProgram(shader->program);
1417	glBindVertexArray(shader->vao);
1418	glActiveTexture(GL_TEXTURE0);
1419	glBindTexture(GL_TEXTURE_2D, renderer->vramTex);
1420	glActiveTexture(GL_TEXTURE0 + 1);
1421	glBindTexture(GL_TEXTURE_2D, renderer->paletteTex);
1422	glUniform2i(uniforms[GBA_GL_VS_LOC], totalHeight, 0);
1423	glUniform2i(uniforms[GBA_GL_VS_MAXPOS], (GBAObjAttributesBIsHFlip(sprite->b) && !GBAObjAttributesAIsTransformed(sprite->a)) ? -totalWidth : totalWidth, totalHeight);
1424	glUniform1i(uniforms[GBA_GL_OBJ_VRAM], 0);
1425	glUniform1i(uniforms[GBA_GL_OBJ_PALETTE], 1);
1426	glUniform1i(uniforms[GBA_GL_OBJ_CHARBASE], charBase);
1427	glUniform1i(uniforms[GBA_GL_OBJ_STRIDE], stride);
1428	glUniform1i(uniforms[GBA_GL_OBJ_LOCALPALETTE], GBAObjAttributesCGetPalette(sprite->c));
1429	glUniform4i(uniforms[GBA_GL_OBJ_INFLAGS], GBAObjAttributesCGetPriority(sprite->c) << 3,
1430	                                          (renderer->target1Obj || GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT) | (renderer->target2Obj * 2) | (renderer->blendEffect * 4),
1431	                                          renderer->blda, GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT);
1432	if (GBAObjAttributesAIsTransformed(sprite->a)) {
1433		struct GBAOAMMatrix mat;
1434		LOAD_16(mat.a, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].a);
1435		LOAD_16(mat.b, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].b);
1436		LOAD_16(mat.c, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].c);
1437		LOAD_16(mat.d, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].d);
1438
1439		glUniformMatrix2fv(uniforms[GBA_GL_OBJ_TRANSFORM], 1, GL_FALSE, (GLfloat[]) { mat.a / 256.f, mat.c / 256.f, mat.b / 256.f, mat.d / 256.f });
1440	} else {
1441		glUniformMatrix2fv(uniforms[GBA_GL_OBJ_TRANSFORM], 1, GL_FALSE, (GLfloat[]) { 1.f, 0, 0, 1.f });
1442	}
1443	glUniform4i(uniforms[GBA_GL_OBJ_DIMS], width, height, totalWidth, totalHeight);
1444	if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_OBJWIN) {
1445		int window = ~renderer->objwin & 0x3F;
1446		glUniform4f(uniforms[GBA_GL_OBJ_OBJWIN], 1, window / 128.f, renderer->bldb / 16.f, renderer->bldy / 16.f);
1447		glDrawBuffers(3, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 });
1448	} else {
1449		glUniform4f(uniforms[GBA_GL_OBJ_OBJWIN], 0, 0, 0, 0);
1450		glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
1451	}
1452	if (GBAObjAttributesAIsMosaic(sprite->a) && GBAObjAttributesAGetMode(sprite->a) != OBJ_MODE_OBJWIN) {
1453		int mosaicH = GBAMosaicControlGetObjH(renderer->mosaic) + 1;
1454		if (GBAObjAttributesBIsHFlip(sprite->b)) {
1455			mosaicH = -mosaicH;
1456		}
1457		glUniform4i(uniforms[GBA_GL_OBJ_MOSAIC], mosaicH, GBAMosaicControlGetObjV(renderer->mosaic) + 1, x, spriteY);
1458	} else {
1459		glUniform4i(uniforms[GBA_GL_OBJ_MOSAIC], 0, 0, 0, 0);
1460	}
1461	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1462	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1463}
1464
1465void _prepareBackground(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, const GLuint* uniforms) {
1466	glBindFramebuffer(GL_FRAMEBUFFER, background->fbo);
1467	glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
1468	glActiveTexture(GL_TEXTURE0);
1469	glBindTexture(GL_TEXTURE_2D, renderer->vramTex);
1470	glActiveTexture(GL_TEXTURE0 + 1);
1471	glBindTexture(GL_TEXTURE_2D, renderer->paletteTex);
1472	glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
1473	glUniform1i(uniforms[GBA_GL_BG_VRAM], 0);
1474	glUniform1i(uniforms[GBA_GL_BG_PALETTE], 1);
1475	if (background->mosaic) {
1476		glUniform2i(uniforms[GBA_GL_BG_MOSAIC], GBAMosaicControlGetBgV(renderer->mosaic) + 1, GBAMosaicControlGetBgH(renderer->mosaic) + 1);
1477	} else {
1478		glUniform2i(uniforms[GBA_GL_BG_MOSAIC], 0, 0);
1479	}
1480	glUniform4i(uniforms[GBA_GL_BG_INFLAGS], (background->priority << 3) + (background->index << 1) + 1,
1481		                                     background->target1 | (background->target2 * 2) | (renderer->blendEffect * 4),
1482		                                     renderer->blda, 0);
1483	glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
1484}
1485
1486void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
1487	int inY0 = renderer->firstY + background->y;
1488	int yDiv = (((y + background->y) & ~0xFF) - background->y) & 0xFF;
1489	int inY1 = yDiv + background->y;
1490	int yBase0 = inY0 & 0xFF;
1491	int yBase1 = inY1 & 0xFF;
1492	if (background->size == 2) {
1493		yBase0 += inY0 & 0x100;
1494		yBase1 += inY1 & 0x100;
1495	} else if (background->size == 3) {
1496		yBase0 += (inY0 & 0x100) << 1;
1497		yBase1 += (inY1 & 0x100) << 1;
1498	}
1499
1500	const struct GBAVideoGLShader* shader = &renderer->bgShader[background->multipalette ? 1 : 0];
1501	const GLuint* uniforms = shader->uniforms;
1502	glUseProgram(shader->program);
1503	glBindVertexArray(shader->vao);
1504	_prepareBackground(renderer, background, uniforms);
1505	glUniform1i(uniforms[GBA_GL_BG_SCREENBASE], background->screenBase);
1506	glUniform1i(uniforms[GBA_GL_BG_CHARBASE], background->charBase);
1507	glUniform1i(uniforms[GBA_GL_BG_SIZE], background->size);
1508
1509	if (yDiv > renderer->firstY) {
1510		int end = yDiv - 1;
1511		if (end > y) {
1512			end = y;
1513		}
1514		glScissor(0, renderer->firstY * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, (end - renderer->firstY + 1) * renderer->scale);
1515		glUniform2i(uniforms[GBA_GL_VS_LOC], end - renderer->firstY + 1, renderer->firstY);
1516		glUniform2i(uniforms[GBA_GL_BG_OFFSET], background->x, yBase0 - renderer->firstY);
1517		glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1518	}
1519
1520	if (y >= yDiv) {
1521		int start = yDiv;
1522		if (yDiv < renderer->firstY) {
1523			start = renderer->firstY;
1524		}
1525		glScissor(0, start * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, (y - start + 1) * renderer->scale);
1526		glUniform2i(uniforms[GBA_GL_VS_LOC], y - start + 1, start);
1527		glUniform2i(uniforms[GBA_GL_BG_OFFSET], background->x, yBase1 - yDiv);
1528		glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1529	}
1530
1531	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1532}
1533
1534void _prepareTransform(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, const GLuint* uniforms, int y) {
1535	glScissor(0, renderer->firstY * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->scale * (y - renderer->firstY + 1));
1536	glUniform2i(uniforms[GBA_GL_VS_LOC], y - renderer->firstY + 1, renderer->firstY);
1537
1538	glActiveTexture(GL_TEXTURE0 + 2);
1539	glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_AFFINE_2 + background->index - 2]);
1540	glUniform1i(uniforms[GBA_GL_BG_TRANSFORM], 2);
1541	_prepareBackground(renderer, background, uniforms);
1542}
1543
1544void GBAVideoGLRendererDrawBackgroundMode2(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
1545	const struct GBAVideoGLShader* shader = &renderer->bgShader[background->overflow ? 2 : 3];
1546	const GLuint* uniforms = shader->uniforms;
1547	glUseProgram(shader->program);
1548	glBindVertexArray(shader->vao);
1549	_prepareTransform(renderer, background, uniforms, y);
1550	glUniform1i(uniforms[GBA_GL_BG_SCREENBASE], background->screenBase);
1551	glUniform1i(uniforms[GBA_GL_BG_CHARBASE], background->charBase);
1552	glUniform1i(uniforms[GBA_GL_BG_SIZE], background->size);
1553	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1554	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1555}
1556
1557void GBAVideoGLRendererDrawBackgroundMode3(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
1558	const struct GBAVideoGLShader* shader = &renderer->bgShader[5];
1559	const GLuint* uniforms = shader->uniforms;
1560	glBindFramebuffer(GL_FRAMEBUFFER, background->fbo);
1561	glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
1562	glUseProgram(shader->program);
1563	glBindVertexArray(shader->vao);
1564	_prepareTransform(renderer, background, uniforms, y);
1565	glUniform1i(uniforms[GBA_GL_BG_CHARBASE], 0);
1566	glUniform2i(uniforms[GBA_GL_BG_SIZE], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
1567	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1568	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1569}
1570
1571void GBAVideoGLRendererDrawBackgroundMode4(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
1572	const struct GBAVideoGLShader* shader = &renderer->bgShader[4];
1573	const GLuint* uniforms = shader->uniforms;
1574	glBindFramebuffer(GL_FRAMEBUFFER, background->fbo);
1575	glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
1576	glUseProgram(shader->program);
1577	glBindVertexArray(shader->vao);
1578	_prepareTransform(renderer, background, uniforms, y);
1579	glUniform1i(uniforms[GBA_GL_BG_CHARBASE], GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt) ? 0xA000 : 0);
1580	glUniform2i(uniforms[GBA_GL_BG_SIZE], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
1581	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1582	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1583}
1584
1585void GBAVideoGLRendererDrawBackgroundMode5(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
1586	const struct GBAVideoGLShader* shader = &renderer->bgShader[5];
1587	const GLuint* uniforms = shader->uniforms;
1588	glBindFramebuffer(GL_FRAMEBUFFER, background->fbo);
1589	glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
1590	glUseProgram(shader->program);
1591	glBindVertexArray(shader->vao);
1592	_prepareTransform(renderer, background, uniforms, y);
1593	glUniform1i(uniforms[GBA_GL_BG_CHARBASE], GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt) ? 0x5000 : 0);
1594	glUniform2i(uniforms[GBA_GL_BG_SIZE], 160, 128);
1595	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1596	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1597}
1598
1599static void _scissorWindow(int start, int end, int y, int lines, int scale) {
1600	if (start > end) {
1601		_scissorWindow(start, GBA_VIDEO_HORIZONTAL_PIXELS * scale, y, lines, scale);
1602		_scissorWindow(0, end, y, lines, scale);
1603		return;
1604	}
1605	glScissor(start, y, end - start, lines);
1606	glClear(GL_COLOR_BUFFER_BIT);	
1607}
1608
1609static void _scissorWindowN(const struct GBAVideoWindowRegion* region, const struct GBAVideoWindowRegion* v, const struct GBAVideoWindowRegion* y, int scale) {
1610	int sdelta = region[0].start - region[1].start;
1611	int edelta = region[0].end - region[1].end;
1612	int maxDelta = 0;
1613	if (sdelta > maxDelta) {
1614		maxDelta = sdelta;
1615	} else if (-sdelta > maxDelta) {
1616		maxDelta = -sdelta;
1617	}
1618	if (edelta > maxDelta) {
1619		maxDelta = edelta;
1620	} else if (-edelta > maxDelta) {
1621		maxDelta = -edelta;
1622	}
1623	int startY = y->start;
1624	int endY = y->end;
1625	if (startY < v->start) {
1626		startY = v->start;
1627	}
1628	if (endY >= v->end) {
1629		endY = v->end - 1;
1630	}
1631	if (!(sdelta | edelta) || maxDelta >= GBA_VIDEO_VERTICAL_PIXELS / 2) {
1632		_scissorWindow(region[0].start * scale, region[0].end * scale, startY * scale, (endY - startY + 1) * scale, scale);
1633	} else {
1634		int i;
1635		for (i = 0; i < scale * (endY - startY + 1); ++i) {
1636			int start = region[1].start * scale + sdelta * i;
1637			int end = region[1].end * scale + edelta * i;
1638			_scissorWindow(start, end, startY * scale + i, 1, scale);
1639		}
1640	}
1641}
1642
1643static void _clearWindow(GBAWindowControl window, int bldb, int bldy) {
1644	window = ~window & 0x3F;
1645	glClearColor(window / 128.f, bldb / 16.f, bldy / 16.f, 0);
1646}
1647
1648void GBAVideoGLRendererDrawWindow(struct GBAVideoGLRenderer* renderer, int y) {
1649	glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_WINDOW]);
1650	int dispcnt = ((renderer->dispcnt >> 8) & 0x1F) | 0x20;
1651	if (!(renderer->dispcnt & 0xE000)) {
1652		_clearWindow(dispcnt, renderer->bldb, renderer->bldy);
1653		_scissorWindow(0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->firstY * renderer->scale, (y - renderer->firstY + 1) * renderer->scale, renderer->scale);
1654	} else {
1655		_clearWindow(renderer->winout & dispcnt, renderer->bldb, renderer->bldy);
1656		_scissorWindow(0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->firstY * renderer->scale, (y - renderer->firstY + 1) * renderer->scale, renderer->scale);
1657		struct GBAVideoWindowRegion yRegion = {
1658			y,
1659			renderer->firstY
1660		};
1661		if (GBARegisterDISPCNTIsWin1Enable(renderer->dispcnt) && y >= renderer->winN[1].v.start && renderer->firstY < renderer->winN[1].v.end) {
1662			_clearWindow(renderer->winN[1].control & dispcnt, renderer->bldb, renderer->bldy);
1663			_scissorWindowN(renderer->winN[1].h, &renderer->winN[1].v, &yRegion, renderer->scale);
1664		}
1665		if (GBARegisterDISPCNTIsWin0Enable(renderer->dispcnt) && y >= renderer->winN[0].v.start && renderer->firstY < renderer->winN[0].v.end) {
1666			_clearWindow(renderer->winN[0].control & dispcnt, renderer->bldb, renderer->bldy);
1667			_scissorWindowN(renderer->winN[0].h, &renderer->winN[0].v, &yRegion, renderer->scale);
1668		}
1669	}
1670}
1671
1672#endif