all repos — mgba @ bdb584a39849899db5062f0894058c7783860381

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	"		incoord.x = int(clamp(incoord.x - mod(mosaic.z + incoord.x, mosaic.x), 0, dims.z - 1));\n"
 408	"	} else if (mosaic.x < -1) {\n"
 409	"		incoord.x = dims.z - incoord.x - 1;"
 410	"		incoord.x = clamp(dims.z - int(incoord.x - mod(mosaic.z + incoord.x, -mosaic.x)) - 1, 0, dims.z - 1);\n"
 411	"	}\n"
 412	"	if (mosaic.y > 1) {\n"
 413	"		incoord.y = int(clamp(incoord.y - mod(mosaic.w + incoord.y, mosaic.y), 0, dims.w - 1));\n"
 414	"	}\n"
 415	"	ivec2 coord = ivec2(transform * (incoord - dims.zw / 2) + dims.xy / 2);\n"
 416	"	if ((coord & ~(dims.xy - 1)) != ivec2(0, 0)) {\n"
 417	"		discard;\n"
 418	"	}\n"
 419	"	vec4 pix = renderTile((coord.x >> 3) + (coord.y >> 3) * stride, 16 + localPalette, coord & 7);\n"
 420	"	if (objwin.x > 0) {\n"
 421	"		pix.a = 0;\n"
 422	"	}\n"
 423	"	color = pix;\n"
 424	"	flags = inflags / flagCoeff;\n"
 425	"	window = objwin.yzw;\n"
 426	"}";
 427
 428static const struct GBAVideoGLUniform _uniformsFinalize[] = {
 429	{ "loc", GBA_GL_VS_LOC, },
 430	{ "maxPos", GBA_GL_VS_MAXPOS, },
 431	{ "scale", GBA_GL_FINALIZE_SCALE, },
 432	{ "layers", GBA_GL_FINALIZE_LAYERS, },
 433	{ "flags", GBA_GL_FINALIZE_FLAGS, },
 434	{ "window", GBA_GL_FINALIZE_WINDOW, },
 435	{ "backdrop", GBA_GL_FINALIZE_BACKDROP, },
 436	{ "backdropFlags", GBA_GL_FINALIZE_BACKDROPFLAGS, },
 437	{ 0 }
 438};
 439
 440static const char* const _finalize =
 441	"in vec2 texCoord;\n"
 442	"uniform int scale;\n"
 443	"uniform sampler2D layers[5];\n"
 444	"uniform sampler2D flags[5];\n"
 445	"uniform sampler2D window;\n"
 446	"uniform sampler2D backdrop;\n"
 447	"uniform sampler2D backdropFlags;\n"
 448	FLAG_CONST
 449	"out vec4 color;\n"
 450
 451	"void composite(vec4 pixel, ivec4 flags, inout vec4 topPixel, inout ivec4 topFlags, inout vec4 bottomPixel, inout ivec4 bottomFlags) {\n"
 452	"	if (pixel.a == 0) {\n"
 453	"		return;\n"
 454	"	}\n"
 455	"	if (flags.x >= topFlags.x) {\n"
 456	"		if (flags.x >= bottomFlags.x) {\n"
 457	"			return;\n"
 458	"		}\n"
 459	"		bottomFlags = flags;\n"
 460	"		bottomPixel = pixel;\n"
 461	"	} else {\n"
 462	"		bottomFlags = topFlags;\n"
 463	"		topFlags = flags;\n"
 464	"		bottomPixel = topPixel;\n"
 465	"		topPixel = pixel;\n"
 466	"	}\n"
 467	"}\n"
 468
 469	"void main() {\n"
 470	"	vec4 topPixel = texelFetch(backdrop, ivec2(0, texCoord.y), 0);\n"
 471	"	vec4 bottomPixel = topPixel;\n"
 472	"	ivec4 topFlags = ivec4(texelFetch(backdropFlags, ivec2(0, texCoord.y), 0) * flagCoeff);\n"
 473	"	ivec4 bottomFlags = topFlags;\n"
 474	"	vec4 windowFlags = texelFetch(window, ivec2(texCoord * scale), 0);\n"
 475	"	int layerWindow = int(windowFlags.x * 128);\n"
 476	"	if ((layerWindow & 1) == 0) {\n"
 477	"		vec4 pix = texelFetch(layers[0], ivec2(texCoord * scale), 0);\n"
 478	"		ivec4 inflags = ivec4(texelFetch(flags[0], ivec2(texCoord * scale), 0).xyz * flagCoeff.xyz, 0);\n"
 479	"		composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n"
 480	"	}\n"
 481	"	if ((layerWindow & 2) == 0) {\n"
 482	"		vec4 pix = texelFetch(layers[1], ivec2(texCoord * scale), 0);\n"
 483	"		ivec4 inflags = ivec4(texelFetch(flags[1], ivec2(texCoord * scale), 0).xyz * flagCoeff.xyz, 0);\n"
 484	"		composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n"
 485	"	}\n"
 486	"	if ((layerWindow & 4) == 0) {\n"
 487	"		vec4 pix = texelFetch(layers[2], ivec2(texCoord * scale), 0);\n"
 488	"		ivec4 inflags = ivec4(texelFetch(flags[2], ivec2(texCoord * scale), 0).xyz * flagCoeff.xyz, 0);\n"
 489	"		composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n"
 490	"	}\n"
 491	"	if ((layerWindow & 8) == 0) {\n"
 492	"		vec4 pix = texelFetch(layers[3], ivec2(texCoord * scale), 0);\n"
 493	"		ivec4 inflags = ivec4(texelFetch(flags[3], ivec2(texCoord * scale), 0).xyz * flagCoeff.xyz, 0);\n"
 494	"		composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n"
 495	"	}\n"
 496	"	if ((layerWindow & 16) == 0) {\n"
 497	"		vec4 pix = texelFetch(layers[4], ivec2(texCoord * scale), 0);\n"
 498	"		ivec4 inflags = ivec4(texelFetch(flags[4], ivec2(texCoord * scale), 0) * flagCoeff);\n"
 499	"		composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n"
 500	"	}\n"
 501	"	if ((layerWindow & 32) != 0) {\n"
 502	"		topFlags.y &= ~1;\n"
 503	"	}\n"
 504	"	if (((topFlags.y & 13) == 5 || topFlags.w > 0) && (bottomFlags.y & 2) == 2) {\n"
 505	"		topPixel *= topFlags.z / 16.;\n"
 506	"		topPixel += bottomPixel * windowFlags.y;\n"
 507	"	} else if ((topFlags.y & 13) == 9) {\n"
 508	"		topPixel += (1. - topPixel) * windowFlags.z;\n"
 509	"	} else if ((topFlags.y & 13) == 13) {\n"
 510	"		topPixel -= topPixel * windowFlags.z;\n"
 511	"	}\n"
 512	"	color = topPixel;\n"
 513	"}";
 514
 515static const GLint _vertices[] = {
 516	0, 0,
 517	0, 1,
 518	1, 1,
 519	1, 0,
 520};
 521
 522void GBAVideoGLRendererCreate(struct GBAVideoGLRenderer* renderer) {
 523	renderer->d.init = GBAVideoGLRendererInit;
 524	renderer->d.reset = GBAVideoGLRendererReset;
 525	renderer->d.deinit = GBAVideoGLRendererDeinit;
 526	renderer->d.writeVideoRegister = GBAVideoGLRendererWriteVideoRegister;
 527	renderer->d.writeVRAM = GBAVideoGLRendererWriteVRAM;
 528	renderer->d.writeOAM = GBAVideoGLRendererWriteOAM;
 529	renderer->d.writePalette = GBAVideoGLRendererWritePalette;
 530	renderer->d.drawScanline = GBAVideoGLRendererDrawScanline;
 531	renderer->d.finishFrame = GBAVideoGLRendererFinishFrame;
 532	renderer->d.getPixels = GBAVideoGLRendererGetPixels;
 533	renderer->d.putPixels = GBAVideoGLRendererPutPixels;
 534
 535	renderer->d.disableBG[0] = false;
 536	renderer->d.disableBG[1] = false;
 537	renderer->d.disableBG[2] = false;
 538	renderer->d.disableBG[3] = false;
 539	renderer->d.disableOBJ = false;
 540
 541	renderer->scale = 1;
 542}
 543
 544static void _compileShader(struct GBAVideoGLRenderer* glRenderer, struct GBAVideoGLShader* shader, const char** shaderBuffer, int shaderBufferLines, GLuint vs, const struct GBAVideoGLUniform* uniforms, char* log) {
 545	GLuint program = glCreateProgram();
 546	shader->program = program;
 547
 548	GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
 549	glAttachShader(program, vs);
 550	glAttachShader(program, fs);
 551	glShaderSource(fs, shaderBufferLines, shaderBuffer, 0);
 552	glCompileShader(fs);
 553	glGetShaderInfoLog(fs, 1024, 0, log);
 554	if (log[0]) {
 555		mLOG(GBA_VIDEO, ERROR, "Fragment shader compilation failure: %s", log);
 556	}
 557	glLinkProgram(program);
 558	glGetProgramInfoLog(program, 1024, 0, log);
 559	if (log[0]) {
 560		mLOG(GBA_VIDEO, ERROR, "Program link failure: %s", log);
 561	}
 562	glDeleteShader(fs);
 563#ifndef BUILD_GLES3
 564	glBindFragDataLocation(program, 0, "color");
 565	glBindFragDataLocation(program, 1, "flags");
 566#endif
 567
 568	glGenVertexArrays(1, &shader->vao);
 569	glBindVertexArray(shader->vao);
 570	glBindBuffer(GL_ARRAY_BUFFER, glRenderer->vbo);
 571	GLuint positionLocation = glGetAttribLocation(program, "position");
 572	glVertexAttribPointer(positionLocation, 2, GL_INT, GL_FALSE, 0, NULL);
 573
 574	size_t i;
 575	for (i = 0; uniforms[i].name; ++i) {
 576		shader->uniforms[uniforms[i].type] = glGetUniformLocation(program, uniforms[i].name);
 577	}
 578}
 579
 580static void _deleteShader(struct GBAVideoGLShader* shader) {
 581	glDeleteProgram(shader->program);
 582	glDeleteVertexArrays(1, &shader->vao);
 583}
 584
 585static void _initFramebufferTexture(GLuint tex, GLenum format, GLenum attachment, int scale) {
 586	glBindTexture(GL_TEXTURE_2D, tex);
 587	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 588	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 589	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 590	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 591	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);
 592	glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0);	
 593}
 594
 595void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) {
 596	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 597	glRenderer->temporaryBuffer = NULL;
 598
 599	glGenFramebuffers(GBA_GL_FBO_MAX, glRenderer->fbo);
 600	glGenTextures(GBA_GL_TEX_MAX, glRenderer->layers);
 601
 602	glGenTextures(1, &glRenderer->paletteTex);
 603	glBindTexture(GL_TEXTURE_2D, glRenderer->paletteTex);
 604	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 605	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 606
 607	glGenTextures(1, &glRenderer->vramTex);
 608	glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex);
 609	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 610	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 611	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, 256, 192, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 0);
 612
 613	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OBJ]);
 614	_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_OBJ_COLOR], GL_RGBA, GL_COLOR_ATTACHMENT0, glRenderer->scale);
 615	_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_OBJ_FLAGS], GL_RGBA, GL_COLOR_ATTACHMENT1, glRenderer->scale);
 616	_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_WINDOW], GL_RGBA, GL_COLOR_ATTACHMENT2, glRenderer->scale);
 617
 618	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_BACKDROP]);
 619	_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_BACKDROP_COLOR], GL_RGB, GL_COLOR_ATTACHMENT0, 0);
 620	_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_BACKDROP_FLAGS], GL_RGBA, GL_COLOR_ATTACHMENT1, 0);
 621
 622	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_WINDOW]);
 623	_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_WINDOW], GL_RGB, GL_COLOR_ATTACHMENT0, glRenderer->scale);
 624
 625	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OUTPUT]);
 626	_initFramebufferTexture(glRenderer->outputTex, GL_RGB, GL_COLOR_ATTACHMENT0, glRenderer->scale);
 627
 628	glBindFramebuffer(GL_FRAMEBUFFER, 0);
 629
 630	glGenBuffers(1, &glRenderer->vbo);
 631	glBindBuffer(GL_ARRAY_BUFFER, glRenderer->vbo);
 632	glBufferData(GL_ARRAY_BUFFER, sizeof(_vertices), _vertices, GL_STATIC_DRAW);
 633
 634	int i;
 635	for (i = 0; i < 4; ++i) {
 636		struct GBAVideoGLBackground* bg = &glRenderer->bg[i];
 637		bg->index = i;
 638		bg->enabled = 0;
 639		bg->priority = 0;
 640		bg->charBase = 0;
 641		bg->mosaic = 0;
 642		bg->multipalette = 0;
 643		bg->screenBase = 0;
 644		bg->overflow = 0;
 645		bg->size = 0;
 646		bg->target1 = 0;
 647		bg->target2 = 0;
 648		bg->x = 0;
 649		bg->y = 0;
 650		bg->refx = 0;
 651		bg->refy = 0;
 652		bg->affine[0].dx = 256;
 653		bg->affine[0].dmx = 0;
 654		bg->affine[0].dy = 0;
 655		bg->affine[0].dmy = 256;
 656		bg->affine[0].sx = 0;
 657		bg->affine[0].sy = 0;
 658		glGenFramebuffers(1, &bg->fbo);
 659		glGenTextures(1, &bg->tex);
 660		glGenTextures(1, &bg->flags);
 661		glBindFramebuffer(GL_FRAMEBUFFER, bg->fbo);
 662		_initFramebufferTexture(bg->tex, GL_RGBA, GL_COLOR_ATTACHMENT0, glRenderer->scale);
 663		_initFramebufferTexture(bg->flags, GL_RGB, GL_COLOR_ATTACHMENT1, glRenderer->scale);
 664		glBindFramebuffer(GL_FRAMEBUFFER, 0);
 665	}
 666
 667	char log[1024];
 668	const GLchar* shaderBuffer[8];
 669	shaderBuffer[0] = _gl3Header;
 670
 671	GLuint vs = glCreateShader(GL_VERTEX_SHADER);
 672	shaderBuffer[1] = _vertexShader;
 673	glShaderSource(vs, 2, shaderBuffer, 0);
 674	glCompileShader(vs);
 675	glGetShaderInfoLog(vs, 1024, 0, log);
 676	if (log[0]) {
 677		mLOG(GBA_VIDEO, ERROR, "Vertex shader compilation failure: %s", log);
 678	}
 679
 680	shaderBuffer[1] = _renderMode0;
 681
 682	shaderBuffer[2] = _renderTile16;
 683	_compileShader(glRenderer, &glRenderer->bgShader[0], shaderBuffer, 3, vs, _uniformsMode0, log);
 684
 685	shaderBuffer[2] = _renderTile256;
 686	_compileShader(glRenderer, &glRenderer->bgShader[1], shaderBuffer, 3, vs, _uniformsMode0, log);
 687
 688	shaderBuffer[1] = _renderMode2;
 689	shaderBuffer[2] = _interpolate;
 690
 691	shaderBuffer[3] = _fetchTileOverflow;
 692	_compileShader(glRenderer, &glRenderer->bgShader[2], shaderBuffer, 4, vs, _uniformsMode2, log);
 693
 694	shaderBuffer[3] = _fetchTileNoOverflow;
 695	_compileShader(glRenderer, &glRenderer->bgShader[3], shaderBuffer, 4, vs, _uniformsMode2, log);
 696
 697	shaderBuffer[1] = _renderMode4;
 698	shaderBuffer[2] = _interpolate;
 699	_compileShader(glRenderer, &glRenderer->bgShader[4], shaderBuffer, 3, vs, _uniformsMode4, log);
 700
 701	shaderBuffer[1] = _renderMode35;
 702	shaderBuffer[2] = _interpolate;
 703	_compileShader(glRenderer, &glRenderer->bgShader[5], shaderBuffer, 3, vs, _uniformsMode35, log);
 704
 705	shaderBuffer[1] = _renderObj;
 706
 707	shaderBuffer[2] = _renderTile16;
 708	_compileShader(glRenderer, &glRenderer->objShader[0], shaderBuffer, 3, vs, _uniformsObj, log);
 709#ifndef BUILD_GLES3
 710	glBindFragDataLocation(glRenderer->objShader[0].program, 2, "window");
 711#endif
 712
 713	shaderBuffer[2] = _renderTile256;
 714	_compileShader(glRenderer, &glRenderer->objShader[1], shaderBuffer, 3, vs, _uniformsObj, log);
 715#ifndef BUILD_GLES3
 716	glBindFragDataLocation(glRenderer->objShader[1].program, 2, "window");
 717#endif
 718
 719	shaderBuffer[1] = _finalize;
 720	_compileShader(glRenderer, &glRenderer->finalizeShader, shaderBuffer, 2, vs, _uniformsFinalize, log);
 721
 722	glBindVertexArray(0);
 723	glDeleteShader(vs);
 724
 725	GBAVideoGLRendererReset(renderer);
 726}
 727
 728void GBAVideoGLRendererDeinit(struct GBAVideoRenderer* renderer) {
 729	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 730	if (glRenderer->temporaryBuffer) {
 731		mappedMemoryFree(glRenderer->temporaryBuffer, GBA_VIDEO_HORIZONTAL_PIXELS * GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale * glRenderer->scale);
 732	}
 733	glDeleteFramebuffers(GBA_GL_FBO_MAX, glRenderer->fbo);
 734	glDeleteTextures(GBA_GL_TEX_MAX, glRenderer->layers);
 735	glDeleteTextures(1, &glRenderer->paletteTex);
 736	glDeleteTextures(1, &glRenderer->vramTex);
 737
 738	_deleteShader(&glRenderer->bgShader[0]);
 739	_deleteShader(&glRenderer->bgShader[1]);
 740	_deleteShader(&glRenderer->bgShader[2]);
 741	_deleteShader(&glRenderer->bgShader[3]);
 742	_deleteShader(&glRenderer->objShader[0]);
 743	_deleteShader(&glRenderer->objShader[1]);
 744	_deleteShader(&glRenderer->finalizeShader);
 745
 746	int i;
 747	for (i = 0; i < 4; ++i) {
 748		struct GBAVideoGLBackground* bg = &glRenderer->bg[i];
 749		glDeleteFramebuffers(1, &bg->fbo);
 750		glDeleteTextures(1, &bg->tex);
 751		glDeleteTextures(1, &bg->flags);
 752	}
 753}
 754
 755void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer) {
 756	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 757
 758	glRenderer->paletteDirty = true;
 759	glRenderer->vramDirty = 0xFFFFFF;
 760	glRenderer->firstAffine = -1;
 761	glRenderer->dispcnt = 0;
 762	glRenderer->mosaic = 0;
 763}
 764
 765void GBAVideoGLRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
 766	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 767	glRenderer->vramDirty |= 1 << (address >> 12);
 768}
 769
 770void GBAVideoGLRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
 771	UNUSED(oam);
 772	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 773	glRenderer->oamDirty = true;
 774}
 775
 776void GBAVideoGLRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
 777	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 778#ifdef BUILD_GLES3
 779	glRenderer->shadowPalette[address >> 1] = (value & 0x3F) | ((value & 0x7FE0) << 1);
 780#else
 781	UNUSED(address);
 782	UNUSED(value);
 783#endif
 784	glRenderer->paletteDirty = true;
 785}
 786
 787uint16_t GBAVideoGLRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
 788	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 789	if (renderer->cache) {
 790		GBAVideoCacheWriteVideoRegister(renderer->cache, address, value);
 791	}
 792
 793	switch (address) {
 794	case REG_DISPCNT:
 795		value &= 0xFFF7;
 796		glRenderer->dispcnt = value;
 797		GBAVideoGLRendererUpdateDISPCNT(glRenderer);
 798		break;
 799	case REG_BG0CNT:
 800		value &= 0xDFFF;
 801		GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[0], value);
 802		break;
 803	case REG_BG1CNT:
 804		value &= 0xDFFF;
 805		GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[1], value);
 806		break;
 807	case REG_BG2CNT:
 808		value &= 0xFFFF;
 809		GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[2], value);
 810		break;
 811	case REG_BG3CNT:
 812		value &= 0xFFFF;
 813		GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[3], value);
 814		break;
 815	case REG_BG0HOFS:
 816		value &= 0x01FF;
 817		glRenderer->bg[0].x = value;
 818		break;
 819	case REG_BG0VOFS:
 820		value &= 0x01FF;
 821		glRenderer->bg[0].y = value;
 822		break;
 823	case REG_BG1HOFS:
 824		value &= 0x01FF;
 825		glRenderer->bg[1].x = value;
 826		break;
 827	case REG_BG1VOFS:
 828		value &= 0x01FF;
 829		glRenderer->bg[1].y = value;
 830		break;
 831	case REG_BG2HOFS:
 832		value &= 0x01FF;
 833		glRenderer->bg[2].x = value;
 834		break;
 835	case REG_BG2VOFS:
 836		value &= 0x01FF;
 837		glRenderer->bg[2].y = value;
 838		break;
 839	case REG_BG3HOFS:
 840		value &= 0x01FF;
 841		glRenderer->bg[3].x = value;
 842		break;
 843	case REG_BG3VOFS:
 844		value &= 0x01FF;
 845		glRenderer->bg[3].y = value;
 846		break;
 847	case REG_BG2PA:
 848		glRenderer->bg[2].affine[0].dx = value;
 849		break;
 850	case REG_BG2PB:
 851		glRenderer->bg[2].affine[0].dmx = value;
 852		break;
 853	case REG_BG2PC:
 854		glRenderer->bg[2].affine[0].dy = value;
 855		break;
 856	case REG_BG2PD:
 857		glRenderer->bg[2].affine[0].dmy = value;
 858		break;
 859	case REG_BG2X_LO:
 860		GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[2], value);
 861		break;
 862	case REG_BG2X_HI:
 863		GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[2], value);
 864		break;
 865	case REG_BG2Y_LO:
 866		GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[2], value);
 867		break;
 868	case REG_BG2Y_HI:
 869		GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[2], value);
 870		break;
 871	case REG_BG3PA:
 872		glRenderer->bg[3].affine[0].dx = value;
 873		break;
 874	case REG_BG3PB:
 875		glRenderer->bg[3].affine[0].dmx = value;
 876		break;
 877	case REG_BG3PC:
 878		glRenderer->bg[3].affine[0].dy = value;
 879		break;
 880	case REG_BG3PD:
 881		glRenderer->bg[3].affine[0].dmy = value;
 882		break;
 883	case REG_BG3X_LO:
 884		GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[3], value);
 885		break;
 886	case REG_BG3X_HI:
 887		GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[3], value);
 888		break;
 889	case REG_BG3Y_LO:
 890		GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[3], value);
 891		break;
 892	case REG_BG3Y_HI:
 893		GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[3], value);
 894		break;
 895	case REG_BLDCNT:
 896		GBAVideoGLRendererWriteBLDCNT(glRenderer, value);
 897		value &= 0x3FFF;
 898		break;
 899	case REG_BLDALPHA:
 900		glRenderer->blda = value & 0x1F;
 901		if (glRenderer->blda > 0x10) {
 902			glRenderer->blda = 0x10;
 903		}
 904		glRenderer->bldb = (value >> 8) & 0x1F;
 905		if (glRenderer->bldb > 0x10) {
 906			glRenderer->bldb = 0x10;
 907		}
 908		value &= 0x1F1F;
 909		break;
 910	case REG_BLDY:
 911		value &= 0x1F;
 912		if (value > 0x10) {
 913			value = 0x10;
 914		}
 915		glRenderer->bldy = value;
 916		break;
 917	case REG_WIN0H:
 918		glRenderer->winN[0].h[0].end = value;
 919		glRenderer->winN[0].h[0].start = value >> 8;
 920		if (glRenderer->winN[0].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[0].h[0].start > glRenderer->winN[0].h[0].end) {
 921			glRenderer->winN[0].h[0].start = 0;
 922		}
 923		if (glRenderer->winN[0].h[0].end > GBA_VIDEO_HORIZONTAL_PIXELS) {
 924			glRenderer->winN[0].h[0].end = GBA_VIDEO_HORIZONTAL_PIXELS;
 925			if (glRenderer->winN[0].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS) {
 926				glRenderer->winN[0].h[0].start = GBA_VIDEO_HORIZONTAL_PIXELS;
 927			}
 928		}
 929		break;
 930	case REG_WIN1H:
 931		glRenderer->winN[1].h[0].end = value;
 932		glRenderer->winN[1].h[0].start = value >> 8;
 933		if (glRenderer->winN[1].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[1].h[0].start > glRenderer->winN[1].h[0].end) {
 934			glRenderer->winN[1].h[0].start = 0;
 935		}
 936		if (glRenderer->winN[1].h[0].end > GBA_VIDEO_HORIZONTAL_PIXELS) {
 937			glRenderer->winN[1].h[0].end = GBA_VIDEO_HORIZONTAL_PIXELS;
 938			if (glRenderer->winN[1].h[0].start > GBA_VIDEO_HORIZONTAL_PIXELS) {
 939				glRenderer->winN[1].h[0].start = GBA_VIDEO_HORIZONTAL_PIXELS;
 940			}
 941		}
 942		break;
 943	case REG_WIN0V:
 944		glRenderer->winN[0].v.end = value;
 945		glRenderer->winN[0].v.start = value >> 8;
 946		if (glRenderer->winN[0].v.start > GBA_VIDEO_VERTICAL_PIXELS && glRenderer->winN[0].v.start > glRenderer->winN[0].v.end) {
 947			glRenderer->winN[0].v.start = 0;
 948		}
 949		if (glRenderer->winN[0].v.end > GBA_VIDEO_VERTICAL_PIXELS) {
 950			glRenderer->winN[0].v.end = GBA_VIDEO_VERTICAL_PIXELS;
 951			if (glRenderer->winN[0].v.start > GBA_VIDEO_VERTICAL_PIXELS) {
 952				glRenderer->winN[0].v.start = GBA_VIDEO_VERTICAL_PIXELS;
 953			}
 954		}
 955		break;
 956	case REG_WIN1V:
 957		glRenderer->winN[1].v.end = value;
 958		glRenderer->winN[1].v.start = value >> 8;
 959		if (glRenderer->winN[1].v.start > GBA_VIDEO_VERTICAL_PIXELS && glRenderer->winN[1].v.start > glRenderer->winN[1].v.end) {
 960			glRenderer->winN[1].v.start = 0;
 961		}
 962		if (glRenderer->winN[1].v.end > GBA_VIDEO_VERTICAL_PIXELS) {
 963			glRenderer->winN[1].v.end = GBA_VIDEO_VERTICAL_PIXELS;
 964			if (glRenderer->winN[1].v.start > GBA_VIDEO_VERTICAL_PIXELS) {
 965				glRenderer->winN[1].v.start = GBA_VIDEO_VERTICAL_PIXELS;
 966			}
 967		}
 968		break;
 969	case REG_WININ:
 970		value &= 0x3F3F;
 971		glRenderer->winN[0].control = value;
 972		glRenderer->winN[1].control = value >> 8;
 973		break;
 974	case REG_WINOUT:
 975		value &= 0x3F3F;
 976		glRenderer->winout = value;
 977		glRenderer->objwin = value >> 8;
 978		break;
 979	case REG_MOSAIC:
 980		glRenderer->mosaic = value;
 981		break;
 982	default:
 983		break;
 984	}
 985	return value;
 986}
 987
 988void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
 989	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
 990	if (glRenderer->paletteDirty) {
 991		glBindTexture(GL_TEXTURE_2D, glRenderer->paletteTex);
 992#ifdef BUILD_GLES3
 993		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, 16, 32, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_6_5, glRenderer->shadowPalette);
 994#else
 995		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, 16, 32, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, glRenderer->d.palette);
 996#endif
 997		glRenderer->paletteDirty = false;
 998	}
 999	int i;
