Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/pc/gfx/gfx_opengl.c
7861 views
1
#include <stdint.h>
2
#include <stdbool.h>
3
4
#ifndef _LANGUAGE_C
5
#define _LANGUAGE_C
6
#endif
7
#include <PR/gbi.h>
8
9
#ifdef __MINGW32__
10
#define FOR_WINDOWS 1
11
#else
12
#define FOR_WINDOWS 0
13
#endif
14
15
#if FOR_WINDOWS
16
#include <GL/glew.h>
17
#include "SDL.h"
18
#define GL_GLEXT_PROTOTYPES 1
19
#include "SDL_opengl.h"
20
#else
21
#ifndef TARGET_MACOS
22
#include <SDL2/SDL.h>
23
#else
24
#include <SDL_opengl.h>
25
#include <stdio.h>
26
#endif
27
#define GL_GLEXT_PROTOTYPES 1
28
#ifndef TARGET_MACOS
29
#include <SDL2/SDL_opengles2.h>
30
#endif
31
#endif
32
33
#include "gfx_cc.h"
34
#include "gfx_pc.h"
35
#include "gfx_rendering_api.h"
36
37
#include "./game/settings.h"
38
39
struct ShaderProgram {
40
uint32_t shader_id;
41
GLuint opengl_program_id;
42
uint8_t num_inputs;
43
bool used_textures[2];
44
uint8_t num_floats;
45
GLint attrib_locations[7];
46
uint8_t attrib_sizes[7];
47
uint8_t num_attribs;
48
bool used_noise;
49
GLint frame_count_location;
50
GLint window_height_location;
51
GLint uTex0Size_location;
52
GLint uTex1Size_location;
53
GLint uTex0Filter_location;
54
GLint uTex1Filter_location;
55
};
56
57
struct GLTexture {
58
GLuint gltex;
59
float size[2];
60
bool filter;
61
};
62
63
#define TEX_CACHE_STEP 512
64
65
static int tex_cache_size = 0;
66
static int num_textures = 0;
67
static struct GLTexture *tex_cache = NULL;
68
69
static struct ShaderProgram *current_program = NULL;
70
static struct GLTexture *opengl_tex[2] = {NULL, NULL};
71
static int opengl_curtex = 0;
72
73
static struct ShaderProgram shader_program_pool[64];
74
static uint8_t shader_program_pool_size;
75
static GLuint opengl_vbo;
76
77
static uint32_t frame_count;
78
static uint32_t current_height;
79
80
// Internal resolution framebuffer objects
81
static GLuint internal_framebuffer = 0;
82
static GLuint internal_color_texture = 0;
83
static GLuint internal_depth_renderbuffer = 0;
84
static uint32_t internal_width = 0;
85
static uint32_t internal_height = 0;
86
87
// Resolved framebuffer for multisampling
88
static GLuint resolved_framebuffer = 0;
89
static GLuint resolved_color_texture = 0;
90
91
// Blit shader for upscaling
92
static GLuint blit_program = 0;
93
static GLuint blit_vbo = 0;
94
static GLint blit_texture_location = -1;
95
static GLint blit_position_location = -1;
96
static GLint blit_texcoord_location = -1;
97
98
// N64-style horizontal blur
99
static GLuint blur_framebuffer = 0;
100
static GLuint blur_texture = 0;
101
static GLuint blur_program = 0;
102
static GLint blur_texture_location = -1;
103
static GLint blur_position_location = -1;
104
static GLint blur_texcoord_location = -1;
105
static GLint blur_screen_width_location = -1;
106
107
static int get_display_index(void) {
108
int display_index = (int) configDefaultMonitor - 1;
109
int num_displays = SDL_GetNumVideoDisplays();
110
111
if (display_index < 0 || display_index >= num_displays) {
112
return 0;
113
}
114
115
return display_index;
116
}
117
118
// Get display dimensions based on fullscreen/windowed mode
119
static void get_display_dimensions(uint32_t *width, uint32_t *height) {
120
if (configFullscreen) {
121
SDL_DisplayMode mode;
122
int display_index = get_display_index();
123
124
if (configFullscreenDisplayMode > 0 &&
125
SDL_GetDisplayMode(display_index, (int) configFullscreenDisplayMode - 1, &mode) == 0) {
126
*width = mode.w;
127
*height = mode.h;
128
return;
129
}
130
131
if (SDL_GetDesktopDisplayMode(display_index, &mode) == 0) {
132
*width = mode.w;
133
*height = mode.h;
134
return;
135
}
136
}
137
138
*width = configWindowWidth;
139
*height = configWindowHeight;
140
}
141
142
// Create a framebuffer object
143
static void create_framebuffer(GLuint* framebuffer, GLuint* texture, GLuint* renderbuffer, uint32_t width, uint32_t height, bool multisample, bool depth) {
144
glGenFramebuffers(1, framebuffer);
145
glBindFramebuffer(GL_FRAMEBUFFER, *framebuffer);
146
147
glGenTextures(1, texture);
148
if (multisample) {
149
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, *texture);
150
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, configAntiAliasing, GL_RGBA8, width, height, GL_TRUE);
151
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, *texture, 0);
152
}
153
else {
154
glBindTexture(GL_TEXTURE_2D, *texture);
155
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
156
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
157
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
158
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *texture, 0);
159
}
160
161
if (depth) {
162
glGenRenderbuffers(1, renderbuffer);
163
glBindRenderbuffer(GL_RENDERBUFFER, *renderbuffer);
164
if (multisample) {
165
glRenderbufferStorageMultisample(GL_RENDERBUFFER, configAntiAliasing, GL_DEPTH_COMPONENT24, width, height);
166
}
167
else {
168
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
169
}
170
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, *renderbuffer);
171
}
172
173
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
174
printf("Error: Framebuffer is not complete!\n");
175
}
176
177
glBindFramebuffer(GL_FRAMEBUFFER, 0);
178
}
179
180
// Create internal framebuffer for render-to-texture
181
static void create_internal_framebuffer(uint32_t width, uint32_t height) {
182
if (internal_framebuffer != 0) {
183
glDeleteFramebuffers(1, &internal_framebuffer);
184
glDeleteTextures(1, &internal_color_texture);
185
glDeleteRenderbuffers(1, &internal_depth_renderbuffer);
186
}
187
188
if (resolved_framebuffer != 0) {
189
glDeleteFramebuffers(1, &resolved_framebuffer);
190
glDeleteTextures(1, &resolved_color_texture);
191
}
192
193
internal_width = width;
194
internal_height = height;
195
196
bool multisample = configAntiAliasing > 0;
197
create_framebuffer(&internal_framebuffer, &internal_color_texture, &internal_depth_renderbuffer, width, height, multisample, true);
198
199
if (multisample) {
200
create_framebuffer(&resolved_framebuffer, &resolved_color_texture, NULL, width, height, false, false);
201
}
202
}
203
204
// Destroy internal framebuffer
205
static void destroy_internal_framebuffer(void) {
206
if (internal_framebuffer != 0) {
207
glDeleteFramebuffers(1, &internal_framebuffer);
208
glDeleteTextures(1, &internal_color_texture);
209
glDeleteRenderbuffers(1, &internal_depth_renderbuffer);
210
internal_framebuffer = 0;
211
}
212
if (resolved_framebuffer != 0) {
213
glDeleteFramebuffers(1, &resolved_framebuffer);
214
glDeleteTextures(1, &resolved_color_texture);
215
resolved_framebuffer = 0;
216
}
217
}
218
219
// Create blur framebuffer
220
static void create_blur_framebuffer(uint32_t width, uint32_t height) {
221
if (blur_framebuffer != 0) {
222
glDeleteFramebuffers(1, &blur_framebuffer);
223
glDeleteTextures(1, &blur_texture);
224
}
225
226
create_framebuffer(&blur_framebuffer, &blur_texture, NULL, width, height, false, false);
227
}
228
229
// Destroy blur framebuffer
230
static void destroy_blur_framebuffer(void) {
231
if (blur_framebuffer != 0) {
232
glDeleteFramebuffers(1, &blur_framebuffer);
233
glDeleteTextures(1, &blur_texture);
234
blur_framebuffer = 0;
235
}
236
}
237
238
static GLuint create_shader_program(const char* vertex_shader_source, const char* fragment_shader_source) {
239
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
240
glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
241
glCompileShader(vertex_shader);
242
243
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
244
glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
245
glCompileShader(fragment_shader);
246
247
GLuint program = glCreateProgram();
248
glAttachShader(program, vertex_shader);
249
glAttachShader(program, fragment_shader);
250
glLinkProgram(program);
251
252
glDeleteShader(vertex_shader);
253
glDeleteShader(fragment_shader);
254
255
return program;
256
}
257
258
// Blit shader source code
259
static const char* blit_vertex_shader_source =
260
"#version 120\n"
261
"attribute vec2 position;\n"
262
"attribute vec2 texCoord;\n"
263
"varying vec2 v_texCoord;\n"
264
"void main() {\n"
265
" gl_Position = vec4(position, 0.0, 1.0);\n"
266
" v_texCoord = texCoord;\n"
267
"}\n";
268
269
static const char* blit_fragment_shader_source =
270
"#version 120\n"
271
"uniform sampler2D u_texture;\n"
272
"varying vec2 v_texCoord;\n"
273
"void main() {\n"
274
" gl_FragColor = texture2D(u_texture, v_texCoord);\n"
275
"}\n";
276
277
// Create and compile blit shaders
278
static void create_blit_shader(void) {
279
if (blit_program != 0) return;
280
281
blit_program = create_shader_program(blit_vertex_shader_source, blit_fragment_shader_source);
282
283
blit_texture_location = glGetUniformLocation(blit_program, "u_texture");
284
blit_position_location = glGetAttribLocation(blit_program, "position");
285
blit_texcoord_location = glGetAttribLocation(blit_program, "texCoord");
286
287
glGenBuffers(1, &blit_vbo);
288
float quad_vertices[] = {
289
-1.0f, 1.0f, 0.0f, 1.0f,
290
-1.0f, -1.0f, 0.0f, 0.0f,
291
1.0f, -1.0f, 1.0f, 0.0f,
292
-1.0f, 1.0f, 0.0f, 1.0f,
293
1.0f, -1.0f, 1.0f, 0.0f,
294
1.0f, 1.0f, 1.0f, 1.0f
295
};
296
glBindBuffer(GL_ARRAY_BUFFER, blit_vbo);
297
glBufferData(GL_ARRAY_BUFFER, sizeof(quad_vertices), quad_vertices, GL_STATIC_DRAW);
298
glBindBuffer(GL_ARRAY_BUFFER, opengl_vbo);
299
}
300
301
// Destroy blit shader
302
static void destroy_blit_shader(void) {
303
if (blit_program != 0) {
304
glDeleteProgram(blit_program);
305
blit_program = 0;
306
}
307
if (blit_vbo != 0) {
308
glDeleteBuffers(1, &blit_vbo);
309
blit_vbo = 0;
310
}
311
}
312
313
// Blur shader source code
314
static const char* blur_fragment_shader_source =
315
"#version 120\n"
316
"uniform sampler2D u_texture;\n"
317
"uniform float u_screen_width;\n"
318
"varying vec2 v_texCoord;\n"
319
"void main() {\n"
320
" float pixel_width = 1.0 / u_screen_width;\n"
321
" vec4 color = vec4(0.0);\n"
322
" float strength = 0.5;\n"
323
" color += texture2D(u_texture, v_texCoord + vec2(-1.0 * pixel_width, 0.0)) * 0.25 * strength;\n"
324
" color += texture2D(u_texture, v_texCoord) * (1.0 - 0.5 * strength);\n"
325
" color += texture2D(u_texture, v_texCoord + vec2(1.0 * pixel_width, 0.0)) * 0.25 * strength;\n"
326
" gl_FragColor = color;\n"
327
"}\n";
328
329
// Create and compile blur shaders
330
static void create_blur_shader(void) {
331
if (blur_program != 0) return;
332
333
blur_program = create_shader_program(blit_vertex_shader_source, blur_fragment_shader_source); // Reusing the blit vertex shader
334
335
blur_texture_location = glGetUniformLocation(blur_program, "u_texture");
336
blur_position_location = glGetAttribLocation(blur_program, "position");
337
blur_texcoord_location = glGetAttribLocation(blur_program, "texCoord");
338
blur_screen_width_location = glGetUniformLocation(blur_program, "u_screen_width");
339
}
340
341
// Destroy blur shader
342
static void destroy_blur_shader(void) {
343
if (blur_program != 0) {
344
glDeleteProgram(blur_program);
345
blur_program = 0;
346
}
347
}
348
349
// Blit internal framebuffer to main framebuffer with nearest neighbor filtering
350
static void blit_internal_framebuffer(GLuint texture) {
351
if (configAntiAliasing > 0 && !configN64Blur) {
352
// Resolve multisampled framebuffer
353
glBindFramebuffer(GL_READ_FRAMEBUFFER, internal_framebuffer);
354
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolved_framebuffer);
355
glBlitFramebuffer(0, 0, internal_width, internal_height, 0, 0, internal_width, internal_height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
356
}
357
358
// Save the current state
359
GLboolean depth_test_enabled = glIsEnabled(GL_DEPTH_TEST);
360
GLboolean blend_enabled = glIsEnabled(GL_BLEND);
361
GLboolean scissor_test_enabled = glIsEnabled(GL_SCISSOR_TEST);
362
GLint array_buffer_binding;
363
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &array_buffer_binding);
364
365
uint32_t window_width, window_height;
366
get_display_dimensions(&window_width, &window_height);
367
368
glBindFramebuffer(GL_FRAMEBUFFER, 0);
369
370
// Calculate the viewport for aspect ratio shit
371
int viewport_x = 0, viewport_y = 0;
372
int viewport_width = window_width, viewport_height = window_height;
373
374
if (configAspectRatio == 1 /* 4:3 */ || configAspectRatio == 2 /* 16:9 */) {
375
float window_aspect = (float)window_width / (float)window_height;
376
float target_aspect = (configAspectRatio == 1) ? (4.0f / 3.0f) : (16.0f / 9.0f);
377
378
if (window_aspect > target_aspect) {
379
viewport_width = (int)((float)window_height * target_aspect);
380
viewport_x = (window_width - viewport_width) / 2;
381
}
382
else if (window_aspect < target_aspect) {
383
viewport_height = (int)((float)window_width / target_aspect);
384
viewport_y = (window_height - viewport_height) / 2;
385
}
386
}
387
388
glViewport(viewport_x, viewport_y, viewport_width, viewport_height);
389
390
glDisable(GL_DEPTH_TEST);
391
glDisable(GL_BLEND);
392
glDisable(GL_SCISSOR_TEST);
393
394
// Clear the entire window with black, then draw the centered framebuffer
395
if ((configAspectRatio == 1 || configAspectRatio == 2) && (viewport_x > 0 || viewport_y > 0)) {
396
glViewport(0, 0, window_width, window_height);
397
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
398
glClear(GL_COLOR_BUFFER_BIT);
399
glViewport(viewport_x, viewport_y, viewport_width, viewport_height);
400
}
401
402
glUseProgram(blit_program);
403
404
glActiveTexture(GL_TEXTURE0);
405
glBindTexture(GL_TEXTURE_2D, texture);
406
glUniform1i(blit_texture_location, 0);
407
408
glBindBuffer(GL_ARRAY_BUFFER, blit_vbo);
409
glEnableVertexAttribArray(blit_position_location);
410
glVertexAttribPointer(blit_position_location, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
411
glEnableVertexAttribArray(blit_texcoord_location);
412
glVertexAttribPointer(blit_texcoord_location, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
413
414
glDrawArrays(GL_TRIANGLES, 0, 6);
415
416
glDisableVertexAttribArray(blit_position_location);
417
glDisableVertexAttribArray(blit_texcoord_location);
418
419
// Restore the state
420
glBindBuffer(GL_ARRAY_BUFFER, array_buffer_binding);
421
422
if (depth_test_enabled)
423
glEnable(GL_DEPTH_TEST);
424
if (blend_enabled)
425
glEnable(GL_BLEND);
426
if (scissor_test_enabled)
427
glEnable(GL_SCISSOR_TEST);
428
}
429
430
static bool gfx_opengl_z_is_from_0_to_1(void) {
431
return false;
432
}
433
434
static void gfx_opengl_vertex_array_set_attribs(struct ShaderProgram *prg) {
435
size_t num_floats = prg->num_floats;
436
size_t pos = 0;
437
438
for (int i = 0; i < prg->num_attribs; i++) {
439
glEnableVertexAttribArray(prg->attrib_locations[i]);
440
glVertexAttribPointer(prg->attrib_locations[i], prg->attrib_sizes[i], GL_FLOAT, GL_FALSE, num_floats * sizeof(float), (void *) (pos * sizeof(float)));
441
pos += prg->attrib_sizes[i];
442
}
443
}
444
445
static void gfx_opengl_set_shader_uniforms(struct ShaderProgram *prg) {
446
if (prg->used_noise) {
447
glUniform1i(prg->frame_count_location, frame_count);
448
glUniform1i(prg->window_height_location, current_height);
449
}
450
}
451
452
static void gfx_opengl_set_texture_uniforms(struct ShaderProgram *prg, const int tile) {
453
if (prg != NULL && prg->used_textures[tile] && opengl_tex[tile]) {
454
if (tile == 0) {
455
glUniform2f(prg->uTex0Size_location, opengl_tex[tile]->size[0], opengl_tex[tile]->size[1]);
456
glUniform1i(prg->uTex0Filter_location, opengl_tex[tile]->filter);
457
}
458
else {
459
glUniform2f(prg->uTex1Size_location, opengl_tex[tile]->size[0], opengl_tex[tile]->size[1]);
460
glUniform1i(prg->uTex1Filter_location, opengl_tex[tile]->filter);
461
}
462
}
463
}
464
465
static void gfx_opengl_unload_shader(struct ShaderProgram *old_prg) {
466
if (old_prg != NULL) {
467
for (int i = 0; i < old_prg->num_attribs; i++) {
468
glDisableVertexAttribArray(old_prg->attrib_locations[i]);
469
}
470
}
471
current_program = NULL;
472
}
473
474
static void gfx_opengl_load_shader(struct ShaderProgram *new_prg) {
475
current_program = new_prg;
476
glUseProgram(new_prg->opengl_program_id);
477
gfx_opengl_vertex_array_set_attribs(new_prg);
478
gfx_opengl_set_shader_uniforms(new_prg);
479
gfx_opengl_set_texture_uniforms(new_prg, 0);
480
gfx_opengl_set_texture_uniforms(new_prg, 1);
481
}
482
483
static void append_str(char *buf, size_t *len, const char *str) {
484
while (*str != '\0') buf[(*len)++] = *str++;
485
}
486
487
static void append_line(char *buf, size_t *len, const char *str) {
488
while (*str != '\0') buf[(*len)++] = *str++;
489
buf[(*len)++] = '\n';
490
}
491
492
static const char *shader_item_to_str(uint32_t item, bool with_alpha, bool only_alpha, bool inputs_have_alpha, bool hint_single_element) {
493
if (!only_alpha) {
494
switch (item) {
495
case SHADER_0:
496
return with_alpha ? "vec4(0.0, 0.0, 0.0, 0.0)" : "vec3(0.0, 0.0, 0.0)";
497
case SHADER_INPUT_1:
498
return with_alpha || !inputs_have_alpha ? "vInput1" : "vInput1.rgb";
499
case SHADER_INPUT_2:
500
return with_alpha || !inputs_have_alpha ? "vInput2" : "vInput2.rgb";
501
case SHADER_INPUT_3:
502
return with_alpha || !inputs_have_alpha ? "vInput3" : "vInput3.rgb";
503
case SHADER_INPUT_4:
504
return with_alpha || !inputs_have_alpha ? "vInput4" : "vInput4.rgb";
505
case SHADER_TEXEL0:
506
return with_alpha ? "texVal0" : "texVal0.rgb";
507
case SHADER_TEXEL0A:
508
return hint_single_element ? "texVal0.a" :
509
(with_alpha ? "vec4(texVal0.a, texVal0.a, texVal0.a, texVal0.a)" : "vec3(texVal0.a, texVal0.a, texVal0.a)");
510
case SHADER_TEXEL1:
511
return with_alpha ? "texVal1" : "texVal1.rgb";
512
}
513
} else {
514
switch (item) {
515
case SHADER_0:
516
return "0.0";
517
case SHADER_INPUT_1:
518
return "vInput1.a";
519
case SHADER_INPUT_2:
520
return "vInput2.a";
521
case SHADER_INPUT_3:
522
return "vInput3.a";
523
case SHADER_INPUT_4:
524
return "vInput4.a";
525
case SHADER_TEXEL0:
526
return "texVal0.a";
527
case SHADER_TEXEL0A:
528
return "texVal0.a";
529
case SHADER_TEXEL1:
530
return "texVal1.a";
531
}
532
}
533
}
534
535
static void append_formula(char *buf, size_t *len, uint8_t c[2][4], bool do_single, bool do_multiply, bool do_mix, bool with_alpha, bool only_alpha, bool opt_alpha) {
536
if (do_single) {
537
append_str(buf, len, shader_item_to_str(c[only_alpha][3], with_alpha, only_alpha, opt_alpha, false));
538
} else if (do_multiply) {
539
append_str(buf, len, shader_item_to_str(c[only_alpha][0], with_alpha, only_alpha, opt_alpha, false));
540
append_str(buf, len, " * ");
541
append_str(buf, len, shader_item_to_str(c[only_alpha][2], with_alpha, only_alpha, opt_alpha, true));
542
} else if (do_mix) {
543
append_str(buf, len, "mix(");
544
append_str(buf, len, shader_item_to_str(c[only_alpha][1], with_alpha, only_alpha, opt_alpha, false));
545
append_str(buf, len, ", ");
546
append_str(buf, len, shader_item_to_str(c[only_alpha][0], with_alpha, only_alpha, opt_alpha, false));
547
append_str(buf, len, ", ");
548
append_str(buf, len, shader_item_to_str(c[only_alpha][2], with_alpha, only_alpha, opt_alpha, true));
549
append_str(buf, len, ")");
550
} else {
551
append_str(buf, len, "(");
552
append_str(buf, len, shader_item_to_str(c[only_alpha][0], with_alpha, only_alpha, opt_alpha, false));
553
append_str(buf, len, " - ");
554
append_str(buf, len, shader_item_to_str(c[only_alpha][1], with_alpha, only_alpha, opt_alpha, false));
555
append_str(buf, len, ") * ");
556
append_str(buf, len, shader_item_to_str(c[only_alpha][2], with_alpha, only_alpha, opt_alpha, true));
557
append_str(buf, len, " + ");
558
append_str(buf, len, shader_item_to_str(c[only_alpha][3], with_alpha, only_alpha, opt_alpha, false));
559
}
560
}
561
562
static struct ShaderProgram *gfx_opengl_create_and_load_new_shader(uint32_t shader_id) {
563
struct CCFeatures cc_features;
564
gfx_cc_get_features(shader_id, &cc_features);
565
566
char vs_buf[1024];
567
char fs_buf[1024];
568
size_t vs_len = 0;
569
size_t fs_len = 0;
570
size_t num_floats = 4;
571
572
// Vertex shader
573
append_line(vs_buf, &vs_len, "#version 110");
574
append_line(vs_buf, &vs_len, "attribute vec4 aVtxPos;");
575
if (cc_features.used_textures[0] || cc_features.used_textures[1]) {
576
append_line(vs_buf, &vs_len, "attribute vec2 aTexCoord;");
577
append_line(vs_buf, &vs_len, "varying vec2 vTexCoord;");
578
num_floats += 2;
579
}
580
if (cc_features.opt_fog) {
581
append_line(vs_buf, &vs_len, "attribute vec4 aFog;");
582
append_line(vs_buf, &vs_len, "varying vec4 vFog;");
583
num_floats += 4;
584
}
585
for (int i = 0; i < cc_features.num_inputs; i++) {
586
vs_len += sprintf(vs_buf + vs_len, "attribute vec%d aInput%d;\n", cc_features.opt_alpha ? 4 : 3, i + 1);
587
vs_len += sprintf(vs_buf + vs_len, "varying vec%d vInput%d;\n", cc_features.opt_alpha ? 4 : 3, i + 1);
588
num_floats += cc_features.opt_alpha ? 4 : 3;
589
}
590
append_line(vs_buf, &vs_len, "void main() {");
591
if (cc_features.used_textures[0] || cc_features.used_textures[1]) {
592
append_line(vs_buf, &vs_len, "vTexCoord = aTexCoord;");
593
}
594
if (cc_features.opt_fog) {
595
append_line(vs_buf, &vs_len, "vFog = aFog;");
596
}
597
for (int i = 0; i < cc_features.num_inputs; i++) {
598
vs_len += sprintf(vs_buf + vs_len, "vInput%d = aInput%d;\n", i + 1, i + 1);
599
}
600
append_line(vs_buf, &vs_len, "gl_Position = aVtxPos;");
601
append_line(vs_buf, &vs_len, "}");
602
603
// Fragment shader
604
append_line(fs_buf, &fs_len, "#version 110");
605
//append_line(fs_buf, &fs_len, "precision mediump float;");
606
if (cc_features.used_textures[0] || cc_features.used_textures[1]) {
607
append_line(fs_buf, &fs_len, "varying vec2 vTexCoord;");
608
}
609
if (cc_features.opt_fog) {
610
append_line(fs_buf, &fs_len, "varying vec4 vFog;");
611
}
612
for (int i = 0; i < cc_features.num_inputs; i++) {
613
fs_len += sprintf(fs_buf + fs_len, "varying vec%d vInput%d;\n", cc_features.opt_alpha ? 4 : 3, i + 1);
614
}
615
if (cc_features.used_textures[0]) {
616
append_line(fs_buf, &fs_len, "uniform sampler2D uTex0;");
617
append_line(fs_buf, &fs_len, "uniform vec2 uTex0Size;");
618
append_line(fs_buf, &fs_len, "uniform bool uTex0Filter;");
619
}
620
if (cc_features.used_textures[1]) {
621
append_line(fs_buf, &fs_len, "uniform sampler2D uTex1;");
622
append_line(fs_buf, &fs_len, "uniform vec2 uTex1Size;");
623
append_line(fs_buf, &fs_len, "uniform bool uTex1Filter;");
624
}
625
626
if (cc_features.used_textures[0] || cc_features.used_textures[1]) {
627
if (configTextureFiltering == 0) {
628
append_line(fs_buf, &fs_len, "vec4 filter3point(in sampler2D tex, in vec2 texCoord, in vec2 texSize) {");
629
append_line(fs_buf, &fs_len, " vec2 texel_pos = texCoord * texSize - 0.5;");
630
append_line(fs_buf, &fs_len, " vec2 frac_pos = fract(texel_pos);");
631
append_line(fs_buf, &fs_len, " vec2 floor_pos = floor(texel_pos);");
632
append_line(fs_buf, &fs_len, " float s = step(1.0, frac_pos.x + frac_pos.y);");
633
append_line(fs_buf, &fs_len, " vec2 p0_offset = floor_pos + vec2(s);");
634
append_line(fs_buf, &fs_len, " vec2 p1_offset = floor_pos + vec2(1.0 - s, s);");
635
append_line(fs_buf, &fs_len, " vec2 p2_offset = floor_pos + vec2(s, 1.0 - s);");
636
append_line(fs_buf, &fs_len, " vec4 c0 = texture2D(tex, (p0_offset + 0.5) / texSize);");
637
append_line(fs_buf, &fs_len, " vec4 c1 = texture2D(tex, (p1_offset + 0.5) / texSize);");
638
append_line(fs_buf, &fs_len, " vec4 c2 = texture2D(tex, (p2_offset + 0.5) / texSize);");
639
append_line(fs_buf, &fs_len, " vec2 weights = abs(frac_pos - s);");
640
append_line(fs_buf, &fs_len, " return c0 + weights.x * (c1 - c0) + weights.y * (c2 - c0);");
641
append_line(fs_buf, &fs_len, "}");
642
append_line(fs_buf, &fs_len, "vec4 sampleTexture(in sampler2D tex, in vec2 uv, in vec2 texSize, in bool dofilter) {");
643
append_line(fs_buf, &fs_len, "if (dofilter)");
644
append_line(fs_buf, &fs_len, " return filter3point(tex, uv, texSize);");
645
append_line(fs_buf, &fs_len, "else");
646
append_line(fs_buf, &fs_len, " return texture2D(tex, uv);");
647
append_line(fs_buf, &fs_len, "}");
648
}
649
else {
650
append_line(fs_buf, &fs_len, "vec4 sampleTexture(in sampler2D tex, in vec2 uv, in vec2 texSize, in bool dofilter) {");
651
append_line(fs_buf, &fs_len, "return texture2D(tex, uv);");
652
append_line(fs_buf, &fs_len, "}");
653
}
654
}
655
656
if (cc_features.opt_alpha && cc_features.opt_noise) {
657
append_line(fs_buf, &fs_len, "uniform int frame_count;");
658
append_line(fs_buf, &fs_len, "uniform int window_height;");
659
660
append_line(fs_buf, &fs_len, "float random(in vec3 value) {");
661
append_line(fs_buf, &fs_len, " float random = dot(sin(value), vec3(12.9898, 78.233, 37.719));");
662
append_line(fs_buf, &fs_len, " return fract(sin(random) * 143758.5453);");
663
append_line(fs_buf, &fs_len, "}");
664
}
665
666
append_line(fs_buf, &fs_len, "void main() {");
667
668
if (cc_features.used_textures[0]) {
669
append_line(fs_buf, &fs_len, "vec4 texVal0 = sampleTexture(uTex0, vTexCoord, uTex0Size, uTex0Filter);");
670
}
671
if (cc_features.used_textures[1]) {
672
append_line(fs_buf, &fs_len, "vec4 texVal1 = sampleTexture(uTex1, vTexCoord, uTex1Size, uTex1Filter);");
673
}
674
675
append_str(fs_buf, &fs_len, cc_features.opt_alpha ? "vec4 texel = " : "vec3 texel = ");
676
if (!cc_features.color_alpha_same && cc_features.opt_alpha) {
677
append_str(fs_buf, &fs_len, "vec4(");
678
append_formula(fs_buf, &fs_len, cc_features.c, cc_features.do_single[0], cc_features.do_multiply[0], cc_features.do_mix[0], false, false, true);
679
append_str(fs_buf, &fs_len, ", ");
680
append_formula(fs_buf, &fs_len, cc_features.c, cc_features.do_single[1], cc_features.do_multiply[1], cc_features.do_mix[1], true, true, true);
681
append_str(fs_buf, &fs_len, ")");
682
} else {
683
append_formula(fs_buf, &fs_len, cc_features.c, cc_features.do_single[0], cc_features.do_multiply[0], cc_features.do_mix[0], cc_features.opt_alpha, false, cc_features.opt_alpha);
684
}
685
append_line(fs_buf, &fs_len, ";");
686
687
if (cc_features.opt_texture_edge && cc_features.opt_alpha) {
688
append_line(fs_buf, &fs_len, "if (texel.a > 0.3) texel.a = 1.0; else discard;");
689
}
690
// TODO discard if alpha is 0?
691
if (cc_features.opt_fog) {
692
if (cc_features.opt_alpha) {
693
append_line(fs_buf, &fs_len, "texel = vec4(mix(texel.rgb, vFog.rgb, vFog.a), texel.a);");
694
} else {
695
append_line(fs_buf, &fs_len, "texel = mix(texel, vFog.rgb, vFog.a);");
696
}
697
}
698
699
if (cc_features.opt_alpha && cc_features.opt_noise) {
700
if (configNoiseType) {
701
append_line(fs_buf, &fs_len, "texel.a *= floor(clamp(random(vec3(floor(gl_FragCoord.xy), float(frame_count)) + texel.a - 0.5), 0.0, 1.0) + 0.5);");
702
}
703
else {
704
append_line(fs_buf, &fs_len, "texel.a *= floor(clamp(random(vec3(floor(gl_FragCoord.xy * (240.0 / float(window_height))), float(frame_count)) + texel.a - 0.5), 0.0, 1.0) + 0.5);");
705
}
706
}
707
708
if (cc_features.opt_alpha) {
709
append_line(fs_buf, &fs_len, "gl_FragColor = texel;");
710
} else {
711
append_line(fs_buf, &fs_len, "gl_FragColor = vec4(texel, 1.0);");
712
}
713
append_line(fs_buf, &fs_len, "}");
714
715
vs_buf[vs_len] = '\0';
716
fs_buf[fs_len] = '\0';
717
718
/*puts("Vertex shader:");
719
puts(vs_buf);
720
puts("Fragment shader:");
721
puts(fs_buf);
722
puts("End");*/
723
724
const GLchar *sources[2] = { vs_buf, fs_buf };
725
const GLint lengths[2] = { vs_len, fs_len };
726
GLint success;
727
728
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
729
glShaderSource(vertex_shader, 1, &sources[0], &lengths[0]);
730
glCompileShader(vertex_shader);
731
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
732
if (!success) {
733
GLint max_length = 0;
734
glGetShaderiv(vertex_shader, GL_INFO_LOG_LENGTH, &max_length);
735
char error_log[1024];
736
fprintf(stderr, "Vertex shader compilation failed\n");
737
glGetShaderInfoLog(vertex_shader, max_length, &max_length, &error_log[0]);
738
fprintf(stderr, "%s\n", &error_log[0]);
739
abort();
740
}
741
742
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
743
glShaderSource(fragment_shader, 1, &sources[1], &lengths[1]);
744
glCompileShader(fragment_shader);
745
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
746
if (!success) {
747
GLint max_length = 0;
748
glGetShaderiv(fragment_shader, GL_INFO_LOG_LENGTH, &max_length);
749
char error_log[1024];
750
fprintf(stderr, "Fragment shader compilation failed\n");
751
glGetShaderInfoLog(fragment_shader, max_length, &max_length, &error_log[0]);
752
fprintf(stderr, "%s\n", &error_log[0]);
753
abort();
754
}
755
756
GLuint shader_program = glCreateProgram();
757
glAttachShader(shader_program, vertex_shader);
758
glAttachShader(shader_program, fragment_shader);
759
glLinkProgram(shader_program);
760
761
size_t cnt = 0;
762
763
struct ShaderProgram *prg = &shader_program_pool[shader_program_pool_size++];
764
prg->attrib_locations[cnt] = glGetAttribLocation(shader_program, "aVtxPos");
765
prg->attrib_sizes[cnt] = 4;
766
++cnt;
767
768
if (cc_features.used_textures[0] || cc_features.used_textures[1]) {
769
prg->attrib_locations[cnt] = glGetAttribLocation(shader_program, "aTexCoord");
770
prg->attrib_sizes[cnt] = 2;
771
++cnt;
772
}
773
774
if (cc_features.opt_fog) {
775
prg->attrib_locations[cnt] = glGetAttribLocation(shader_program, "aFog");
776
prg->attrib_sizes[cnt] = 4;
777
++cnt;
778
}
779
780
for (int i = 0; i < cc_features.num_inputs; i++) {
781
char name[16];
782
sprintf(name, "aInput%d", i + 1);
783
prg->attrib_locations[cnt] = glGetAttribLocation(shader_program, name);
784
prg->attrib_sizes[cnt] = cc_features.opt_alpha ? 4 : 3;
785
++cnt;
786
}
787
788
prg->shader_id = shader_id;
789
prg->opengl_program_id = shader_program;
790
prg->num_inputs = cc_features.num_inputs;
791
prg->used_textures[0] = cc_features.used_textures[0];
792
prg->used_textures[1] = cc_features.used_textures[1];
793
prg->num_floats = num_floats;
794
prg->num_attribs = cnt;
795
796
gfx_opengl_load_shader(prg);
797
798
if (cc_features.used_textures[0]) {
799
GLint sampler_location = glGetUniformLocation(shader_program, "uTex0");
800
prg->uTex0Size_location = glGetUniformLocation(shader_program, "uTex0Size");
801
prg->uTex0Filter_location = glGetUniformLocation(shader_program, "uTex0Filter");
802
glUniform1i(sampler_location, 0);
803
}
804
if (cc_features.used_textures[1]) {
805
GLint sampler_location = glGetUniformLocation(shader_program, "uTex1");
806
prg->uTex1Size_location = glGetUniformLocation(shader_program, "uTex1Size");
807
prg->uTex1Filter_location = glGetUniformLocation(shader_program, "uTex1Filter");
808
glUniform1i(sampler_location, 1);
809
}
810
811
if (cc_features.opt_alpha && cc_features.opt_noise) {
812
prg->frame_count_location = glGetUniformLocation(shader_program, "frame_count");
813
prg->window_height_location = glGetUniformLocation(shader_program, "window_height");
814
prg->used_noise = true;
815
} else {
816
prg->used_noise = false;
817
}
818
819
return prg;
820
}
821
822
static struct ShaderProgram *gfx_opengl_lookup_shader(uint32_t shader_id) {
823
for (size_t i = 0; i < shader_program_pool_size; i++) {
824
if (shader_program_pool[i].shader_id == shader_id) {
825
return &shader_program_pool[i];
826
}
827
}
828
return NULL;
829
}
830
831
static void gfx_opengl_shader_get_info(struct ShaderProgram *prg, uint8_t *num_inputs, bool used_textures[2]) {
832
*num_inputs = prg->num_inputs;
833
used_textures[0] = prg->used_textures[0];
834
used_textures[1] = prg->used_textures[1];
835
}
836
837
static GLuint gfx_opengl_new_texture(void) {
838
if (num_textures >= tex_cache_size) {
839
tex_cache_size += TEX_CACHE_STEP;
840
tex_cache = realloc(tex_cache, sizeof(struct GLTexture) * tex_cache_size);
841
// Invalidate to prevent pointing to garbage
842
opengl_tex[0] = NULL;
843
opengl_tex[1] = NULL;
844
}
845
glGenTextures(1, &tex_cache[num_textures].gltex);
846
return num_textures++;
847
}
848
849
static void gfx_opengl_select_texture(int tile, GLuint texture_id) {
850
opengl_tex[tile] = tex_cache + texture_id;
851
opengl_curtex = tile;
852
glActiveTexture(GL_TEXTURE0 + tile);
853
glBindTexture(GL_TEXTURE_2D, opengl_tex[tile]->gltex);
854
gfx_opengl_set_texture_uniforms(current_program, tile);
855
}
856
857
static void gfx_opengl_upload_texture(const uint8_t *rgba32_buf, int width, int height) {
858
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba32_buf);
859
opengl_tex[opengl_curtex]->size[0] = width;
860
opengl_tex[opengl_curtex]->size[1] = height;
861
}
862
863
static uint32_t gfx_cm_to_opengl(uint32_t val) {
864
if (val & G_TX_CLAMP) {
865
return GL_CLAMP_TO_EDGE;
866
}
867
return (val & G_TX_MIRROR) ? GL_MIRRORED_REPEAT : GL_REPEAT;
868
}
869
870
static void gfx_opengl_set_sampler_parameters(int tile, bool linear_filter, uint32_t cms, uint32_t cmt) {
871
glActiveTexture(GL_TEXTURE0 + tile);
872
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear_filter ? GL_LINEAR : GL_NEAREST);
873
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear_filter ? GL_LINEAR : GL_NEAREST);
874
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gfx_cm_to_opengl(cms));
875
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gfx_cm_to_opengl(cmt));
876
if (configTextureFiltering > 0 && configAnisotropicFiltering > 0) {
877
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, configAnisotropicFiltering);
878
}
879
opengl_curtex = tile;
880
if (opengl_tex[tile]) {
881
opengl_tex[tile]->filter = (configTextureFiltering == 0 && linear_filter);
882
gfx_opengl_set_texture_uniforms(current_program, tile);
883
}
884
}
885
886
static void gfx_opengl_set_depth_test(bool depth_test) {
887
if (depth_test) {
888
glEnable(GL_DEPTH_TEST);
889
} else {
890
glDisable(GL_DEPTH_TEST);
891
}
892
}
893
894
static void gfx_opengl_set_depth_mask(bool z_upd) {
895
glDepthMask(z_upd ? GL_TRUE : GL_FALSE);
896
}
897
898
static void gfx_opengl_set_zmode_decal(bool zmode_decal) {
899
if (zmode_decal) {
900
glPolygonOffset(-2, -2);
901
glEnable(GL_POLYGON_OFFSET_FILL);
902
} else {
903
glPolygonOffset(0, 0);
904
glDisable(GL_POLYGON_OFFSET_FILL);
905
}
906
}
907
908
static void gfx_opengl_set_viewport(int x, int y, int width, int height) {
909
glViewport(x, y, width, height);
910
current_height = height;
911
}
912
913
static void gfx_opengl_set_scissor(int x, int y, int width, int height) {
914
glScissor(x, y, width, height);
915
}
916
917
static void gfx_opengl_set_use_alpha(bool use_alpha) {
918
if (use_alpha) {
919
glEnable(GL_BLEND);
920
} else {
921
glDisable(GL_BLEND);
922
}
923
}
924
925
static void gfx_opengl_draw_triangles(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris) {
926
//printf("flushing %d tris\n", buf_vbo_num_tris);
927
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * buf_vbo_len, buf_vbo, GL_STREAM_DRAW);
928
glDrawArrays(GL_TRIANGLES, 0, 3 * buf_vbo_num_tris);
929
}
930
931
static void gfx_opengl_init(void) {
932
#if FOR_WINDOWS
933
glewInit();
934
#endif
935
936
tex_cache_size = TEX_CACHE_STEP;
937
tex_cache = calloc(tex_cache_size, sizeof(struct GLTexture));
938
if (!tex_cache) {
939
// Handling memory allocation failure
940
fprintf(stderr, "Out of memory allocating texture cache!\n");
941
exit(1);
942
}
943
944
glGenBuffers(1, &opengl_vbo);
945
946
glBindBuffer(GL_ARRAY_BUFFER, opengl_vbo);
947
948
glDepthFunc(GL_LEQUAL);
949
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
950
951
// Enable multisampling if anti-aliasing is configured
952
if (configAntiAliasing > 0) {
953
glEnable(GL_MULTISAMPLE);
954
}
955
}
956
957
static void gfx_opengl_on_resize(void) {
958
}
959
960
static void gfx_opengl_start_frame(void) {
961
frame_count++;
962
963
uint32_t window_width, window_height;
964
get_display_dimensions(&window_width, &window_height);
965
966
// I'm sweating BULLETS here
967
if (internal_framebuffer == 0 || internal_width != gfx_current_dimensions.width || internal_height != gfx_current_dimensions.height) {
968
create_internal_framebuffer(gfx_current_dimensions.width, gfx_current_dimensions.height);
969
create_blit_shader();
970
if (configN64Blur) {
971
create_blur_framebuffer(gfx_current_dimensions.width, gfx_current_dimensions.height);
972
create_blur_shader();
973
}
974
}
975
glBindFramebuffer(GL_FRAMEBUFFER, internal_framebuffer);
976
glViewport(0, 0, internal_width, internal_height);
977
978
glDisable(GL_SCISSOR_TEST);
979
glDepthMask(GL_TRUE); // Must be set to clear Z-buffer
980
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
981
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
982
glClear(GL_DEPTH_BUFFER_BIT);
983
glEnable(GL_SCISSOR_TEST);
984
}
985
986
static void apply_n64_blur(GLuint final_texture) {
987
// Save the current state
988
// MORS TODO: Surely, this is wildly inefficient? I don't think I'm supposed to do it like this, but it works so idc
989
GLboolean depth_test_enabled = glIsEnabled(GL_DEPTH_TEST);
990
GLboolean blend_enabled = glIsEnabled(GL_BLEND);
991
GLboolean scissor_test_enabled = glIsEnabled(GL_SCISSOR_TEST);
992
GLint array_buffer_binding;
993
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &array_buffer_binding);
994
995
glBindFramebuffer(GL_FRAMEBUFFER, blur_framebuffer);
996
glViewport(0, 0, internal_width, internal_height);
997
998
glDisable(GL_DEPTH_TEST);
999
glDisable(GL_BLEND);
1000
glDisable(GL_SCISSOR_TEST);
1001
1002
glUseProgram(blur_program);
1003
1004
glActiveTexture(GL_TEXTURE0);
1005
glBindTexture(GL_TEXTURE_2D, final_texture);
1006
glUniform1i(blur_texture_location, 0);
1007
glUniform1f(blur_screen_width_location, (float)internal_width);
1008
1009
glBindBuffer(GL_ARRAY_BUFFER, blit_vbo);
1010
glEnableVertexAttribArray(blur_position_location);
1011
glVertexAttribPointer(blur_position_location, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
1012
glEnableVertexAttribArray(blur_texcoord_location);
1013
glVertexAttribPointer(blur_texcoord_location, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
1014
1015
glDrawArrays(GL_TRIANGLES, 0, 6);
1016
1017
glDisableVertexAttribArray(blur_position_location);
1018
glDisableVertexAttribArray(blur_texcoord_location);
1019
1020
// Restore the state
1021
glBindBuffer(GL_ARRAY_BUFFER, array_buffer_binding);
1022
1023
if (depth_test_enabled)
1024
glEnable(GL_DEPTH_TEST);
1025
if (blend_enabled)
1026
glEnable(GL_BLEND);
1027
if (scissor_test_enabled)
1028
glEnable(GL_SCISSOR_TEST);
1029
}
1030
1031
static void gfx_opengl_end_frame(void) {
1032
if (configN64Blur) {
1033
GLuint final_texture = (configAntiAliasing > 0) ? resolved_color_texture : internal_color_texture;
1034
if (configAntiAliasing > 0) {
1035
glBindFramebuffer(GL_READ_FRAMEBUFFER, internal_framebuffer);
1036
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolved_framebuffer);
1037
glBlitFramebuffer(0, 0, internal_width, internal_height, 0, 0, internal_width, internal_height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
1038
}
1039
apply_n64_blur(final_texture);
1040
blit_internal_framebuffer(blur_texture);
1041
}
1042
else {
1043
blit_internal_framebuffer((configAntiAliasing > 0) ? resolved_color_texture : internal_color_texture);
1044
}
1045
}
1046
1047
static void gfx_opengl_finish_render(void) {
1048
}
1049
1050
struct GfxRenderingAPI gfx_opengl_api = {
1051
gfx_opengl_z_is_from_0_to_1,
1052
gfx_opengl_unload_shader,
1053
gfx_opengl_load_shader,
1054
gfx_opengl_create_and_load_new_shader,
1055
gfx_opengl_lookup_shader,
1056
gfx_opengl_shader_get_info,
1057
gfx_opengl_new_texture,
1058
gfx_opengl_select_texture,
1059
gfx_opengl_upload_texture,
1060
gfx_opengl_set_sampler_parameters,
1061
gfx_opengl_set_depth_test,
1062
gfx_opengl_set_depth_mask,
1063
gfx_opengl_set_zmode_decal,
1064
gfx_opengl_set_viewport,
1065
gfx_opengl_set_scissor,
1066
gfx_opengl_set_use_alpha,
1067
gfx_opengl_draw_triangles,
1068
gfx_opengl_init,
1069
gfx_opengl_on_resize,
1070
gfx_opengl_start_frame,
1071
gfx_opengl_end_frame,
1072
gfx_opengl_finish_render
1073
};
1074
1075