pcem/src/wx-sdl2-video-gl3.c
C.W. Betts 14d199b004 Partially revert 1eb1cc5e5b: it broke the Windows build.
Go back to using SDL2 in front of SDL2 header includes.
2021-05-24 16:20:43 -06:00

1676 lines
68 KiB
C

#include <SDL2/SDL.h>
#define BITMAP WINDOWS_BITMAP
#include <SDL2/SDL_opengl.h>
#if SDL_VERSION_ATLEAST(2, 0, 4)
#include <SDL2/SDL_opengl_glext.h>
#endif
#undef BITMAP
#include "wx-sdl2-glw.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "video.h"
#include "wx-sdl2-video.h"
#include "wx-sdl2-video-renderer.h"
#include "ibm.h"
#include "wx-glslp-parser.h"
#include "wx-utils.h"
#include "config.h"
#include "wx-glsl.h"
extern char* get_filename(char*);
extern int take_screenshot;
extern void screenshot_taken(unsigned char* rgb, int width, int height);
#define SCALE_SOURCE 0
#define SCALE_VIEWPORT 1
#define SCALE_ABSOLUTE 2
float gl3_shader_refresh_rate = 0;
float gl3_input_scale = 1.0f;
int gl3_input_stretch = FULLSCR_SCALE_FULL;
char gl3_shader_file[MAX_USER_SHADERS][512];
static int max_texture_size;
static SDL_GLContext context = NULL;
static struct shader_texture scene_texture;
static glsl_t* active_shader;
static glw_t* glw;
static GLfloat matrix[] = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
};
extern int video_scale_mode;
extern int video_vsync;
extern int video_focus_dim;
extern int video_refresh_rate;
static int glsl_version[2];
const char* vertex_shader_default_tex_src =
"#version 130\n"
"\n"
"in vec4 VertexCoord;\n"
"in vec2 TexCoord;\n"
"\n"
"out vec2 texCoord;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = VertexCoord;\n"
" texCoord = TexCoord;\n"
"}\n";
const char* fragment_shader_default_tex_src =
"#version 130\n"
"\n"
"in vec2 texCoord;\n"
"uniform sampler2D Texture;\n"
"\n"
"out vec4 color;"
"\n"
"void main()\n"
"{\n"
" color = texture(Texture, texCoord);\n"
"}\n";
const char* vertex_shader_default_color_src =
"#version 130\n"
"\n"
"in vec4 VertexCoord;\n"
"in vec4 Color;\n"
"\n"
"out vec4 color;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = VertexCoord;\n"
" color = Color;\n"
"}\n";
const char* fragment_shader_default_color_src =
"#version 130\n"
"\n"
"in vec4 color;\n"
"\n"
"out vec4 outColor;"
"\n"
"void main()\n"
"{\n"
" outColor = color;\n"
"}\n";
static int next_pow2(unsigned int n)
{
n--;
n |= n >> 1; // Divide by 2^k for consecutive doublings of k up to 32,
n |= n >> 2; // and then or the results.
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n++;
return n;
}
static int compile_shader(GLenum shader_type, const char* prepend, const char* program, int* dst)
{
const char* source[3];
char version[50];
int ver = 0;
char *version_loc = strstr(program, "#version");
if (version_loc)
ver = (int)strtol(version_loc + 8, (char**)&program, 10);
else
{
ver = glsl_version[0] * 100 + glsl_version[1] * 10;
if (ver == 300)
ver = 130;
else if (ver == 310)
ver = 140;
else if (ver == 320)
ver = 150;
}
sprintf(version, "#version %d\n", ver);
source[0] = version;
source[1] = prepend ? prepend : "";
source[2] = program;
pclog("GLSL version %d\n", ver);
GLuint shader = glw->glCreateShader(shader_type);
glw->glShaderSource(shader, 3, source, NULL);
glw->glCompileShader(shader);
GLint status = 0;
glw->glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (!status)
{
GLint length;
glw->glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
char* log = malloc(length);
glw->glGetShaderInfoLog(shader, length, &length, log);
wx_simple_messagebox("GLSL Error", "Could not compile shader:\n%s", log);
pclog("Could not compile shader: %s\n", log);
// pclog("Shader: %s\n", program);
free(log);
return 0;
}
*dst = shader;
return 1;
}
static int create_program(struct shader_program* program)
{
GLint status;
program->id = glw->glCreateProgram();
glw->glAttachShader(program->id, program->vertex_shader);
glw->glAttachShader(program->id, program->fragment_shader);
glw->glLinkProgram(program->id);
glw->glDeleteShader(program->vertex_shader);
glw->glDeleteShader(program->fragment_shader);
program->vertex_shader = program->fragment_shader = 0;
glw->glGetProgramiv(program->id, GL_LINK_STATUS, &status);
if (!status)
{
int maxLength;
int length;
glw->glGetProgramiv(program->id, GL_INFO_LOG_LENGTH, &maxLength);
char* log = malloc(maxLength);
glw->glGetProgramInfoLog(program->id, maxLength, &length, log);
wx_simple_messagebox("GLSL Error", "Program not linked:\n%s", log);
free(log);
return 0;
}
return 1;
}
static GLuint get_uniform(GLuint program, const char* name)
{
return glw->glGetUniformLocation(program, name);
}
static GLuint get_attrib(GLuint program, const char* name)
{
return glw->glGetAttribLocation(program, name);
}
static void find_uniforms(struct glsl_shader* glsl, int num_pass)
{
int i;
char s[50];
struct shader_pass* pass = &glsl->passes[num_pass];
int p = pass->program.id;
glw->glUseProgram(p);
struct shader_uniforms* u = &pass->uniforms;
u->mvp_matrix = get_uniform(p, "MVPMatrix");
u->vertex_coord = get_attrib(p, "VertexCoord");
u->tex_coord = get_attrib(p, "TexCoord");
u->color = get_attrib(p, "Color");
u->frame_count = get_uniform(p, "FrameCount");
u->frame_direction = get_uniform(p, "FrameDirection");
u->texture = get_uniform(p, "Texture");
u->input_size = get_uniform(p, "InputSize");
u->texture_size = get_uniform(p, "TextureSize");
u->output_size = get_uniform(p, "OutputSize");
u->orig.texture = get_uniform(p, "OrigTexture");
u->orig.input_size = get_uniform(p, "OrigInputSize");
u->orig.texture_size = get_uniform(p, "OrigTextureSize");
for (i = 0; i < glsl->num_passes; ++i)
{
sprintf(s, "Pass%dTexture", (i+1));
u->pass[i].texture = get_uniform(p, s);
sprintf(s, "Pass%dInputSize", (i+1));
u->pass[i].input_size = get_uniform(p, s);
sprintf(s, "Pass%dTextureSize", (i+1));
u->pass[i].texture_size = get_uniform(p, s);
sprintf(s, "PassPrev%dTexture", num_pass-i);
u->prev_pass[i].texture = get_uniform(p, s);
sprintf(s, "PassPrev%dInputSize", num_pass-i);
u->prev_pass[i].input_size = get_uniform(p, s);
sprintf(s, "PassPrev%dTextureSize", num_pass-i);
u->prev_pass[i].texture_size = get_uniform(p, s);
}
u->prev[0].texture = get_uniform(p, "PrevTexture");
u->prev[0].tex_coord = get_attrib(p, "PrevTexCoord");
for (i = 1; i < MAX_PREV; ++i)
{
sprintf(s, "Prev%dTexture", i);
u->prev[i].texture = get_uniform(p, s);
sprintf(s, "Prev%dTexCoord", i);
u->prev[i].tex_coord = get_attrib(p, s);
}
for (i = 0; i < MAX_PREV; ++i)
if (u->prev[i].texture >= 0) glsl->has_prev = 1;
for (i = 0; i < glsl->num_lut_textures; ++i)
u->lut_textures[i] = get_uniform(p, glsl->lut_textures[i].name);
for (i = 0; i < glsl->num_parameters; ++i)
u->parameters[i] = get_uniform(p, glsl->parameters[i].id);
glw->glUseProgram(0);
}
static void set_scale_mode(char* scale, int* dst)
{
if (!strcmp(scale, "viewport"))
*dst = SCALE_VIEWPORT;
else if (!strcmp(scale, "absolute"))
*dst = SCALE_ABSOLUTE;
else
*dst = SCALE_SOURCE;
}
static void setup_scale(struct shader* shader, struct shader_pass* pass)
{
set_scale_mode(shader->scale_type_x, &pass->scale.mode[0]);
set_scale_mode(shader->scale_type_y, &pass->scale.mode[1]);
pass->scale.value[0] = shader->scale_x;
pass->scale.value[1] = shader->scale_y;
}
static void create_texture(struct shader_texture* tex)
{
if (tex->width > max_texture_size)
tex->width = max_texture_size;
if (tex->height > max_texture_size)
tex->height = max_texture_size;
pclog("Create texture with size %dx%d\n", tex->width, tex->height);
glGenTextures(1, (GLuint *)&tex->id);
glBindTexture(GL_TEXTURE_2D, tex->id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, tex->wrap_mode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tex->wrap_mode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tex->min_filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tex->mag_filter);
glTexImage2D(GL_TEXTURE_2D, 0, tex->internal_format, tex->width, tex->height, 0, tex->format, tex->type, tex->data);
if (tex->mipmap)
glw->glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
}
static void delete_texture(struct shader_texture* tex)
{
if (tex->id > 0)
glDeleteTextures(1, (GLuint *)&tex->id);
tex->id = 0;
}
static void delete_fbo(struct shader_fbo* fbo)
{
if (fbo->id >= 0)
{
glw->glDeleteFramebuffers(1, (GLuint *)&fbo->id);
delete_texture(&fbo->texture);
}
}
static void delete_program(struct shader_program* program)
{
if (program->vertex_shader) glw->glDeleteShader(program->vertex_shader);
if (program->fragment_shader) glw->glDeleteShader(program->fragment_shader);
glw->glDeleteProgram(program->id);
}
static void delete_vbo(struct shader_vbo* vbo)
{
if (vbo->color >= 0) glw->glDeleteBuffers(1, (GLuint *)&vbo->color);
glw->glDeleteBuffers(1, (GLuint *)&vbo->vertex_coord);
glw->glDeleteBuffers(1, (GLuint *)&vbo->tex_coord);
}
static void delete_pass(struct shader_pass* pass)
{
delete_fbo(&pass->fbo);
delete_vbo(&pass->vbo);
delete_program(&pass->program);
glw->glDeleteVertexArrays(1, (GLuint *)&pass->vertex_array);
}
static void delete_prev(struct shader_prev* prev)
{
delete_fbo(&prev->fbo);
delete_vbo(&prev->vbo);
}
static void delete_shader(struct glsl_shader* glsl)
{
int i;
for (i = 0; i < glsl->num_passes; ++i)
delete_pass(&glsl->passes[i]);
if (glsl->has_prev)
{
delete_pass(&glsl->prev_scene);
for (i = 0; i < MAX_PREV; ++i)
delete_prev(&glsl->prev[i]);
}
for (i = 0; i < glsl->num_lut_textures; ++i)
delete_texture(&glsl->lut_textures[i].texture);
}
static void delete_glsl(glsl_t* glsl)
{
int i;
for (i = 0; i < glsl->num_shaders; ++i)
delete_shader(&glsl->shaders[i]);
delete_pass(&glsl->scene);
delete_pass(&glsl->fs_color);
delete_pass(&glsl->final_pass);
#ifdef SDL2_SHADER_DEBUG
delete_pass(&glsl->debug);
#endif
}
static void create_fbo(struct shader_fbo* fbo)
{
create_texture(&fbo->texture);
glw->glGenFramebuffers(1, (GLuint *)&fbo->id);
glw->glBindFramebuffer(GL_FRAMEBUFFER, fbo->id);
glw->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->texture.id, 0);
if (glw->glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
pclog("Could not create framebuffer!\n");
glw->glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
static void setup_fbo(struct shader* shader, struct shader_fbo* fbo)
{
fbo->texture.internal_format = GL_RGBA8;
fbo->texture.format = GL_RGBA;
fbo->texture.min_filter = fbo->texture.mag_filter = shader->filter_linear ? GL_LINEAR : GL_NEAREST;
fbo->texture.width = 1024;
fbo->texture.height = 1024;
fbo->texture.type = GL_UNSIGNED_BYTE;
if (!strcmp(shader->wrap_mode, "repeat"))
fbo->texture.wrap_mode = GL_REPEAT;
else if (!strcmp(shader->wrap_mode, "mirrored_repeat"))
fbo->texture.wrap_mode = GL_MIRRORED_REPEAT;
else if (!strcmp(shader->wrap_mode, "clamp_to_edge"))
fbo->texture.wrap_mode = GL_CLAMP_TO_EDGE;
else
fbo->texture.wrap_mode = GL_CLAMP_TO_BORDER;
fbo->srgb = 0;
if (shader->srgb_framebuffer)
{
fbo->texture.internal_format = GL_SRGB8_ALPHA8;
fbo->srgb = 1;
}
else if (shader->float_framebuffer)
{
fbo->texture.internal_format = GL_RGBA32F;
fbo->texture.type = GL_FLOAT;
}
if (fbo->texture.mipmap)
fbo->texture.min_filter = shader->filter_linear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST;
create_fbo(fbo);
}
static void recreate_fbo(struct shader_fbo* fbo, int width, int height)
{
if (width != fbo->texture.width || height != fbo->texture.height)
{
glw->glDeleteFramebuffers(1, (GLuint *)&fbo->id);
glDeleteTextures(1, (GLuint *)&fbo->texture.id);
fbo->texture.width = width;
fbo->texture.height = height;
create_fbo(fbo);
}
}
static int create_default_shader_tex(struct shader_pass* pass)
{
if (
!compile_shader(GL_VERTEX_SHADER, 0, vertex_shader_default_tex_src, &pass->program.vertex_shader) ||
!compile_shader(GL_FRAGMENT_SHADER, 0, fragment_shader_default_tex_src, &pass->program.fragment_shader) ||
!create_program(&pass->program))
return 0;
glw->glGenVertexArrays(1, (GLuint *)&pass->vertex_array);
struct shader_uniforms* u = &pass->uniforms;
int p = pass->program.id;
memset(u, -1, sizeof(struct shader_uniforms));
u->vertex_coord = get_attrib(p, "VertexCoord");
u->tex_coord = get_attrib(p, "TexCoord");
u->texture = get_uniform(p, "Texture");
pass->scale.mode[0] = pass->scale.mode[1] = SCALE_SOURCE;
pass->scale.value[0] = pass->scale.value[1] = 1.0f;
pass->fbo.id = -1;
pass->active = 1;
return 1;
}
static int create_default_shader_color(struct shader_pass* pass)
{
if (
!compile_shader(GL_VERTEX_SHADER, 0, vertex_shader_default_color_src, &pass->program.vertex_shader) ||
!compile_shader(GL_FRAGMENT_SHADER, 0, fragment_shader_default_color_src, &pass->program.fragment_shader) ||
!create_program(&pass->program))
return 0;
glw->glGenVertexArrays(1, (GLuint *)&pass->vertex_array);
struct shader_uniforms* u = &pass->uniforms;
int p = pass->program.id;
memset(u, -1, sizeof(struct shader_uniforms));
u->vertex_coord = get_attrib(p, "VertexCoord");
u->color = get_attrib(p, "Color");
pass->scale.mode[0] = pass->scale.mode[1] = SCALE_SOURCE;
pass->scale.value[0] = pass->scale.value[1] = 1.0f;
pass->fbo.id = -1;
pass->active = 1;
return 1;
}
/* create the default scene shader */
static void create_scene_shader()
{
struct shader scene_shader_conf;
memset(&scene_shader_conf, 0, sizeof(struct shader));
create_default_shader_tex(&active_shader->scene);
scene_shader_conf.filter_linear = video_scale_mode;
if (active_shader->num_shaders > 0 && active_shader->shaders[0].input_filter_linear >= 0)
scene_shader_conf.filter_linear = active_shader->shaders[0].input_filter_linear;
setup_fbo(&scene_shader_conf, &active_shader->scene.fbo);
memset(&scene_shader_conf, 0, sizeof(struct shader));
create_default_shader_color(&active_shader->fs_color);
setup_fbo(&scene_shader_conf, &active_shader->fs_color.fbo);
}
static int load_texture(const char* f, struct shader_texture* tex)
{
void* img = wx_image_load(f);
if (!img)
return 0;
int width, height;
wx_image_get_size(img, &width, &height);
if (width != next_pow2(width) || height != next_pow2(height))
wx_image_rescale(img, next_pow2(width), next_pow2(height));
wx_image_get_size(img, &width, &height);
GLubyte* rgb = wx_image_get_data(img);
GLubyte* alpha = wx_image_get_alpha(img);
int bpp = alpha ? 4 : 3;
GLubyte* data = malloc(width*height*bpp);
int x, y, Y;
for (y = 0; y < height; ++y)
{
Y = height-y-1;
for (x = 0; x < width; x++)
{
data[(y*width+x)*bpp+0] = rgb[(Y*width+x)*3+0];
data[(y*width+x)*bpp+1] = rgb[(Y*width+x)*3+1];
data[(y*width+x)*bpp+2] = rgb[(Y*width+x)*3+2];
if (alpha)
data[(y*width+x)*bpp+3] = alpha[Y*width+x];
}
}
wx_image_free(img);
tex->width = width;
tex->height = height;
tex->internal_format = alpha ? GL_RGBA8 : GL_RGB8;
tex->format = alpha ? GL_RGBA : GL_RGB;
tex->type = GL_UNSIGNED_BYTE;
tex->data = data;
return 1;
}
static glsl_t* load_glslp(glsl_t* glsl, int num_shader, const char* f)
{
int i, j;
glslp_t* p = glslp_parse(f);
if (p)
{
char path[512];
char file[1024];
int failed = 0;
strcpy(path, f);
char* filename = get_filename(path);
struct glsl_shader* gshader = &glsl->shaders[num_shader];
strcpy(gshader->name, p->name);
*filename = 0;
gshader->num_lut_textures = p->num_textures;
for (i = 0; i < p->num_textures; ++i)
{
struct texture* texture = &p->textures[i];
sprintf(file, "%s%s", path, texture->path);
struct shader_lut_texture* tex = &gshader->lut_textures[i];
strcpy(tex->name, texture->name);
pclog("Load texture %s...\n", file);
if (!load_texture(file, &tex->texture))
{
wx_simple_messagebox("GLSL Error", "Could not load texture: %s", file);
pclog("Could not load texture %s!\n", file);
failed = 1;
break;
}
if (!strcmp(texture->wrap_mode, "repeat"))
tex->texture.wrap_mode = GL_REPEAT;
else if (!strcmp(texture->wrap_mode, "mirrored_repeat"))
tex->texture.wrap_mode = GL_MIRRORED_REPEAT;
else if (!strcmp(texture->wrap_mode, "clamp_to_edge"))
tex->texture.wrap_mode = GL_CLAMP_TO_EDGE;
else
tex->texture.wrap_mode = GL_CLAMP_TO_BORDER;
tex->texture.mipmap = texture->mipmap;
tex->texture.min_filter = tex->texture.mag_filter = texture->linear ? GL_LINEAR : GL_NEAREST;
if (tex->texture.mipmap)
tex->texture.min_filter = texture->linear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST;
create_texture(&tex->texture);
free(tex->texture.data);
tex->texture.data = 0;
}
if (!failed)
{
gshader->input_filter_linear = p->input_filter_linear;
gshader->num_parameters = p->num_parameters;
for (j = 0; j < gshader->num_parameters; ++j)
memcpy(&gshader->parameters[j], &p->parameters[j], sizeof(struct shader_parameter));
gshader->num_passes = p->num_shaders;
for (i = 0; i < p->num_shaders; ++i)
{
struct shader* shader = &p->shaders[i];
struct shader_pass* pass = &gshader->passes[i];
strcpy(pass->alias, shader->alias);
if (!strlen(pass->alias))
sprintf(pass->alias, "Pass %u", (i+1));
pclog("Creating pass %u (%s)\n", (i+1), pass->alias);
pclog("Loading shader %s...\n", shader->shader_fn);
if (!shader->shader_program)
{
wx_simple_messagebox("GLSL Error", "Could not load shader: %s", shader->shader_fn);
pclog("Could not load shader %s\n", shader->shader_fn);
failed = 1;
break;
}
else
pclog("Shader %s loaded\n", shader->shader_fn);
failed =
!compile_shader(GL_VERTEX_SHADER, "#define VERTEX\n#define PARAMETER_UNIFORM\n", shader->shader_program, &pass->program.vertex_shader) ||
!compile_shader(GL_FRAGMENT_SHADER, "#define FRAGMENT\n#define PARAMETER_UNIFORM\n", shader->shader_program, &pass->program.fragment_shader);
if (failed)
break;
if (!create_program(&pass->program))
{
failed = 1;
break;
}
pass->frame_count_mod = shader->frame_count_mod;
pass->fbo.mipmap_input = shader->mipmap_input;
glw->glGenVertexArrays(1, (GLuint *)&pass->vertex_array);
find_uniforms(gshader, i);
setup_scale(shader, pass);
if (i == p->num_shaders-1) /* last pass may or may not be an fbo depending on scale */
{
if (num_shader == glsl->num_shaders-1)
{
pass->fbo.id = -1;
for (j = 0; j < 2; ++j)
{
if (pass->scale.mode[j] != SCALE_SOURCE || pass->scale.value[j] != 1)
{
setup_fbo(shader, &pass->fbo);
break;
}
}
}
else
{
/* check if next shaders' first pass wants the input mipmapped (will this ever happen?) */
pass->fbo.texture.mipmap = glsl->shaders[num_shader+1].num_passes > 0 && glsl->shaders[num_shader+1].passes[0].fbo.mipmap_input;
/* check if next shader wants the output of this pass to be filtered */
if (glsl->shaders[num_shader+1].num_passes > 0 && glsl->shaders[num_shader+1].input_filter_linear >= 0)
shader->filter_linear = glsl->shaders[num_shader+1].input_filter_linear;
setup_fbo(shader, &pass->fbo);
}
}
else
{
/* check if next pass wants the input mipmapped, if so we need to generate mipmaps of this pass */
pass->fbo.texture.mipmap = (i+1) < p->num_shaders && p->shaders[i+1].mipmap_input;
setup_fbo(shader, &pass->fbo);
}
if (pass->fbo.srgb) glsl->srgb = 1;
pass->active = 1;
}
if (!failed)
{
if (gshader->has_prev)
{
struct shader scene_shader_conf;
memset(&scene_shader_conf, 0, sizeof(struct shader));
for (i = 0; i < MAX_PREV; ++i)
{
setup_fbo(&scene_shader_conf, &gshader->prev[i].fbo);
}
}
}
}
glslp_free(p);
return glsl;
}
return 0;
}
static glsl_t* load_shaders(int num, char shaders[MAX_USER_SHADERS][512])
{
int i;
glsl_t* glsl;
glsl = malloc(sizeof(glsl_t));
memset(glsl, 0, sizeof(glsl_t));
glsl->num_shaders = num;
int failed = 0;
for (i = num-1; i >= 0; --i)
{
const char* f = shaders[i];
if (f && strlen(f))
{
if (!load_glslp(glsl, i, f))
{
failed = 1;
break;
}
}
}
if (failed)
{
delete_glsl(glsl);
memset(glsl, 0, sizeof(glsl_t));
}
return glsl;
}
static void read_shader_config()
{
char s[512];
int i, j;
for (i = 0; i < active_shader->num_shaders; ++i)
{
struct glsl_shader* shader = &active_shader->shaders[i];
char* name = shader->name;
sprintf(s, "GL3 Shaders - %s", name);
// shader->shader_refresh_rate = config_get_float(CFG_MACHINE, s, "shader_refresh_rate", -1);
for (j = 0; j < shader->num_parameters; ++j)
{
struct shader_parameter* param = &shader->parameters[j];
param->value = config_get_float(CFG_MACHINE, s, param->id, param->default_value);
}
}
}
int gl3_init(SDL_Window* window, sdl_render_driver requested_render_driver, SDL_Rect screen)
{
int i, j;
strcpy(current_render_driver_name, requested_render_driver.name);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
#ifdef __APPLE__
// without an explicit request for the core profile 3.0 macOS falls back to default ancient 2.1
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
#endif
context = SDL_GL_CreateContext(window);
if (!context)
{
pclog("Could not create GL context.\n");
return FALSE;
}
SDL_GL_SetSwapInterval(video_vsync ? 1 : 0);
pclog("OpenGL information: [%s] %s (%s)\n", glGetString(GL_VENDOR), glGetString(GL_RENDERER), glGetString(GL_VERSION));
glsl_version[0] = glsl_version[1] = -1;
glGetIntegerv(GL_MAJOR_VERSION, &glsl_version[0]);
glGetIntegerv(GL_MINOR_VERSION, &glsl_version[1]);
if (glsl_version[0] < 3)
{
pclog("OpenGL 3.0 is not available.");
return SDL_FALSE;
}
pclog("Using OpenGL %s\n", glGetString(GL_VERSION));
pclog("Using Shading Language %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
pclog("Max texture size: %dx%d\n", max_texture_size, max_texture_size);
//SDL_GL_MakeCurrent(window, context);
glw = glw_init();
glEnable(GL_TEXTURE_2D);
scene_texture.data = NULL;
scene_texture.width = screen.w;
scene_texture.height = screen.h;
scene_texture.internal_format = GL_RGBA8;
scene_texture.format = GL_RGBA;
scene_texture.type = GL_UNSIGNED_INT_8_8_8_8_REV;
scene_texture.wrap_mode = GL_CLAMP_TO_BORDER;
scene_texture.min_filter = scene_texture.mag_filter = video_scale_mode ? GL_LINEAR : GL_NEAREST;
scene_texture.mipmap = 0;
create_texture(&scene_texture);
/* load shader */
// const char* shaders[1];
// shaders[0] = gl3_shader_file;
//
// active_shader = load_shaders(1, shaders);
// const char* shaders[3];
// shaders[0] = "/home/phantasy/git/glsl-shaders/ntsc/ntsc-320px.glslp";
// shaders[1] = "/home/phantasy/git/glsl-shaders/motionblur/motionblur-simple.glslp";
// shaders[2] = "/home/phantasy/git/glsl-shaders/crt/crt-lottes-multipass.glslp";
//
// active_shader = load_shaders(3, shaders);
int num_shaders = 0;
for (i = 0; i < MAX_USER_SHADERS; ++i)
{
if (strlen(gl3_shader_file[i]))
++num_shaders;
else
break;
}
active_shader = load_shaders(num_shaders, gl3_shader_file);
create_scene_shader();
/* read config */
read_shader_config();
/* buffers */
GLfloat vertex[] = {
-1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, -1.0f,
1.0f, 1.0f
};
GLfloat inv_vertex[] = {
-1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, 1.0f,
1.0f, -1.0f
};
GLfloat tex_coords[] = {
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f
};
GLfloat colors[] = {
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f
};
/* set the scene shader buffers */
{
glw->glBindVertexArray(active_shader->scene.vertex_array);
struct shader_vbo* vbo = &active_shader->scene.vbo;
glw->glGenBuffers(1, (GLuint *)&vbo->vertex_coord);
glw->glBindBuffer(GL_ARRAY_BUFFER, vbo->vertex_coord);
glw->glBufferData(GL_ARRAY_BUFFER, sizeof(inv_vertex), inv_vertex, GL_STATIC_DRAW);
glw->glVertexAttribPointer(active_shader->scene.uniforms.vertex_coord, 2, GL_FLOAT, GL_FALSE, 2*sizeof(GLfloat), (GLvoid*)0);
glw->glGenBuffers(1, (GLuint *)&vbo->tex_coord);
glw->glBindBuffer(GL_ARRAY_BUFFER, vbo->tex_coord);
glw->glBufferData(GL_ARRAY_BUFFER, sizeof(tex_coords), tex_coords, GL_DYNAMIC_DRAW);
glw->glVertexAttribPointer(active_shader->scene.uniforms.tex_coord, 2, GL_FLOAT, GL_TRUE, 2*sizeof(GLfloat), (GLvoid*)0);
}
/* set buffers for all passes */
for (j = 0; j < active_shader->num_shaders; ++j)
{
struct glsl_shader* shader = &active_shader->shaders[j];
for (i = 0; i < shader->num_passes; ++i)
{
struct shader_uniforms* u = &shader->passes[i].uniforms;
glw->glBindVertexArray(shader->passes[i].vertex_array);
struct shader_vbo* vbo = &shader->passes[i].vbo;
glw->glGenBuffers(1, (GLuint *)&vbo->vertex_coord);
glw->glBindBuffer(GL_ARRAY_BUFFER, vbo->vertex_coord);
glw->glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);
glw->glVertexAttribPointer(u->vertex_coord, 2, GL_FLOAT, GL_FALSE, 2*sizeof(GLfloat), (GLvoid*)0);
glw->glGenBuffers(1, (GLuint *)&vbo->tex_coord);
glw->glBindBuffer(GL_ARRAY_BUFFER, vbo->tex_coord);
glw->glBufferData(GL_ARRAY_BUFFER, sizeof(tex_coords), tex_coords, GL_DYNAMIC_DRAW);
glw->glVertexAttribPointer(u->tex_coord, 2, GL_FLOAT, GL_TRUE, 2*sizeof(GLfloat), (GLvoid*)0);
if (u->color)
{
glw->glGenBuffers(1, (GLuint *)&vbo->color);
glw->glBindBuffer(GL_ARRAY_BUFFER, vbo->color);
glw->glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
glw->glVertexAttribPointer(u->color, 4, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (GLvoid*)0);
}
}
}
for (i = 0; i < active_shader->num_shaders; ++i)
{
struct glsl_shader* shader = &active_shader->shaders[i];
if (shader->has_prev)
{
struct shader_pass* prev_pass = &shader->prev_scene;
create_default_shader_tex(prev_pass);
struct shader_vbo* vbo = &prev_pass->vbo;
glw->glBindVertexArray(prev_pass->vertex_array);
glw->glGenBuffers(1, (GLuint *)&vbo->vertex_coord);
glw->glBindBuffer(GL_ARRAY_BUFFER, vbo->vertex_coord);
glw->glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);
glw->glVertexAttribPointer(prev_pass->uniforms.vertex_coord, 2, GL_FLOAT, GL_FALSE, 2*sizeof(GLfloat), (GLvoid*)0);
glw->glGenBuffers(1, (GLuint *)&vbo->tex_coord);
glw->glBindBuffer(GL_ARRAY_BUFFER, vbo->tex_coord);
glw->glBufferData(GL_ARRAY_BUFFER, sizeof(tex_coords), tex_coords, GL_DYNAMIC_DRAW);
glw->glVertexAttribPointer(prev_pass->uniforms.tex_coord, 2, GL_FLOAT, GL_TRUE, 2*sizeof(GLfloat), (GLvoid*)0);
for (j = 0; j < MAX_PREV; ++j)
{
struct shader_prev* prev = &shader->prev[j];
struct shader_vbo* prev_vbo = &prev->vbo;
glw->glGenBuffers(1, (GLuint *)&prev_vbo->vertex_coord);
glw->glBindBuffer(GL_ARRAY_BUFFER, prev_vbo->vertex_coord);
glw->glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);
glw->glGenBuffers(1, (GLuint *)&prev_vbo->tex_coord);
glw->glBindBuffer(GL_ARRAY_BUFFER, prev_vbo->tex_coord);
glw->glBufferData(GL_ARRAY_BUFFER, sizeof(tex_coords), tex_coords, GL_DYNAMIC_DRAW);
}
}
}
/* create final pass */
if (active_shader->num_shaders == 0 || active_shader->shaders[active_shader->num_shaders-1].passes[active_shader->shaders[active_shader->num_shaders-1].num_passes-1].fbo.id >= 0)
{
struct shader_pass* final_pass = &active_shader->final_pass;
create_default_shader_tex(final_pass);
glw->glBindVertexArray(final_pass->vertex_array);
struct shader_vbo* vbo = &final_pass->vbo;
glw->glGenBuffers(1, (GLuint *)&vbo->vertex_coord);
glw->glBindBuffer(GL_ARRAY_BUFFER, vbo->vertex_coord);
glw->glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);
glw->glVertexAttribPointer(final_pass->uniforms.vertex_coord, 2, GL_FLOAT, GL_FALSE, 2*sizeof(GLfloat), (GLvoid*)0);
glw->glGenBuffers(1, (GLuint *)&vbo->tex_coord);
glw->glBindBuffer(GL_ARRAY_BUFFER, vbo->tex_coord);
glw->glBufferData(GL_ARRAY_BUFFER, sizeof(tex_coords), tex_coords, GL_DYNAMIC_DRAW);
glw->glVertexAttribPointer(final_pass->uniforms.tex_coord, 2, GL_FLOAT, GL_TRUE, 2*sizeof(GLfloat), (GLvoid*)0);
}
{
struct shader_pass* color_pass = &active_shader->fs_color;
create_default_shader_color(color_pass);
glw->glBindVertexArray(color_pass->vertex_array);
struct shader_vbo* vbo = &color_pass->vbo;
glw->glGenBuffers(1, (GLuint *)&vbo->vertex_coord);
glw->glBindBuffer(GL_ARRAY_BUFFER, vbo->vertex_coord);
glw->glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);
glw->glVertexAttribPointer(color_pass->uniforms.vertex_coord, 2, GL_FLOAT, GL_FALSE, 2*sizeof(GLfloat), (GLvoid*)0);
glw->glGenBuffers(1, (GLuint *)&vbo->color);
glw->glBindBuffer(GL_ARRAY_BUFFER, vbo->color);
glw->glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_DYNAMIC_DRAW);
glw->glVertexAttribPointer(color_pass->uniforms.color, 4, GL_FLOAT, GL_TRUE, 4*sizeof(GLfloat), (GLvoid*)0);
}
#ifdef SDL2_SHADER_DEBUG
struct shader_pass* debug_pass = &active_shader->debug;
create_default_shader(debug_pass);
glBindVertexArray(debug_pass->vertex_array);
struct shader_vbo* vbo = &debug_pass->vbo;
glGenBuffers(1, &vbo->vertex_coord);
glBindBuffer(GL_ARRAY_BUFFER, vbo->vertex_coord);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex), vertex, GL_STATIC_DRAW);
glVertexAttribPointer(debug_pass->uniforms.vertex_coord, 2, GL_FLOAT, GL_FALSE, 2*sizeof(GLfloat), (GLvoid*)0);
glGenBuffers(1, &vbo->tex_coord);
glBindBuffer(GL_ARRAY_BUFFER, vbo->tex_coord);
glBufferData(GL_ARRAY_BUFFER, sizeof(tex_coords), tex_coords, GL_DYNAMIC_DRAW);
glVertexAttribPointer(debug_pass->uniforms.tex_coord, 2, GL_FLOAT, GL_TRUE, 2*sizeof(GLfloat), (GLvoid*)0);
#endif
glw->glBindBuffer(GL_ARRAY_BUFFER, 0);
glw->glBindVertexArray(0);
return SDL_TRUE;
}
void gl3_close()
{
if (context)
{
delete_texture(&scene_texture);
if (active_shader)
{
delete_glsl(active_shader);
free(active_shader);
}
active_shader = NULL;
SDL_GL_DeleteContext(context);
context = NULL;
}
glw_free(glw);
}
void gl3_update(SDL_Window* window, SDL_Rect updated_rect, BITMAP* screen)
{
if (!context)
return;
glBindTexture(GL_TEXTURE_2D, scene_texture.id);
glPixelStorei(GL_UNPACK_ROW_LENGTH, screen->w);
glTexSubImage2D(GL_TEXTURE_2D, 0, updated_rect.x, updated_rect.y, updated_rect.w, updated_rect.h, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, &((uint32_t*) screen->dat)[updated_rect.y * screen->w + updated_rect.x]);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glBindTexture(GL_TEXTURE_2D, 0);
}
struct render_data {
int pass;
struct glsl_shader* shader;
struct shader_pass* shader_pass;
GLfloat* output_size;
struct shader_pass* orig_pass;
GLint texture;
int frame_count;
};
static void render_pass(struct render_data* data)
{
int i;
GLuint texture_unit = 0;
// pclog("pass %d: %gx%g, %gx%g -> %gx%g, %gx%g, %gx%g\n", num_pass, pass->state.input_size[0], pass->state.input_size[1], pass->state.input_texture_size[0], pass->state.input_texture_size[1], pass->state.output_size[0], pass->state.output_size[1], pass->state.output_texture_size[0], pass->state.output_texture_size[1], output_size[0], output_size[1]);
glw->glBindVertexArray(data->shader_pass->vertex_array);
GLint p = data->shader_pass->program.id;
struct shader_uniforms* u = &data->shader_pass->uniforms;
glw->glUseProgram(p);
if (data->texture)
{
glw->glActiveTexture(GL_TEXTURE0 + texture_unit);
glBindTexture(GL_TEXTURE_2D, data->texture);
glw->glUniform1i(u->texture, texture_unit);
texture_unit++;
}
if (u->color >= 0) glw->glEnableVertexAttribArray(u->color);
if (u->mvp_matrix >= 0) glw->glUniformMatrix4fv(u->mvp_matrix, 1, 0, matrix);
if (u->frame_direction >= 0) glw->glUniform1i(u->frame_direction, 1);
int framecnt = data->frame_count;
if (data->shader_pass->frame_count_mod > 0)
framecnt = framecnt%data->shader_pass->frame_count_mod;
if (u->frame_count >= 0) glw->glUniform1i(u->frame_count, framecnt);
if (u->input_size >= 0) glw->glUniform2fv(u->input_size, 1, data->shader_pass->state.input_size);
if (u->texture_size >= 0) glw->glUniform2fv(u->texture_size, 1, data->shader_pass->state.input_texture_size);
if (u->output_size >= 0) glw->glUniform2fv(u->output_size, 1, data->output_size);
if (data->shader)
{
/* parameters */
for (i = 0; i < data->shader->num_parameters; ++i)
if (u->parameters[i] >= 0) glw->glUniform1f(u->parameters[i], data->shader->parameters[i].value);
if (data->pass > 0)
{
struct shader_pass* passes = data->shader->passes;
struct shader_pass* orig = data->orig_pass;
if (u->orig.texture >= 0)
{
glw->glActiveTexture(GL_TEXTURE0 + texture_unit);
glBindTexture(GL_TEXTURE_2D, orig->fbo.texture.id);
glw->glUniform1i(u->orig.texture, texture_unit);
texture_unit++;
}
if (u->orig.input_size >= 0) glw->glUniform2fv(u->orig.input_size, 1, orig->state.input_size);
if (u->orig.texture_size >= 0) glw->glUniform2fv(u->orig.texture_size, 1, orig->state.input_texture_size);
for (i = 0; i < data->pass; ++i)
{
if (u->pass[i].texture >= 0)
{
glw->glActiveTexture(GL_TEXTURE0 + texture_unit);
glBindTexture(GL_TEXTURE_2D, passes[i].fbo.texture.id);
glw->glUniform1i(u->pass[i].texture, texture_unit);
texture_unit++;
}
if (u->pass[i].texture_size >= 0) glw->glUniform2fv(u->pass[i].texture_size, 1, passes[i].state.input_texture_size);
if (u->pass[i].input_size >= 0) glw->glUniform2fv(u->pass[i].input_size, 1, passes[i].state.input_size);
if (u->prev_pass[i].texture >= 0)
{
glw->glActiveTexture(GL_TEXTURE0 + texture_unit);
glBindTexture(GL_TEXTURE_2D, passes[i].fbo.texture.id);
glw->glUniform1i(u->prev_pass[i].texture, texture_unit);
texture_unit++;
}
if (u->prev_pass[i].texture_size >= 0) glw->glUniform2fv(u->prev_pass[i].texture_size, 1, passes[i].state.input_texture_size);
if (u->prev_pass[i].input_size >= 0) glw->glUniform2fv(u->prev_pass[i].input_size, 1, passes[i].state.input_size);
}
}
if (data->shader->has_prev)
{
/* loop through each previous frame */
for (i = 0; i < MAX_PREV; ++i)
{
if (u->prev[i].texture >= 0)
{
glw->glActiveTexture(GL_TEXTURE0 + texture_unit);
glBindTexture(GL_TEXTURE_2D, data->shader->prev[i].fbo.texture.id);
glw->glUniform1i(u->prev[i].texture, texture_unit);
texture_unit++;
}
if (u->prev[i].tex_coord >= 0)
{
glw->glBindBuffer(GL_ARRAY_BUFFER, data->shader->prev[i].vbo.tex_coord);
glw->glVertexAttribPointer(u->prev[i].tex_coord, 2, GL_FLOAT, GL_TRUE, 2*sizeof(GLfloat), (GLvoid*)0);
glw->glEnableVertexAttribArray(u->prev[i].tex_coord);
glw->glBindBuffer(GL_ARRAY_BUFFER, 0);
}
}
}
for (i = 0; i < data->shader->num_lut_textures; ++i)
{
if (u->lut_textures[i] >= 0)
{
glw->glActiveTexture(GL_TEXTURE0 + texture_unit);
glBindTexture(GL_TEXTURE_2D, data->shader->lut_textures[i].texture.id);
glw->glUniform1i(u->lut_textures[i], texture_unit);
texture_unit++;
}
}
}
glw->glEnableVertexAttribArray(u->vertex_coord);
glw->glEnableVertexAttribArray(u->tex_coord);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glw->glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glw->glDisableVertexAttribArray(data->shader_pass->uniforms.vertex_coord);
glw->glDisableVertexAttribArray(data->shader_pass->uniforms.tex_coord);
if (data->shader_pass->uniforms.color >= 0) glw->glDisableVertexAttribArray(data->shader_pass->uniforms.color);
if (data->shader && data->shader->has_prev)
{
for (i = 0; i < MAX_PREV; ++i)
{
if (u->prev[i].tex_coord >= 0)
glw->glDisableVertexAttribArray(u->prev[i].tex_coord);
}
}
glw->glBindVertexArray(0);
glw->glUseProgram(0);
}
void gl3_present(SDL_Window* window, SDL_Rect video_rect, SDL_Rect window_rect, SDL_Rect screen)
{
if (!context)
return;
int s, i, j;
Uint32 ticks = SDL_GetTicks();
GLfloat orig_output_size[] = {
window_rect.w, window_rect.h
};
if (active_shader->srgb) glEnable(GL_FRAMEBUFFER_SRGB);
struct render_data data;
/* render scene to texture */
{
struct shader_pass* pass = &active_shader->scene;
SDL_Rect rect;
rect.x = rect.y = 0;
rect.w = video_rect.w*gl3_input_scale;
rect.h = video_rect.h*gl3_input_scale;
sdl_scale(gl3_input_stretch, rect, &rect, video_rect.w, video_rect.h);
pass->state.input_size[0] = pass->state.output_size[0] = rect.w;
pass->state.input_size[1] = pass->state.output_size[1] = rect.h;
pass->state.input_texture_size[0] = pass->state.output_texture_size[0] = next_pow2(pass->state.output_size[0]);
pass->state.input_texture_size[1] = pass->state.output_texture_size[1] = next_pow2(pass->state.output_size[1]);
recreate_fbo(&active_shader->scene.fbo, pass->state.output_texture_size[0], pass->state.output_texture_size[1]);
glw->glBindFramebuffer(GL_FRAMEBUFFER, active_shader->scene.fbo.id);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, pass->state.output_size[0], pass->state.output_size[1]);
GLfloat minx = 0;
GLfloat miny = 0;
GLfloat maxx = pass->state.output_size[0]/(GLfloat)pass->state.output_texture_size[0];
GLfloat maxy = pass->state.output_size[1]/(GLfloat)pass->state.output_texture_size[1];
pass->state.tex_coords[0] = minx;
pass->state.tex_coords[1] = miny;
pass->state.tex_coords[2] = minx;
pass->state.tex_coords[3] = maxy;
pass->state.tex_coords[4] = maxx;
pass->state.tex_coords[5] = miny;
pass->state.tex_coords[6] = maxx;
pass->state.tex_coords[7] = maxy;
// create input tex coords
minx = video_rect.x/(float)screen.w;
miny = video_rect.y/(float)screen.h;
maxx = (video_rect.x+video_rect.w)/(float)screen.w;
maxy = (video_rect.y+video_rect.h)/(float)screen.h;
GLfloat tex_coords[] = {
minx, miny,
minx, maxy,
maxx, miny,
maxx, maxy
};
glw->glBindVertexArray(pass->vertex_array);
glw->glBindBuffer(GL_ARRAY_BUFFER, pass->vbo.tex_coord);
glw->glBufferSubData(GL_ARRAY_BUFFER, 0, 8*sizeof(GLfloat), tex_coords);
glw->glBindBuffer(GL_ARRAY_BUFFER, 0);
memset(&data, 0, sizeof(struct render_data));
data.pass = -1;
data.shader_pass = &active_shader->scene;
data.texture = scene_texture.id;
data.output_size = orig_output_size;
render_pass(&data);
glw->glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
struct shader_pass* orig = &active_shader->scene;
struct shader_pass* input = &active_shader->scene;
for (s = 0; s < active_shader->num_shaders; ++s)
{
struct glsl_shader* shader = &active_shader->shaders[s];
// float refresh_rate = shader->shader_refresh_rate;
// if (refresh_rate < 0)
// refresh_rate = gl3_shader_refresh_rate;
float refresh_rate = gl3_shader_refresh_rate;
if (refresh_rate == 0)
refresh_rate = video_refresh_rate;
int frame_count = ticks/(1000.0f/refresh_rate);
/* loop through each pass */
for (i = 0; i < shader->num_passes; ++i)
{
struct shader_pass* pass = &shader->passes[i];
memcpy(pass->state.input_size, input->state.output_size, 2*sizeof(GLfloat));
memcpy(pass->state.input_texture_size, input->state.output_texture_size, 2*sizeof(GLfloat));
for (j = 0; j < 2; ++j)
{
if (pass->scale.mode[j] == SCALE_VIEWPORT)
pass->state.output_size[j] = orig_output_size[j]*pass->scale.value[j];
else if (pass->scale.mode[j] == SCALE_ABSOLUTE)
pass->state.output_size[j] = pass->scale.value[j];
else
pass->state.output_size[j] = pass->state.input_size[j]*pass->scale.value[j];
pass->state.output_texture_size[j] = next_pow2(pass->state.output_size[j]);
}
if (pass->fbo.id >= 0)
{
recreate_fbo(&pass->fbo, pass->state.output_texture_size[0], pass->state.output_texture_size[1]);
glw->glBindFramebuffer(GL_FRAMEBUFFER, pass->fbo.id);
glViewport(0, 0, pass->state.output_size[0], pass->state.output_size[1]);
}
else
glViewport(window_rect.x, window_rect.y, window_rect.w, window_rect.h);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
GLfloat minx = 0;
GLfloat miny = 0;
GLfloat maxx = pass->state.output_size[0]/(GLfloat)pass->state.output_texture_size[0];
GLfloat maxy = pass->state.output_size[1]/(GLfloat)pass->state.output_texture_size[1];
pass->state.tex_coords[0] = minx;
pass->state.tex_coords[1] = miny;
pass->state.tex_coords[2] = minx;
pass->state.tex_coords[3] = maxy;
pass->state.tex_coords[4] = maxx;
pass->state.tex_coords[5] = miny;
pass->state.tex_coords[6] = maxx;
pass->state.tex_coords[7] = maxy;
glw->glBindVertexArray(pass->vertex_array);
glw->glBindBuffer(GL_ARRAY_BUFFER, pass->vbo.tex_coord);
glw->glBufferSubData(GL_ARRAY_BUFFER, 0, 8*sizeof(GLfloat), input->state.tex_coords);
glw->glBindBuffer(GL_ARRAY_BUFFER, 0);
memset(&data, 0, sizeof(struct render_data));
data.shader = shader;
data.pass = i;
data.shader_pass = pass;
data.texture = input->fbo.texture.id;
data.output_size = orig_output_size;
data.orig_pass = orig;
data.frame_count = frame_count;
render_pass(&data);
glw->glBindFramebuffer(GL_FRAMEBUFFER, 0);
if (pass->fbo.texture.mipmap)
{
glw->glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, pass->fbo.texture.id);
glw->glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
}
input = pass;
}
if (shader->has_prev && (ticks-shader->last_prev_update) >= (1000.f/refresh_rate))
{
shader->last_prev_update = ticks;
/* shift array */
memmove(&shader->prev[1], &shader->prev[0], MAX_PREV*sizeof(struct shader_prev));
memcpy(&shader->prev[0], &shader->prev[MAX_PREV], sizeof(struct shader_prev));
struct shader_pass* pass = orig;
struct shader_pass* prev_pass = &shader->prev_scene;
struct shader_prev* prev = &shader->prev[0];
memcpy(&prev_pass->state, &pass->state, sizeof(struct shader_state));
recreate_fbo(&prev->fbo, prev_pass->state.output_texture_size[0], prev_pass->state.output_texture_size[1]);
memcpy(&prev_pass->fbo, &prev->fbo, sizeof(struct shader_fbo));
glw->glBindFramebuffer(GL_FRAMEBUFFER, prev->fbo.id);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, pass->state.output_size[0], pass->state.output_size[1]);
glw->glBindVertexArray(prev_pass->vertex_array);
glw->glBindBuffer(GL_ARRAY_BUFFER, prev->vbo.tex_coord);
glw->glBufferSubData(GL_ARRAY_BUFFER, 0, 8*sizeof(GLfloat), pass->state.tex_coords);
glw->glBindBuffer(GL_ARRAY_BUFFER, 0);
glw->glBindBuffer(GL_ARRAY_BUFFER, prev_pass->vbo.tex_coord);
glw->glBufferSubData(GL_ARRAY_BUFFER, 0, 8*sizeof(GLfloat), pass->state.tex_coords);
glw->glBindBuffer(GL_ARRAY_BUFFER, 0);
memset(&data, 0, sizeof(struct render_data));
data.shader = shader;
data.pass = -10;
data.shader_pass = prev_pass;
data.texture = pass->fbo.texture.id;
data.output_size = orig_output_size;
render_pass(&data);
glw->glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
orig = input;
}
if (active_shader->final_pass.active)
{
struct shader_pass* pass = &active_shader->final_pass;
memcpy(pass->state.input_size, input->state.output_size, 2*sizeof(GLfloat));
memcpy(pass->state.input_texture_size, input->state.output_texture_size, 2*sizeof(GLfloat));
for (j = 0; j < 2; ++j)
{
if (pass->scale.mode[j] == SCALE_VIEWPORT)
pass->state.output_size[j] = orig_output_size[j]*pass->scale.value[j];
else if (pass->scale.mode[j] == SCALE_ABSOLUTE)
pass->state.output_size[j] = pass->scale.value[j];
else
pass->state.output_size[j] = pass->state.input_size[j]*pass->scale.value[j];
pass->state.output_texture_size[j] = next_pow2(pass->state.output_size[j]);
}
glViewport(window_rect.x, window_rect.y, window_rect.w, window_rect.h);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
GLfloat minx = 0;
GLfloat miny = 0;
GLfloat maxx = pass->state.output_size[0]/(GLfloat)pass->state.output_texture_size[0];
GLfloat maxy = pass->state.output_size[1]/(GLfloat)pass->state.output_texture_size[1];
pass->state.tex_coords[0] = minx;
pass->state.tex_coords[1] = miny;
pass->state.tex_coords[2] = minx;
pass->state.tex_coords[3] = maxy;
pass->state.tex_coords[4] = maxx;
pass->state.tex_coords[5] = miny;
pass->state.tex_coords[6] = maxx;
pass->state.tex_coords[7] = maxy;
glw->glBindVertexArray(pass->vertex_array);
glw->glBindBuffer(GL_ARRAY_BUFFER, pass->vbo.tex_coord);
glw->glBufferSubData(GL_ARRAY_BUFFER, 0, 8*sizeof(GLfloat), input->state.tex_coords);
glw->glBindBuffer(GL_ARRAY_BUFFER, 0);
memset(&data, 0, sizeof(struct render_data));
data.pass = -2;
data.shader_pass = pass;
data.texture = input->fbo.texture.id;
data.output_size = orig_output_size;
data.orig_pass = orig;
render_pass(&data);
}
if (!take_screenshot)
{
if (video_focus_dim && !(SDL_GetWindowFlags(window)&SDL_WINDOW_INPUT_FOCUS))
{
struct shader_pass* pass = &active_shader->fs_color;
GLfloat r = 0;
GLfloat g = 0;
GLfloat b = 0;
GLfloat a = 0x80/(float)0xff;
GLfloat colors[] = {
r, g, b, a,
r, g, b, a,
r, g, b, a,
r, g, b, a
};
glw->glBindVertexArray(pass->vertex_array);
glw->glBindBuffer(GL_ARRAY_BUFFER, pass->vbo.color);
glw->glBufferSubData(GL_ARRAY_BUFFER, 0, 16*sizeof(GLfloat), colors);
glw->glBindBuffer(GL_ARRAY_BUFFER, 0);
memset(&data, 0, sizeof(struct render_data));
data.pass = -3;
data.shader_pass = pass;
data.texture = 0;
data.output_size = orig_output_size;
data.orig_pass = orig;
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
render_pass(&data);
glDisable(GL_BLEND);
}
if (flash.enabled)
{
struct shader_pass* pass = &active_shader->fs_color;
GLfloat r = (flash.color[0]&0xff)/(float)0xff;
GLfloat g = (flash.color[1]&0xff)/(float)0xff;
GLfloat b = (flash.color[2]&0xff)/(float)0xff;
GLfloat a = (flash.color[3]&0xff)/(float)0xff;
GLfloat colors[] = {
r, g, b, a,
r, g, b, a,
r, g, b, a,
r, g, b, a
};
glw->glBindVertexArray(pass->vertex_array);
glw->glBindBuffer(GL_ARRAY_BUFFER, pass->vbo.color);
glw->glBufferSubData(GL_ARRAY_BUFFER, 0, 16*sizeof(GLfloat), colors);
glw->glBindBuffer(GL_ARRAY_BUFFER, 0);
memset(&data, 0, sizeof(struct render_data));
data.pass = -3;
data.shader_pass = pass;
data.texture = 0;
data.output_size = orig_output_size;
data.orig_pass = orig;
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
render_pass(&data);
glDisable(GL_BLEND);
}
}
else
{
take_screenshot = 0;
int width = window_rect.w;
int height = window_rect.h;
SDL_GetWindowSize(window, &width, &height);
unsigned char* rgba = (unsigned char*)malloc(width*height*4);
glFinish();
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, rgba);
int x, y;
unsigned char* rgb = (unsigned char*)malloc(width*height*3);
for (x = 0; x < width; ++x)
{
for (y = 0; y < height; ++y)
{
rgb[(y*width+x)*3+0] = rgba[((height-y-1)*width+x)*4+0];
rgb[(y*width+x)*3+1] = rgba[((height-y-1)*width+x)*4+1];
rgb[(y*width+x)*3+2] = rgba[((height-y-1)*width+x)*4+2];
}
}
screenshot_taken(rgb, width, height);
free(rgb);
free(rgba);
}
// DEBUG: render FBO
#ifdef SDL2_SHADER_DEBUG
// GLint texture = scene_texture.id;
GLint texture = active_shader->lut_textures[0].texture.id;
// GLint texture = active_shader->scene.fbo.texture;
glClearColor(1, 1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, orig_output_size[0], orig_output_size[1]);
struct shader_pass* pass = &active_shader->debug;
render_pass(-999, pass, orig_output_size, texture);
#endif
glDisable(GL_FRAMEBUFFER_SRGB);
SDL_GL_SwapWindow(window);
}
sdl_renderer_t* gl3_renderer_create()
{
sdl_renderer_t* renderer = malloc(sizeof(sdl_renderer_t));
renderer->init = gl3_init;
renderer->close = gl3_close;
renderer->update = gl3_update;
renderer->present = gl3_present;
renderer->always_update = 1;
return renderer;
}
void gl3_renderer_close(sdl_renderer_t* renderer)
{
free(renderer);
}
static int available = -1;
int gl3_renderer_available(struct sdl_render_driver* driver)
{
if (available < 0)
{
available = 0;
// GL SetAttribute should be done *before* window creation for the attributes to apply on
// context creation (seems to depend on OpenGL impl. but it souldn't hurt other platforms
// to do it here (earlier than before)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
#ifdef __APPLE__
// without an explicit request for the core profile 3.0 macOS falls back to default ancient 2.1
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
#endif
SDL_Window* window = SDL_CreateWindow("GL3 test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1, 1, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
if (window)
{
SDL_GLContext context = SDL_GL_CreateContext(window);
if (context)
{
int version = -1;
glGetIntegerv(GL_MAJOR_VERSION, &version);
SDL_GL_DeleteContext(context);
available = version >= 3;
}
SDL_DestroyWindow(window);
}
}
return available;
}