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