all repos — mgba @ a0af0ce1419eca9cc03f4b7a66638603898f66c7

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	glEnableVertexAttribArray(positionLocation);
 575	glVertexAttribPointer(positionLocation, 2, GL_INT, GL_FALSE, 0, NULL);
 576
 577	size_t i;
 578	for (i = 0; uniforms[i].name; ++i) {
 579		shader->uniforms[uniforms[i].type] = glGetUniformLocation(program, uniforms[i].name);
 580	}
 581}
 582
 583static void _deleteShader(struct GBAVideoGLShader* shader) {
 584	glDeleteProgram(shader->program);
 585	glDeleteVertexArrays(1, &shader->vao);
 586}
 587
 588static void _initFramebufferTexture(GLuint tex, GLenum format, GLenum attachment, int scale) {
 589	glBindTexture(GL_TEXTURE_2D, tex);
 590	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 591	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 592	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 593	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 594	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);
 595	glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0);	
 596}
 597
 598void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) {
 599	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 600	glRenderer->temporaryBuffer = NULL;
 601
 602	glGenFramebuffers(GBA_GL_FBO_MAX, glRenderer->fbo);
 603	glGenTextures(GBA_GL_TEX_MAX, glRenderer->layers);
 604
 605	glGenTextures(1, &glRenderer->paletteTex);
 606	glBindTexture(GL_TEXTURE_2D, glRenderer->paletteTex);
 607	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 608	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 609
 610	glGenTextures(1, &glRenderer->vramTex);
 611	glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex);
 612	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 613	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 614	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, 256, 192, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 0);
 615
 616	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OBJ]);
 617	_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_OBJ_COLOR], GL_RGBA, GL_COLOR_ATTACHMENT0, glRenderer->scale);
 618	_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_OBJ_FLAGS], GL_RGBA, GL_COLOR_ATTACHMENT1, glRenderer->scale);
 619	_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_WINDOW], GL_RGBA, GL_COLOR_ATTACHMENT2, glRenderer->scale);
 620
 621	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_BACKDROP]);
 622	_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_BACKDROP_COLOR], GL_RGB, GL_COLOR_ATTACHMENT0, 0);
 623	_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_BACKDROP_FLAGS], GL_RGBA, GL_COLOR_ATTACHMENT1, 0);
 624
 625	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_WINDOW]);
 626	_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_WINDOW], GL_RGB, GL_COLOR_ATTACHMENT0, glRenderer->scale);
 627
 628	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OUTPUT]);
 629	_initFramebufferTexture(glRenderer->outputTex, GL_RGB, GL_COLOR_ATTACHMENT0, glRenderer->scale);
 630
 631	glBindFramebuffer(GL_FRAMEBUFFER, 0);
 632
 633	glGenBuffers(1, &glRenderer->vbo);
 634	glBindBuffer(GL_ARRAY_BUFFER, glRenderer->vbo);
 635	glBufferData(GL_ARRAY_BUFFER, sizeof(_vertices), _vertices, GL_STATIC_DRAW);
 636
 637	int i;
 638	for (i = 0; i < 4; ++i) {
 639		struct GBAVideoGLBackground* bg = &glRenderer->bg[i];
 640		bg->index = i;
 641		bg->enabled = 0;
 642		bg->priority = 0;
 643		bg->charBase = 0;
 644		bg->mosaic = 0;
 645		bg->multipalette = 0;
 646		bg->screenBase = 0;
 647		bg->overflow = 0;
 648		bg->size = 0;
 649		bg->target1 = 0;
 650		bg->target2 = 0;
 651		bg->x = 0;
 652		bg->y = 0;
 653		bg->refx = 0;
 654		bg->refy = 0;
 655		bg->affine[0].dx = 256;
 656		bg->affine[0].dmx = 0;
 657		bg->affine[0].dy = 0;
 658		bg->affine[0].dmy = 256;
 659		bg->affine[0].sx = 0;
 660		bg->affine[0].sy = 0;
 661		glGenFramebuffers(1, &bg->fbo);
 662		glGenTextures(1, &bg->tex);
 663		glGenTextures(1, &bg->flags);
 664		glBindFramebuffer(GL_FRAMEBUFFER, bg->fbo);
 665		_initFramebufferTexture(bg->tex, GL_RGBA, GL_COLOR_ATTACHMENT0, glRenderer->scale);
 666		_initFramebufferTexture(bg->flags, GL_RGB, GL_COLOR_ATTACHMENT1, glRenderer->scale);
 667		glBindFramebuffer(GL_FRAMEBUFFER, 0);
 668	}
 669
 670	char log[1024];
 671	const GLchar* shaderBuffer[8];
 672	shaderBuffer[0] = _gl3Header;
 673
 674	GLuint vs = glCreateShader(GL_VERTEX_SHADER);
 675	shaderBuffer[1] = _vertexShader;
 676	glShaderSource(vs, 2, shaderBuffer, 0);
 677	glCompileShader(vs);
 678	glGetShaderInfoLog(vs, 1024, 0, log);
 679	if (log[0]) {
 680		mLOG(GBA_VIDEO, ERROR, "Vertex shader compilation failure: %s", log);
 681	}
 682
 683	shaderBuffer[1] = _renderMode0;
 684
 685	shaderBuffer[2] = _renderTile16;
 686	_compileShader(glRenderer, &glRenderer->bgShader[0], shaderBuffer, 3, vs, _uniformsMode0, log);
 687
 688	shaderBuffer[2] = _renderTile256;
 689	_compileShader(glRenderer, &glRenderer->bgShader[1], shaderBuffer, 3, vs, _uniformsMode0, log);
 690
 691	shaderBuffer[1] = _renderMode2;
 692	shaderBuffer[2] = _interpolate;
 693
 694	shaderBuffer[3] = _fetchTileOverflow;
 695	_compileShader(glRenderer, &glRenderer->bgShader[2], shaderBuffer, 4, vs, _uniformsMode2, log);
 696
 697	shaderBuffer[3] = _fetchTileNoOverflow;
 698	_compileShader(glRenderer, &glRenderer->bgShader[3], shaderBuffer, 4, vs, _uniformsMode2, log);
 699
 700	shaderBuffer[1] = _renderMode4;
 701	shaderBuffer[2] = _interpolate;
 702	_compileShader(glRenderer, &glRenderer->bgShader[4], shaderBuffer, 3, vs, _uniformsMode4, log);
 703
 704	shaderBuffer[1] = _renderMode35;
 705	shaderBuffer[2] = _interpolate;
 706	_compileShader(glRenderer, &glRenderer->bgShader[5], shaderBuffer, 3, vs, _uniformsMode35, log);
 707
 708	shaderBuffer[1] = _renderObj;
 709
 710	shaderBuffer[2] = _renderTile16;
 711	_compileShader(glRenderer, &glRenderer->objShader[0], shaderBuffer, 3, vs, _uniformsObj, log);
 712#ifndef BUILD_GLES3
 713	glBindFragDataLocation(glRenderer->objShader[0].program, 2, "window");
 714#endif
 715
 716	shaderBuffer[2] = _renderTile256;
 717	_compileShader(glRenderer, &glRenderer->objShader[1], shaderBuffer, 3, vs, _uniformsObj, log);
 718#ifndef BUILD_GLES3
 719	glBindFragDataLocation(glRenderer->objShader[1].program, 2, "window");
 720#endif
 721
 722	shaderBuffer[1] = _finalize;
 723	_compileShader(glRenderer, &glRenderer->finalizeShader, shaderBuffer, 2, vs, _uniformsFinalize, log);
 724
 725	glBindVertexArray(0);
 726	glDeleteShader(vs);
 727
 728	GBAVideoGLRendererReset(renderer);
 729}
 730
 731void GBAVideoGLRendererDeinit(struct GBAVideoRenderer* renderer) {
 732	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 733	if (glRenderer->temporaryBuffer) {
 734		mappedMemoryFree(glRenderer->temporaryBuffer, GBA_VIDEO_HORIZONTAL_PIXELS * GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale * glRenderer->scale);
 735	}
 736	glDeleteFramebuffers(GBA_GL_FBO_MAX, glRenderer->fbo);
 737	glDeleteTextures(GBA_GL_TEX_MAX, glRenderer->layers);
 738	glDeleteTextures(1, &glRenderer->paletteTex);
 739	glDeleteTextures(1, &glRenderer->vramTex);
 740
 741	_deleteShader(&glRenderer->bgShader[0]);
 742	_deleteShader(&glRenderer->bgShader[1]);
 743	_deleteShader(&glRenderer->bgShader[2]);
 744	_deleteShader(&glRenderer->bgShader[3]);
 745	_deleteShader(&glRenderer->objShader[0]);
 746	_deleteShader(&glRenderer->objShader[1]);
 747	_deleteShader(&glRenderer->finalizeShader);
 748
 749	int i;
 750	for (i = 0; i < 4; ++i) {
 751		struct GBAVideoGLBackground* bg = &glRenderer->bg[i];
 752		glDeleteFramebuffers(1, &bg->fbo);
 753		glDeleteTextures(1, &bg->tex);
 754		glDeleteTextures(1, &bg->flags);
 755	}
 756}
 757
 758void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer) {
 759	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 760
 761	glRenderer->paletteDirty = true;
 762	glRenderer->vramDirty = 0xFFFFFF;
 763	glRenderer->firstAffine = -1;
 764	glRenderer->dispcnt = 0;
 765	glRenderer->mosaic = 0;
 766}
 767
 768void GBAVideoGLRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
 769	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 770	glRenderer->vramDirty |= 1 << (address >> 12);
 771}
 772
 773void GBAVideoGLRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
 774	UNUSED(oam);
 775	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 776	glRenderer->oamDirty = true;
 777}
 778
 779void GBAVideoGLRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
 780	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 781#ifdef BUILD_GLES3
 782	glRenderer->shadowPalette[address >> 1] = (value & 0x3F) | ((value & 0x7FE0) << 1);
 783#else
 784	UNUSED(address);
 785	UNUSED(value);
 786#endif
 787	glRenderer->paletteDirty = true;
 788}
 789
 790uint16_t GBAVideoGLRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
 791	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 792	if (renderer->cache) {
 793		GBAVideoCacheWriteVideoRegister(renderer->cache, address, value);
 794	}
 795
 796	switch (address) {
 797	case REG_DISPCNT:
 798		value &= 0xFFF7;
 799		glRenderer->dispcnt = value;
 800		GBAVideoGLRendererUpdateDISPCNT(glRenderer);
 801		break;
 802	case REG_BG0CNT:
 803		value &= 0xDFFF;
 804		GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[0], value);
 805		break;
 806	case REG_BG1CNT:
 807		value &= 0xDFFF;
 808		GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[1], value);
 809		break;
 810	case REG_BG2CNT:
 811		value &= 0xFFFF;
 812		GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[2], value);
 813		break;
 814	case REG_BG3CNT:
 815		value &= 0xFFFF;
 816		GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[3], value);
 817		break;
 818	case REG_BG0HOFS:
 819		value &= 0x01FF;
 820		glRenderer->bg[0].x = value;
 821		break;
 822	case REG_BG0VOFS:
 823		value &= 0x01FF;
 824		glRenderer->bg[0].y = value;
 825		break;
 826	case REG_BG1HOFS:
 827		value &= 0x01FF;
 828		glRenderer->bg[1].x = value;
 829		break;
 830	case REG_BG1VOFS:
 831		value &= 0x01FF;
 832		glRenderer->bg[1].y = value;
 833		break;
 834	case REG_BG2HOFS:
 835		value &= 0x01FF;
 836		glRenderer->bg[2].x = value;
 837		break;
 838	case REG_BG2VOFS:
 839		value &= 0x01FF;
 840		glRenderer->bg[2].y = value;
 841		break;
 842	case REG_BG3HOFS:
 843		value &= 0x01FF;
 844		glRenderer->bg[3].x = value;
 845		break;
 846	case REG_BG3VOFS:
 847		value &= 0x01FF;
 848		glRenderer->bg[3].y = value;
 849		break;
 850	case REG_BG2PA:
 851		glRenderer->bg[2].affine[0].dx = value;
 852		break;
 853	case REG_BG2PB:
 854		glRenderer->bg[2].affine[0].dmx = value;
 855		break;
 856	case REG_BG2PC:
 857		glRenderer->bg[2].affine[0].dy = value;
 858		break;
 859	case REG_BG2PD:
 860		glRenderer->bg[2].affine[0].dmy = value;
 861		break;
 862	case REG_BG2X_LO:
 863		GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[2], value);
 864		break;
 865	case REG_BG2X_HI:
 866		GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[2], value);
 867		break;
 868	case REG_BG2Y_LO:
 869		GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[2], value);
 870		break;
 871	case REG_BG2Y_HI:
 872		GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[2], value);
 873		break;
 874	case REG_BG3PA:
 875		glRenderer->bg[3].affine[0].dx = value;
 876		break;
 877	case REG_BG3PB:
 878		glRenderer->bg[3].affine[0].dmx = value;
 879		break;
 880	case REG_BG3PC:
 881		glRenderer->bg[3].affine[0].dy = value;
 882		break;
 883	case REG_BG3PD:
 884		glRenderer->bg[3].affine[0].dmy = value;
 885		break;
 886	case REG_BG3X_LO:
 887		GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[3], value);
 888		break;
 889	case REG_BG3X_HI:
 890		GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[3], value);
 891		break;
 892	case REG_BG3Y_LO:
 893		GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[3], value);
 894		break;
 895	case REG_BG3Y_HI:
 896		GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[3], value);
 897		break;
 898	case REG_BLDCNT:
 899		GBAVideoGLRendererWriteBLDCNT(glRenderer, value);
 900		value &= 0x3FFF;
 901		break;
 902	case REG_BLDALPHA:
 903		glRenderer->blda = value & 0x1F;
 904		if (glRenderer->blda > 0x10) {
 905			glRenderer->blda = 0x10;
 906		}
 907		glRenderer->bldb = (value >> 8) & 0x1F;
 908		if (glRenderer->bldb > 0x10) {
 909			glRenderer->bldb = 0x10;
 910		}
 911		value &= 0x1F1F;
 912		break;
 913	case REG_BLDY:
 914		value &= 0x1F;
 915		if (value > 0x10) {
 916			value = 0x10;
 917		}
 918		glRenderer->bldy = value;
 919		break;
 920	case REG_WIN0H:
 921		glRenderer->winN[0].h[0].end = value;
 922		glRenderer->winN[0].h[0].start = value >> 8;
 923		if (glRenderer->winN[0].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[0].h[0].start > glRenderer->winN[0].h[0].end) {
 924			glRenderer->winN[0].h[0].start = 0;
 925		}
 926		if (glRenderer->winN[0].h[0].end > GBA_VIDEO_HORIZONTAL_PIXELS) {
 927			glRenderer->winN[0].h[0].end = GBA_VIDEO_HORIZONTAL_PIXELS;
 928			if (glRenderer->winN[0].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS) {
 929				glRenderer->winN[0].h[0].start = GBA_VIDEO_HORIZONTAL_PIXELS;
 930			}
 931		}
 932		break;
 933	case REG_WIN1H:
 934		glRenderer->winN[1].h[0].end = value;
 935		glRenderer->winN[1].h[0].start = value >> 8;
 936		if (glRenderer->winN[1].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[1].h[0].start > glRenderer->winN[1].h[0].end) {
 937			glRenderer->winN[1].h[0].start = 0;
 938		}
 939		if (glRenderer->winN[1].h[0].end > GBA_VIDEO_HORIZONTAL_PIXELS) {
 940			glRenderer->winN[1].h[0].end = GBA_VIDEO_HORIZONTAL_PIXELS;
 941			if (glRenderer->winN[1].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS) {
 942				glRenderer->winN[1].h[0].start = GBA_VIDEO_HORIZONTAL_PIXELS;
 943			}
 944		}
 945		break;
 946	case REG_WIN0V:
 947		glRenderer->winN[0].v.end = value;
 948		glRenderer->winN[0].v.start = value >> 8;
 949		if (glRenderer->winN[0].v.start > GBA_VIDEO_VERTICAL_PIXELS && glRenderer->winN[0].v.start > glRenderer->winN[0].v.end) {
 950			glRenderer->winN[0].v.start = 0;
 951		}
 952		if (glRenderer->winN[0].v.end > GBA_VIDEO_VERTICAL_PIXELS) {
 953			glRenderer->winN[0].v.end = GBA_VIDEO_VERTICAL_PIXELS;
 954			if (glRenderer->winN[0].v.start > GBA_VIDEO_VERTICAL_PIXELS) {
 955				glRenderer->winN[0].v.start = GBA_VIDEO_VERTICAL_PIXELS;
 956			}
 957		}
 958		break;
 959	case REG_WIN1V:
 960		glRenderer->winN[1].v.end = value;
 961		glRenderer->winN[1].v.start = value >> 8;
 962		if (glRenderer->winN[1].v.start > GBA_VIDEO_VERTICAL_PIXELS && glRenderer->winN[1].v.start > glRenderer->winN[1].v.end) {
 963			glRenderer->winN[1].v.start = 0;
 964		}
 965		if (glRenderer->winN[1].v.end > GBA_VIDEO_VERTICAL_PIXELS) {
 966			glRenderer->winN[1].v.end = GBA_VIDEO_VERTICAL_PIXELS;
 967			if (glRenderer->winN[1].v.start > GBA_VIDEO_VERTICAL_PIXELS) {
 968				glRenderer->winN[1].v.start = GBA_VIDEO_VERTICAL_PIXELS;
 969			}
 970		}
 971		break;
 972	case REG_WININ:
 973		value &= 0x3F3F;
 974		glRenderer->winN[0].control = value;
 975		glRenderer->winN[1].control = value >> 8;
 976		break;
 977	case REG_WINOUT:
 978		value &= 0x3F3F;
 979		glRenderer->winout = value;
 980		glRenderer->objwin = value >> 8;
 981		break;
 982	case REG_MOSAIC:
 983		glRenderer->mosaic = value;
 984		break;
 985	default:
 986		break;
 987	}
 988	return value;
 989}
 990
 991void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
 992	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 993	if (glRenderer->paletteDirty) {
 994		glBindTexture(GL_TEXTURE_2D, glRenderer->paletteTex);
 995#ifdef BUILD_GLES3
 996		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, 16, 32, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_6_5, glRenderer->shadowPalette);
 997#else
 998		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, 16, 32, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, glRenderer->d.palette);
 999#endif
