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#include <mgba/core/cache-set.h>
9#include <mgba/internal/arm/macros.h>
10#include <mgba/internal/gba/io.h>
11#include <mgba/internal/gba/renderers/cache-set.h>
12
13static void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer);
14static void GBAVideoGLRendererDeinit(struct GBAVideoRenderer* renderer);
15static void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer);
16static void GBAVideoGLRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
17static void GBAVideoGLRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
18static void GBAVideoGLRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
19static uint16_t GBAVideoGLRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
20static void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
21static void GBAVideoGLRendererFinishFrame(struct GBAVideoRenderer* renderer);
22static void GBAVideoGLRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels);
23static void GBAVideoGLRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
24
25static void GBAVideoGLRendererUpdateDISPCNT(struct GBAVideoGLRenderer* renderer);
26static void GBAVideoGLRendererWriteBGCNT(struct GBAVideoGLBackground* bg, uint16_t value);
27static void GBAVideoGLRendererWriteBGX_LO(struct GBAVideoGLBackground* bg, uint16_t value);
28static void GBAVideoGLRendererWriteBGX_HI(struct GBAVideoGLBackground* bg, uint16_t value);
29static void GBAVideoGLRendererWriteBGY_LO(struct GBAVideoGLBackground* bg, uint16_t value);
30static void GBAVideoGLRendererWriteBGY_HI(struct GBAVideoGLBackground* bg, uint16_t value);
31static void GBAVideoGLRendererWriteBLDCNT(struct GBAVideoGLRenderer* renderer, uint16_t value);
32
33static void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y);
34static void GBAVideoGLRendererDrawBackgroundMode2(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y);
35static void GBAVideoGLRendererDrawBackgroundMode3(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y);
36static void GBAVideoGLRendererDrawBackgroundMode4(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y);
37static void GBAVideoGLRendererDrawBackgroundMode5(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y);
38
39#define TEST_LAYER_ENABLED(X) !renderer->disableBG[X] && glRenderer->bg[X].enabled == 4 && glRenderer->bg[X].priority == priority
40
41static const GLchar* const _gl3Header =
42 "#version 130\n";
43
44static const char* const _vertexShader =
45 "attribute vec2 position;\n"
46 "uniform ivec2 loc;\n"
47 "const ivec2 maxPos = ivec2(240, 160);\n"
48 "varying vec2 texCoord;\n"
49
50 "void main() {\n"
51 " vec2 local = (position * loc.x + vec2(0, loc.y)) / vec2(1., maxPos.y);\n"
52 " gl_Position = vec4(local * 2. - 1., 0., 1.);\n"
53 " texCoord = local * maxPos.xy;\n"
54 "}";
55
56static const char* const _renderTile16 =
57 "vec4 renderTile(int tile, int paletteId, ivec2 localCoord) {\n"
58 " int address = charBase + tile * 16 + (localCoord.x >> 2) + (localCoord.y << 1);\n"
59 " vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
60 " int entry = int(halfrow[3 - (localCoord.x & 3)] * 15.9);\n"
61 " vec4 color = texelFetch(palette, ivec2(entry, paletteId), 0);\n"
62 " if (entry == 0) {\n"
63 " color.a = 0;\n"
64 " } else {\n"
65 " color.a = 1;\n"
66 " }\n"
67 " return color;\n"
68 "}";
69
70static const char* const _renderTile256 =
71 "vec4 renderTile(int tile, int paletteId, ivec2 localCoord) {\n"
72 " int address = charBase + tile * 32 + (localCoord.x >> 1) + (localCoord.y << 2);\n"
73 " vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
74 " int entry = int(halfrow[3 - 2 * (localCoord.x & 1)] * 15.9);\n"
75 " int pal2 = int(halfrow[2 - 2 * (localCoord.x & 1)] * 15.9);\n"
76 " vec4 color = texelFetch(palette, ivec2(entry, pal2 + (paletteId & 16)), 0);\n"
77 " if (pal2 > 0 || entry > 0) {\n"
78 " color.a = 1.;\n"
79 " } else {\n"
80 " color.a = 0.;\n"
81 " }\n"
82 " return color;\n"
83 "}";
84
85static const char* const _renderMode0 =
86 "varying vec2 texCoord;\n"
87 "uniform sampler2D vram;\n"
88 "uniform sampler2D palette;\n"
89 "uniform int screenBase;\n"
90 "uniform int charBase;\n"
91 "uniform int size;\n"
92 "uniform ivec2 offset;\n"
93
94 "vec4 renderTile(int tile, int paletteId, ivec2 localCoord);\n"
95
96 "void main() {\n"
97 " ivec2 coord = ivec2(texCoord) + offset;\n"
98 " if ((size & 1) == 1) {\n"
99 " coord.y += coord.x & 256;\n"
100 " }\n"
101 " coord.x &= 255;\n"
102 " int mapAddress = screenBase + (coord.x >> 3) + (coord.y >> 3) * 32;\n"
103 " vec4 map = texelFetch(vram, ivec2(mapAddress & 255, mapAddress >> 8), 0);\n"
104 " int flags = int(map.g * 15.9);\n"
105 " if ((flags & 4) == 4) {\n"
106 " coord.x ^= 7;\n"
107 " }\n"
108 " if ((flags & 8) == 8) {\n"
109 " coord.y ^= 7;\n"
110 " }\n"
111 " int tile = int(map.a * 15.9) + int(map.b * 15.9) * 16 + (flags & 0x3) * 256;\n"
112 " gl_FragColor = renderTile(tile, int(map.r * 15.9), coord & 7);\n"
113 "}";
114
115static const char* const _fetchTileOverflow =
116 "vec4 fetchTile(ivec2 coord) {\n"
117 " int sizeAdjusted = (0x8000 << size) - 1;\n"
118 " coord &= sizeAdjusted;\n"
119 " return renderTile(coord);\n"
120 "}";
121
122static const char* const _fetchTileNoOverflow =
123 "vec4 fetchTile(ivec2 coord) {\n"
124 " int sizeAdjusted = (0x8000 << size) - 1;\n"
125 " ivec2 outerCoord = coord & ~sizeAdjusted;\n"
126 " if ((outerCoord.x | outerCoord.y) != 0) {\n"
127 " vec4 color = texelFetch(palette, ivec2(0, 0), 0);\n"
128 " color.a = 0;\n"
129 " return color;\n"
130 " }\n"
131 " return renderTile(coord);\n"
132 "}";
133
134static const char* const _renderMode2 =
135 "varying vec2 texCoord;\n"
136 "uniform sampler2D vram;\n"
137 "uniform sampler2D palette;\n"
138 "uniform int screenBase;\n"
139 "uniform int charBase;\n"
140 "uniform int size;\n"
141 "uniform ivec2 offset;\n"
142 "uniform ivec2 oldOffset;\n"
143 "uniform mat2x2 transform;\n"
144 "uniform mat2x2 oldTransform;\n"
145 "uniform int firstD;\n"
146
147 "vec4 fetchTile(ivec2 coord);\n"
148
149 "vec4 renderTile(ivec2 coord) {\n"
150 " int map = (coord.x >> 11) + (((coord.y >> 7) & 0x7F0) << size);\n"
151 " int mapAddress = screenBase + (map >> 1);\n"
152 " vec4 twomaps = texelFetch(vram, ivec2(mapAddress & 255, mapAddress >> 8), 0);\n"
153 " int tile = int(twomaps[3 - 2 * (map & 1)] * 15.9) + int(twomaps[2 - 2 * (map & 1)] * 15.9) * 16;\n"
154 " int address = charBase + tile * 32 + ((coord.x >> 9) & 3) + ((coord.y >> 6) & 0x1C);\n"
155 " vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
156 " int entry = int(halfrow[3 - ((coord.x >> 7) & 2)] * 15.9);\n"
157 " int pal2 = int(halfrow[2 - ((coord.x >> 7) & 2)] * 15.9);\n"
158 " vec4 color = texelFetch(palette, ivec2(entry, pal2), 0);\n"
159 " if (pal2 > 0 || entry > 0) {\n"
160 " color.a = 1.;\n"
161 " } else {\n"
162 " color.a = 0.;\n"
163 " }\n"
164 " return color;\n"
165 "}"
166
167 "void main() {\n"
168 " float y = fract(texCoord.y);\n"
169 " float lin = y / ceil(y);\n"
170 " vec2 mixedTransform = mix(oldTransform[0], transform[0], lin);\n"
171 " vec2 mixedOffset = mix(oldOffset, offset, lin);\n"
172 " gl_FragColor = fetchTile(ivec2(mixedTransform * texCoord.x + mixedOffset));\n"
173 "}";
174
175static const char* const _composite =
176 "varying vec2 texCoord;\n"
177 "uniform ivec3 inflags;\n"
178 "uniform int scale;\n"
179 "uniform vec3 blend;\n"
180 "uniform sampler2D layer;\n"
181 "uniform sampler2D oldLayer;\n"
182 "uniform sampler2D buffer;\n"
183 "out vec4 color;\n"
184 "out vec3 flags;\n"
185
186 "void main() {\n"
187 " vec4 pix = texelFetch(layer, ivec2(texCoord * scale), 0);\n"
188 " if (pix.a == 0) {\n"
189 " discard;\n"
190 " }\n"
191 " ivec3 oldFlags = ivec3(texelFetch(buffer, ivec2(texCoord * scale), 0).xyz * vec3(32., 4., 1.));\n"
192 " ivec3 outflags = ivec3(0, 0, 0);\n"
193 " if (inflags.x < oldFlags.x) {\n"
194 " outflags = inflags;\n"
195 " if ((inflags.y & 1) == 1 && (oldFlags.y & 2) == 2) {\n"
196 " vec4 oldpix = texelFetch(oldLayer, ivec2(texCoord * scale), 0);\n"
197 " pix *= blend.x;\n"
198 " pix += oldpix * blend.y;\n"
199 " }\n"
200 " } else {\n"
201 " pix = texelFetch(oldLayer, ivec2(texCoord * scale), 0);\n"
202 " }\n"
203 " color = pix;\n"
204 " flags = outflags / vec3(32., 4., 1.);\n"
205 "}";
206
207static const GLint _vertices[] = {
208 0, 0,
209 0, 1,
210 1, 1,
211 1, 0,
212};
213
214void GBAVideoGLRendererCreate(struct GBAVideoGLRenderer* renderer) {
215 renderer->d.init = GBAVideoGLRendererInit;
216 renderer->d.reset = GBAVideoGLRendererReset;
217 renderer->d.deinit = GBAVideoGLRendererDeinit;
218 renderer->d.writeVideoRegister = GBAVideoGLRendererWriteVideoRegister;
219 renderer->d.writeVRAM = GBAVideoGLRendererWriteVRAM;
220 renderer->d.writeOAM = GBAVideoGLRendererWriteOAM;
221 renderer->d.writePalette = GBAVideoGLRendererWritePalette;
222 renderer->d.drawScanline = GBAVideoGLRendererDrawScanline;
223 renderer->d.finishFrame = GBAVideoGLRendererFinishFrame;
224 renderer->d.getPixels = GBAVideoGLRendererGetPixels;
225 renderer->d.putPixels = GBAVideoGLRendererPutPixels;
226
227 renderer->d.disableBG[0] = false;
228 renderer->d.disableBG[1] = false;
229 renderer->d.disableBG[2] = false;
230 renderer->d.disableBG[3] = false;
231 renderer->d.disableOBJ = false;
232
233 renderer->scale = 1;
234}
235
236void _compileBackground(struct GBAVideoGLRenderer* glRenderer, GLuint program, const char** shaderBuffer, int shaderBufferLines, GLuint vs, char* log) {
237 GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
238 glAttachShader(program, vs);
239 glAttachShader(program, fs);
240 glShaderSource(fs, shaderBufferLines, shaderBuffer, 0);
241 glCompileShader(fs);
242 glGetShaderInfoLog(fs, 1024, 0, log);
243 if (log[0]) {
244 mLOG(GBA_VIDEO, ERROR, "Fragment shader compilation failure: %s", log);
245 }
246 glLinkProgram(program);
247 glGetProgramInfoLog(program, 1024, 0, log);
248 if (log[0]) {
249 mLOG(GBA_VIDEO, ERROR, "Program link failure: %s", log);
250 }
251 glDeleteShader(fs);
252}
253
254void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) {
255 struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
256 glGenFramebuffers(2, glRenderer->fbo);
257 glGenTextures(3, glRenderer->layers);
258
259 glGenTextures(1, &glRenderer->paletteTex);
260 glBindTexture(GL_TEXTURE_2D, glRenderer->paletteTex);
261 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
262 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
263
264 glGenTextures(1, &glRenderer->vramTex);
265 glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex);
266 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
267 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
268 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA4, 256, 192, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 0);
269
270 glGenTextures(1, &glRenderer->oamTex);
271 glBindTexture(GL_TEXTURE_2D, glRenderer->oamTex);
272 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
273 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
274
275 glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[1]);
276 glBindTexture(GL_TEXTURE_2D, glRenderer->outputTex);
277 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
278 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
279 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
280 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
281 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
282 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glRenderer->outputTex, 0);
283
284 glBindTexture(GL_TEXTURE_2D, glRenderer->layers[2]);
285 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
286 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
287 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
288 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
289 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
290 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, glRenderer->layers[2], 0);
291
292 glBindFramebuffer(GL_FRAMEBUFFER, 0);
293
294 int i;
295 for (i = 0; i < 4; ++i) {
296 glRenderer->bg[i].index = i;
297 glGenFramebuffers(1, &glRenderer->bg[i].fbo);
298 glGenTextures(1, &glRenderer->bg[i].tex);
299 glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->bg[i].fbo);
300 glBindTexture(GL_TEXTURE_2D, glRenderer->bg[i].tex);
301 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
302 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
303 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
304 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
305 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
306 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glRenderer->bg[i].tex, 0);
307 glBindFramebuffer(GL_FRAMEBUFFER, 0);
308 }
309
310 glRenderer->compositeProgram = glCreateProgram();
311 glRenderer->objProgram = glCreateProgram();
312 glRenderer->bgProgram[0] = glCreateProgram();
313 glRenderer->bgProgram[1] = glCreateProgram();
314 glRenderer->bgProgram[2] = glCreateProgram();
315 glRenderer->bgProgram[3] = glCreateProgram();
316 glRenderer->bgProgram[4] = glCreateProgram();
317 glRenderer->bgProgram[5] = glCreateProgram();
318
319 char log[1024];
320 const GLchar* shaderBuffer[8];
321 shaderBuffer[0] = _gl3Header;
322
323 GLuint vs = glCreateShader(GL_VERTEX_SHADER);
324 shaderBuffer[1] = _vertexShader;
325 glShaderSource(vs, 2, shaderBuffer, 0);
326 glCompileShader(vs);
327 glGetShaderInfoLog(vs, 1024, 0, log);
328 if (log[0]) {
329 mLOG(GBA_VIDEO, ERROR, "Vertex shader compilation failure: %s", log);
330 }
331
332 shaderBuffer[1] = _renderMode0;
333
334 shaderBuffer[2] = _renderTile16;
335 _compileBackground(glRenderer, glRenderer->bgProgram[0], shaderBuffer, 3, vs, log);
336
337 shaderBuffer[2] = _renderTile256;
338 _compileBackground(glRenderer, glRenderer->bgProgram[1], shaderBuffer, 3, vs, log);
339
340 shaderBuffer[1] = _renderMode2;
341
342 shaderBuffer[2] = _fetchTileOverflow;
343 _compileBackground(glRenderer, glRenderer->bgProgram[2], shaderBuffer, 3, vs, log);
344
345 shaderBuffer[2] = _fetchTileNoOverflow;
346 _compileBackground(glRenderer, glRenderer->bgProgram[3], shaderBuffer, 3, vs, log);
347
348 shaderBuffer[1] = _composite;
349 _compileBackground(glRenderer, glRenderer->compositeProgram, shaderBuffer, 2, vs, log);
350 glBindFragDataLocation(glRenderer->compositeProgram, 0, "color");
351 glBindFragDataLocation(glRenderer->compositeProgram, 1, "flags");
352
353 glDeleteShader(vs);
354
355 GBAVideoGLRendererReset(renderer);
356}
357
358void GBAVideoGLRendererDeinit(struct GBAVideoRenderer* renderer) {
359 struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
360 glDeleteFramebuffers(2, glRenderer->fbo);
361 glDeleteTextures(3, glRenderer->layers);
362 glDeleteTextures(1, &glRenderer->paletteTex);
363 glDeleteTextures(1, &glRenderer->vramTex);
364 glDeleteTextures(1, &glRenderer->oamTex);
365}
366
367void GBAVideoGLRendererReset(struct GBAVideoRenderer* renderer) {
368 struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
369
370 glRenderer->paletteDirty = true;
371 glRenderer->vramDirty = 0xFFFFFF;
372}
373
374void GBAVideoGLRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
375 struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
376 glRenderer->vramDirty |= 1 << (address >> 12);
377}
378
379void GBAVideoGLRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
380 UNUSED(oam);
381 struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
382 glRenderer->oamDirty = true;
383}
384
385void GBAVideoGLRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
386 UNUSED(address);
387 UNUSED(value);
388 struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
389 glRenderer->paletteDirty = true;
390}
391
392uint16_t GBAVideoGLRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
393 struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
394 if (renderer->cache) {
395 GBAVideoCacheWriteVideoRegister(renderer->cache, address, value);
396 }
397
398 switch (address) {
399 case REG_DISPCNT:
400 value &= 0xFFF7;
401 glRenderer->dispcnt = value;
402 GBAVideoGLRendererUpdateDISPCNT(glRenderer);
403 break;
404 case REG_BG0CNT:
405 value &= 0xDFFF;
406 GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[0], value);
407 break;
408 case REG_BG1CNT:
409 value &= 0xDFFF;
410 GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[1], value);
411 break;
412 case REG_BG2CNT:
413 value &= 0xFFFF;
414 GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[2], value);
415 break;
416 case REG_BG3CNT:
417 value &= 0xFFFF;
418 GBAVideoGLRendererWriteBGCNT(&glRenderer->bg[3], value);
419 break;
420 case REG_BG0HOFS:
421 value &= 0x01FF;
422 glRenderer->bg[0].x = value;
423 break;
424 case REG_BG0VOFS:
425 value &= 0x01FF;
426 glRenderer->bg[0].y = value;
427 break;
428 case REG_BG1HOFS:
429 value &= 0x01FF;
430 glRenderer->bg[1].x = value;
431 break;
432 case REG_BG1VOFS:
433 value &= 0x01FF;
434 glRenderer->bg[1].y = value;
435 break;
436 case REG_BG2HOFS:
437 value &= 0x01FF;
438 glRenderer->bg[2].x = value;
439 break;
440 case REG_BG2VOFS:
441 value &= 0x01FF;
442 glRenderer->bg[2].y = value;
443 break;
444 case REG_BG3HOFS:
445 value &= 0x01FF;
446 glRenderer->bg[3].x = value;
447 break;
448 case REG_BG3VOFS:
449 value &= 0x01FF;
450 glRenderer->bg[3].y = value;
451 break;
452 case REG_BG2PA:
453 glRenderer->bg[2].affine[0].dx = value;
454 break;
455 case REG_BG2PB:
456 glRenderer->bg[2].affine[0].dmx = value;
457 break;
458 case REG_BG2PC:
459 glRenderer->bg[2].affine[0].dy = value;
460 break;
461 case REG_BG2PD:
462 glRenderer->bg[2].affine[0].dmy = value;
463 break;
464 case REG_BG2X_LO:
465 GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[2], value);
466 break;
467 case REG_BG2X_HI:
468 GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[2], value);
469 break;
470 case REG_BG2Y_LO:
471 GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[2], value);
472 break;
473 case REG_BG2Y_HI:
474 GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[2], value);
475 break;
476 case REG_BG3PA:
477 glRenderer->bg[3].affine[0].dx = value;
478 break;
479 case REG_BG3PB:
480 glRenderer->bg[3].affine[0].dmx = value;
481 break;
482 case REG_BG3PC:
483 glRenderer->bg[3].affine[0].dy = value;
484 break;
485 case REG_BG3PD:
486 glRenderer->bg[3].affine[0].dmy = value;
487 break;
488 case REG_BG3X_LO:
489 GBAVideoGLRendererWriteBGX_LO(&glRenderer->bg[3], value);
490 break;
491 case REG_BG3X_HI:
492 GBAVideoGLRendererWriteBGX_HI(&glRenderer->bg[3], value);
493 break;
494 case REG_BG3Y_LO:
495 GBAVideoGLRendererWriteBGY_LO(&glRenderer->bg[3], value);
496 break;
497 case REG_BG3Y_HI:
498 GBAVideoGLRendererWriteBGY_HI(&glRenderer->bg[3], value);
499 break;
500 case REG_BLDCNT:
501 GBAVideoGLRendererWriteBLDCNT(glRenderer, value);
502 value &= 0x3FFF;
503 break;
504 case REG_BLDALPHA:
505 glRenderer->blda = value & 0x1F;
506 if (glRenderer->blda > 0x10) {
507 glRenderer->blda = 0x10;
508 }
509 glRenderer->bldb = (value >> 8) & 0x1F;
510 if (glRenderer->bldb > 0x10) {
511 glRenderer->bldb = 0x10;
512 }
513 value &= 0x1F1F;
514 break;
515 case REG_BLDY:
516 value &= 0x1F;
517 if (value > 0x10) {
518 value = 0x10;
519 }
520 if (glRenderer->bldy != value) {
521 glRenderer->bldy = value;
522 }
523 break;
524 case REG_WIN0H:
525 /*glRenderer->winN[0].h.end = value;
526 glRenderer->winN[0].h.start = value >> 8;
527 if (glRenderer->winN[0].h.start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[0].h.start > glRenderer->winN[0].h.end) {
528 glRenderer->winN[0].h.start = 0;
529 }
530 if (glRenderer->winN[0].h.end > GBA_VIDEO_HORIZONTAL_PIXELS) {
531 glRenderer->winN[0].h.end = GBA_VIDEO_HORIZONTAL_PIXELS;
532 if (glRenderer->winN[0].h.start > GBA_VIDEO_HORIZONTAL_PIXELS) {
533 glRenderer->winN[0].h.start = GBA_VIDEO_HORIZONTAL_PIXELS;
534 }
535 }*/
536 break;
537 case REG_WIN1H:
538 /*glRenderer->winN[1].h.end = value;
539 glRenderer->winN[1].h.start = value >> 8;
540 if (glRenderer->winN[1].h.start > GBA_VIDEO_HORIZONTAL_PIXELS && glRenderer->winN[1].h.start > glRenderer->winN[1].h.end) {
541 glRenderer->winN[1].h.start = 0;
542 }
543 if (glRenderer->winN[1].h.end > GBA_VIDEO_HORIZONTAL_PIXELS) {
544 glRenderer->winN[1].h.end = GBA_VIDEO_HORIZONTAL_PIXELS;
545 if (glRenderer->winN[1].h.start > GBA_VIDEO_HORIZONTAL_PIXELS) {
546 glRenderer->winN[1].h.start = GBA_VIDEO_HORIZONTAL_PIXELS;
547 }
548 }*/
549 break;
550 case REG_WIN0V:
551 /*glRenderer->winN[0].v.end = value;
552 glRenderer->winN[0].v.start = value >> 8;
553 if (glRenderer->winN[0].v.start > GBA_VIDEO_VERTICAL_PIXELS && glRenderer->winN[0].v.start > glRenderer->winN[0].v.end) {
554 glRenderer->winN[0].v.start = 0;
555 }
556 if (glRenderer->winN[0].v.end > GBA_VIDEO_VERTICAL_PIXELS) {
557 glRenderer->winN[0].v.end = GBA_VIDEO_VERTICAL_PIXELS;
558 if (glRenderer->winN[0].v.start > GBA_VIDEO_VERTICAL_PIXELS) {
559 glRenderer->winN[0].v.start = GBA_VIDEO_VERTICAL_PIXELS;
560 }
561 }*/
562 break;
563 case REG_WIN1V:
564 /*glRenderer->winN[1].v.end = value;
565 glRenderer->winN[1].v.start = value >> 8;
566 if (glRenderer->winN[1].v.start > GBA_VIDEO_VERTICAL_PIXELS && glRenderer->winN[1].v.start > glRenderer->winN[1].v.end) {
567 glRenderer->winN[1].v.start = 0;
568 }
569 if (glRenderer->winN[1].v.end > GBA_VIDEO_VERTICAL_PIXELS) {
570 glRenderer->winN[1].v.end = GBA_VIDEO_VERTICAL_PIXELS;
571 if (glRenderer->winN[1].v.start > GBA_VIDEO_VERTICAL_PIXELS) {
572 glRenderer->winN[1].v.start = GBA_VIDEO_VERTICAL_PIXELS;
573 }
574 }*/
575 break;
576 case REG_WININ:
577 value &= 0x3F3F;
578 //glRenderer->winN[0].control.packed = value;
579 //glRenderer->winN[1].control.packed = value >> 8;
580 break;
581 case REG_WINOUT:
582 value &= 0x3F3F;
583 //glRenderer->winout.packed = value;
584 //glRenderer->objwin.packed = value >> 8;
585 break;
586 case REG_MOSAIC:
587 glRenderer->mosaic = value;
588 break;
589 case REG_GREENSWP:
590 mLOG(GBA_VIDEO, STUB, "Stub video register write: 0x%03X", address);
591 break;
592 default:
593 mLOG(GBA_VIDEO, GAME_ERROR, "Invalid video register: 0x%03X", address);
594 }
595 return value;
596}
597
598void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
599 struct GBAVideoGLRenderer* glRenderer = (struct GBAVideoGLRenderer*) renderer;
600 if (glRenderer->paletteDirty) {
601 glBindTexture(GL_TEXTURE_2D, glRenderer->paletteTex);
602 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5_A1, 16, 32, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, glRenderer->d.palette);
603 glRenderer->paletteDirty = false;
604 }
605 if (glRenderer->oamDirty) {
606 glBindTexture(GL_TEXTURE_2D, glRenderer->oamTex);
607 glTexImage2D(GL_TEXTURE_2D, 0, GL_R16UI, 4, 128, 0, GL_RED_INTEGER, GL_UNSIGNED_SHORT, glRenderer->d.oam);
608 glRenderer->oamDirty = false;
609 }
610 int i;
611 for (i = 0; i < 24; ++i) {
612 if (!(glRenderer->vramDirty & (1 << i))) {
613 continue;
614 }
615 // TODO: PBOs
616 glBindTexture(GL_TEXTURE_2D, glRenderer->vramTex);
617 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 8 * i, 256, 8, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, &glRenderer->d.vram[2048 * i]);
618 }
619 glRenderer->vramDirty = 0;
620
621 uint32_t backdrop = M_RGB5_TO_RGB8(renderer->palette[0]);
622 glClearColor(((backdrop >> 16) & 0xFF) / 256., ((backdrop >> 8) & 0xFF) / 256., (backdrop & 0xFF) / 256., 0.f);
623 glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[1]);
624 glEnable(GL_SCISSOR_TEST);
625 glScissor(0, y * glRenderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, glRenderer->scale);
626 glClear(GL_COLOR_BUFFER_BIT);
627 glDisable(GL_SCISSOR_TEST);
628 if (y == 0) {
629 glDrawBuffer(GL_COLOR_ATTACHMENT1);
630 glClearColor(1, (glRenderer->target1Bd | (glRenderer->target2Bd * 2)) / 4.f, 0, 1);
631 glClear(GL_COLOR_BUFFER_BIT);
632 glDrawBuffer(GL_COLOR_ATTACHMENT0);
633 }
634 glBindFramebuffer(GL_FRAMEBUFFER, 0);
635
636 unsigned priority;
637 for (priority = 4; priority--;) {
638 if (TEST_LAYER_ENABLED(0) && GBARegisterDISPCNTGetMode(glRenderer->dispcnt) < 2) {
639 GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[0], y);
640 }
641 if (TEST_LAYER_ENABLED(1) && GBARegisterDISPCNTGetMode(glRenderer->dispcnt) < 2) {
642 GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[1], y);
643 }
644 if (TEST_LAYER_ENABLED(2)) {
645 switch (GBARegisterDISPCNTGetMode(glRenderer->dispcnt)) {
646 case 0:
647 GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[2], y);
648 break;
649 case 1:
650 case 2:
651 GBAVideoGLRendererDrawBackgroundMode2(glRenderer, &glRenderer->bg[2], y);
652 break;
653 case 3:
654 //GBAVideoGLRendererDrawBackgroundMode3(glRenderer, &glRenderer->bg[2], y);
655 break;
656 case 4:
657 //GBAVideoGLRendererDrawBackgroundMode4(glRenderer, &glRenderer->bg[2], y);
658 break;
659 case 5:
660 //GBAVideoGLRendererDrawBackgroundMode5(glRenderer, &glRenderer->bg[2], y);
661 break;
662 }
663 }
664 if (TEST_LAYER_ENABLED(3)) {
665 switch (GBARegisterDISPCNTGetMode(glRenderer->dispcnt)) {
666 case 0:
667 GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[3], y);
668 break;
669 case 2:
670 GBAVideoGLRendererDrawBackgroundMode2(glRenderer, &glRenderer->bg[3], y);
671 break;
672 }
673 }
674 }
675
676 if (GBARegisterDISPCNTGetMode(glRenderer->dispcnt) != 0) {
677 memcpy(&glRenderer->bg[2].affine[1], &glRenderer->bg[2].affine[0], sizeof(struct GBAVideoGLAffine));
678 memcpy(&glRenderer->bg[3].affine[1], &glRenderer->bg[3].affine[0], sizeof(struct GBAVideoGLAffine));
679
680 glRenderer->bg[2].affine[0].sx += glRenderer->bg[2].affine[0].dmx;
681 glRenderer->bg[2].affine[0].sy += glRenderer->bg[2].affine[0].dmy;
682 glRenderer->bg[3].affine[0].sx += glRenderer->bg[3].affine[0].dmx;
683 glRenderer->bg[3].affine[0].sy += glRenderer->bg[3].affine[0].dmy;
684 }
685}
686
687void GBAVideoGLRendererFinishFrame(struct GBAVideoRenderer* renderer) {
688 UNUSED(renderer);
689 glFlush();
690}
691
692void GBAVideoGLRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
693
694}
695
696void GBAVideoGLRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
697
698}
699
700static void _enableBg(struct GBAVideoGLRenderer* renderer, int bg, bool active) {
701 int wasActive = renderer->bg[bg].enabled;
702 if (!active) {
703 renderer->bg[bg].enabled = 0;
704 } else if (!wasActive && active) {
705 /*if (renderer->nextY == 0 || GBARegisterDISPCNTGetMode(renderer->dispcnt) > 2) {
706 // TODO: Investigate in more depth how switching background works in different modes
707 renderer->bg[bg].enabled = 4;
708 } else {
709 renderer->bg[bg].enabled = 1;
710 }*/
711 renderer->bg[bg].enabled = 4;
712 }
713}
714
715static void GBAVideoGLRendererUpdateDISPCNT(struct GBAVideoGLRenderer* renderer) {
716 _enableBg(renderer, 0, GBARegisterDISPCNTGetBg0Enable(renderer->dispcnt));
717 _enableBg(renderer, 1, GBARegisterDISPCNTGetBg1Enable(renderer->dispcnt));
718 _enableBg(renderer, 2, GBARegisterDISPCNTGetBg2Enable(renderer->dispcnt));
719 _enableBg(renderer, 3, GBARegisterDISPCNTGetBg3Enable(renderer->dispcnt));
720}
721
722static void GBAVideoGLRendererWriteBGCNT(struct GBAVideoGLBackground* bg, uint16_t value) {
723 bg->priority = GBARegisterBGCNTGetPriority(value);
724 bg->charBase = GBARegisterBGCNTGetCharBase(value) << 13;
725 bg->mosaic = GBARegisterBGCNTGetMosaic(value);
726 bg->multipalette = GBARegisterBGCNTGet256Color(value);
727 bg->screenBase = GBARegisterBGCNTGetScreenBase(value) << 10;
728 bg->overflow = GBARegisterBGCNTGetOverflow(value);
729 bg->size = GBARegisterBGCNTGetSize(value);
730}
731
732static void GBAVideoGLRendererWriteBGX_LO(struct GBAVideoGLBackground* bg, uint16_t value) {
733 bg->refx = (bg->refx & 0xFFFF0000) | value;
734 bg->affine[0].sx = bg->refx;
735}
736
737static void GBAVideoGLRendererWriteBGX_HI(struct GBAVideoGLBackground* bg, uint16_t value) {
738 bg->refx = (bg->refx & 0x0000FFFF) | (value << 16);
739 bg->refx <<= 4;
740 bg->refx >>= 4;
741 bg->affine[0].sx = bg->refx;
742}
743
744static void GBAVideoGLRendererWriteBGY_LO(struct GBAVideoGLBackground* bg, uint16_t value) {
745 bg->refy = (bg->refy & 0xFFFF0000) | value;
746 bg->affine[0].sy = bg->refy;
747}
748
749static void GBAVideoGLRendererWriteBGY_HI(struct GBAVideoGLBackground* bg, uint16_t value) {
750 bg->refy = (bg->refy & 0x0000FFFF) | (value << 16);
751 bg->refy <<= 4;
752 bg->refy >>= 4;
753 bg->affine[0].sy = bg->refy;
754}
755
756static void GBAVideoGLRendererWriteBLDCNT(struct GBAVideoGLRenderer* renderer, uint16_t value) {
757 renderer->bg[0].target1 = GBARegisterBLDCNTGetTarget1Bg0(value);
758 renderer->bg[1].target1 = GBARegisterBLDCNTGetTarget1Bg1(value);
759 renderer->bg[2].target1 = GBARegisterBLDCNTGetTarget1Bg2(value);
760 renderer->bg[3].target1 = GBARegisterBLDCNTGetTarget1Bg3(value);
761 renderer->bg[0].target2 = GBARegisterBLDCNTGetTarget2Bg0(value);
762 renderer->bg[1].target2 = GBARegisterBLDCNTGetTarget2Bg1(value);
763 renderer->bg[2].target2 = GBARegisterBLDCNTGetTarget2Bg2(value);
764 renderer->bg[3].target2 = GBARegisterBLDCNTGetTarget2Bg3(value);
765
766 renderer->blendEffect = GBARegisterBLDCNTGetEffect(value);
767 renderer->target1Obj = GBARegisterBLDCNTGetTarget1Obj(value);
768 renderer->target1Bd = GBARegisterBLDCNTGetTarget1Bd(value);
769 renderer->target2Obj = GBARegisterBLDCNTGetTarget2Obj(value);
770 renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value);
771}
772
773static void _compositeLayer(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y, int flags) {
774 if ((y & 0x1F) != 0x1F) {
775 return;
776 }
777 glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[1]);
778 glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
779 glScissor(0, (y * renderer->scale) % (0x20 * renderer->scale), GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, 0x20 * renderer->scale);
780 glUseProgram(renderer->compositeProgram);
781 glActiveTexture(GL_TEXTURE0);
782 glBindTexture(GL_TEXTURE_2D, background->tex);
783 glActiveTexture(GL_TEXTURE0 + 1);
784 glBindTexture(GL_TEXTURE_2D, renderer->outputTex);
785 glActiveTexture(GL_TEXTURE0 + 2);
786 glBindTexture(GL_TEXTURE_2D, renderer->layers[2]);
787 glUniform2i(0, 0x20, y & ~0x1F);
788 glUniform3i(1, (background->priority << 3) + (background->index << 1) + 1, flags, renderer->blendEffect);
789 glUniform1i(2, renderer->scale);
790 glUniform3f(3, renderer->blda / 16.f, renderer->bldb / 16.f, renderer->bldy / 16.f);
791 glUniform1i(4, 0);
792 glUniform1i(5, 1);
793 glUniform1i(6, 2);
794 glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, _vertices);
795 glEnableVertexAttribArray(0);
796 glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
797 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
798 glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
799}
800
801void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
802 int inY = y + background->y;
803 int yBase = inY & 0xFF;
804 if (background->size == 2) {
805 yBase += inY & 0x100;
806 } else if (background->size == 3) {
807 yBase += (inY & 0x100) << 1;
808 }
809 glBindFramebuffer(GL_FRAMEBUFFER, background->fbo);
810 glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
811 glScissor(0, y * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->scale);
812 glUseProgram(renderer->bgProgram[background->multipalette ? 1 : 0]);
813 glActiveTexture(GL_TEXTURE0);
814 glBindTexture(GL_TEXTURE_2D, renderer->vramTex);
815 glActiveTexture(GL_TEXTURE0 + 1);
816 glBindTexture(GL_TEXTURE_2D, renderer->paletteTex);
817 glUniform2i(0, 1, y);
818 glUniform1i(1, 0);
819 glUniform1i(2, 1);
820 glUniform1i(3, background->screenBase);
821 glUniform1i(4, background->charBase);
822 glUniform1i(5, background->size);
823 glUniform2i(6, background->x, yBase - y);
824 glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, _vertices);
825 glEnableVertexAttribArray(0);
826 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
827
828 _compositeLayer(renderer, background, y, background->target1 | (background->target2 * 2));
829
830 glBindFramebuffer(GL_FRAMEBUFFER, 0);
831}
832
833void GBAVideoGLRendererDrawBackgroundMode2(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
834 glBindFramebuffer(GL_FRAMEBUFFER, background->fbo);
835 glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
836 glScissor(0, y * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->scale);
837 glUseProgram(renderer->bgProgram[background->overflow ? 2 : 3]);
838 glActiveTexture(GL_TEXTURE0);
839 glBindTexture(GL_TEXTURE_2D, renderer->vramTex);
840 glActiveTexture(GL_TEXTURE0 + 1);
841 glBindTexture(GL_TEXTURE_2D, renderer->paletteTex);
842 glUniform2i(0, 1, y);
843 glUniform1i(1, 0);
844 glUniform1i(2, 1);
845 glUniform1i(3, background->screenBase);
846 glUniform1i(4, background->charBase);
847 glUniform1i(5, background->size);
848 glUniform2i(6, background->affine[0].sx, background->affine[0].sy);
849 glUniformMatrix2fv(8, 1, GL_FALSE, (GLfloat[]) { background->affine[0].dx, background->affine[0].dy, background->affine[0].dmx, background->affine[0].dmy });
850 if (renderer->scale > 1) {
851 glUniform2i(7, background->affine[1].sx, background->affine[1].sy);
852 glUniformMatrix2fv(9, 1, GL_FALSE, (GLfloat[]) { background->affine[1].dx, background->affine[1].dy, background->affine[1].dmx, background->affine[1].dmy });
853 } else {
854 glUniform2i(7, background->affine[0].sx, background->affine[0].sy);
855 glUniformMatrix2fv(9, 1, GL_FALSE, (GLfloat[]) { background->affine[0].dx, background->affine[0].dy, background->affine[0].dmx, background->affine[0].dmy });
856 }
857 glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, _vertices);
858 glEnableVertexAttribArray(0);
859 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
860
861 _compositeLayer(renderer, background, y, background->target1 | (background->target2 * 2));
862
863 glBindFramebuffer(GL_FRAMEBUFFER, 0);
864}