snes9x/shaders/glsl.cpp

1072 lines
32 KiB
C++
Raw Normal View History

2018-05-11 01:47:55 +02:00
#include "glsl.h"
#include "../../conffile.h"
#include "shader_helpers.h"
2018-05-14 01:22:54 +02:00
#include "shader_platform.h"
2018-05-11 01:47:55 +02:00
static const GLfloat tex_coords[16] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f };
2018-05-11 01:47:55 +02:00
static const GLfloat mvp_ortho[16] = { 2.0f, 0.0f, 0.0f, 0.0f,
0.0f, 2.0f, 0.0f, 0.0f,
0.0f, 0.0f, -1.0f, 0.0f,
-1.0f, -1.0f, 0.0f, 1.0f };
static void reduce_to_path(char* filename)
{
for (int i = strlen(filename); i >= 0; i--)
{
if (filename[i] == '\\' || filename[i] == '/')
{
filename[i] = 0;
break;
}
}
}
static char* read_file(const char *filename)
{
FILE* file = NULL;
int size;
char* contents;
file = fopen(filename, "rb");
if (!file)
return NULL;
fseek(file, 0, SEEK_END);
size = ftell(file);
fseek(file, 0, SEEK_SET);
contents = new char[size + 1];
fread(contents, size, 1, file);
contents[size] = '\0';
fclose(file);
return contents;
}
static int scale_string_to_enum(const char *string, bool last)
{
if (!strcasecmp(string, "source"))
{
return GLSL_SOURCE;
}
else if (!strcasecmp(string, "viewport"))
{
return GLSL_VIEWPORT;
}
else if (!strcasecmp(string, "absolute"))
{
return GLSL_ABSOLUTE;
}
else if (last)
return GLSL_VIEWPORT;
else
return GLSL_SOURCE;
}
2018-05-12 22:07:07 +02:00
static const char *scale_enum_to_string (int val)
{
switch (val)
{
case GLSL_SOURCE:
return "source";
case GLSL_VIEWPORT:
return "viewport";
case GLSL_ABSOLUTE:
return "absolute";
default:
return "source";
}
}
static int wrap_mode_string_to_enum(const char *string)
{
if (!strcasecmp(string, "repeat"))
{
return GL_REPEAT;
}
else if (!strcasecmp(string, "clamp_to_edge"))
{
return GL_CLAMP_TO_EDGE;
}
else if (!strcasecmp(string, "clamp"))
{
return GL_CLAMP;
}
else
return GL_CLAMP_TO_BORDER;
}
2018-05-12 22:07:07 +02:00
static const char *wrap_mode_enum_to_string(int val)
{
switch (val)
{
case GL_REPEAT:
return "repeat";
case GL_CLAMP_TO_EDGE:
return "clamp_to_edge";
case GL_CLAMP:
return "clamp";
default:
return "clamp_to_border";
}
}
2018-05-11 01:47:55 +02:00
bool GLSLShader::load_shader_file (char *filename)
{
char key[256];
if (strlen(filename) < 6 || strcasecmp(&filename[strlen(filename) - 6], ".glslp"))
{
GLSLPass pass;
2018-05-11 01:57:52 +02:00
this->pass.push_back (GLSLPass());
pass.scale_type_x = pass.scale_type_y = GLSL_VIEWPORT;
2018-05-11 01:47:55 +02:00
pass.filter = GLSL_UNDEFINED;
2018-05-20 19:49:58 +02:00
pass.wrap_mode = GL_CLAMP_TO_BORDER;
2018-05-11 01:47:55 +02:00
strcpy(pass.filename, filename);
2018-05-11 01:57:52 +02:00
pass.frame_count_mod = 0;
pass.frame_count = 0;
pass.fp = 0;
pass.scale_x = 1.0;
pass.scale_y = 1.0;
this->pass.push_back (pass);
return true;
2018-05-11 01:47:55 +02:00
}
else
{
conf.LoadFile(filename);
}
int shader_count = conf.GetInt("::shaders", 0);
if (shader_count < 1)
return false;
this->pass.push_back (GLSLPass());
for (int i = 0; i < shader_count; i++)
{
GLSLPass pass;
snprintf(key, 256, "::filter_linear%u", i);
2018-05-13 00:36:45 +02:00
pass.filter = conf.Exists(key) ? (conf.GetBool(key) ? GL_LINEAR : GL_NEAREST) : GLSL_UNDEFINED;
2018-05-11 01:47:55 +02:00
sprintf(key, "::scale_type%u", i);
const char* scaleType = conf.GetString(key, "");
if (!strcasecmp(scaleType, ""))
{
sprintf(key, "::scale_type_x%u", i);
const char* scaleTypeX = conf.GetString(key, "");
pass.scale_type_x = scale_string_to_enum(scaleTypeX, i == shader_count - 1);
sprintf(key, "::scale_type_y%u", i);
const char* scaleTypeY = conf.GetString(key, "");
pass.scale_type_y = scale_string_to_enum(scaleTypeY, i == shader_count - 1);
}
else
{
int scale_type = scale_string_to_enum(scaleType, i == shader_count - 1);
pass.scale_type_x = scale_type;
pass.scale_type_y = scale_type;
}
sprintf(key, "::scale%u", i);
const char* scaleFloat = conf.GetString(key, "");
if (!strcasecmp(scaleFloat, ""))
{
sprintf(key, "::scale_x%u", i);
const char* scaleFloatX = conf.GetString(key, "1.0");
pass.scale_x = atof(scaleFloatX);
sprintf(key, "::scale_y%u", i);
const char* scaleFloatY = conf.GetString(key, "1.0");
pass.scale_y = atof(scaleFloatY);
}
else
{
pass.scale_x = pass.scale_y = atof(scaleFloat);
}
sprintf(key, "::shader%u", i);
strcpy(pass.filename, conf.GetString(key, ""));
2018-05-14 21:27:07 +02:00
sprintf(key, "::wrap_mode%u", i);
pass.wrap_mode = wrap_mode_string_to_enum (conf.GetString (key ,""));
2018-05-11 01:47:55 +02:00
sprintf(key, "::frame_count_mod%u", i);
pass.frame_count_mod = conf.GetInt(key, 0);
if (float_texture_available ())
{
sprintf(key, "::float_framebuffer%u", i);
pass.fp = conf.GetBool(key);
}
else
pass.fp = false;
if (srgb_available ())
{
sprintf(key, "::srgb_framebuffer%u", i);
pass.srgb = conf.GetBool(key);
}
else
pass.srgb = false;
sprintf(key, "::alias%u", i);
strcpy(pass.alias, conf.GetString(key, ""));
2018-05-11 01:47:55 +02:00
this->pass.push_back(pass);
}
char* ids = conf.GetStringDup("::textures", "");
char* id = strtok(ids, ";");
while (id != NULL)
{
GLSLLut lut;
sprintf(key, "::%s", id);
strcpy(lut.id, id);
strcpy(lut.filename, conf.GetString(key, ""));
sprintf(key, "::%s_wrap_mode", id);
lut.wrap_mode = wrap_mode_string_to_enum (conf.GetString (key, ""));
sprintf(key, "::%s_mipmap", id);
lut.mipmap = conf.GetBool (key);
2018-05-11 01:47:55 +02:00
sprintf(key, "::%s_linear", id);
lut.filter = (conf.GetBool(key, false)) ? GL_LINEAR : GL_NEAREST;
if (lut.mipmap)
{
lut.filter = (lut.filter == GL_LINEAR) ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST;
}
2018-05-11 01:47:55 +02:00
this->lut.push_back(lut);
id = strtok(NULL, ";");
}
2018-05-12 22:07:07 +02:00
2018-05-11 01:47:55 +02:00
free(ids);
return true;
}
2018-05-12 00:08:13 +02:00
void GLSLShader::strip_parameter_pragmas(char *buffer)
2018-05-11 01:47:55 +02:00
{
2018-05-14 01:39:56 +02:00
// #pragma parameter lines tend to have " characters in them,
// which is not legal GLSL.
2018-05-11 01:47:55 +02:00
char *s = strstr(buffer, "#pragma parameter");
while (s)
{
2018-05-12 00:08:13 +02:00
GLSLParam par;
unsigned int i;
sscanf (s, "#pragma parameter %s \"%[^\"]\" %f %f %f %f",
par.id, par.name, &par.val, &par.min, &par.max, &par.step);
2018-05-12 22:07:07 +02:00
if (par.step == 0.0f)
par.step = 1.0f;
2018-05-12 00:08:13 +02:00
for (i = 0; i < param.size(); i++)
{
if (!strcmp(param[i].id, par.id))
break;
}
if (i >= param.size())
param.push_back (par);
2018-05-14 01:39:56 +02:00
// blank out the line
2018-05-12 00:08:13 +02:00
while (*s != '\0' && *s != '\n')
*s++ = ' ';
s = strstr(s, "#pragma parameter");
2018-05-11 01:47:55 +02:00
}
}
2018-05-12 00:08:13 +02:00
GLuint GLSLShader::compile_shader(char *program,
const char *aliases,
const char *defines,
GLuint type,
GLuint *out)
2018-05-11 01:47:55 +02:00
{
char info_log[1024];
char *ptr = program;
std::string complete_program = "";
char version[32];
const char *existing_version = strstr(ptr, "#version");
if (existing_version)
{
unsigned version_no = (unsigned) strtoul (existing_version + 8, &ptr, 10);
snprintf (version, 32, "#version %u\n", version_no);
}
else
{
snprintf (version, 32, "#version 150\n");
}
complete_program += version;
complete_program += aliases;
2018-05-11 01:47:55 +02:00
complete_program += defines;
complete_program += ptr;
GLuint shader = glCreateShader (type);
GLint status;
GLint length = complete_program.length();
GLchar *prog = (GLchar *) complete_program.c_str();
glShaderSource (shader, 1, &prog, &length);
glCompileShader (shader);
glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
2018-05-14 03:17:02 +02:00
info_log[0] = '\0';
2018-05-11 01:47:55 +02:00
glGetShaderInfoLog (shader, 1024, NULL, info_log);
2018-05-14 03:17:02 +02:00
if (*info_log)
2018-05-11 01:47:55 +02:00
printf ("%s\n", info_log);
*out = shader;
return status;
}
bool GLSLShader::load_shader (char *filename)
{
char shader_path[PATH_MAX];
char temp[PATH_MAX];
std::string aliases = "";
2018-05-11 01:47:55 +02:00
GLint status;
char log[1024];
if (this->pass.size())
return false;
if (!filename || *filename == '\0')
return false;
strcpy(shader_path, filename);
reduce_to_path(shader_path);
chdir(shader_path);
if (!load_shader_file(filename))
return false;
/*
for (unsigned int i = 1; i < pass.size(); i++)
{
if (pass[i].alias && *pass[i].alias)
{
aliases += "#define ";
aliases += pass[i].alias;
aliases += " Pass";
aliases += std::to_string(i - 1);
aliases += "Texture\n";
printf ("%s\n", aliases.c_str());
}
}*/
2018-05-11 01:47:55 +02:00
for (unsigned int i = 1; i < pass.size(); i++)
{
GLSLPass *p = &pass[i];
GLuint vertex_shader = 0, fragment_shader = 0;
realpath(p->filename, temp);
2018-05-12 00:08:13 +02:00
strcpy (p->filename, temp);
2018-05-11 01:47:55 +02:00
2018-05-12 00:08:13 +02:00
char *contents = read_file (p->filename);
2018-05-11 01:47:55 +02:00
if (!contents)
{
printf("Couldn't read shader file %s\n", temp);
return false;
}
strip_parameter_pragmas(contents);
if (!compile_shader (contents,
2018-05-12 00:08:13 +02:00
"#define VERTEX\n#define PARAMETER_UNIFORM\n",
aliases.c_str(),
2018-05-11 01:47:55 +02:00
GL_VERTEX_SHADER,
&vertex_shader) || !vertex_shader)
{
2018-05-15 00:19:55 +02:00
printf("Couldn't compile vertex shader in %s.\n", p->filename);
2018-05-11 01:47:55 +02:00
return false;
}
if (!compile_shader (contents,
2018-05-12 00:08:13 +02:00
"#define FRAGMENT\n#define PARAMETER_UNIFORM\n",
aliases.c_str(),
2018-05-11 01:47:55 +02:00
GL_FRAGMENT_SHADER,
&fragment_shader) || !fragment_shader)
{
2018-05-15 00:19:55 +02:00
printf("Couldn't compile fragment shader in %s.\n", p->filename);
2018-05-11 01:47:55 +02:00
return false;
}
2018-05-15 01:27:06 +02:00
delete[] contents;
2018-05-11 01:47:55 +02:00
p->program = glCreateProgram ();
glAttachShader (p->program, vertex_shader);
glAttachShader (p->program, fragment_shader);
glLinkProgram (p->program);
glGetProgramiv (p->program, GL_LINK_STATUS, &status);
2018-05-14 03:17:02 +02:00
log[0] = '\0';
2018-05-11 01:47:55 +02:00
glGetProgramInfoLog(p->program, 1024, NULL, log);
2018-05-14 03:17:02 +02:00
if (*log)
2018-05-11 01:47:55 +02:00
printf ("%s\n", log);
glDeleteShader (vertex_shader);
glDeleteShader (fragment_shader);
if (status != GL_TRUE)
{
printf ("Failed to link program\n");
glDeleteProgram (p->program);
return false;
}
glGenFramebuffers(1, &p->fbo);
glGenTextures(1, &p->texture);
glBindTexture(GL_TEXTURE_2D, p->texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
p->frame_count = 0;
}
for (unsigned int i = 0; i < lut.size(); i++)
{
GLSLLut *l = &lut[i];
2018-05-14 01:39:56 +02:00
// generate texture for the lut and apply specified filter setting
2018-05-11 01:47:55 +02:00
glGenTextures(1, &l->texture);
glBindTexture(GL_TEXTURE_2D, l->texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, l->wrap_mode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, l->wrap_mode);
2018-05-11 01:47:55 +02:00
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, l->filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, l->filter);
realpath(l->filename, temp);
2018-05-12 22:07:07 +02:00
strcpy (l->filename, temp);
2018-05-11 01:47:55 +02:00
// simple file extension png/tga decision
int length = strlen(temp);
if (length > 4)
{
if (!strcasecmp(&temp[length - 4], ".png"))
{
int width, height;
bool hasAlpha;
GLubyte* texData;
if (loadPngImage(temp, width, height, hasAlpha, &texData))
{
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
width,
height,
0,
hasAlpha ? GL_RGBA : GL_RGB,
GL_UNSIGNED_BYTE,
texData);
free(texData);
}
else
{
printf ("Failed to load PNG LUT: %s\n", temp);
}
}
else if (!strcasecmp(&temp[length - 4], ".tga"))
{
STGA stga;
if (loadTGA(temp, stga))
{
glPixelStorei(GL_UNPACK_ROW_LENGTH, stga.width);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
stga.width,
stga.height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
stga.data);
}
else
{
printf ("Failed to load TGA LUT: %s\n", temp);
}
}
}
if (l->mipmap)
glGenerateMipmap (GL_TEXTURE_2D);
2018-05-11 01:47:55 +02:00
}
2018-05-14 01:39:56 +02:00
// Check for parameters specified in file
2018-05-12 00:08:13 +02:00
for (unsigned int i = 0; i < param.size(); i++)
{
char key[266];
const char *value;
snprintf (key, 266, "::%s", param[i].id);
value = conf.GetString (key, NULL);
if (value)
{
param[i].val = atof (value);
if (param[i].val < param[i].min)
param[i].val = param[i].min;
if (param[i].val > param[i].max)
param[i].val = param[i].max;
2018-05-12 00:08:13 +02:00
}
}
2018-05-11 01:47:55 +02:00
glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, tex_coords);
2018-05-11 01:47:55 +02:00
glActiveTexture(GL_TEXTURE0);
register_uniforms();
prev_frame.resize (max_prev_frame);
for (unsigned int i = 0; i < prev_frame.size(); i++)
{
glGenTextures(1, &(prev_frame[i].texture));
prev_frame[i].width = prev_frame[i].height = 0;
}
glGenBuffers(1, &vbo);
frame_count = 0;
return true;
}
2018-05-13 00:36:45 +02:00
void GLSLShader::render(GLuint &orig, int width, int height, int viewport_width, int viewport_height, int viewport_x, int viewport_y)
2018-05-11 01:47:55 +02:00
{
frame_count++;
2018-05-14 01:39:56 +02:00
// set up our dummy pass for easier loop code
2018-05-11 01:47:55 +02:00
pass[0].texture = orig;
pass[0].width = width;
pass[0].height = height;
2018-05-14 01:39:56 +02:00
// loop through all real passes
2018-05-11 01:47:55 +02:00
for (unsigned int i = 1; i < pass.size(); i++)
{
2018-05-13 00:36:45 +02:00
bool lastpass = (i == pass.size() - 1);
2018-05-11 01:47:55 +02:00
switch (pass[i].scale_type_x)
{
case GLSL_ABSOLUTE:
pass[i].width = pass[i].scale_x;
break;
case GLSL_SOURCE:
pass[i].width = pass[i-1].width * pass[i].scale_x;
break;
case GLSL_VIEWPORT:
pass[i].width = viewport_width * pass[i].scale_x;
break;
default:
pass[i].width = viewport_width;
}
switch (pass[i].scale_type_y)
{
case GLSL_ABSOLUTE:
pass[i].height = pass[i].scale_y;
break;
case GLSL_SOURCE:
pass[i].height = pass[i - 1].height * pass[i].scale_y;
break;
case GLSL_VIEWPORT:
pass[i].height = viewport_height * pass[i].scale_y;
break;
default:
pass[i].height = viewport_height;
}
if (!lastpass)
{
2018-05-14 01:39:56 +02:00
// Output to a framebuffer texture
glBindTexture(GL_TEXTURE_2D, pass[i].texture);
if (pass[i].srgb)
{
glEnable(GL_FRAMEBUFFER_SRGB);
glTexImage2D(GL_TEXTURE_2D,
0,
GL_SRGB8_ALPHA8,
(unsigned int) pass[i].width,
(unsigned int) pass[i].height,
0,
GL_RGBA,
GL_UNSIGNED_INT_8_8_8_8,
NULL);
}
else
{
glTexImage2D(GL_TEXTURE_2D,
0,
(pass[i].fp ? GL_RGBA32F : GL_RGBA),
(unsigned int) pass[i].width,
(unsigned int) pass[i].height,
0,
GL_RGBA,
(pass[i].fp ? GL_FLOAT : GL_UNSIGNED_INT_8_8_8_8),
NULL);
}
2018-05-14 01:39:56 +02:00
// viewport determines the area we render into the output texture
glViewport(0, 0, pass[i].width, pass[i].height);
2018-05-14 01:39:56 +02:00
// set up framebuffer and attach output texture
glBindFramebuffer(GL_FRAMEBUFFER, pass[i].fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
pass[i].texture,
0);
}
else
{
2018-05-14 01:39:56 +02:00
// output to the screen
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(viewport_x, viewport_y, viewport_width, viewport_height);
}
2018-05-11 01:47:55 +02:00
// set up input texture (output of previous pass) and apply filter settings
2018-05-13 00:36:45 +02:00
GLuint filter = (pass[i].filter == GLSL_UNDEFINED) ?
(lastpass ?
(Settings.BilinearFilter ? GL_LINEAR : GL_NEAREST) : GL_NEAREST
2018-05-13 00:36:45 +02:00
) : pass[i].filter;
2018-05-11 01:47:55 +02:00
glBindTexture(GL_TEXTURE_2D, pass[i - 1].texture);
glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint) pass[i - 1].width);
glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_MAG_FILTER,
2018-05-13 00:36:45 +02:00
filter);
2018-05-11 01:47:55 +02:00
glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_MIN_FILTER,
2018-05-13 00:36:45 +02:00
filter);
2018-05-14 21:27:07 +02:00
glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_WRAP_S,
pass[i].wrap_mode);
glTexParameteri(GL_TEXTURE_2D,
GL_TEXTURE_WRAP_T,
pass[i].wrap_mode);
2018-05-11 01:47:55 +02:00
2018-05-19 21:10:48 +02:00
glTexCoordPointer(2, GL_FLOAT, 0, tex_coords + (lastpass ? 8 : 0));
2018-05-11 01:47:55 +02:00
glUseProgram (pass[i].program);
set_shader_vars(i);
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_QUADS, 0, 4);
2018-05-14 01:39:56 +02:00
// reset vertex attribs set in set_shader_vars
2018-05-11 01:47:55 +02:00
clear_shader_vars();
if (pass[i].srgb)
{
glDisable (GL_FRAMEBUFFER_SRGB);
}
2018-05-11 01:47:55 +02:00
}
2018-05-14 01:39:56 +02:00
// Disable framebuffer
2018-05-11 01:47:55 +02:00
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glActiveTexture(GL_TEXTURE0);
glUseProgram (0);
2018-05-14 01:39:56 +02:00
// Pop back of previous frame stack and use as upload buffer
2018-05-11 01:47:55 +02:00
if (prev_frame.size() > 0)
{
GLint internal_format;
orig = prev_frame.back().texture;
prev_frame.pop_back();
GLSLPass newprevframe;
newprevframe.width = width;
newprevframe.height = height;
newprevframe.texture = pass[0].texture;
prev_frame.push_front(newprevframe);
glBindTexture(GL_TEXTURE_2D, newprevframe.texture);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &internal_format);
glBindTexture(GL_TEXTURE_2D, orig);
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
glTexImage2D(GL_TEXTURE_2D,
0,
internal_format,
width,
height,
0,
GL_RGB,
GL_UNSIGNED_BYTE,
NULL);
}
glBindTexture(GL_TEXTURE_2D, orig);
2018-05-11 01:47:55 +02:00
glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pass.back().width);
glTexCoordPointer(2, GL_FLOAT, 0, tex_coords + 8);
2018-05-11 01:47:55 +02:00
}
void GLSLShader::register_uniforms ()
{
max_prev_frame = 0;
char varname[100];
for (unsigned int i = 1; i < pass.size(); i++)
{
GLSLUniforms *u = &pass[i].unif;
GLuint program = pass[i].program;
glUseProgram (program);
GLint mvp = glGetUniformLocation (program, "MVPMatrix");
if (mvp > -1)
glUniformMatrix4fv(mvp, 1, GL_FALSE, mvp_ortho);
u->Texture = glGetUniformLocation (program, "Texture");
u->InputSize = glGetUniformLocation (program, "InputSize");
u->OutputSize = glGetUniformLocation (program, "OutputSize");
u->TextureSize = glGetUniformLocation (program, "TextureSize");
u->TexCoord = glGetAttribLocation (program, "TexCoord");
u->LUTTexCoord = glGetAttribLocation (program, "LUTTexCoord");
u->VertexCoord = glGetAttribLocation (program, "VertexCoord");
u->FrameCount = glGetUniformLocation (program, "FrameCount");
u->FrameDirection = glGetUniformLocation (program, "FrameDirection");
u->OrigTexture = glGetUniformLocation (program, "OrigTexture");
u->OrigInputSize = glGetUniformLocation (program, "OrigInputSize");
u->OrigTextureSize = glGetUniformLocation (program, "OrigTextureSize");
u->OrigTexCoord = glGetAttribLocation (program, "OrigTexCoord");
u->Prev[0].Texture = glGetUniformLocation (program, "PrevTexture");
u->Prev[0].InputSize = glGetUniformLocation (program, "PrevInputSize");
u->Prev[0].TextureSize = glGetUniformLocation (program, "PrevTextureSize");
u->Prev[0].TexCoord = glGetAttribLocation (program, "PrevTexCoord");
if (u->Prev[0].Texture > -1)
max_prev_frame = 1;
for (unsigned int j = 1; j < 7; j++)
{
sprintf(varname, "Prev%dTexture", j);
u->Prev[j].Texture = glGetUniformLocation (program, varname);
sprintf(varname, "Prev%dInputSize", j);
u->Prev[j].InputSize = glGetUniformLocation (program, varname);
sprintf(varname, "Prev%dTextureSize", j);
u->Prev[j].TextureSize = glGetUniformLocation (program, varname);
sprintf(varname, "Prev%dTexCoord", j);
u->Prev[j].TexCoord = glGetAttribLocation (program, varname);
if (u->Prev[j].Texture > -1)
max_prev_frame = j + 1;
}
for (unsigned int j = 0; j < pass.size(); j++)
{
sprintf(varname, "Pass%dTexture", j);
u->Pass[j].Texture = glGetUniformLocation (program, varname);
sprintf(varname, "Pass%dInputSize", j);
u->Pass[j].InputSize = glGetUniformLocation (program, varname);
sprintf(varname, "Pass%dTextureSize", j);
u->Pass[j].TextureSize = glGetUniformLocation (program, varname);
sprintf(varname, "Pass%dTexCoord", j);
u->Pass[j].TexCoord = glGetAttribLocation (program, varname);
if (u->Pass[j].Texture)
u->max_pass = j;
sprintf(varname, "PassPrev%dTexture", j);
u->PassPrev[j].Texture = glGetUniformLocation (program, varname);
sprintf(varname, "PassPrev%dInputSize", j);
u->PassPrev[j].InputSize = glGetUniformLocation (program, varname);
sprintf(varname, "PassPrev%dTextureSize", j);
u->PassPrev[j].TextureSize = glGetUniformLocation (program, varname);
sprintf(varname, "PassPrev%dTexCoord", j);
u->PassPrev[j].TexCoord = glGetAttribLocation (program, varname);
if (u->PassPrev[j].Texture > -1)
u->max_prevpass = j;
}
for (unsigned int j = 0; j < lut.size(); j++)
{
u->Lut[j] = glGetUniformLocation (program, lut[j].id);
}
2018-05-12 00:08:13 +02:00
for (unsigned int j = 0; j < param.size(); j++)
{
param[j].unif[i] = glGetUniformLocation (program, param[j].id);
}
2018-05-11 01:47:55 +02:00
}
glUseProgram (0);
}
void GLSLShader::set_shader_vars (unsigned int p)
2018-05-11 01:47:55 +02:00
{
unsigned int texunit = 0;
unsigned int offset = 0;
if (p == pass.size() - 1)
offset = 8;
2018-05-11 01:47:55 +02:00
GLSLUniforms *u = &pass[p].unif;
GLint mvp = glGetUniformLocation (pass[p].program, "MVPMatrix");
if (mvp > -1)
glUniformMatrix4fv(mvp, 1, GL_FALSE, mvp_ortho);
#define setUniform2fv(uni, val) if (uni > -1) glUniform2fv (uni, 1, val);
#define setUniform1f(uni, val) if (uni > -1) glUniform1f (uni, val);
#define setUniform1i(uni, val) if (uni > -1) glUniform1i (uni, val);
#define setTexture1i(uni, val) \
if (uni > -1) \
{ \
glActiveTexture (GL_TEXTURE0 + texunit); \
glBindTexture (GL_TEXTURE_2D, val); \
glUniform1i (uni, texunit); \
texunit++; \
}
2018-05-14 01:39:56 +02:00
// We use non-power-of-two textures,
// so no need to mess with input size/texture size
2018-05-11 01:47:55 +02:00
#define setTexCoords(attr) \
if (attr > -1) \
{ \
glEnableVertexAttribArray(attr); \
2018-05-14 03:17:02 +02:00
glVertexAttribPointer(attr, 2, GL_FLOAT, GL_FALSE, 0, (void *)(sizeof (float) * offset)); \
vaos.push_back (attr); \
}
#define setTexCoordsNoOffset(attr) \
2018-05-11 01:47:55 +02:00
if (attr > -1) \
{ \
glEnableVertexAttribArray(attr); \
glVertexAttribPointer(attr, 2, GL_FLOAT, GL_FALSE, 0, NULL); \
vaos.push_back (attr); \
}
glBindBuffer (GL_ARRAY_BUFFER, vbo);
glBufferData (GL_ARRAY_BUFFER, sizeof (GLfloat) * 16, tex_coords, GL_STATIC_DRAW);
2018-05-11 01:47:55 +02:00
float inputSize[2] = { (float) pass[p - 1].width, (float) pass[p - 1].height };
float outputSize[2] = { (float) pass[p].width, (float) pass[p].height };
setTexture1i (u->Texture, pass[p - 1].texture);
setUniform2fv(u->InputSize, inputSize);
setUniform2fv(u->OutputSize, outputSize);
setUniform2fv(u->TextureSize, inputSize);
unsigned int shaderFrameCnt = frame_count;
if (pass[p].frame_count_mod)
shaderFrameCnt %= pass[p].frame_count_mod;
setUniform1i(u->FrameCount, (float) shaderFrameCnt);
setUniform1i(u->FrameDirection, Settings.Rewinding ? -1.0f : 1.0f);
2018-05-11 01:47:55 +02:00
setTexCoords (u->TexCoord);
setTexCoords (u->LUTTexCoord);
setTexCoordsNoOffset (u->VertexCoord);
2018-05-14 01:39:56 +02:00
// Orig parameter
2018-05-11 01:47:55 +02:00
float orig_videoSize[2] = { (float) pass[0].width, (float) pass[0].height };
setUniform2fv(u->OrigInputSize, orig_videoSize);
setUniform2fv(u->OrigTextureSize, orig_videoSize);
setTexture1i (u->OrigTexture, pass[0].texture);
setTexCoords (u->OrigTexCoord);
2018-05-14 01:39:56 +02:00
// Prev parameter
2018-05-11 01:47:55 +02:00
if (max_prev_frame >= 1 && prev_frame[0].width > 0) {
float prevSize[2] = { (float) prev_frame[0].width, (float) prev_frame[0].height };
setUniform2fv(u->Prev[0].InputSize, prevSize);
setUniform2fv(u->Prev[0].TextureSize, prevSize);
setTexture1i (u->Prev[0].Texture, prev_frame[0].texture);
setTexCoords (u->Prev[0].TexCoord);
}
2018-05-14 01:39:56 +02:00
// Prev[1-6] parameters
2018-05-11 01:47:55 +02:00
for (unsigned int i = 1; i < prev_frame.size(); i++)
{
if (prev_frame[i].width <= 0)
break;
float prevSize[2] = { (float) prev_frame[i].width, (float) prev_frame[i].height };
setUniform2fv(u->Prev[i].InputSize, prevSize);
setUniform2fv(u->Prev[i].TextureSize, prevSize);
setTexture1i (u->Prev[i].Texture, prev_frame[i].texture);
setTexCoords (u->Prev[i].TexCoord);
}
2018-05-14 01:39:56 +02:00
// LUT parameters
2018-05-11 01:47:55 +02:00
for (unsigned int i = 0; i < lut.size(); i++)
{
setTexture1i (u->Lut[i], lut[i].texture);
}
2018-05-14 01:39:56 +02:00
// PassX parameters, only for third pass and up
2018-05-11 01:47:55 +02:00
if (p > 2) {
for (unsigned int i = 1; i < p - 1; i++) {
2018-05-11 01:47:55 +02:00
float passSize[2] = { (float) pass[i].width, (float) pass[i].height };
setUniform2fv(u->Pass[i].InputSize, passSize);
setUniform2fv(u->Pass[i].TextureSize, passSize);
setTexture1i (u->Pass[i].Texture, pass[i].texture);
setTexCoords (u->Pass[i].TexCoord);
}
}
2018-05-14 01:39:56 +02:00
// PassPrev parameter
for (unsigned int i = 0; i < p; i++)
2018-05-11 01:47:55 +02:00
{
float passSize[2] = { (float) pass[i].width, (float) pass[i].height };
setUniform2fv(u->PassPrev[p - i].InputSize, passSize);
setUniform2fv(u->PassPrev[p - i].TextureSize, passSize);
setTexture1i (u->PassPrev[p - i].Texture, pass[i].texture);
setTexCoords (u->PassPrev[p - i].TexCoord);
}
2018-05-14 01:39:56 +02:00
// User and Preset Parameters
2018-05-12 00:08:13 +02:00
for (unsigned int i = 0; i < param.size(); i++)
{
setUniform1f (param[i].unif[p], param[i].val);
}
2018-05-11 01:47:55 +02:00
glActiveTexture (GL_TEXTURE0);
glBindBuffer (GL_ARRAY_BUFFER, 0);
}
2018-05-12 22:07:07 +02:00
void GLSLShader::clear_shader_vars (void)
{
for (unsigned int i = 0; i < vaos.size(); i++)
glDisableVertexAttribArray (vaos[i]);
vaos.clear();
}
#define outs(s, v) fprintf (file, "%s%d = \"%s\"\n", s, i, v)
#define outf(s, v) fprintf (file, "%s%d = \"%f\"\n", s, i, v)
#define outd(s, v) fprintf (file, "%s%d = \"%d\"\n", s, i, v)
2018-05-13 18:02:52 +02:00
void GLSLShader::save (const char *filename)
2018-05-12 22:07:07 +02:00
{
2018-05-13 18:02:52 +02:00
FILE *file = fopen (filename, "wb");
2018-05-12 22:07:07 +02:00
if (!file)
{
2018-05-13 18:02:52 +02:00
printf ("Couldn't save shader config to %s\n", filename);
2018-05-12 22:07:07 +02:00
}
fprintf (file, "shaders = \"%d\"\n", (unsigned int) pass.size() - 1);
for (unsigned int pn = 1; pn < pass.size(); pn++)
{
GLSLPass *p = &pass[pn];
unsigned int i = pn - 1;
outs("shader", p->filename);
if (p->filter != GLSL_UNDEFINED)
{
outs("filter_linear", p->filter == GL_LINEAR ? "true" : "false");
}
2018-05-15 00:19:55 +02:00
outs ("wrap_mode", wrap_mode_enum_to_string(p->wrap_mode));
outs ("alias", p->alias);
2018-05-12 22:07:07 +02:00
outs ("float_framebuffer", p->fp ? "true" : "false");
outs ("srgb_framebuffer", p->srgb ? "true" : "false");
outs ("scale_type_x", scale_enum_to_string(p->scale_type_x));
if (p->scale_type_x == GLSL_ABSOLUTE)
outd ("scale_x", (int) p->scale_x);
else
outf ("scale_x", p->scale_x);
outs ("scale_type_y", scale_enum_to_string(p->scale_type_y));
if (p->scale_type_y == GLSL_ABSOLUTE)
outd ("scale_y", (int) p->scale_y);
else
outf ("scale_y", p->scale_y);
if (p->frame_count_mod)
outd ("frame_count_mod", p->frame_count_mod);
}
if (param.size() > 0)
{
fprintf (file, "parameters = \"");
for (unsigned int i = 0; i < param.size(); i++)
{
fprintf (file, "%s%c", param[i].id, (i == param.size() - 1) ? '\"' : ';');
}
fprintf (file, "\n");
}
for (unsigned int i = 0; i < param.size(); i++)
{
fprintf (file, "%s = \"%f\"\n", param[i].id, param[i].val);
}
if (lut.size() > 0)
{
fprintf (file, "textures = \"");
for (unsigned int i = 0; i < lut.size(); i++)
{
fprintf (file, "%s%c", lut[i].id, (i == lut.size() - 1) ? '\"' : ';');
}
fprintf (file, "\n");
}
for (unsigned int i = 0; i < lut.size(); i++)
{
fprintf (file, "%s = \"%s\"\n", lut[i].id, lut[i].filename);
fprintf (file, "%s_linear = \"%s\"\n", lut[i].id, lut[i].filter == GL_LINEAR || lut[i].filter == GL_LINEAR_MIPMAP_LINEAR ? "true" : "false");
fprintf (file, "%s_wrap_mode = \"%s\"\n", lut[i].id, wrap_mode_enum_to_string(lut[i].wrap_mode));
fprintf (file, "%s_mipmap = \"%s\"\n", lut[i].id, lut[i].mipmap ? "true" : "false");
}
fclose (file);
}
#undef outf
#undef outs
#undef outd
2018-05-11 01:47:55 +02:00
void GLSLShader::destroy (void)
{
2018-05-12 22:07:07 +02:00
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
glActiveTexture(GL_TEXTURE0);
2018-05-11 01:47:55 +02:00
for (unsigned int i = 1; i < pass.size(); i++)
{
2018-05-12 22:07:07 +02:00
glDeleteProgram(pass[i].program);
glDeleteTextures(1, &pass[i].texture);
2018-05-11 01:47:55 +02:00
glDeleteFramebuffers(1, &pass[i].fbo);
}
for (unsigned int i = 0; i < lut.size(); i++)
{
glDeleteTextures (1, &lut[i].texture);
}
for (unsigned int i = 0; i < prev_frame.size (); i++)
{
glDeleteTextures (1, &prev_frame[i].texture);
}
2018-05-12 00:08:13 +02:00
param.clear();
2018-05-11 01:47:55 +02:00
pass.clear();
lut.clear();
prev_frame.clear();
2018-05-20 19:50:28 +02:00
conf.Clear();
2018-05-11 01:47:55 +02:00
}