all repos — mgba @ eb5a7951010f00548007b549f08d72d2bc3d4940

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