1000		glRenderer->paletteDirty = false;
1001	}
1002	int i;
1003	int first = -1;
1004	glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex);
1005	for (i = 0; i < 25; ++i) {
1006		if (!(glRenderer->vramDirty & (1 << i))) {
1007			if (first >= 0) {
1008				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]);
1009				first = -1;
1010			}
1011		} else if (first < 0) {
1012			first = i;
1013		}
1014	}
1015	glRenderer->vramDirty = 0;
1016
1017	if (y == 0) {
1018		memcpy(&glRenderer->winN[0].h[1], &glRenderer->winN[0].h[0], sizeof(struct GBAVideoWindowRegion));
1019		memcpy(&glRenderer->winN[1].h[1], &glRenderer->winN[1].h[0], sizeof(struct GBAVideoWindowRegion));
1020
1021		glDisable(GL_SCISSOR_TEST);		
1022		glClearColor(0, 0, 0, 0);
1023		glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OBJ]);
1024		glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
1025		glClear(GL_COLOR_BUFFER_BIT);
1026
1027		for (i = 0; i < 4; ++i) {
1028			glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->bg[i].fbo);
1029			glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
1030			glClear(GL_COLOR_BUFFER_BIT);
1031		}
1032	}
1033	glEnable(GL_SCISSOR_TEST);
1034
1035	uint32_t backdrop = M_RGB5_TO_RGB8(glRenderer->d.palette[0]);
1036	glViewport(0, 0, 1, GBA_VIDEO_VERTICAL_PIXELS);
1037	glScissor(0, y, 1, 1);
1038	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_BACKDROP]);
1039	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1040	glClearColor(((backdrop >> 16) & 0xFF) / 256., ((backdrop >> 8) & 0xFF) / 256., (backdrop & 0xFF) / 256., 0.f);
1041	glClear(GL_COLOR_BUFFER_BIT);
1042	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT1 });
1043	glClearColor(1, (glRenderer->target1Bd | (glRenderer->target2Bd * 2) | (glRenderer->blendEffect * 4)) / 32.f, glRenderer->blda / 16.f, 0);
1044	glClear(GL_COLOR_BUFFER_BIT);
1045	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1046
1047	if (GBARegisterDISPCNTGetMode(glRenderer->dispcnt) != 0) {
1048		if (glRenderer->firstAffine < 0) {
1049			memcpy(&glRenderer->bg[2].affine[3], &glRenderer->bg[2].affine[0], sizeof(struct GBAVideoGLAffine));
1050			memcpy(&glRenderer->bg[3].affine[3], &glRenderer->bg[3].affine[0], sizeof(struct GBAVideoGLAffine));
1051			memcpy(&glRenderer->bg[2].affine[2], &glRenderer->bg[2].affine[0], sizeof(struct GBAVideoGLAffine));
1052			memcpy(&glRenderer->bg[3].affine[2], &glRenderer->bg[3].affine[0], sizeof(struct GBAVideoGLAffine));
1053			memcpy(&glRenderer->bg[2].affine[1], &glRenderer->bg[2].affine[0], sizeof(struct GBAVideoGLAffine));
1054			memcpy(&glRenderer->bg[3].affine[1], &glRenderer->bg[3].affine[0], sizeof(struct GBAVideoGLAffine));
1055			glRenderer->firstAffine = y;
1056		}
1057	} else {
1058		glRenderer->firstAffine = -1;
1059	}
1060
1061	GBAVideoGLRendererDrawWindow(glRenderer, y);
1062	if (GBARegisterDISPCNTIsObjEnable(glRenderer->dispcnt) && !glRenderer->d.disableOBJ) {
1063		if (glRenderer->oamDirty) {
1064			glRenderer->oamMax = GBAVideoRendererCleanOAM(glRenderer->d.oam->obj, glRenderer->sprites, 0);
1065			glRenderer->oamDirty = false;
1066		}
1067		int i;
1068		for (i = glRenderer->oamMax; i--;) {
1069			struct GBAVideoRendererSprite* sprite = &glRenderer->sprites[i];
1070			if ((y < sprite->y && (sprite->endY - 256 < 0 || y >= sprite->endY - 256)) || y >= sprite->endY) {
1071				continue;
1072			}
1073
1074			GBAVideoGLRendererDrawSprite(glRenderer, &sprite->obj, y, sprite->y);
1075		}
1076	}
1077
1078	if (TEST_LAYER_ENABLED(0) && GBARegisterDISPCNTGetMode(glRenderer->dispcnt) < 2) {
1079		GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[0], y);
1080	}
1081	if (TEST_LAYER_ENABLED(1) && GBARegisterDISPCNTGetMode(glRenderer->dispcnt) < 2) {
1082		GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[1], y);
1083	}
1084	if (TEST_LAYER_ENABLED(2)) {
1085		switch (GBARegisterDISPCNTGetMode(glRenderer->dispcnt)) {
1086		case 0:
1087			GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[2], y);
1088			break;
1089		case 1:
1090		case 2:
1091			GBAVideoGLRendererDrawBackgroundMode2(glRenderer, &glRenderer->bg[2], y);
1092			break;
1093		case 3:
1094			GBAVideoGLRendererDrawBackgroundMode3(glRenderer, &glRenderer->bg[2], y);
1095			break;
1096		case 4:
1097			GBAVideoGLRendererDrawBackgroundMode4(glRenderer, &glRenderer->bg[2], y);
1098			break;
1099		case 5:
1100			GBAVideoGLRendererDrawBackgroundMode5(glRenderer, &glRenderer->bg[2], y);
1101			break;
1102		}
1103	}
1104	if (TEST_LAYER_ENABLED(3)) {
1105		switch (GBARegisterDISPCNTGetMode(glRenderer->dispcnt)) {
1106		case 0:
1107			GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[3], y);
1108			break;
1109		case 2:
1110			GBAVideoGLRendererDrawBackgroundMode2(glRenderer, &glRenderer->bg[3], y);
1111			break;
1112		}
1113	}
1114
1115	if (GBARegisterDISPCNTGetMode(glRenderer->dispcnt) != 0) {
1116		memcpy(&glRenderer->bg[2].affine[3], &glRenderer->bg[2].affine[2], sizeof(struct GBAVideoGLAffine));
1117		memcpy(&glRenderer->bg[3].affine[3], &glRenderer->bg[3].affine[2], sizeof(struct GBAVideoGLAffine));
1118		memcpy(&glRenderer->bg[2].affine[2], &glRenderer->bg[2].affine[1], sizeof(struct GBAVideoGLAffine));
1119		memcpy(&glRenderer->bg[3].affine[2], &glRenderer->bg[3].affine[1], sizeof(struct GBAVideoGLAffine));
1120		memcpy(&glRenderer->bg[2].affine[1], &glRenderer->bg[2].affine[0], sizeof(struct GBAVideoGLAffine));
1121		memcpy(&glRenderer->bg[3].affine[1], &glRenderer->bg[3].affine[0], sizeof(struct GBAVideoGLAffine));
1122
1123		glRenderer->bg[2].affine[0].sx += glRenderer->bg[2].affine[0].dmx;
1124		glRenderer->bg[2].affine[0].sy += glRenderer->bg[2].affine[0].dmy;
1125		glRenderer->bg[3].affine[0].sx += glRenderer->bg[3].affine[0].dmx;
1126		glRenderer->bg[3].affine[0].sy += glRenderer->bg[3].affine[0].dmy;
1127	}
1128	memcpy(&glRenderer->winN[0].h[1], &glRenderer->winN[0].h[0], sizeof(struct GBAVideoWindowRegion));
1129	memcpy(&glRenderer->winN[1].h[1], &glRenderer->winN[1].h[0], sizeof(struct GBAVideoWindowRegion));
1130}
1131
1132void GBAVideoGLRendererFinishFrame(struct GBAVideoRenderer* renderer) {
1133	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
1134	_finalizeLayers(glRenderer);
1135	glBindVertexArray(0);
1136	glRenderer->firstAffine = -1;
1137	glRenderer->bg[2].affine[0].sx = glRenderer->bg[2].refx;
1138	glRenderer->bg[2].affine[0].sy = glRenderer->bg[2].refy;
1139	glRenderer->bg[3].affine[0].sx = glRenderer->bg[3].refx;
1140	glRenderer->bg[3].affine[0].sy = glRenderer->bg[3].refy;
1141}
1142
1143void GBAVideoGLRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
1144	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
1145	*stride = GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale;
1146	if (!glRenderer->temporaryBuffer) {
1147		glRenderer->temporaryBuffer = anonymousMemoryMap(GBA_VIDEO_HORIZONTAL_PIXELS * GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale * glRenderer->scale * BYTES_PER_PIXEL);
1148	}
1149	glFinish();
1150	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OUTPUT]);
1151	glPixelStorei(GL_PACK_ROW_LENGTH, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale);
1152	glPixelStorei(GL_PACK_ALIGNMENT, 1);
1153	glReadPixels(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale, GL_RGBA, GL_UNSIGNED_BYTE, (void*) glRenderer->temporaryBuffer);
1154	*pixels = glRenderer->temporaryBuffer;
1155}
1156
1157void GBAVideoGLRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
1158
1159}
1160
1161static void _enableBg(struct GBAVideoGLRenderer* renderer, int bg, bool active) {
1162	int wasActive = renderer->bg[bg].enabled;
1163	if (!active) {
1164		renderer->bg[bg].enabled = 0;
1165	} else if (!wasActive && active) {
1166		/*if (renderer->nextY == 0 || GBARegisterDISPCNTGetMode(renderer->dispcnt) > 2) {
1167			// TODO: Investigate in more depth how switching background works in different modes
1168			renderer->bg[bg].enabled = 4;
1169		} else {
1170			renderer->bg[bg].enabled = 1;
1171		}*/
1172		renderer->bg[bg].enabled = 4;
1173	}
1174}
1175
1176static void GBAVideoGLRendererUpdateDISPCNT(struct GBAVideoGLRenderer* renderer) {
1177	_enableBg(renderer, 0, GBARegisterDISPCNTGetBg0Enable(renderer->dispcnt));
1178	_enableBg(renderer, 1, GBARegisterDISPCNTGetBg1Enable(renderer->dispcnt));
1179	_enableBg(renderer, 2, GBARegisterDISPCNTGetBg2Enable(renderer->dispcnt));
1180	_enableBg(renderer, 3, GBARegisterDISPCNTGetBg3Enable(renderer->dispcnt));
1181}
1182
1183static void GBAVideoGLRendererWriteBGCNT(struct GBAVideoGLBackground* bg, uint16_t value) {
1184	bg->priority = GBARegisterBGCNTGetPriority(value);
1185	bg->charBase = GBARegisterBGCNTGetCharBase(value) << 13;
1186	bg->mosaic = GBARegisterBGCNTGetMosaic(value);
1187	bg->multipalette = GBARegisterBGCNTGet256Color(value);
1188	bg->screenBase = GBARegisterBGCNTGetScreenBase(value) << 10;
1189	bg->overflow = GBARegisterBGCNTGetOverflow(value);
1190	bg->size = GBARegisterBGCNTGetSize(value);
1191}
1192
1193static void GBAVideoGLRendererWriteBGX_LO(struct GBAVideoGLBackground* bg, uint16_t value) {
1194	bg->refx = (bg->refx & 0xFFFF0000) | value;
1195	bg->affine[0].sx = bg->refx;
1196}
1197
1198static void GBAVideoGLRendererWriteBGX_HI(struct GBAVideoGLBackground* bg, uint16_t value) {
1199	bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
1200	bg->refx <<= 4;
1201	bg->refx >>= 4;
1202	bg->affine[0].sx = bg->refx;
1203}
1204
1205static void GBAVideoGLRendererWriteBGY_LO(struct GBAVideoGLBackground* bg, uint16_t value) {
1206	bg->refy = (bg->refy & 0xFFFF0000) | value;
1207	bg->affine[0].sy = bg->refy;
1208}
1209
1210static void GBAVideoGLRendererWriteBGY_HI(struct GBAVideoGLBackground* bg, uint16_t value) {
1211	bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
1212	bg->refy <<= 4;
1213	bg->refy >>= 4;
1214	bg->affine[0].sy = bg->refy;
1215}
1216
1217static void GBAVideoGLRendererWriteBLDCNT(struct GBAVideoGLRenderer* renderer, uint16_t value) {
1218	renderer->bg[0].target1 = GBARegisterBLDCNTGetTarget1Bg0(value);
1219	renderer->bg[1].target1 = GBARegisterBLDCNTGetTarget1Bg1(value);
1220	renderer->bg[2].target1 = GBARegisterBLDCNTGetTarget1Bg2(value);
1221	renderer->bg[3].target1 = GBARegisterBLDCNTGetTarget1Bg3(value);
1222	renderer->bg[0].target2 = GBARegisterBLDCNTGetTarget2Bg0(value);
1223	renderer->bg[1].target2 = GBARegisterBLDCNTGetTarget2Bg1(value);
1224	renderer->bg[2].target2 = GBARegisterBLDCNTGetTarget2Bg2(value);
1225	renderer->bg[3].target2 = GBARegisterBLDCNTGetTarget2Bg3(value);
1226
1227	renderer->blendEffect = GBARegisterBLDCNTGetEffect(value);
1228	renderer->target1Obj = GBARegisterBLDCNTGetTarget1Obj(value);
1229	renderer->target1Bd = GBARegisterBLDCNTGetTarget1Bd(value);
1230	renderer->target2Obj = GBARegisterBLDCNTGetTarget2Obj(value);
1231	renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value);
1232}
1233
1234void _finalizeLayers(struct GBAVideoGLRenderer* renderer) {
1235	const GLuint* uniforms = renderer->finalizeShader.uniforms;
1236	glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_OUTPUT]);
1237	glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
1238	glScissor(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
1239	glUseProgram(renderer->finalizeShader.program);
1240	glBindVertexArray(renderer->finalizeShader.vao);
1241	glActiveTexture(GL_TEXTURE0);
1242	glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_WINDOW]);
1243	glActiveTexture(GL_TEXTURE0 + 1);
1244	glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_OBJ_COLOR]);
1245	glActiveTexture(GL_TEXTURE0 + 2);
1246	glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_OBJ_FLAGS]);
1247	glActiveTexture(GL_TEXTURE0 + 3);
1248	glBindTexture(GL_TEXTURE_2D, renderer->bg[0].tex);
1249	glActiveTexture(GL_TEXTURE0 + 4);
1250	glBindTexture(GL_TEXTURE_2D, renderer->bg[0].flags);
1251	glActiveTexture(GL_TEXTURE0 + 5);
1252	glBindTexture(GL_TEXTURE_2D, renderer->bg[1].tex);
1253	glActiveTexture(GL_TEXTURE0 + 6);
1254	glBindTexture(GL_TEXTURE_2D, renderer->bg[1].flags);
1255	glActiveTexture(GL_TEXTURE0 + 7);
1256	glBindTexture(GL_TEXTURE_2D, renderer->bg[2].tex);
1257	glActiveTexture(GL_TEXTURE0 + 8);
1258	glBindTexture(GL_TEXTURE_2D, renderer->bg[2].flags);
1259	glActiveTexture(GL_TEXTURE0 + 9);
1260	glBindTexture(GL_TEXTURE_2D, renderer->bg[3].tex);
1261	glActiveTexture(GL_TEXTURE0 + 10);
1262	glBindTexture(GL_TEXTURE_2D, renderer->bg[3].flags);
1263	glActiveTexture(GL_TEXTURE0 + 11);
1264	glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_BACKDROP_COLOR]);
1265	glActiveTexture(GL_TEXTURE0 + 12);
1266	glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_BACKDROP_FLAGS]);
1267
1268	glUniform2i(uniforms[GBA_GL_VS_LOC], GBA_VIDEO_VERTICAL_PIXELS, 0);
1269	glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
1270	glUniform1i(uniforms[GBA_GL_FINALIZE_SCALE], renderer->scale);
1271	glUniform1iv(uniforms[GBA_GL_FINALIZE_LAYERS], 5, (GLint[]) { 3, 5, 7, 9, 1 });
1272	glUniform1iv(uniforms[GBA_GL_FINALIZE_FLAGS], 5, (GLint[]) { 4, 6, 8, 10, 2 });
1273	glUniform1i(uniforms[GBA_GL_FINALIZE_WINDOW], 0);
1274	glUniform1i(uniforms[GBA_GL_FINALIZE_WINDOW], 0);
1275	glUniform1i(uniforms[GBA_GL_FINALIZE_BACKDROP], 11);
1276	glUniform1i(uniforms[GBA_GL_FINALIZE_BACKDROPFLAGS], 12);
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	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1357	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1358}
1359
1360void _prepareBackground(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, const GLuint* uniforms) {
1361	glBindFramebuffer(GL_FRAMEBUFFER, background->fbo);
1362	glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
1363	glActiveTexture(GL_TEXTURE0);
1364	glBindTexture(GL_TEXTURE_2D, renderer->vramTex);
1365	glActiveTexture(GL_TEXTURE0 + 1);
1366	glBindTexture(GL_TEXTURE_2D, renderer->paletteTex);
1367	glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
1368	glUniform1i(uniforms[GBA_GL_BG_VRAM], 0);
1369	glUniform1i(uniforms[GBA_GL_BG_PALETTE], 1);
1370	if (background->mosaic) {
1371		glUniform2i(uniforms[GBA_GL_BG_MOSAIC], GBAMosaicControlGetBgV(renderer->mosaic) + 1, GBAMosaicControlGetBgH(renderer->mosaic) + 1);
1372	} else {
1373		glUniform2i(uniforms[GBA_GL_BG_MOSAIC], 0, 0);
1374	}
1375	glUniform4i(uniforms[GBA_GL_BG_INFLAGS], (background->priority << 3) + (background->index << 1) + 1,
1376		                                     background->target1 | (background->target2 * 2) | (renderer->blendEffect * 4),
1377		                                     renderer->blda, 0);
1378	glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
1379}
1380
1381void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
1382	int inY = y + background->y;
1383	int yBase = inY & 0xFF;
1384	if (background->size == 2) {
1385		yBase += inY & 0x100;
1386	} else if (background->size == 3) {
1387		yBase += (inY & 0x100) << 1;
1388	}
1389
1390	const struct GBAVideoGLShader* shader = &renderer->bgShader[background->multipalette ? 1 : 0];
1391	const GLuint* uniforms = shader->uniforms;
1392	glScissor(0, y * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->scale);
1393	glUseProgram(shader->program);
1394	glBindVertexArray(shader->vao);
1395	_prepareBackground(renderer, background, uniforms);
1396	glUniform2i(uniforms[GBA_GL_VS_LOC], 1, y);
1397	glUniform1i(uniforms[GBA_GL_BG_SCREENBASE], background->screenBase);
1398	glUniform1i(uniforms[GBA_GL_BG_CHARBASE], background->charBase);
1399	glUniform1i(uniforms[GBA_GL_BG_SIZE], background->size);
1400	glUniform2i(uniforms[GBA_GL_BG_OFFSET], background->x, yBase - y);
1401	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1402	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1403}
1404
1405void _prepareTransform(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, const GLuint* uniforms, int y) {
1406	int reverse = 0;
1407	int forward = 1;
1408	if (renderer->scale > 1) {
1409		switch (y - renderer->firstAffine) {
1410		case 0:
1411		case 1:
1412		case 2:
1413		case 3:
1414			return;
1415		case 4:
1416			forward = 2;
1417			reverse = 4;
1418			break;
1419		case 5:
1420			forward = 2;
1421			reverse = 3;
1422			break;
1423		case 6:
1424			forward = 2;
1425			reverse = 2;
1426			break;
1427		case 7:
1428			forward = 2;
1429			reverse = 1;
1430			break;
1431		}
1432	}
1433
1434	glScissor(0, (y - reverse) * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->scale * forward);
1435	glUniform2i(uniforms[GBA_GL_VS_LOC], forward, y - reverse);
1436	glUniform2f(uniforms[GBA_GL_BG_RANGE], y - 1, 1);
1437	if (renderer->scale > 1) {
1438		glUniform2iv(uniforms[GBA_GL_BG_OFFSET], 4, (GLint[]) {
1439			background->affine[0].sx, background->affine[0].sy,
1440			background->affine[1].sx, background->affine[1].sy,
1441			background->affine[2].sx, background->affine[2].sy,
1442			background->affine[3].sx, background->affine[3].sy,
1443		});
1444		glUniform2iv(uniforms[GBA_GL_BG_TRANSFORM], 4, (GLint[]) {
1445			background->affine[0].dx, background->affine[0].dy,
1446			background->affine[1].dx, background->affine[1].dy,
1447			background->affine[2].dx, background->affine[2].dy,
1448			background->affine[3].dx, background->affine[3].dy,
1449		});
1450	} else {
1451		glUniform2iv(uniforms[GBA_GL_BG_OFFSET], 4, (GLint[]) {
1452			background->affine[0].sx, background->affine[0].sy,
1453			background->affine[0].sx, background->affine[0].sy,
1454			background->affine[0].sx, background->affine[0].sy,
1455			background->affine[0].sx, background->affine[0].sy,
1456		});
1457		glUniform2iv(uniforms[GBA_GL_BG_TRANSFORM], 4, (GLint[]) {
1458			background->affine[0].dx, background->affine[0].dy,
1459			background->affine[0].dx, background->affine[0].dy,
1460			background->affine[0].dx, background->affine[0].dy,
1461			background->affine[0].dx, background->affine[0].dy,
1462		});
1463	}
1464	_prepareBackground(renderer, background, uniforms);
1465}
1466
1467void GBAVideoGLRendererDrawBackgroundMode2(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
1468	const struct GBAVideoGLShader* shader = &renderer->bgShader[background->overflow ? 2 : 3];
1469	const GLuint* uniforms = shader->uniforms;
1470	glUseProgram(shader->program);
1471	glBindVertexArray(shader->vao);
1472	_prepareTransform(renderer, background, uniforms, y);
1473	glUniform1i(uniforms[GBA_GL_BG_SCREENBASE], background->screenBase);
1474	glUniform1i(uniforms[GBA_GL_BG_CHARBASE], background->charBase);
1475	glUniform1i(uniforms[GBA_GL_BG_SIZE], background->size);
1476	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1477	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1478}
1479
1480void GBAVideoGLRendererDrawBackgroundMode3(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
1481	const struct GBAVideoGLShader* shader = &renderer->bgShader[5];
1482	const GLuint* uniforms = shader->uniforms;
1483	glBindFramebuffer(GL_FRAMEBUFFER, background->fbo);
1484	glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
1485	glUseProgram(shader->program);
1486	glBindVertexArray(shader->vao);
1487	_prepareTransform(renderer, background, uniforms, y);
1488	glUniform1i(uniforms[GBA_GL_BG_CHARBASE], 0);
1489	glUniform2i(uniforms[GBA_GL_BG_SIZE], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
1490	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1491	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1492}
1493
1494void GBAVideoGLRendererDrawBackgroundMode4(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
1495	const struct GBAVideoGLShader* shader = &renderer->bgShader[4];
1496	const GLuint* uniforms = shader->uniforms;
1497	glBindFramebuffer(GL_FRAMEBUFFER, background->fbo);
1498	glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
1499	glUseProgram(shader->program);
1500	glBindVertexArray(shader->vao);
1501	_prepareTransform(renderer, background, uniforms, y);
1502	glUniform1i(uniforms[GBA_GL_BG_CHARBASE], GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt) ? 0xA000 : 0);
1503	glUniform2i(uniforms[GBA_GL_BG_SIZE], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
1504	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1505	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1506}
1507
1508void GBAVideoGLRendererDrawBackgroundMode5(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
1509	const struct GBAVideoGLShader* shader = &renderer->bgShader[5];
1510	const GLuint* uniforms = shader->uniforms;
1511	glBindFramebuffer(GL_FRAMEBUFFER, background->fbo);
1512	glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
1513	glUseProgram(shader->program);
1514	glBindVertexArray(shader->vao);
1515	_prepareTransform(renderer, background, uniforms, y);
1516	glUniform1i(uniforms[GBA_GL_BG_CHARBASE], GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt) ? 0x5000 : 0);
1517	glUniform2i(uniforms[GBA_GL_BG_SIZE], 160, 128);
1518	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1519	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1520}
1521
1522static void _scissorWindow(int start, int end, int y, int lines, int scale) {
1523	if (start > end) {
1524		_scissorWindow(start, GBA_VIDEO_HORIZONTAL_PIXELS * scale, y, lines, scale);
1525		_scissorWindow(0, end, y, lines, scale);
1526		return;
1527	}
1528	glScissor(start, y, end - start, lines);
1529	glClear(GL_COLOR_BUFFER_BIT);	
1530}
1531
1532static void _scissorWindowN(struct GBAVideoWindowRegion* region, int y, int scale) {
1533	int sdelta = region[0].start - region[1].start;
1534	int edelta = region[0].end - region[1].end;
1535	int maxDelta = 0;
1536	if (sdelta > maxDelta) {
1537		maxDelta = sdelta;
1538	} else if (-sdelta > maxDelta) {
1539		maxDelta = -sdelta;
1540	}
1541	if (edelta > maxDelta) {
1542		maxDelta = edelta;
1543	} else if (-edelta > maxDelta) {
1544		maxDelta = -edelta;
1545	}
1546	if (!(sdelta | edelta) || maxDelta >= GBA_VIDEO_VERTICAL_PIXELS / 2) {
1547		_scissorWindow(region[0].start * scale, region[0].end * scale, y, scale, scale);
1548	} else {
1549		int i;
1550		for (i = 0; i < scale; ++i) {
1551			int start = region[1].start * scale + sdelta * i;
1552			int end = region[1].end * scale + edelta * i;
1553			_scissorWindow(start, end, y + i, 1, scale);
1554		}
1555	}
1556}
1557
1558static void _clearWindow(GBAWindowControl window, int bldb, int bldy) {
1559	window = ~window & 0x3F;
1560	glClearColor(window / 128.f, bldb / 16.f, bldy / 16.f, 0);
1561}
1562
1563void GBAVideoGLRendererDrawWindow(struct GBAVideoGLRenderer* renderer, int y) {
1564	glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_WINDOW]);
1565	int dispcnt = ((renderer->dispcnt >> 8) & 0x1F) | 0x20;
1566	if (!(renderer->dispcnt & 0xE000)) {
1567		_clearWindow(dispcnt, renderer->bldb, renderer->bldy);
1568		_scissorWindow(0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, y * renderer->scale, renderer->scale, renderer->scale);
1569	} else {
1570		_clearWindow(renderer->winout & dispcnt, renderer->bldb, renderer->bldy);
1571		_scissorWindow(0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, y * renderer->scale, renderer->scale, renderer->scale);
1572		if (GBARegisterDISPCNTIsWin1Enable(renderer->dispcnt) && y >= renderer->winN[1].v.start && y < renderer->winN[1].v.end) {
1573			_clearWindow(renderer->winN[1].control & dispcnt, renderer->bldb, renderer->bldy);
1574			_scissorWindowN(renderer->winN[1].h, y * renderer->scale, renderer->scale);
1575		}
1576		if (GBARegisterDISPCNTIsWin0Enable(renderer->dispcnt) && y >= renderer->winN[0].v.start && y < renderer->winN[0].v.end) {
1577			_clearWindow(renderer->winN[0].control & dispcnt, renderer->bldb, renderer->bldy);
1578			_scissorWindowN(renderer->winN[0].h, y * renderer->scale, renderer->scale);
1579		}
1580	}
1581}
1582
1583#endif