src/gba/renderers/video-glsl.c (view raw)
1#include "video-glsl.h"
2
3#include "gba-io.h"
4
5#include <string.h>
6
7#define UNIFORM_LOCATION(UNIFORM) (glGetUniformLocation(glslRenderer->program, UNIFORM))
8
9static const GLfloat _vertices[4] = {
10 -1, 0,
11 1, 0
12};
13
14static const GLchar* _fragmentShader =
15 "#extension GL_EXT_gpu_shader4 : enable\n"
16 "varying float x;\n"
17 "uniform float y;\n"
18 "uniform sampler2D vram;\n"
19 "uniform int dispcnt;\n"
20 "uniform int bg0cnt;\n"
21 "uniform int bg1cnt;\n"
22 "uniform int bg2cnt;\n"
23 "uniform int bg3cnt;\n"
24 "uniform int bg0hofs;\n"
25 "uniform int bg0vofs;\n"
26 "uniform int bg1hofs;\n"
27 "uniform int bg1vofs;\n"
28 "uniform int bg2hofs;\n"
29 "uniform int bg2vofs;\n"
30 "uniform int bg3hofs;\n"
31 "uniform int bg3vofs;\n"
32 "#define VRAM_INDEX(i) (vec2(mod(float(i + 1), 512.0) / 512.0 - 1.0 / 1024.0, (160.0 + floor(float(i) / 512.0)) / 255.0))\n"
33 "#define DESERIALIZE(vec) int(dot(vec4(63488.0, 1984.0, 62.0, 1.0), vec))\n"
34
35 "vec4 backgroundMode0(int bgcnt, int hofs, int vofs) {\n"
36 " int charBase = ((bgcnt / 4) & 3) * 8192;\n"
37 " int screenBase = ((bgcnt / 256) & 0x1F) * 1024;\n"
38 " int localX = hofs + int(x);\n"
39 " int localY = vofs + int(y);\n"
40 " int xBase = localX & 0xF8;\n"
41 " int yBase = localY & 0xF8;\n"
42 " screenBase = screenBase + (xBase / 8) + (yBase * 4);\n"
43 " int mapData = DESERIALIZE(texture2D(vram, VRAM_INDEX(screenBase)));\n"
44 " charBase = charBase + ((mapData & 0x3FF) * 16) + (localX & 0x4) / 4 + (localY & 0x7) * 2;\n"
45 " int tileData = DESERIALIZE(texture2D(vram, VRAM_INDEX(charBase)));\n"
46 " tileData = ((tileData >> ((localX & 3) * 4)) & 0xF);\n"
47 " if (tileData == 0) {\n"
48 " return vec4(0, 0, 0, 0);\n"
49 " }\n"
50 " return texture2D(vram, vec2(float(tileData + (mapData / 4096) * 16) / 512.0, y / 256.0));\n"
51 "}\n"
52
53 "void runPriority(int priority, inout vec4 color) {\n"
54 " if (color.a > 0.0) {\n"
55 " return;\n"
56 " }\n"
57 " if ((dispcnt & 0x100) != 0 && (bg0cnt & 0x3) == priority) {\n"
58 " color = backgroundMode0(bg0cnt, bg0hofs, bg0vofs);\n"
59 " }\n"
60 " if (color.a > 0.0) {\n"
61 " return;\n"
62 " }\n"
63 " if ((dispcnt & 0x200) != 0 && (bg1cnt & 0x3) == priority) {\n"
64 " color = backgroundMode0(bg1cnt, bg1hofs, bg1vofs);\n"
65 " }\n"
66 " if (color.a > 0.0) {\n"
67 " return;\n"
68 " }\n"
69 " if ((dispcnt & 0x400) != 0 && (bg2cnt & 0x3) == priority) {\n"
70 " color = backgroundMode0(bg2cnt, bg2hofs, bg2vofs);\n"
71 " }\n"
72 " if (color.a > 0.0) {\n"
73 " return;\n"
74 " }\n"
75 " if ((dispcnt & 0x800) != 0 && (bg3cnt & 0x3) == priority) {\n"
76 " color = backgroundMode0(bg3cnt, bg3hofs, bg3vofs);\n"
77 " }\n"
78 "}\n"
79
80 "void main() {\n"
81 " vec4 color = vec4(0.0, 0.0, 0.0, 0.0);\n"
82 " runPriority(0, color);\n"
83 " runPriority(1, color);\n"
84 " runPriority(2, color);\n"
85 " runPriority(3, color);\n"
86 " if (color.a == 0.0) {\n"
87 " color = texture2D(vram, vec2(0.0, y / 256.0));\n"
88 " }\n"
89 " gl_FragColor = color;\n"
90 "}\n";
91
92static const GLchar* _vertexShader[] = {
93 "varying float x;",
94 "attribute vec2 vert;",
95 "uniform float y;",
96
97 "void main() {",
98 " x = vert.x * 120.0 + 120.0;",
99 " gl_Position = vec4(vert.x, 1.0 - y / 80.0, 0, 1.0);",
100 "}"
101};
102
103static void GBAVideoGLSLRendererInit(struct GBAVideoRenderer* renderer);
104static void GBAVideoGLSLRendererDeinit(struct GBAVideoRenderer* renderer);
105static void GBAVideoGLSLRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
106static uint16_t GBAVideoGLSLRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
107static void GBAVideoGLSLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
108static void GBAVideoGLSLRendererFinishFrame(struct GBAVideoRenderer* renderer);
109
110void GBAVideoGLSLRendererCreate(struct GBAVideoGLSLRenderer* glslRenderer) {
111 glslRenderer->d.init = GBAVideoGLSLRendererInit;
112 glslRenderer->d.deinit = GBAVideoGLSLRendererDeinit;
113 glslRenderer->d.writeVideoRegister = GBAVideoGLSLRendererWriteVideoRegister;
114 glslRenderer->d.writePalette = GBAVideoGLSLRendererWritePalette;
115 glslRenderer->d.drawScanline = GBAVideoGLSLRendererDrawScanline;
116 glslRenderer->d.finishFrame = GBAVideoGLSLRendererFinishFrame;
117
118 glslRenderer->d.turbo = 0;
119 glslRenderer->d.framesPending = 0;
120 glslRenderer->d.frameskip = 0;
121
122 glslRenderer->fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
123 glslRenderer->vertexShader = glCreateShader(GL_VERTEX_SHADER);
124 glslRenderer->program = glCreateProgram();
125
126 glShaderSource(glslRenderer->fragmentShader, 1, (const GLchar**) &_fragmentShader, 0);
127 glShaderSource(glslRenderer->vertexShader, 7, _vertexShader, 0);
128
129 glAttachShader(glslRenderer->program, glslRenderer->vertexShader);
130 glAttachShader(glslRenderer->program, glslRenderer->fragmentShader);
131 char log[1024];
132 glCompileShader(glslRenderer->fragmentShader);
133 glCompileShader(glslRenderer->vertexShader);
134 glGetShaderInfoLog(glslRenderer->fragmentShader, 1024, 0, log);
135 glGetShaderInfoLog(glslRenderer->vertexShader, 1024, 0, log);
136 glLinkProgram(glslRenderer->program);
137
138 glGenTextures(1, &glslRenderer->vramTexture);
139 glBindTexture(GL_TEXTURE_2D, glslRenderer->vramTexture);
140 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
141 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
142 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
143 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
144
145 memset(glslRenderer->vram, 0, sizeof (glslRenderer->vram));
146
147 {
148 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
149 glslRenderer->mutex = mutex;
150 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
151 glslRenderer->upCond = cond;
152 glslRenderer->downCond = cond;
153 }
154}
155
156void GBAVideoGLSLRendererProcessEvents(struct GBAVideoGLSLRenderer* glslRenderer) {
157 glUseProgram(glslRenderer->program);
158 glUniform1i(UNIFORM_LOCATION("vram"), 0);
159 glUniform1i(UNIFORM_LOCATION("dispcnt"), glslRenderer->io[0][REG_DISPCNT >> 1]);
160 glUniform1i(UNIFORM_LOCATION("bg0cnt"), glslRenderer->io[0][REG_BG0CNT >> 1]);
161 glUniform1i(UNIFORM_LOCATION("bg1cnt"), glslRenderer->io[0][REG_BG1CNT >> 1]);
162 glUniform1i(UNIFORM_LOCATION("bg2cnt"), glslRenderer->io[0][REG_BG2CNT >> 1]);
163 glUniform1i(UNIFORM_LOCATION("bg3cnt"), glslRenderer->io[0][REG_BG3CNT >> 1]);
164 glUniform1i(UNIFORM_LOCATION("bg0hofs"), glslRenderer->io[0][REG_BG0HOFS >> 1]);
165 glUniform1i(UNIFORM_LOCATION("bg0vofs"), glslRenderer->io[0][REG_BG0VOFS >> 1]);
166 glUniform1i(UNIFORM_LOCATION("bg1hofs"), glslRenderer->io[0][REG_BG1HOFS >> 1]);
167 glUniform1i(UNIFORM_LOCATION("bg1vofs"), glslRenderer->io[0][REG_BG1VOFS >> 1]);
168 glUniform1i(UNIFORM_LOCATION("bg2hofs"), glslRenderer->io[0][REG_BG2HOFS >> 1]);
169 glUniform1i(UNIFORM_LOCATION("bg2vofs"), glslRenderer->io[0][REG_BG2VOFS >> 1]);
170 glUniform1i(UNIFORM_LOCATION("bg3hofs"), glslRenderer->io[0][REG_BG3HOFS >> 1]);
171 glUniform1i(UNIFORM_LOCATION("bg3vofs"), glslRenderer->io[0][REG_BG3VOFS >> 1]);
172
173 glEnable(GL_TEXTURE_2D);
174 glActiveTexture(GL_TEXTURE0);
175 glBindTexture(GL_TEXTURE_2D, glslRenderer->vramTexture);
176 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 512, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, glslRenderer->vram);
177
178 GLuint location = glGetAttribLocation(glslRenderer->program, "vert");
179 glEnableVertexAttribArray(location);
180 glVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, _vertices);
181 int y;
182 for (y = 0; y < VIDEO_VERTICAL_PIXELS; ++y) {
183 glUniform1f(UNIFORM_LOCATION("y"), y);
184 glDrawArrays(GL_LINES, 0, 2);
185 }
186 glDisableVertexAttribArray(location);
187 glFlush();
188}
189
190static void GBAVideoGLSLRendererInit(struct GBAVideoRenderer* renderer) {
191 struct GBAVideoGLSLRenderer* glslRenderer = (struct GBAVideoGLSLRenderer*) renderer;
192
193 glslRenderer->state = GLSL_NONE;
194 glslRenderer->y = 0;
195
196 glslRenderer->oldVram = renderer->vram;
197 renderer->vram = &glslRenderer->vram[512 * 160];
198
199 pthread_mutex_init(&glslRenderer->mutex, 0);
200 pthread_cond_init(&glslRenderer->upCond, 0);
201 pthread_cond_init(&glslRenderer->downCond, 0);
202}
203
204static void GBAVideoGLSLRendererDeinit(struct GBAVideoRenderer* renderer) {
205 struct GBAVideoGLSLRenderer* glslRenderer = (struct GBAVideoGLSLRenderer*) renderer;
206
207 /*glDeleteShader(glslRenderer->fragmentShader);
208 glDeleteShader(glslRenderer->vertexShader);
209 glDeleteProgram(glslRenderer->program);
210
211 glDeleteTextures(1, &glslRenderer->paletteTexture);*/
212
213 renderer->vram = glslRenderer->oldVram;
214
215 pthread_mutex_lock(&glslRenderer->mutex);
216 pthread_cond_broadcast(&glslRenderer->upCond);
217 pthread_mutex_unlock(&glslRenderer->mutex);
218
219 pthread_mutex_destroy(&glslRenderer->mutex);
220 pthread_cond_destroy(&glslRenderer->upCond);
221 pthread_cond_destroy(&glslRenderer->downCond);
222}
223
224static void GBAVideoGLSLRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
225 struct GBAVideoGLSLRenderer* glslRenderer = (struct GBAVideoGLSLRenderer*) renderer;
226 GLshort color = 1;
227 color |= (value & 0x001F) << 11;
228 color |= (value & 0x03E0) << 1;
229 color |= (value & 0x7C00) >> 9;
230 glslRenderer->vram[(address >> 1) + glslRenderer->y * 512] = color;
231}
232
233static uint16_t GBAVideoGLSLRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
234 struct GBAVideoGLSLRenderer* glslRenderer = (struct GBAVideoGLSLRenderer*) renderer;
235 glslRenderer->io[glslRenderer->y][address >> 1] = value;
236
237 return value;
238}
239
240static void GBAVideoGLSLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
241 struct GBAVideoGLSLRenderer* glslRenderer = (struct GBAVideoGLSLRenderer*) renderer;
242
243 glslRenderer->y = y + 1;
244 if (y + 1 < VIDEO_VERTICAL_PIXELS) {
245 memcpy(&glslRenderer->vram[(y + 1) * 512], &glslRenderer->vram[y * 512], 1024);
246 memcpy(glslRenderer->io[y + 1], glslRenderer->io[y], sizeof(*glslRenderer->io));
247 } else {
248 glslRenderer->y = 0;
249 memcpy(&glslRenderer->vram[0], &glslRenderer->vram[y * 512], 1024);
250 memcpy(glslRenderer->io[0], glslRenderer->io[y], sizeof(*glslRenderer->io));
251 }
252}
253
254static void GBAVideoGLSLRendererFinishFrame(struct GBAVideoRenderer* renderer) {
255 struct GBAVideoGLSLRenderer* glslRenderer = (struct GBAVideoGLSLRenderer*) renderer;
256
257 pthread_mutex_lock(&glslRenderer->mutex);
258 glslRenderer->state = GLSL_NONE;
259 if (renderer->frameskip > 0) {
260 --renderer->frameskip;
261 } else {
262 renderer->framesPending++;
263 pthread_cond_broadcast(&glslRenderer->upCond);
264 if (!renderer->turbo) {
265 pthread_cond_wait(&glslRenderer->downCond, &glslRenderer->mutex);
266 }
267 }
268 pthread_mutex_unlock(&glslRenderer->mutex);
269}