snes9x/shaders/glsl.cpp
Brandon Wright 8c8805da40 Slang: Implement feedback support. See below.
This uses a complex workaround because we normally draw the last
pass straight to the screen. Now, only in the event that feedback is
enabled and only if the last pass uses it, we draw to a texture and use
glBlitFramebuffer to draw to the screen (saved_framebuffer) instead.
2019-01-27 17:43:22 -06:00

1169 lines
36 KiB
C++

/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include <vector>
#include <string>
#include <sstream>
#include "glsl.h"
#include "../../conffile.h"
#include "shader_helpers.h"
#include "shader_platform.h"
static const GLfloat tex_coords[16] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f };
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 int scale_string_to_enum(const char *string)
{
if (!strcasecmp(string, "source"))
{
return GLSL_SOURCE;
}
else if (!strcasecmp(string, "viewport"))
{
return GLSL_VIEWPORT;
}
else if (!strcasecmp(string, "absolute"))
{
return GLSL_ABSOLUTE;
}
else
return GLSL_NONE;
}
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 "undefined";
}
}
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;
}
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";
}
}
bool GLSLShader::load_shader_preset_file(char *filename)
{
char key[256];
int length = strlen(filename);
bool singlepass = false;
if (length > 6 && (!strcasecmp(&filename[length - 5], ".glsl") ||
!strcasecmp(&filename[length - 6], ".slang")))
singlepass = true;
if (length > 7 && (!strcasecmp(&filename[length - 6], ".slang") ||
!strcasecmp(&filename[length - 7], ".slangp")))
{
#ifdef USE_SLANG
this->using_slang = true;
#else
return false;
#endif
}
if (singlepass)
{
GLSLPass pass;
this->pass.push_back(GLSLPass());
pass.scale_type_x = pass.scale_type_y = GLSL_VIEWPORT;
pass.filter = GLSL_UNDEFINED;
pass.wrap_mode = GL_CLAMP_TO_BORDER;
strcpy(pass.filename, filename);
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;
}
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);
pass.filter = conf.Exists(key) ? (conf.GetBool(key) ? GL_LINEAR : GL_NEAREST) : GLSL_UNDEFINED;
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);
sprintf(key, "::scale_type_y%u", i);
const char* scaleTypeY = conf.GetString(key, "");
pass.scale_type_y = scale_string_to_enum(scaleTypeY);
}
else
{
int scale_type = scale_string_to_enum(scaleType);
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, ""));
sprintf(key, "::wrap_mode%u", i);
pass.wrap_mode = wrap_mode_string_to_enum (conf.GetString (key ,""));
sprintf(key, "::frame_count_mod%u", i);
pass.frame_count_mod = conf.GetInt(key, 0);
if (gl_float_texture_available())
{
sprintf(key, "::float_framebuffer%u", i);
pass.fp = conf.GetBool(key);
}
else
pass.fp = false;
if (gl_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, ""));
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);
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;
}
this->lut.push_back(lut);
id = strtok(NULL, ";");
}
free(ids);
return true;
}
void GLSLShader::strip_parameter_pragmas(std::vector<std::string> &lines)
{
// #pragma parameter lines tend to have " characters in them,
// which is not legal GLSL.
auto it = lines.begin();
while (it != lines.end())
{
if (it->find("#pragma parameter") != std::string::npos)
{
GLSLParam par;
sscanf(it->c_str(), "#pragma parameter %s \"%[^\"]\" %f %f %f %f",
par.id, par.name, &par.val, &par.min, &par.max, &par.step);
if (par.step == 0.0f)
par.step = 1.0f;
unsigned int i = 0;
for (; i < param.size(); i++)
{
if (!strcmp(param[i].id, par.id))
break;
}
if (i >= param.size())
param.push_back(par);
it = lines.erase(it);
}
else
++it;
}
}
GLuint GLSLShader::compile_shader(std::vector<std::string> &lines,
const char *aliases,
const char *defines,
GLuint type,
GLuint *out)
{
char info_log[1024];
std::string source;
unsigned int first_line = 0;
if (lines.empty())
return 0;
if (lines[0].find("#version") == std::string::npos)
{
int version = gl_version();
if (version >= 33)
version *= 10;
else
version = 150;
source += "#version " + std::to_string(version) + "\n";
}
else
{
source += lines[0] + "\n";
first_line++;
}
source += aliases;
source += defines;
for (unsigned int i = first_line; i < lines.size(); i++)
source += lines[i] + "\n";
GLuint shader = glCreateShader(type);
GLint status;
GLint length = source.length();
GLchar *prog = (GLchar *)source.c_str();
glShaderSource(shader, 1, &prog, &length);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
info_log[0] = '\0';
glGetShaderInfoLog(shader, 1024, NULL, info_log);
if (*info_log)
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 = "";
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_preset_file(filename))
return false;
for (unsigned int i = 1; i < pass.size(); i++)
{
GLSLPass *p = &pass[i];
GLuint vertex_shader = 0, fragment_shader = 0;
realpath(p->filename, temp);
strcpy(p->filename, temp);
std::vector<std::string> lines;
read_shader_file_with_includes(p->filename, lines);
if (lines.empty())
{
printf("Couldn't read shader file %s\n", temp);
return false;
}
strip_parameter_pragmas(lines);
#ifdef USE_SLANG
if (using_slang)
{
slang_parse_pragmas(lines, i);
GLint retval;
retval = slang_compile(lines, "vertex");
if (retval < 0)
{
printf("Vertex shader in %s failed to compile.\n", p->filename);
return false;
}
vertex_shader = retval;
retval = slang_compile(lines, "fragment");
if (retval < 0)
{
printf ("Fragment shader in %s failed to compile.\n", p->filename);
return false;
}
fragment_shader = retval;
}
else
#endif
{
if (!compile_shader(lines,
"#define VERTEX\n#define PARAMETER_UNIFORM\n",
aliases.c_str(),
GL_VERTEX_SHADER,
&vertex_shader) || !vertex_shader)
{
printf("Couldn't compile vertex shader in %s.\n", p->filename);
return false;
}
if (!compile_shader(lines,
"#define FRAGMENT\n#define PARAMETER_UNIFORM\n",
aliases.c_str(),
GL_FRAGMENT_SHADER,
&fragment_shader) || !fragment_shader)
{
printf("Couldn't compile fragment shader in %s.\n", p->filename);
return false;
}
}
p->program = glCreateProgram();
glAttachShader(p->program, vertex_shader);
glAttachShader(p->program, fragment_shader);
glLinkProgram(p->program);
glGetProgramiv(p->program, GL_LINK_STATUS, &status);
log[0] = '\0';
glGetProgramInfoLog(p->program, 1024, NULL, log);
if (*log)
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];
// generate texture for the lut and apply specified filter setting
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);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, l->filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, l->filter);
realpath(l->filename, temp);
strcpy(l->filename, temp);
// simple file extension png/tga decision
int length = strlen(temp);
if (length > 4)
{
if (!strcasecmp(&temp[length - 4], ".png"))
{
int width, height;
bool hasAlpha;
bool grayscale;
GLubyte* texData;
if (loadPngImage(temp, width, height, grayscale, hasAlpha,
&texData))
{
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
if (grayscale)
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
width,
height,
0,
GL_LUMINANCE,
GL_UNSIGNED_BYTE,
texData);
else
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA,
width,
height,
0,
hasAlpha ? GL_RGBA : GL_RGB,
GL_UNSIGNED_BYTE,
texData);
l->width = width;
l->height = height;
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);
l->width = stga.width;
l->height = stga.height;
}
else
{
printf("Failed to load TGA LUT: %s\n", temp);
}
}
}
if (l->mipmap)
glGenerateMipmap(GL_TEXTURE_2D);
}
// Check for parameters specified in file
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;
}
}
glActiveTexture(GL_TEXTURE0);
#ifdef USE_SLANG
if (using_slang)
slang_introspect();
else
#endif
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;
}
void GLSLShader::render(GLuint &orig,
int width, int height,
int viewport_x, int viewport_y,
int viewport_width, int viewport_height,
GLSLViewportCallback vpcallback)
{
GLint saved_framebuffer;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &saved_framebuffer);
frame_count++;
// set up our dummy pass for easier loop code
pass[0].texture = orig;
pass[0].width = width;
pass[0].height = height;
#ifdef USE_SLANG
if (using_slang && using_feedback)
for (int i = 1; i < (int)pass.size(); i++)
{
if (!pass[i].uses_feedback)
continue;
GLuint tmp = pass[i].texture;
pass[i].texture = pass[i].feedback_texture;
pass[i].feedback_texture = tmp;
}
#endif
// loop through all real passes
for (unsigned int i = 1; i < pass.size(); i++)
{
bool lastpass = (i == pass.size() - 1);
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:
if (lastpass)
pass[i].width = viewport_width;
else
pass[i].width = pass[i - 1].width * pass[i].scale_x;
}
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;
}
bool direct_lastpass = true;
#ifdef USE_SLANG
if (lastpass && using_feedback && pass[i].scale_type_x != GLSL_NONE &&
pass[i].scale_type_y != GLSL_NONE && pass[i].uses_feedback)
{
direct_lastpass = false;
}
#endif
if (!lastpass || !direct_lastpass)
{
// 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);
}
// viewport determines the area we render into the output texture
glViewport(0, 0, pass[i].width, pass[i].height);
// 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
{
int out_x = 0;
int out_y = 0;
int out_width = 0;
int out_height = 0;
// output to the screen
glBindFramebuffer(GL_FRAMEBUFFER, saved_framebuffer);
vpcallback (pass[i].width, pass[i].height,
viewport_x, viewport_y,
viewport_width, viewport_height,
&out_x, &out_y,
&out_width, &out_height);
glViewport(out_x, out_y, out_width, out_height);
}
// set up input texture (output of previous pass) and apply filter settings
GLuint filter = pass[i].filter;
if (filter == GLSL_UNDEFINED)
{
if (lastpass && Settings.BilinearFilter)
filter = GL_LINEAR;
else
filter = GL_NEAREST;
}
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, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, pass[i].wrap_mode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, pass[i].wrap_mode);
glUseProgram(pass[i].program);
#ifdef USE_SLANG
if (using_slang)
slang_set_shader_vars(i, lastpass && direct_lastpass);
else
#endif
set_shader_vars(i, lastpass && direct_lastpass);
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
#ifdef USE_SLANG
if (lastpass && !direct_lastpass)
{
glBindTexture(GL_TEXTURE_2D, pass[i].texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, pass[i].wrap_mode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, pass[i].wrap_mode);
int out_x = 0, out_y = 0, out_width = 0, out_height = 0;
vpcallback (pass[i].width, pass[i].height,
viewport_x, viewport_y,
viewport_width, viewport_height,
&out_x, &out_y,
&out_width, &out_height);
glViewport(out_x, out_y, out_width, out_height);
glBindFramebuffer(GL_FRAMEBUFFER, saved_framebuffer);
glClear(GL_COLOR_BUFFER_BIT);
glBindFramebuffer(GL_READ_FRAMEBUFFER, pass[i].fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, saved_framebuffer);
glBlitFramebuffer(0, 0, pass[i].width, pass[i].height, out_x,
out_height + out_y, out_x + out_width, out_y,
GL_COLOR_BUFFER_BIT,
Settings.BilinearFilter ? GL_LINEAR : GL_NEAREST);
}
// reset vertex attribs set in set_shader_vars
if (using_slang)
slang_clear_shader_vars();
else
#endif
clear_shader_vars();
if (pass[i].srgb)
{
glDisable(GL_FRAMEBUFFER_SRGB);
}
}
// Disable framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, saved_framebuffer);
glActiveTexture(GL_TEXTURE0);
glUseProgram(0);
// Pop back of previous frame stack and use as upload buffer
if (prev_frame.size() > 0)
{
GLint internal_format;
orig = prev_frame.back().texture;
prev_frame.pop_back();
GLSLPass *newprevframe = new GLSLPass;
newprevframe->width = width;
newprevframe->height = height;
newprevframe->texture = pass[0].texture;
prev_frame.push_front(*newprevframe);
glBindTexture(GL_TEXTURE_2D, newprevframe->texture);
delete newprevframe;
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);
glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pass.back().width);
}
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);
}
for (unsigned int j = 0; j < param.size(); j++)
{
param[j].unif[i] = glGetUniformLocation(program, param[j].id);
}
}
glUseProgram(0);
}
void GLSLShader::set_shader_vars(unsigned int p, bool inverted)
{
unsigned int texunit = 0;
unsigned int offset = 0;
if (inverted)
offset = 8;
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++; \
}
// We use non-power-of-two textures,
// so no need to mess with input size/texture size
#define setTexCoords(attr) \
if (attr > -1) \
{ \
glEnableVertexAttribArray(attr); \
glVertexAttribPointer(attr, 2, GL_FLOAT, GL_FALSE, 0, (void *)(sizeof(float) * offset)); \
vaos.push_back(attr); \
}
#define setTexCoordsNoOffset(attr) \
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);
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);
setTexCoords(u->TexCoord);
setTexCoords(u->LUTTexCoord);
setTexCoordsNoOffset(u->VertexCoord);
// Orig parameter
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);
// Prev parameter
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);
}
// Prev[1-6] parameters
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);
}
// LUT parameters
for (unsigned int i = 0; i < lut.size(); i++)
{
setTexture1i(u->Lut[i], lut[i].texture);
}
// PassX parameters, only for third pass and up
if (p > 2) {
for (unsigned int i = 1; i < p - 1; i++) {
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);
}
}
// PassPrev parameter
for (unsigned int i = 0; i < p; i++)
{
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);
}
// User and Preset Parameters
for (unsigned int i = 0; i < param.size(); i++)
{
setUniform1f(param[i].unif[p], param[i].val);
}
glActiveTexture(GL_TEXTURE0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void GLSLShader::clear_shader_vars()
{
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)
void GLSLShader::save(const char *filename)
{
FILE *file = fopen(filename, "wb");
if (!file)
{
printf("Couldn't save shader config to %s\n", filename);
}
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");
}
outs("wrap_mode", wrap_mode_enum_to_string(p->wrap_mode));
outs("alias", p->alias);
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
void GLSLShader::destroy()
{
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
glActiveTexture(GL_TEXTURE0);
for (unsigned int i = 1; i < pass.size(); i++)
{
glDeleteProgram(pass[i].program);
glDeleteTextures(1, &pass[i].texture);
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);
}
param.clear();
pass.clear();
lut.clear();
prev_frame.clear();
conf.Clear();
}