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