all repos — mgba @ 53d9e6b43213d9eed2c7bc5b38d16f4fdad37bc3

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