1000	int first = -1;
1001	glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex);
1002	for (i = 0; i < 25; ++i) {
1003		if (!(glRenderer->vramDirty & (1 << i))) {
1004			if (first >= 0) {
1005				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]);
1006				first = -1;
1007			}
1008		} else if (first < 0) {
1009			first = i;
1010		}
1011	}
1012	glRenderer->vramDirty = 0;
1013
1014	if (y == 0) {
1015		memcpy(&glRenderer->winN[0].h[1], &glRenderer->winN[0].h[0], sizeof(struct GBAVideoWindowRegion));
1016		memcpy(&glRenderer->winN[1].h[1], &glRenderer->winN[1].h[0], sizeof(struct GBAVideoWindowRegion));
1017
1018		glDisable(GL_SCISSOR_TEST);		
1019		glClearColor(0, 0, 0, 0);
1020		glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OBJ]);
1021		glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
1022		glClear(GL_COLOR_BUFFER_BIT);
1023
1024		for (i = 0; i < 4; ++i) {
1025			glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->bg[i].fbo);
1026			glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
1027			glClear(GL_COLOR_BUFFER_BIT);
1028		}
1029	}
1030	glEnable(GL_SCISSOR_TEST);
1031
1032	uint32_t backdrop = M_RGB5_TO_RGB8(glRenderer->d.palette[0]);
1033	glViewport(0, 0, 1, GBA_VIDEO_VERTICAL_PIXELS);
1034	glScissor(0, y, 1, 1);
1035	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_BACKDROP]);
1036	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1037	glClearColor(((backdrop >> 16) & 0xFF) / 256., ((backdrop >> 8) & 0xFF) / 256., (backdrop & 0xFF) / 256., 0.f);
1038	glClear(GL_COLOR_BUFFER_BIT);
1039	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT1 });
1040	glClearColor(1, (glRenderer->target1Bd | (glRenderer->target2Bd * 2) | (glRenderer->blendEffect * 4)) / 32.f, glRenderer->blda / 16.f, 0);
1041	glClear(GL_COLOR_BUFFER_BIT);
1042	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1043
1044	if (GBARegisterDISPCNTGetMode(glRenderer->dispcnt) != 0) {
1045		if (glRenderer->firstAffine < 0) {
1046			memcpy(&glRenderer->bg[2].affine[3], &glRenderer->bg[2].affine[0], sizeof(struct GBAVideoGLAffine));
1047			memcpy(&glRenderer->bg[3].affine[3], &glRenderer->bg[3].affine[0], sizeof(struct GBAVideoGLAffine));
1048			memcpy(&glRenderer->bg[2].affine[2], &glRenderer->bg[2].affine[0], sizeof(struct GBAVideoGLAffine));
1049			memcpy(&glRenderer->bg[3].affine[2], &glRenderer->bg[3].affine[0], sizeof(struct GBAVideoGLAffine));
1050			memcpy(&glRenderer->bg[2].affine[1], &glRenderer->bg[2].affine[0], sizeof(struct GBAVideoGLAffine));
1051			memcpy(&glRenderer->bg[3].affine[1], &glRenderer->bg[3].affine[0], sizeof(struct GBAVideoGLAffine));
1052			glRenderer->firstAffine = y;
1053		}
1054	} else {
1055		glRenderer->firstAffine = -1;
1056	}
1057
1058	GBAVideoGLRendererDrawWindow(glRenderer, y);
1059	if (GBARegisterDISPCNTIsObjEnable(glRenderer->dispcnt) && !glRenderer->d.disableOBJ) {
1060		if (glRenderer->oamDirty) {
1061			glRenderer->oamMax = GBAVideoRendererCleanOAM(glRenderer->d.oam->obj, glRenderer->sprites, 0);
1062			glRenderer->oamDirty = false;
1063		}
1064		int i;
1065		for (i = glRenderer->oamMax; i--;) {
1066			struct GBAVideoRendererSprite* sprite = &glRenderer->sprites[i];
1067			if ((y < sprite->y && (sprite->endY - 256 < 0 || y >= sprite->endY - 256)) || y >= sprite->endY) {
1068				continue;
1069			}
1070
1071			GBAVideoGLRendererDrawSprite(glRenderer, &sprite->obj, y, sprite->y);
1072		}
1073	}
1074
1075	if (TEST_LAYER_ENABLED(0) && GBARegisterDISPCNTGetMode(glRenderer->dispcnt) < 2) {
1076		GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[0], y);
1077	}
1078	if (TEST_LAYER_ENABLED(1) && GBARegisterDISPCNTGetMode(glRenderer->dispcnt) < 2) {
1079		GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[1], y);
1080	}
1081	if (TEST_LAYER_ENABLED(2)) {
1082		switch (GBARegisterDISPCNTGetMode(glRenderer->dispcnt)) {
1083		case 0:
1084			GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[2], y);
1085			break;
1086		case 1:
1087		case 2:
1088			GBAVideoGLRendererDrawBackgroundMode2(glRenderer, &glRenderer->bg[2], y);
1089			break;
1090		case 3:
1091			GBAVideoGLRendererDrawBackgroundMode3(glRenderer, &glRenderer->bg[2], y);
1092			break;
1093		case 4:
1094			GBAVideoGLRendererDrawBackgroundMode4(glRenderer, &glRenderer->bg[2], y);
1095			break;
1096		case 5:
1097			GBAVideoGLRendererDrawBackgroundMode5(glRenderer, &glRenderer->bg[2], y);
1098			break;
1099		}
1100	}
1101	if (TEST_LAYER_ENABLED(3)) {
1102		switch (GBARegisterDISPCNTGetMode(glRenderer->dispcnt)) {
1103		case 0:
1104			GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[3], y);
1105			break;
1106		case 2:
1107			GBAVideoGLRendererDrawBackgroundMode2(glRenderer, &glRenderer->bg[3], y);
1108			break;
1109		}
1110	}
1111
1112	if (GBARegisterDISPCNTGetMode(glRenderer->dispcnt) != 0) {
1113		memcpy(&glRenderer->bg[2].affine[3], &glRenderer->bg[2].affine[2], sizeof(struct GBAVideoGLAffine));
1114		memcpy(&glRenderer->bg[3].affine[3], &glRenderer->bg[3].affine[2], sizeof(struct GBAVideoGLAffine));
1115		memcpy(&glRenderer->bg[2].affine[2], &glRenderer->bg[2].affine[1], sizeof(struct GBAVideoGLAffine));
1116		memcpy(&glRenderer->bg[3].affine[2], &glRenderer->bg[3].affine[1], sizeof(struct GBAVideoGLAffine));
1117		memcpy(&glRenderer->bg[2].affine[1], &glRenderer->bg[2].affine[0], sizeof(struct GBAVideoGLAffine));
1118		memcpy(&glRenderer->bg[3].affine[1], &glRenderer->bg[3].affine[0], sizeof(struct GBAVideoGLAffine));
1119
1120		glRenderer->bg[2].affine[0].sx += glRenderer->bg[2].affine[0].dmx;
1121		glRenderer->bg[2].affine[0].sy += glRenderer->bg[2].affine[0].dmy;
1122		glRenderer->bg[3].affine[0].sx += glRenderer->bg[3].affine[0].dmx;
1123		glRenderer->bg[3].affine[0].sy += glRenderer->bg[3].affine[0].dmy;
1124	}
1125	memcpy(&glRenderer->winN[0].h[1], &glRenderer->winN[0].h[0], sizeof(struct GBAVideoWindowRegion));
1126	memcpy(&glRenderer->winN[1].h[1], &glRenderer->winN[1].h[0], sizeof(struct GBAVideoWindowRegion));
1127}
1128
1129void GBAVideoGLRendererFinishFrame(struct GBAVideoRenderer* renderer) {
1130	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
1131	_finalizeLayers(glRenderer);
1132	glRenderer->firstAffine = -1;
1133	glRenderer->bg[2].affine[0].sx = glRenderer->bg[2].refx;
1134	glRenderer->bg[2].affine[0].sy = glRenderer->bg[2].refy;
1135	glRenderer->bg[3].affine[0].sx = glRenderer->bg[3].refx;
1136	glRenderer->bg[3].affine[0].sy = glRenderer->bg[3].refy;
1137	glFlush();
1138}
1139
1140void GBAVideoGLRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
1141	struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
1142	*stride = GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale;
1143	if (!glRenderer->temporaryBuffer) {
1144		glRenderer->temporaryBuffer = anonymousMemoryMap(GBA_VIDEO_HORIZONTAL_PIXELS * GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale * glRenderer->scale * BYTES_PER_PIXEL);
1145	}
1146	glFinish();
1147	glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OUTPUT]);
1148	glPixelStorei(GL_PACK_ROW_LENGTH, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale);
1149	glPixelStorei(GL_PACK_ALIGNMENT, 1);
1150	glReadPixels(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale, GL_RGBA, GL_UNSIGNED_BYTE, (void*) glRenderer->temporaryBuffer);
1151	*pixels = glRenderer->temporaryBuffer;
1152}
1153
1154void GBAVideoGLRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
1155
1156}
1157
1158static void _enableBg(struct GBAVideoGLRenderer* renderer, int bg, bool active) {
1159	int wasActive = renderer->bg[bg].enabled;
1160	if (!active) {
1161		renderer->bg[bg].enabled = 0;
1162	} else if (!wasActive && active) {
1163		/*if (renderer->nextY == 0 || GBARegisterDISPCNTGetMode(renderer->dispcnt) > 2) {
1164			// TODO: Investigate in more depth how switching background works in different modes
1165			renderer->bg[bg].enabled = 4;
1166		} else {
1167			renderer->bg[bg].enabled = 1;
1168		}*/
1169		renderer->bg[bg].enabled = 4;
1170	}
1171}
1172
1173static void GBAVideoGLRendererUpdateDISPCNT(struct GBAVideoGLRenderer* renderer) {
1174	_enableBg(renderer, 0, GBARegisterDISPCNTGetBg0Enable(renderer->dispcnt));
1175	_enableBg(renderer, 1, GBARegisterDISPCNTGetBg1Enable(renderer->dispcnt));
1176	_enableBg(renderer, 2, GBARegisterDISPCNTGetBg2Enable(renderer->dispcnt));
1177	_enableBg(renderer, 3, GBARegisterDISPCNTGetBg3Enable(renderer->dispcnt));
1178}
1179
1180static void GBAVideoGLRendererWriteBGCNT(struct GBAVideoGLBackground* bg, uint16_t value) {
1181	bg->priority = GBARegisterBGCNTGetPriority(value);
1182	bg->charBase = GBARegisterBGCNTGetCharBase(value) << 13;
1183	bg->mosaic = GBARegisterBGCNTGetMosaic(value);
1184	bg->multipalette = GBARegisterBGCNTGet256Color(value);
1185	bg->screenBase = GBARegisterBGCNTGetScreenBase(value) << 10;
1186	bg->overflow = GBARegisterBGCNTGetOverflow(value);
1187	bg->size = GBARegisterBGCNTGetSize(value);
1188}
1189
1190static void GBAVideoGLRendererWriteBGX_LO(struct GBAVideoGLBackground* bg, uint16_t value) {
1191	bg->refx = (bg->refx & 0xFFFF0000) | value;
1192	bg->affine[0].sx = bg->refx;
1193}
1194
1195static void GBAVideoGLRendererWriteBGX_HI(struct GBAVideoGLBackground* bg, uint16_t value) {
1196	bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
1197	bg->refx <<= 4;
1198	bg->refx >>= 4;
1199	bg->affine[0].sx = bg->refx;
1200}
1201
1202static void GBAVideoGLRendererWriteBGY_LO(struct GBAVideoGLBackground* bg, uint16_t value) {
1203	bg->refy = (bg->refy & 0xFFFF0000) | value;
1204	bg->affine[0].sy = bg->refy;
1205}
1206
1207static void GBAVideoGLRendererWriteBGY_HI(struct GBAVideoGLBackground* bg, uint16_t value) {
1208	bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
1209	bg->refy <<= 4;
1210	bg->refy >>= 4;
1211	bg->affine[0].sy = bg->refy;
1212}
1213
1214static void GBAVideoGLRendererWriteBLDCNT(struct GBAVideoGLRenderer* renderer, uint16_t value) {
1215	renderer->bg[0].target1 = GBARegisterBLDCNTGetTarget1Bg0(value);
1216	renderer->bg[1].target1 = GBARegisterBLDCNTGetTarget1Bg1(value);
1217	renderer->bg[2].target1 = GBARegisterBLDCNTGetTarget1Bg2(value);
1218	renderer->bg[3].target1 = GBARegisterBLDCNTGetTarget1Bg3(value);
1219	renderer->bg[0].target2 = GBARegisterBLDCNTGetTarget2Bg0(value);
1220	renderer->bg[1].target2 = GBARegisterBLDCNTGetTarget2Bg1(value);
1221	renderer->bg[2].target2 = GBARegisterBLDCNTGetTarget2Bg2(value);
1222	renderer->bg[3].target2 = GBARegisterBLDCNTGetTarget2Bg3(value);
1223
1224	renderer->blendEffect = GBARegisterBLDCNTGetEffect(value);
1225	renderer->target1Obj = GBARegisterBLDCNTGetTarget1Obj(value);
1226	renderer->target1Bd = GBARegisterBLDCNTGetTarget1Bd(value);
1227	renderer->target2Obj = GBARegisterBLDCNTGetTarget2Obj(value);
1228	renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value);
1229}
1230
1231void _finalizeLayers(struct GBAVideoGLRenderer* renderer) {
1232	const GLuint* uniforms = renderer->finalizeShader.uniforms;
1233	glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_OUTPUT]);
1234	glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
1235	glScissor(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
1236	glUseProgram(renderer->finalizeShader.program);
1237	glBindVertexArray(renderer->finalizeShader.vao);
1238	glActiveTexture(GL_TEXTURE0);
1239	glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_WINDOW]);
1240	glActiveTexture(GL_TEXTURE0 + 1);
1241	glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_OBJ_COLOR]);
1242	glActiveTexture(GL_TEXTURE0 + 2);
1243	glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_OBJ_FLAGS]);
1244	glActiveTexture(GL_TEXTURE0 + 3);
1245	glBindTexture(GL_TEXTURE_2D, renderer->bg[0].tex);
1246	glActiveTexture(GL_TEXTURE0 + 4);
1247	glBindTexture(GL_TEXTURE_2D, renderer->bg[0].flags);
1248	glActiveTexture(GL_TEXTURE0 + 5);
1249	glBindTexture(GL_TEXTURE_2D, renderer->bg[1].tex);
1250	glActiveTexture(GL_TEXTURE0 + 6);
1251	glBindTexture(GL_TEXTURE_2D, renderer->bg[1].flags);
1252	glActiveTexture(GL_TEXTURE0 + 7);
1253	glBindTexture(GL_TEXTURE_2D, renderer->bg[2].tex);
1254	glActiveTexture(GL_TEXTURE0 + 8);
1255	glBindTexture(GL_TEXTURE_2D, renderer->bg[2].flags);
1256	glActiveTexture(GL_TEXTURE0 + 9);
1257	glBindTexture(GL_TEXTURE_2D, renderer->bg[3].tex);
1258	glActiveTexture(GL_TEXTURE0 + 10);
1259	glBindTexture(GL_TEXTURE_2D, renderer->bg[3].flags);
1260	glActiveTexture(GL_TEXTURE0 + 11);
1261	glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_BACKDROP_COLOR]);
1262	glActiveTexture(GL_TEXTURE0 + 12);
1263	glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_BACKDROP_FLAGS]);
1264
1265	glUniform2i(uniforms[GBA_GL_VS_LOC], GBA_VIDEO_VERTICAL_PIXELS, 0);
1266	glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
1267	glUniform1i(uniforms[GBA_GL_FINALIZE_SCALE], renderer->scale);
1268	glUniform1iv(uniforms[GBA_GL_FINALIZE_LAYERS], 5, (GLint[]) { 3, 5, 7, 9, 1 });
1269	glUniform1iv(uniforms[GBA_GL_FINALIZE_FLAGS], 5, (GLint[]) { 4, 6, 8, 10, 2 });
1270	glUniform1i(uniforms[GBA_GL_FINALIZE_WINDOW], 0);
1271	glUniform1i(uniforms[GBA_GL_FINALIZE_WINDOW], 0);
1272	glUniform1i(uniforms[GBA_GL_FINALIZE_BACKDROP], 11);
1273	glUniform1i(uniforms[GBA_GL_FINALIZE_BACKDROPFLAGS], 12);
1274	glEnableVertexAttribArray(0);
1275	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1276	glBindFramebuffer(GL_FRAMEBUFFER, 0);
1277}
1278
1279void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GBAObj* sprite, int y, int spriteY) {
1280	int width = GBAVideoObjSizes[GBAObjAttributesAGetShape(sprite->a) * 4 + GBAObjAttributesBGetSize(sprite->b)][0];
1281	int height = GBAVideoObjSizes[GBAObjAttributesAGetShape(sprite->a) * 4 + GBAObjAttributesBGetSize(sprite->b)][1];
1282	int32_t x = (uint32_t) GBAObjAttributesBGetX(sprite->b) << 23;
1283	x >>= 23;
1284
1285	int align = GBAObjAttributesAIs256Color(sprite->a) && !GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt);
1286	unsigned charBase = (BASE_TILE >> 1) + (GBAObjAttributesCGetTile(sprite->c) & ~align) * 0x10;
1287	int stride = GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? (width >> 3) : (0x20 >> GBAObjAttributesAGet256Color(sprite->a));
1288
1289	if (spriteY + height >= 256) {
1290		spriteY -= 256;
1291	}
1292
1293	if (!GBAObjAttributesAIsTransformed(sprite->a) && GBAObjAttributesBIsVFlip(sprite->b)) {
1294		spriteY = (y - height) + (y - spriteY) + 1;
1295	}
1296
1297	int totalWidth = width;
1298	int totalHeight = height;
1299	if (GBAObjAttributesAIsTransformed(sprite->a) && GBAObjAttributesAIsDoubleSize(sprite->a)) {
1300		totalWidth <<= 1;
1301		totalHeight <<= 1;
1302	}
1303
1304	const struct GBAVideoGLShader* shader = &renderer->objShader[GBAObjAttributesAGet256Color(sprite->a)];
1305	const GLuint* uniforms = shader->uniforms;
1306	glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_OBJ]);
1307	glViewport(x * renderer->scale, spriteY * renderer->scale, totalWidth * renderer->scale, totalHeight * renderer->scale);
1308	glScissor(x * renderer->scale, y * renderer->scale, totalWidth * renderer->scale, renderer->scale);
1309	glUseProgram(shader->program);
1310	glBindVertexArray(shader->vao);
1311	glActiveTexture(GL_TEXTURE0);
1312	glBindTexture(GL_TEXTURE_2D, renderer->vramTex);
1313	glActiveTexture(GL_TEXTURE0 + 1);
1314	glBindTexture(GL_TEXTURE_2D, renderer->paletteTex);
1315	glUniform2i(uniforms[GBA_GL_VS_LOC], 1, y - spriteY);
1316	glUniform2i(uniforms[GBA_GL_VS_MAXPOS], (GBAObjAttributesBIsHFlip(sprite->b) && !GBAObjAttributesAIsTransformed(sprite->a)) ? -totalWidth : totalWidth, totalHeight);
1317	glUniform1i(uniforms[GBA_GL_OBJ_VRAM], 0);
1318	glUniform1i(uniforms[GBA_GL_OBJ_PALETTE], 1);
1319	glUniform1i(uniforms[GBA_GL_OBJ_CHARBASE], charBase);
1320	glUniform1i(uniforms[GBA_GL_OBJ_STRIDE], stride);
1321	glUniform1i(uniforms[GBA_GL_OBJ_LOCALPALETTE], GBAObjAttributesCGetPalette(sprite->c));
1322	glUniform4i(uniforms[GBA_GL_OBJ_INFLAGS], GBAObjAttributesCGetPriority(sprite->c) << 3,
1323	                                          (renderer->target1Obj || GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT) | (renderer->target2Obj * 2) | (renderer->blendEffect * 4),
1324	                                          renderer->blda, GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT);
1325	if (GBAObjAttributesAIsTransformed(sprite->a)) {
1326		struct GBAOAMMatrix mat;
1327		LOAD_16(mat.a, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].a);
1328		LOAD_16(mat.b, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].b);
1329		LOAD_16(mat.c, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].c);
1330		LOAD_16(mat.d, 0, &renderer->d.oam->mat[GBAObjAttributesBGetMatIndex(sprite->b)].d);
1331
1332		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 });
1333	} else {
1334		glUniformMatrix2fv(uniforms[GBA_GL_OBJ_TRANSFORM], 1, GL_FALSE, (GLfloat[]) { 1.f, 0, 0, 1.f });
1335	}
1336	glUniform4i(uniforms[GBA_GL_OBJ_DIMS], width, height, totalWidth, totalHeight);
1337	if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_OBJWIN) {
1338		int window = ~renderer->objwin & 0x3F;
1339		glUniform4f(uniforms[GBA_GL_OBJ_OBJWIN], 1, window / 128.f, renderer->bldb / 16.f, renderer->bldy / 16.f);
1340		glDrawBuffers(3, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 });
1341	} else {
1342		glUniform4f(uniforms[GBA_GL_OBJ_OBJWIN], 0, 0, 0, 0);
1343		glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
1344	}
1345	if (GBAObjAttributesAIsMosaic(sprite->a)) {
1346		int mosaicH = GBAMosaicControlGetObjH(renderer->mosaic) + 1;
1347		if (GBAObjAttributesBIsHFlip(sprite->b)) {
1348			mosaicH = -mosaicH;
1349		}
1350		glUniform4i(uniforms[GBA_GL_OBJ_MOSAIC], mosaicH, GBAMosaicControlGetObjV(renderer->mosaic) + 1, x, spriteY);
1351	} else {
1352		glUniform4i(uniforms[GBA_GL_OBJ_MOSAIC], 0, 0, 0, 0);
1353	}
1354	glEnableVertexAttribArray(0);
1355	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1356	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1357}
1358
1359void _prepareBackground(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, const GLuint* uniforms) {
1360	glBindFramebuffer(GL_FRAMEBUFFER, background->fbo);
1361	glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
1362	glActiveTexture(GL_TEXTURE0);
1363	glBindTexture(GL_TEXTURE_2D, renderer->vramTex);
1364	glActiveTexture(GL_TEXTURE0 + 1);
1365	glBindTexture(GL_TEXTURE_2D, renderer->paletteTex);
1366	glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
1367	glUniform1i(uniforms[GBA_GL_BG_VRAM], 0);
1368	glUniform1i(uniforms[GBA_GL_BG_PALETTE], 1);
1369	if (background->mosaic) {
1370		glUniform2i(uniforms[GBA_GL_BG_MOSAIC], GBAMosaicControlGetBgV(renderer->mosaic) + 1, GBAMosaicControlGetBgH(renderer->mosaic) + 1);
1371	} else {
1372		glUniform2i(uniforms[GBA_GL_BG_MOSAIC], 0, 0);
1373	}
1374	glUniform4i(uniforms[GBA_GL_BG_INFLAGS], (background->priority << 3) + (background->index << 1) + 1,
1375		                                     background->target1 | (background->target2 * 2) | (renderer->blendEffect * 4),
1376		                                     renderer->blda, 0);
1377	glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
1378}
1379
1380void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
1381	int inY = y + background->y;
1382	int yBase = inY & 0xFF;
1383	if (background->size == 2) {
1384		yBase += inY & 0x100;
1385	} else if (background->size == 3) {
1386		yBase += (inY & 0x100) << 1;
1387	}
1388
1389	const struct GBAVideoGLShader* shader = &renderer->bgShader[background->multipalette ? 1 : 0];
1390	const GLuint* uniforms = shader->uniforms;
1391	glScissor(0, y * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->scale);
1392	glUseProgram(shader->program);
1393	glBindVertexArray(shader->vao);
1394	_prepareBackground(renderer, background, uniforms);
1395	glUniform2i(uniforms[GBA_GL_VS_LOC], 1, y);
1396	glUniform1i(uniforms[GBA_GL_BG_SCREENBASE], background->screenBase);
1397	glUniform1i(uniforms[GBA_GL_BG_CHARBASE], background->charBase);
1398	glUniform1i(uniforms[GBA_GL_BG_SIZE], background->size);
1399	glUniform2i(uniforms[GBA_GL_BG_OFFSET], background->x, yBase - y);
1400	glEnableVertexAttribArray(0);
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	glEnableVertexAttribArray(0);
1477	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1478	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1479}
1480
1481void GBAVideoGLRendererDrawBackgroundMode3(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
1482	const struct GBAVideoGLShader* shader = &renderer->bgShader[5];
1483	const GLuint* uniforms = shader->uniforms;
1484	glBindFramebuffer(GL_FRAMEBUFFER, background->fbo);
1485	glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
1486	glUseProgram(shader->program);
1487	glBindVertexArray(shader->vao);
1488	_prepareTransform(renderer, background, uniforms, y);
1489	glUniform1i(uniforms[GBA_GL_BG_CHARBASE], 0);
1490	glUniform2i(uniforms[GBA_GL_BG_SIZE], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
1491	glEnableVertexAttribArray(0);
1492	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1493	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1494}
1495
1496void GBAVideoGLRendererDrawBackgroundMode4(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
1497	const struct GBAVideoGLShader* shader = &renderer->bgShader[4];
1498	const GLuint* uniforms = shader->uniforms;
1499	glBindFramebuffer(GL_FRAMEBUFFER, background->fbo);
1500	glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
1501	glUseProgram(shader->program);
1502	glBindVertexArray(shader->vao);
1503	_prepareTransform(renderer, background, uniforms, y);
1504	glUniform1i(uniforms[GBA_GL_BG_CHARBASE], GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt) ? 0xA000 : 0);
1505	glUniform2i(uniforms[GBA_GL_BG_SIZE], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
1506	glEnableVertexAttribArray(0);
1507	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1508	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1509}
1510
1511void GBAVideoGLRendererDrawBackgroundMode5(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
1512	const struct GBAVideoGLShader* shader = &renderer->bgShader[5];
1513	const GLuint* uniforms = shader->uniforms;
1514	glBindFramebuffer(GL_FRAMEBUFFER, background->fbo);
1515	glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
1516	glUseProgram(shader->program);
1517	glBindVertexArray(shader->vao);
1518	_prepareTransform(renderer, background, uniforms, y);
1519	glUniform1i(uniforms[GBA_GL_BG_CHARBASE], GBARegisterDISPCNTIsFrameSelect(renderer->dispcnt) ? 0x5000 : 0);
1520	glUniform2i(uniforms[GBA_GL_BG_SIZE], 160, 128);
1521	glEnableVertexAttribArray(0);
1522	glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
1523	glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
1524}
1525
1526static void _scissorWindow(int start, int end, int y, int lines, int scale) {
1527	if (start > end) {
1528		_scissorWindow(start, GBA_VIDEO_HORIZONTAL_PIXELS * scale, y, lines, scale);
1529		_scissorWindow(0, end, y, lines, scale);
1530		return;
1531	}
1532	glScissor(start, y, end - start, lines);
1533	glClear(GL_COLOR_BUFFER_BIT);	
1534}
1535
1536static void _scissorWindowN(struct GBAVideoWindowRegion* region, int y, int scale) {
1537	int sdelta = region[0].start - region[1].start;
1538	int edelta = region[0].end - region[1].end;
1539	int maxDelta = 0;
1540	if (sdelta > maxDelta) {
1541		maxDelta = sdelta;
1542	} else if (-sdelta > maxDelta) {
1543		maxDelta = -sdelta;
1544	}
1545	if (edelta > maxDelta) {
1546		maxDelta = edelta;
1547	} else if (-edelta > maxDelta) {
1548		maxDelta = -edelta;
1549	}
1550	if (!(sdelta | edelta) || maxDelta >= GBA_VIDEO_VERTICAL_PIXELS / 2) {
1551		_scissorWindow(region[0].start * scale, region[0].end * scale, y, scale, scale);
1552	} else {
1553		int i;
1554		for (i = 0; i < scale; ++i) {
1555			int start = region[1].start * scale + sdelta * i;
1556			int end = region[1].end * scale + edelta * i;
1557			_scissorWindow(start, end, y + i, 1, scale);
1558		}
1559	}
1560}
1561
1562static void _clearWindow(GBAWindowControl window, int bldb, int bldy) {
1563	window = ~window & 0x3F;
1564	glClearColor(window / 128.f, bldb / 16.f, bldy / 16.f, 0);
1565}
1566
1567void GBAVideoGLRendererDrawWindow(struct GBAVideoGLRenderer* renderer, int y) {
1568	glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[GBA_GL_FBO_WINDOW]);
1569	int dispcnt = ((renderer->dispcnt >> 8) & 0x1F) | 0x20;
1570	if (!(renderer->dispcnt & 0xE000)) {
1571		_clearWindow(dispcnt, renderer->bldb, renderer->bldy);
1572		_scissorWindow(0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, y * renderer->scale, renderer->scale, renderer->scale);
1573	} else {
1574		_clearWindow(renderer->winout & dispcnt, renderer->bldb, renderer->bldy);
1575		_scissorWindow(0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, y * renderer->scale, renderer->scale, renderer->scale);
1576		if (GBARegisterDISPCNTIsWin1Enable(renderer->dispcnt) && y >= renderer->winN[1].v.start && y < renderer->winN[1].v.end) {
1577			_clearWindow(renderer->winN[1].control & dispcnt, renderer->bldb, renderer->bldy);
1578			_scissorWindowN(renderer->winN[1].h, y * renderer->scale, renderer->scale);
1579		}
1580		if (GBARegisterDISPCNTIsWin0Enable(renderer->dispcnt) && y >= renderer->winN[0].v.start && y < renderer->winN[0].v.end) {
1581			_clearWindow(renderer->winN[0].control & dispcnt, renderer->bldb, renderer->bldy);
1582			_scissorWindowN(renderer->winN[0].h, y * renderer->scale, renderer->scale);
1583		}
1584	}
1585}
1586
1587#endif