snes9x/shaders/glsl.cpp

1259 lines
40 KiB
C++
Raw Normal View History

/*****************************************************************************\
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.
\*****************************************************************************/
2019-01-09 01:43:51 +00:00
#include <vector>
#include <string>
#include <sstream>
#include <fstream>
#include <map>
2018-05-10 23:47:55 +00:00
#include "glsl.h"
#include "shader_helpers.h"
2023-04-27 21:54:03 +00:00
#include "../vulkan/slang_helpers.hpp"
2018-05-13 23:22:54 +00:00
#include "shader_platform.h"
2023-08-15 22:57:45 +00:00
#ifndef _MSC_VER
#include <unistd.h>
#endif
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 };
2018-05-10 23:47:55 +00:00
static const GLfloat mvp_ortho[16] = { 2.0f, 0.0f, 0.0f, 0.0f,
2019-01-08 23:18:17 +00:00
0.0f, 2.0f, 0.0f, 0.0f,
0.0f, 0.0f, -1.0f, 0.0f,
-1.0f, -1.0f, 0.0f, 1.0f };
2018-05-10 23:47:55 +00:00
static int scale_string_to_enum(std::string string)
{
const struct { const char *string; int value; } map[] =
2018-05-10 23:47:55 +00:00
{
{ "source", GLSL_SOURCE },
{ "viewport", GLSL_VIEWPORT },
{ "absolute", GLSL_ABSOLUTE }
};
2023-05-01 20:55:59 +00:00
for (size_t i = 0; i < 3; i++)
if (string == map[i].string)
return map[i].value;
return GLSL_NONE;
2018-05-10 23:47:55 +00:00
}
2019-01-08 23:18:17 +00:00
static const char *scale_enum_to_string(int val)
2018-05-12 20:07:07 +00:00
{
switch (val)
{
case GLSL_SOURCE:
return "source";
case GLSL_VIEWPORT:
return "viewport";
case GLSL_ABSOLUTE:
return "absolute";
default:
return "undefined";
2018-05-12 20:07:07 +00:00
}
}
2023-04-27 21:54:03 +00:00
static int wrap_mode_string_to_enum(std::string string)
{
2023-04-27 21:54:03 +00:00
if (string == "repeat")
{
return GL_REPEAT;
}
2023-04-27 21:54:03 +00:00
else if (string == "clamp_to_edge")
{
return GL_CLAMP_TO_EDGE;
}
2023-04-27 21:54:03 +00:00
else if (string == "clamp")
{
return GL_CLAMP;
}
else
return GL_CLAMP_TO_BORDER;
}
2018-05-12 20:07:07 +00: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";
}
}
bool GLSLShader::load_shader_preset_file(const char *filename)
2018-05-10 23:47:55 +00:00
{
this->using_slang = false;
2023-04-27 21:54:03 +00:00
if (ends_with(filename, ".slang") || ends_with(filename, ".slangp"))
2019-01-25 19:28:24 +00:00
{
#ifdef USE_SLANG
this->using_slang = true;
#else
return false;
#endif
}
2023-04-27 21:54:03 +00:00
if (ends_with(filename, ".glsl") || ends_with(filename, ".slang"))
2018-05-10 23:47:55 +00:00
{
GLSLPass pass;
2019-01-25 19:28:24 +00:00
this->pass.push_back(GLSLPass());
2018-05-10 23:57:52 +00:00
pass.scale_type_x = pass.scale_type_y = GLSL_VIEWPORT;
2018-05-10 23:47:55 +00:00
pass.filter = GLSL_UNDEFINED;
2018-05-20 17:49:58 +00:00
pass.wrap_mode = GL_CLAMP_TO_BORDER;
2018-05-10 23:47:55 +00:00
strcpy(pass.filename, filename);
2018-05-10 23:57:52 +00:00
pass.frame_count_mod = 0;
pass.frame_count = 0;
pass.fp = 0;
pass.scale_x = 1.0;
pass.scale_y = 1.0;
2019-01-25 19:28:24 +00:00
this->pass.push_back(pass);
2018-05-10 23:57:52 +00:00
return true;
2018-05-10 23:47:55 +00:00
}
2023-04-27 21:54:03 +00:00
ini.load_file(filename);
2018-05-10 23:47:55 +00:00
int shader_count = ini.get_int("shaders", 0);
2018-05-10 23:47:55 +00:00
if (shader_count < 1)
return false;
2019-01-08 23:18:17 +00:00
this->pass.push_back(GLSLPass());
2018-05-10 23:47:55 +00:00
for (int i = 0; i < shader_count; i++)
{
GLSLPass pass;
2023-04-27 21:54:03 +00:00
std::string num = std::to_string(i);
std::string key = "filter_linear" + num;
pass.filter = ini.exists(key) ? (ini.get_bool(key, true) ? GL_LINEAR : GL_NEAREST) : GLSL_UNDEFINED;
2018-05-10 23:47:55 +00:00
2023-04-27 21:54:03 +00:00
std::string scaleType = ini.get_string("scale_type" + num, "");
2018-05-10 23:47:55 +00:00
if (scaleType == "")
2018-05-10 23:47:55 +00:00
{
2023-04-27 21:54:03 +00:00
std::string scaleTypeX = ini.get_string("scale_type_x" + num, "");
pass.scale_type_x = scale_string_to_enum(scaleTypeX);
2018-05-10 23:47:55 +00:00
2023-04-27 21:54:03 +00:00
std::string scaleTypeY = ini.get_string("scale_type_y" + num, "");
pass.scale_type_y = scale_string_to_enum(scaleTypeY);
2018-05-10 23:47:55 +00:00
}
else
{
int scale_type = scale_string_to_enum(scaleType);
2018-05-10 23:47:55 +00:00
pass.scale_type_x = scale_type;
pass.scale_type_y = scale_type;
}
2023-04-27 21:54:03 +00:00
auto scaleFloat = ini.get_float("scale" + num, 1.0f);
pass.scale_x = ini.get_float("scale_x" + num, scaleFloat);
pass.scale_y = ini.get_float("scale_y" + num, scaleFloat);
2018-05-14 19:27:07 +00:00
2023-04-27 21:54:03 +00:00
strcpy(pass.filename, ini.get_string("shader" + num, "").c_str());
2019-01-28 02:14:28 +00:00
2023-04-27 21:54:03 +00:00
pass.wrap_mode = wrap_mode_string_to_enum(ini.get_string("wrap_mode" + num, ""));
pass.mipmap_input = ini.get_bool("mipmap_input" + num, true);
pass.frame_count_mod = ini.get_int("frame_count_mod" + num, 1);
2018-05-10 23:47:55 +00:00
pass.fp = false;
2019-01-08 23:18:17 +00:00
if (gl_float_texture_available())
{
2023-04-27 21:54:03 +00:00
pass.fp = ini.get_bool("float_framebuffer" + num, false);
}
pass.srgb = false;
2019-01-08 23:18:17 +00:00
if (gl_srgb_available())
2023-04-27 21:54:03 +00:00
pass.srgb = ini.get_bool("srgb_framebuffer" + num, false);
2023-04-27 21:54:03 +00:00
strcpy(pass.alias, ini.get_string("alias" + num, "").c_str());
2018-05-10 23:47:55 +00:00
this->pass.push_back(pass);
}
auto ids_string = ini.get_string("textures", "");
auto ids = split_string(ids_string, ';');
2018-05-10 23:47:55 +00:00
for (auto &id : ids)
2018-05-10 23:47:55 +00:00
{
GLSLLut lut;
strcpy(lut.id, id.c_str());
strcpy(lut.filename, ini.get_string(id, "").c_str());
2023-04-27 21:54:03 +00:00
lut.wrap_mode = wrap_mode_string_to_enum(ini.get_string(id + "_wrap_mode", ""));
lut.mipmap = ini.get_bool(id + "_mipmap", false);
lut.filter = (ini.get_bool(id + "_linear", false)) ? GL_LINEAR : GL_NEAREST;
if (lut.mipmap)
lut.filter = (lut.filter == GL_LINEAR) ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST;
2018-05-10 23:47:55 +00:00
this->lut.push_back(lut);
}
2018-05-12 20:07:07 +00:00
2018-05-10 23:47:55 +00:00
return true;
}
static std::string folder_from_path(std::string filename)
2018-05-10 23:47:55 +00:00
{
for (int i = filename.length() - 1; i >= 0; i--)
if (filename[i] == '\\' || filename[i] == '/')
return filename.substr(0, i);
return std::string(".");
}
static std::string canonicalize(const std::string &noncanonical)
{
char *temp = realpath(noncanonical.c_str(), NULL);
std::string filename_string(temp);
free(temp);
return filename_string;
}
#ifdef USE_SLANG
static GLuint string_to_format(char *format)
{
const std::map<const char *, int> map =
{
{ "R8_UNORM", GL_R8 },
{ "R8_UINT", GL_R8UI },
{ "R8_SINT", GL_R8I },
{ "R8G8_UNORM", GL_RG8 },
{ "R8G8_UINT", GL_RG8UI },
{ "R8G8_SINT", GL_RG8I },
{ "R8G8B8A8_UNORM", GL_RGBA8 },
{ "R8G8B8A8_UINT", GL_RGBA8UI },
{ "R8G8B8A8_SINT", GL_RGBA8I },
{ "R8G8B8A8_SRGB", GL_SRGB8_ALPHA8 },
{ "R16_UINT", GL_R16UI },
{ "R16_SINT", GL_R16I },
{ "R16_SFLOAT", GL_R16F },
{ "R16G16_UINT", GL_RG16UI },
{ "R16G16_SINT", GL_RG16I },
{ "R16G16_SFLOAT", GL_RG16F },
{ "R16G16B16A16_UINT", GL_RGBA16UI },
{ "R16G16B16A16_SINT", GL_RGBA16I },
{ "R16G16B16A16_SFLOAT", GL_RGBA16F },
{ "R32_UINT", GL_R32UI },
{ "R32_SINT", GL_R32I },
{ "R32_SFLOAT", GL_R32F },
{ "R32G32_UINT", GL_RG32UI },
{ "R32G32_SINT", GL_RG32I },
{ "R32G32_SFLOAT", GL_RG32F },
{ "R32G32B32A32_UINT", GL_RGBA32UI },
{ "R32G32B32A32_SINT", GL_RGBA32I },
{ "R32G32B32A32_SFLOAT", GL_RGBA32F }
};
auto iter = map.find(format);
if (iter != map.end())
return iter->second;
return GL_RGBA;
}
#endif
// filename must be canonical
void GLSLShader::read_shader_file_with_includes(std::string filename,
2019-01-30 16:39:18 +00:00
std::vector<std::string> &lines,
int p)
{
2019-01-30 18:53:47 +00:00
std::ifstream ss(filename.c_str());
if (ss.fail())
{
printf ("Couldn't open file \"%s\"\n", filename.c_str());
return;
}
std::string line;
while (std::getline(ss, line, '\n'))
{
if (line.empty())
continue;
if (line.compare(0, 8, "#include") == 0)
{
char tmp[PATH_MAX];
sscanf(line.c_str(), "#include \"%[^\"]\"", tmp);
std::string fullpath = canonicalize(folder_from_path(filename) + "/" + tmp);
read_shader_file_with_includes(fullpath.c_str(), lines, p);
continue;
}
else if (line.compare(0, 17, "#pragma parameter") == 0)
2019-01-09 01:43:51 +00:00
{
char id[PATH_MAX];
char name[PATH_MAX];
2019-01-09 01:43:51 +00:00
GLSLParam par;
sscanf(line.c_str(), "#pragma parameter %s \"%[^\"]\" %f %f %f %f",
id, name, &par.val, &par.min, &par.max, &par.step);
par.id = id;
par.name = name;
2019-01-09 01:43:51 +00:00
unsigned int last_decimal = line.rfind(".") + 1;
unsigned int index = last_decimal;
while (isdigit(line[index]) && index < line.length())
index++;
par.digits = index - last_decimal;
if (line[index - 1] == '0' && line[index - 2] == '.')
par.digits = 0;
2019-01-09 01:43:51 +00:00
if (par.step == 0.0f)
par.step = 1.0f;
unsigned int i = 0;
for (; i < param.size(); i++)
{
if (param[i].id == par.id)
2019-01-09 01:43:51 +00:00
break;
}
if (i >= param.size())
param.push_back(par);
continue;
2019-01-09 01:43:51 +00:00
}
#ifdef USE_SLANG
else if (line.compare(0, 12, "#pragma name") == 0)
{
sscanf(line.c_str(), "#pragma name %255s", pass[p].alias);
continue;
}
else if (line.compare(0, 14, "#pragma format") == 0)
{
char format[256];
sscanf(line.c_str(), "#pragma format %255s", format);
pass[p].format = string_to_format(format);
if (pass[p].format == GL_RGBA16F || pass[p].format == GL_RGBA32F)
pass[p].fp = TRUE;
else if (pass[p].format == GL_SRGB8_ALPHA8)
pass[p].srgb = TRUE;
continue;
}
#endif
lines.push_back(line);
}
return;
2018-05-10 23:47:55 +00:00
}
2019-01-09 01:43:51 +00:00
GLuint GLSLShader::compile_shader(std::vector<std::string> &lines,
2018-05-11 22:08:13 +00:00
const char *aliases,
const char *defines,
GLuint type,
GLuint *out)
2018-05-10 23:47:55 +00:00
{
char info_log[1024];
2019-01-09 01:43:51 +00:00
std::string source;
unsigned int first_line = 0;
if (lines.empty())
return 0;
2018-05-10 23:47:55 +00:00
2019-01-09 01:43:51 +00:00
if (lines[0].find("#version") == std::string::npos)
2018-05-10 23:47:55 +00:00
{
2019-01-09 01:43:51 +00:00
int version = gl_version();
if (version >= 33)
version *= 10;
else
version = 150;
source += "#version " + std::to_string(version) + "\n";
2018-05-10 23:47:55 +00:00
}
else
{
2019-01-09 01:43:51 +00:00
source += lines[0] + "\n";
first_line++;
2018-05-10 23:47:55 +00:00
}
2019-01-09 01:43:51 +00:00
source += aliases;
source += defines;
for (unsigned int i = first_line; i < lines.size(); i++)
source += lines[i] + "\n";
2018-05-10 23:47:55 +00:00
2019-01-08 23:18:17 +00:00
GLuint shader = glCreateShader(type);
2018-05-10 23:47:55 +00:00
GLint status;
2019-01-09 01:43:51 +00:00
GLint length = source.length();
GLchar *prog = (GLchar *)source.c_str();
2018-05-10 23:47:55 +00:00
2019-01-08 23:18:17 +00:00
glShaderSource(shader, 1, &prog, &length);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
2018-05-10 23:47:55 +00:00
2018-05-14 01:17:02 +00:00
info_log[0] = '\0';
2019-01-08 23:18:17 +00:00
glGetShaderInfoLog(shader, 1024, NULL, info_log);
2018-05-14 01:17:02 +00:00
if (*info_log)
2019-01-08 23:18:17 +00:00
printf("%s\n", info_log);
2018-05-10 23:47:55 +00:00
*out = shader;
return status;
}
bool GLSLShader::load_shader(const char *filename)
2018-05-10 23:47:55 +00:00
{
char shader_path[PATH_MAX];
char temp[PATH_MAX];
std::string aliases = "";
2018-05-10 23:47:55 +00: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);
2019-01-09 01:43:51 +00:00
if (!load_shader_preset_file(filename))
2018-05-10 23:47:55 +00:00
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);
2019-01-08 23:18:17 +00:00
strcpy(p->filename, temp);
2018-05-10 23:47:55 +00:00
2019-01-13 17:39:54 +00:00
std::vector<std::string> lines;
read_shader_file_with_includes(p->filename, lines, i);
2019-01-13 17:39:54 +00:00
2019-01-09 01:43:51 +00:00
if (lines.empty())
2018-05-10 23:47:55 +00:00
{
printf("Couldn't read shader file \"%s\"\n", temp);
2018-05-10 23:47:55 +00:00
return false;
}
2019-01-25 19:28:24 +00:00
#ifdef USE_SLANG
if (using_slang)
2018-05-10 23:47:55 +00:00
{
2019-01-25 19:28:24 +00:00
GLint retval;
retval = slang_compile(lines, "vertex");
if (retval < 0)
{
printf("Vertex shader in \"%s\" failed to compile.\n", p->filename);
2019-01-25 19:28:24 +00:00
return false;
}
vertex_shader = retval;
2018-05-10 23:47:55 +00:00
2019-01-25 19:28:24 +00:00
retval = slang_compile(lines, "fragment");
if (retval < 0)
{
printf ("Fragment shader in \"%s\" failed to compile.\n", p->filename);
2019-01-25 19:28:24 +00:00
return false;
}
fragment_shader = retval;
}
else
#endif
2018-05-10 23:47:55 +00:00
{
2019-01-25 19:28:24 +00:00
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);
2019-01-25 19:28:24 +00:00
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);
2019-01-25 19:28:24 +00:00
return false;
}
2018-05-10 23:47:55 +00:00
}
2019-01-08 23:18:17 +00:00
p->program = glCreateProgram();
2018-05-10 23:47:55 +00:00
2019-01-08 23:18:17 +00:00
glAttachShader(p->program, vertex_shader);
glAttachShader(p->program, fragment_shader);
2018-05-10 23:47:55 +00:00
2019-01-08 23:18:17 +00:00
glLinkProgram(p->program);
glGetProgramiv(p->program, GL_LINK_STATUS, &status);
2018-05-14 01:17:02 +00:00
log[0] = '\0';
2018-05-10 23:47:55 +00:00
glGetProgramInfoLog(p->program, 1024, NULL, log);
2018-05-14 01:17:02 +00:00
if (*log)
2019-01-08 23:18:17 +00:00
printf("%s\n", log);
2018-05-10 23:47:55 +00:00
2019-01-08 23:18:17 +00:00
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
2018-05-10 23:47:55 +00:00
if (status != GL_TRUE)
{
2019-01-08 23:18:17 +00:00
printf("Failed to link program\n");
glDeleteProgram(p->program);
2018-05-10 23:47:55 +00:00
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-13 23:39:56 +00:00
// generate texture for the lut and apply specified filter setting
2018-05-10 23:47:55 +00: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-10 23:47:55 +00: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);
2019-01-08 23:18:17 +00:00
strcpy(l->filename, temp);
2018-05-10 23:47:55 +00: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;
bool grayscale;
2018-05-10 23:47:55 +00:00
GLubyte* texData;
if (loadPngImage(temp, width, height, grayscale, hasAlpha,
&texData))
2018-05-10 23:47:55 +00:00
{
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);
2019-01-25 19:28:24 +00:00
l->width = width;
l->height = height;
2018-05-10 23:47:55 +00:00
free(texData);
}
else
{
printf ("Failed to load PNG LUT: %s\n", temp);
2018-05-10 23:47:55 +00:00
}
}
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);
2019-01-25 19:28:24 +00:00
l->width = stga.width;
l->height = stga.height;
2018-05-10 23:47:55 +00:00
}
else
{
2019-01-08 23:18:17 +00:00
printf("Failed to load TGA LUT: %s\n", temp);
2018-05-10 23:47:55 +00:00
}
}
}
if (l->mipmap)
2019-01-08 23:18:17 +00:00
glGenerateMipmap(GL_TEXTURE_2D);
2018-05-10 23:47:55 +00:00
}
2018-05-13 23:39:56 +00:00
// Check for parameters specified in file
2018-05-11 22:08:13 +00:00
for (unsigned int i = 0; i < param.size(); i++)
{
param[i].val = ini.get_float(param[i].id, param[i].val);
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-11 22:08:13 +00:00
}
2018-05-10 23:47:55 +00:00
glActiveTexture(GL_TEXTURE0);
2019-01-25 19:28:24 +00:00
#ifdef USE_SLANG
if (using_slang)
slang_introspect();
else
#endif
register_uniforms();
2018-05-10 23:47:55 +00:00
2019-01-08 23:18:17 +00:00
prev_frame.resize(max_prev_frame);
2018-05-10 23:47:55 +00:00
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)
2018-05-10 23:47:55 +00:00
{
2018-10-22 18:56:18 +00:00
GLint saved_framebuffer;
2019-01-08 23:18:17 +00:00
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &saved_framebuffer);
2018-10-22 18:56:18 +00:00
2018-05-10 23:47:55 +00:00
frame_count++;
2018-05-13 23:39:56 +00:00
// set up our dummy pass for easier loop code
2018-05-10 23:47:55 +00:00
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
2018-05-13 23:39:56 +00:00
// loop through all real passes
2018-05-10 23:47:55 +00:00
for (unsigned int i = 1; i < pass.size(); i++)
{
2018-05-12 22:36:45 +00:00
bool lastpass = (i == pass.size() - 1);
2018-05-10 23:47:55 +00:00
switch (pass[i].scale_type_x)
{
case GLSL_ABSOLUTE:
2019-02-23 14:00:57 +00:00
pass[i].width = (GLuint)pass[i].scale_x;
2018-05-10 23:47:55 +00:00
break;
case GLSL_SOURCE:
2019-02-23 14:00:57 +00:00
pass[i].width = (GLuint)(pass[i-1].width * pass[i].scale_x);
2018-05-10 23:47:55 +00:00
break;
case GLSL_VIEWPORT:
2019-02-23 14:00:57 +00:00
pass[i].width = (GLuint)(viewport_width * pass[i].scale_x);
2018-05-10 23:47:55 +00:00
break;
default:
if (lastpass)
pass[i].width = viewport_width;
else
2019-02-23 14:00:57 +00:00
pass[i].width = (GLuint)(pass[i - 1].width * pass[i].scale_x);
2018-05-10 23:47:55 +00:00
}
switch (pass[i].scale_type_y)
{
case GLSL_ABSOLUTE:
2019-02-23 14:00:57 +00:00
pass[i].height = (GLuint)pass[i].scale_y;
2018-05-10 23:47:55 +00:00
break;
case GLSL_SOURCE:
2019-02-23 14:00:57 +00:00
pass[i].height = (GLuint)(pass[i - 1].height * pass[i].scale_y);
2018-05-10 23:47:55 +00:00
break;
case GLSL_VIEWPORT:
2019-02-23 14:00:57 +00:00
pass[i].height = (GLuint)(viewport_height * pass[i].scale_y);
2018-05-10 23:47:55 +00:00
break;
default:
if (lastpass)
pass[i].height = viewport_height;
else
2019-02-23 14:00:57 +00:00
pass[i].height = (GLuint)(pass[i - 1].height * pass[i].scale_y);
2018-05-10 23:47:55 +00:00
}
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)
{
2018-05-13 23:39:56 +00: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,
2019-01-08 23:18:17 +00:00
(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),
2019-01-08 23:18:17 +00:00
(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-13 23:39:56 +00:00
// viewport determines the area we render into the output texture
glViewport(0, 0, pass[i].width, pass[i].height);
2018-05-13 23:39:56 +00: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
{
int out_x = 0;
int out_y = 0;
int out_width = 0;
int out_height = 0;
2018-05-13 23:39:56 +00:00
// output to the screen
2018-10-22 18:56:18 +00:00
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);
}
2018-05-10 23:47:55 +00:00
// set up input texture (output of previous pass) and apply filter settings
2019-01-08 23:18:17 +00:00
GLuint filter = pass[i].filter;
if (filter == GLSL_UNDEFINED)
{
if (lastpass && Settings.BilinearFilter)
filter = GL_LINEAR;
else
filter = GL_NEAREST;
}
2018-05-12 22:36:45 +00:00
if (pass[i].mipmap_input)
{
if (filter == GL_LINEAR)
filter = GL_LINEAR_MIPMAP_LINEAR;
else
filter = GL_NEAREST_MIPMAP_NEAREST;
}
2018-05-10 23:47:55 +00: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, 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);
2018-05-10 23:47:55 +00:00
2019-01-28 02:14:28 +00:00
if (pass[i].mipmap_input)
glGenerateMipmap(GL_TEXTURE_2D);
2019-01-08 23:18:17 +00:00
glUseProgram(pass[i].program);
2019-01-25 19:28:24 +00:00
#ifdef USE_SLANG
if (using_slang)
slang_set_shader_vars(i, lastpass && direct_lastpass);
2019-01-25 19:28:24 +00:00
else
#endif
set_shader_vars(i, lastpass && direct_lastpass);
2018-05-10 23:47:55 +00:00
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
2018-05-10 23:47:55 +00:00
2019-01-25 19:28:24 +00:00
#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
2019-01-25 19:28:24 +00:00
if (using_slang)
slang_clear_shader_vars();
else
#endif
clear_shader_vars();
if (pass[i].srgb)
{
2019-01-08 23:18:17 +00:00
glDisable(GL_FRAMEBUFFER_SRGB);
}
2018-05-10 23:47:55 +00:00
}
2018-05-13 23:39:56 +00:00
// Disable framebuffer
2018-10-22 18:56:18 +00:00
glBindFramebuffer(GL_FRAMEBUFFER, saved_framebuffer);
2018-05-10 23:47:55 +00:00
glActiveTexture(GL_TEXTURE0);
2019-01-08 23:18:17 +00:00
glUseProgram(0);
2018-05-10 23:47:55 +00:00
2018-05-13 23:39:56 +00:00
// Pop back of previous frame stack and use as upload buffer
2018-05-10 23:47:55 +00:00
if (prev_frame.size() > 0)
{
GLint internal_format;
orig = prev_frame.back().texture;
prev_frame.pop_back();
2019-01-25 19:28:24 +00:00
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;
2018-05-10 23:47:55 +00:00
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-10 23:47:55 +00:00
glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)pass.back().width);
}
2019-01-08 23:18:17 +00:00
void GLSLShader::register_uniforms()
2018-05-10 23:47:55 +00:00
{
max_prev_frame = 0;
char varname[100];
unif.resize(pass.size());
2018-05-10 23:47:55 +00:00
for (unsigned int i = 1; i < pass.size(); i++)
{
GLSLUniforms *u = &pass[i].unif;
GLuint program = pass[i].program;
glUseProgram (program);
2019-01-08 23:18:17 +00:00
GLint mvp = glGetUniformLocation(program, "MVPMatrix");
2018-05-10 23:47:55 +00:00
if (mvp > -1)
glUniformMatrix4fv(mvp, 1, GL_FALSE, mvp_ortho);
2019-01-08 23:18:17 +00:00
u->Texture = glGetUniformLocation(program, "Texture");
u->InputSize = glGetUniformLocation(program, "InputSize");
u->OutputSize = glGetUniformLocation(program, "OutputSize");
u->TextureSize = glGetUniformLocation(program, "TextureSize");
2018-05-10 23:47:55 +00:00
2019-01-08 23:18:17 +00:00
u->TexCoord = glGetAttribLocation(program, "TexCoord");
u->LUTTexCoord = glGetAttribLocation(program, "LUTTexCoord");
u->VertexCoord = glGetAttribLocation(program, "VertexCoord");
2018-05-10 23:47:55 +00:00
2019-01-08 23:18:17 +00:00
u->FrameCount = glGetUniformLocation(program, "FrameCount");
u->FrameDirection = glGetUniformLocation(program, "FrameDirection");
2018-05-10 23:47:55 +00:00
2019-01-08 23:18:17 +00:00
u->OrigTexture = glGetUniformLocation(program, "OrigTexture");
u->OrigInputSize = glGetUniformLocation(program, "OrigInputSize");
u->OrigTextureSize = glGetUniformLocation(program, "OrigTextureSize");
u->OrigTexCoord = glGetAttribLocation(program, "OrigTexCoord");
2018-05-10 23:47:55 +00:00
2019-01-08 23:18:17 +00:00
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");
2018-05-10 23:47:55 +00:00
if (u->Prev[0].Texture > -1)
max_prev_frame = 1;
for (unsigned int j = 1; j < 7; j++)
{
sprintf(varname, "Prev%dTexture", j);
2019-01-08 23:18:17 +00:00
u->Prev[j].Texture = glGetUniformLocation(program, varname);
2018-05-10 23:47:55 +00:00
sprintf(varname, "Prev%dInputSize", j);
2019-01-08 23:18:17 +00:00
u->Prev[j].InputSize = glGetUniformLocation(program, varname);
2018-05-10 23:47:55 +00:00
sprintf(varname, "Prev%dTextureSize", j);
2019-01-08 23:18:17 +00:00
u->Prev[j].TextureSize = glGetUniformLocation(program, varname);
2018-05-10 23:47:55 +00:00
sprintf(varname, "Prev%dTexCoord", j);
2019-01-08 23:18:17 +00:00
u->Prev[j].TexCoord = glGetAttribLocation(program, varname);
2018-05-10 23:47:55 +00:00
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);
2019-01-08 23:18:17 +00:00
u->Pass[j].Texture = glGetUniformLocation(program, varname);
2018-05-10 23:47:55 +00:00
sprintf(varname, "Pass%dInputSize", j);
2019-01-08 23:18:17 +00:00
u->Pass[j].InputSize = glGetUniformLocation(program, varname);
2018-05-10 23:47:55 +00:00
sprintf(varname, "Pass%dTextureSize", j);
2019-01-08 23:18:17 +00:00
u->Pass[j].TextureSize = glGetUniformLocation(program, varname);
2018-05-10 23:47:55 +00:00
sprintf(varname, "Pass%dTexCoord", j);
2019-01-08 23:18:17 +00:00
u->Pass[j].TexCoord = glGetAttribLocation(program, varname);
2018-05-10 23:47:55 +00:00
if (u->Pass[j].Texture)
u->max_pass = j;
sprintf(varname, "PassPrev%dTexture", j);
2019-01-08 23:18:17 +00:00
u->PassPrev[j].Texture = glGetUniformLocation(program, varname);
2018-05-10 23:47:55 +00:00
sprintf(varname, "PassPrev%dInputSize", j);
2019-01-08 23:18:17 +00:00
u->PassPrev[j].InputSize = glGetUniformLocation(program, varname);
2018-05-10 23:47:55 +00:00
sprintf(varname, "PassPrev%dTextureSize", j);
2019-01-08 23:18:17 +00:00
u->PassPrev[j].TextureSize = glGetUniformLocation(program, varname);
2018-05-10 23:47:55 +00:00
sprintf(varname, "PassPrev%dTexCoord", j);
2019-01-08 23:18:17 +00:00
u->PassPrev[j].TexCoord = glGetAttribLocation(program, varname);
2018-05-10 23:47:55 +00:00
if (u->PassPrev[j].Texture > -1)
u->max_prevpass = j;
}
for (unsigned int j = 0; j < lut.size(); j++)
{
2019-01-08 23:18:17 +00:00
u->Lut[j] = glGetUniformLocation(program, lut[j].id);
2018-05-10 23:47:55 +00:00
}
2018-05-11 22:08:13 +00:00
unif[i].resize(param.size());
for (unsigned int param_num = 0; param_num < param.size(); param_num++)
2018-05-11 22:08:13 +00:00
{
unif[i][param_num] = glGetUniformLocation(program, param[param_num].id.c_str());
2018-05-11 22:08:13 +00:00
}
2018-05-10 23:47:55 +00:00
}
2019-01-08 23:18:17 +00:00
glUseProgram(0);
2018-05-10 23:47:55 +00:00
}
void GLSLShader::set_shader_vars(unsigned int p, bool inverted)
2018-05-10 23:47:55 +00:00
{
unsigned int texunit = 0;
unsigned int offset = 0;
if (inverted)
offset = 8;
2018-05-10 23:47:55 +00:00
GLSLUniforms *u = &pass[p].unif;
2019-01-08 23:18:17 +00:00
GLint mvp = glGetUniformLocation(pass[p].program, "MVPMatrix");
2018-05-10 23:47:55 +00:00
if (mvp > -1)
glUniformMatrix4fv(mvp, 1, GL_FALSE, mvp_ortho);
2019-01-08 23:18:17 +00:00
#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);
2018-05-10 23:47:55 +00:00
#define setTexture1i(uni, val) \
if (uni > -1) \
{ \
2019-01-08 23:18:17 +00:00
glActiveTexture(GL_TEXTURE0 + texunit); \
glBindTexture(GL_TEXTURE_2D, val); \
glUniform1i(uni, texunit); \
2018-05-10 23:47:55 +00:00
texunit++; \
}
2018-05-13 23:39:56 +00:00
// We use non-power-of-two textures,
// so no need to mess with input size/texture size
2018-05-10 23:47:55 +00:00
#define setTexCoords(attr) \
if (attr > -1) \
{ \
glEnableVertexAttribArray(attr); \
2019-01-08 23:18:17 +00:00
glVertexAttribPointer(attr, 2, GL_FLOAT, GL_FALSE, 0, (void *)(sizeof(float) * offset)); \
vaos.push_back(attr); \
}
#define setTexCoordsNoOffset(attr) \
2018-05-10 23:47:55 +00:00
if (attr > -1) \
{ \
glEnableVertexAttribArray(attr); \
glVertexAttribPointer(attr, 2, GL_FLOAT, GL_FALSE, 0, NULL); \
2019-01-08 23:18:17 +00:00
vaos.push_back(attr); \
2018-05-10 23:47:55 +00:00
}
2019-01-08 23:18:17 +00:00
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 16, tex_coords, GL_STATIC_DRAW);
2018-05-10 23:47:55 +00:00
2019-01-08 23:18:17 +00: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 };
2018-05-10 23:47:55 +00:00
2019-01-08 23:18:17 +00:00
setTexture1i(u->Texture, pass[p - 1].texture);
2018-05-10 23:47:55 +00:00
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;
2019-02-23 14:00:57 +00:00
setUniform1i(u->FrameCount, shaderFrameCnt);
setUniform1i(u->FrameDirection, Settings.Rewinding ? -1 : 1);
2018-05-10 23:47:55 +00:00
2019-01-08 23:18:17 +00:00
setTexCoords(u->TexCoord);
setTexCoords(u->LUTTexCoord);
setTexCoordsNoOffset(u->VertexCoord);
2018-05-13 23:39:56 +00:00
// Orig parameter
2019-01-08 23:18:17 +00:00
float orig_videoSize[2] = { (float)pass[0].width, (float)pass[0].height };
2018-05-10 23:47:55 +00:00
setUniform2fv(u->OrigInputSize, orig_videoSize);
setUniform2fv(u->OrigTextureSize, orig_videoSize);
2019-01-08 23:18:17 +00:00
setTexture1i(u->OrigTexture, pass[0].texture);
setTexCoords(u->OrigTexCoord);
2018-05-10 23:47:55 +00:00
2018-05-13 23:39:56 +00:00
// Prev parameter
2018-05-10 23:47:55 +00:00
if (max_prev_frame >= 1 && prev_frame[0].width > 0) {
2019-01-08 23:18:17 +00:00
float prevSize[2] = { (float)prev_frame[0].width, (float)prev_frame[0].height };
2018-05-10 23:47:55 +00:00
setUniform2fv(u->Prev[0].InputSize, prevSize);
setUniform2fv(u->Prev[0].TextureSize, prevSize);
2019-01-08 23:18:17 +00:00
setTexture1i(u->Prev[0].Texture, prev_frame[0].texture);
setTexCoords(u->Prev[0].TexCoord);
2018-05-10 23:47:55 +00:00
}
2018-05-13 23:39:56 +00:00
// Prev[1-6] parameters
2018-05-10 23:47:55 +00:00
for (unsigned int i = 1; i < prev_frame.size(); i++)
{
if (prev_frame[i].width <= 0)
break;
2019-01-08 23:18:17 +00:00
float prevSize[2] = { (float)prev_frame[i].width, (float)prev_frame[i].height };
2018-05-10 23:47:55 +00:00
setUniform2fv(u->Prev[i].InputSize, prevSize);
setUniform2fv(u->Prev[i].TextureSize, prevSize);
2019-01-08 23:18:17 +00:00
setTexture1i(u->Prev[i].Texture, prev_frame[i].texture);
setTexCoords(u->Prev[i].TexCoord);
2018-05-10 23:47:55 +00:00
}
2018-05-13 23:39:56 +00:00
// LUT parameters
2018-05-10 23:47:55 +00:00
for (unsigned int i = 0; i < lut.size(); i++)
{
2019-01-08 23:18:17 +00:00
setTexture1i(u->Lut[i], lut[i].texture);
2018-05-10 23:47:55 +00:00
}
2018-05-13 23:39:56 +00:00
// PassX parameters, only for third pass and up
2018-05-10 23:47:55 +00:00
if (p > 2) {
for (unsigned int i = 1; i < p - 1; i++) {
2019-01-08 23:18:17 +00:00
float passSize[2] = { (float)pass[i].width, (float)pass[i].height };
2018-05-10 23:47:55 +00:00
setUniform2fv(u->Pass[i].InputSize, passSize);
setUniform2fv(u->Pass[i].TextureSize, passSize);
2019-01-08 23:18:17 +00:00
setTexture1i(u->Pass[i].Texture, pass[i].texture);
setTexCoords(u->Pass[i].TexCoord);
2018-05-10 23:47:55 +00:00
}
}
2018-05-13 23:39:56 +00:00
// PassPrev parameter
for (unsigned int i = 0; i < p; i++)
2018-05-10 23:47:55 +00:00
{
2019-01-08 23:18:17 +00:00
float passSize[2] = { (float)pass[i].width, (float)pass[i].height };
2018-05-10 23:47:55 +00:00
setUniform2fv(u->PassPrev[p - i].InputSize, passSize);
setUniform2fv(u->PassPrev[p - i].TextureSize, passSize);
2019-01-08 23:18:17 +00:00
setTexture1i(u->PassPrev[p - i].Texture, pass[i].texture);
setTexCoords(u->PassPrev[p - i].TexCoord);
2018-05-10 23:47:55 +00:00
}
2018-05-13 23:39:56 +00:00
// User and Preset Parameters
2018-05-11 22:08:13 +00:00
for (unsigned int i = 0; i < param.size(); i++)
{
setUniform1f(unif[p][i], param[i].val);
2018-05-11 22:08:13 +00:00
}
2019-01-08 23:18:17 +00:00
glActiveTexture(GL_TEXTURE0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
2018-05-10 23:47:55 +00:00
}
2019-01-25 19:28:24 +00:00
void GLSLShader::clear_shader_vars()
2018-05-12 20:07:07 +00:00
{
for (unsigned int i = 0; i < vaos.size(); i++)
2019-01-08 23:18:17 +00:00
glDisableVertexAttribArray(vaos[i]);
2018-05-12 20:07:07 +00:00
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)
2019-01-08 23:18:17 +00:00
void GLSLShader::save(const char *filename)
2018-05-12 20:07:07 +00:00
{
2019-01-08 23:18:17 +00:00
FILE *file = fopen(filename, "wb");
2018-05-12 20:07:07 +00:00
if (!file)
{
2019-01-08 23:18:17 +00:00
printf("Couldn't save shader config to %s\n", filename);
2018-05-12 20:07:07 +00:00
}
2019-01-08 23:18:17 +00:00
fprintf(file, "shaders = \"%d\"\n", (unsigned int)pass.size() - 1);
2018-05-12 20:07:07 +00:00
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");
}
2019-01-08 23:18:17 +00:00
outs("wrap_mode", wrap_mode_enum_to_string(p->wrap_mode));
if (p->alias[0])
outs("alias", p->alias);
2019-01-08 23:18:17 +00:00
outs("float_framebuffer", p->fp ? "true" : "false");
outs("srgb_framebuffer", p->srgb ? "true" : "false");
outs("mipmap_input", p->mipmap_input ? "true" : "false");
if (p->scale_type_x != GLSL_UNDEFINED)
{
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);
}
if (p->scale_type_y != GLSL_UNDEFINED)
{
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);
}
2018-05-12 20:07:07 +00:00
if (p->frame_count_mod)
2019-01-08 23:18:17 +00:00
outd("frame_count_mod", p->frame_count_mod);
2018-05-12 20:07:07 +00:00
}
if (param.size() > 0)
{
2019-01-08 23:18:17 +00:00
fprintf(file, "parameters = \"");
2018-05-12 20:07:07 +00:00
for (unsigned int i = 0; i < param.size(); i++)
{
fprintf(file, "%s%c", param[i].id.c_str(), (i == param.size() - 1) ? '\"' : ';');
2018-05-12 20:07:07 +00:00
}
2019-01-08 23:18:17 +00:00
fprintf(file, "\n");
2018-05-12 20:07:07 +00:00
}
for (unsigned int i = 0; i < param.size(); i++)
{
fprintf(file, "%s = \"%f\"\n", param[i].id.c_str(), param[i].val);
2018-05-12 20:07:07 +00:00
}
if (lut.size() > 0)
{
2019-01-08 23:18:17 +00:00
fprintf(file, "textures = \"");
2018-05-12 20:07:07 +00:00
for (unsigned int i = 0; i < lut.size(); i++)
{
2019-01-08 23:18:17 +00:00
fprintf(file, "%s%c", lut[i].id, (i == lut.size() - 1) ? '\"' : ';');
2018-05-12 20:07:07 +00:00
}
2019-01-08 23:18:17 +00:00
fprintf(file, "\n");
2018-05-12 20:07:07 +00:00
}
for (unsigned int i = 0; i < lut.size(); i++)
{
2019-01-08 23:18:17 +00:00
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");
2018-05-12 20:07:07 +00:00
}
2019-01-08 23:18:17 +00:00
fclose(file);
2018-05-12 20:07:07 +00:00
}
#undef outf
#undef outs
#undef outd
2019-01-25 19:28:24 +00:00
void GLSLShader::destroy()
2018-05-10 23:47:55 +00:00
{
2018-05-12 20:07:07 +00:00
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
glActiveTexture(GL_TEXTURE0);
2018-05-10 23:47:55 +00:00
for (unsigned int i = 1; i < pass.size(); i++)
{
2018-05-12 20:07:07 +00:00
glDeleteProgram(pass[i].program);
glDeleteTextures(1, &pass[i].texture);
2018-05-10 23:47:55 +00:00
glDeleteFramebuffers(1, &pass[i].fbo);
#ifdef USE_SLANG
if (using_slang)
{
if (pass[i].uses_feedback)
glDeleteTextures(1, &pass[i].feedback_texture);
if (pass[i].ubo_buffer.size() > 0)
glDeleteBuffers(1, &pass[i].ubo);
}
#endif
2018-05-10 23:47:55 +00:00
}
for (unsigned int i = 0; i < lut.size(); i++)
{
2019-01-08 23:18:17 +00:00
glDeleteTextures(1, &lut[i].texture);
2018-05-10 23:47:55 +00:00
}
2019-01-08 23:18:17 +00:00
for (unsigned int i = 0; i < prev_frame.size(); i++)
2018-05-10 23:47:55 +00:00
{
2019-01-08 23:18:17 +00:00
glDeleteTextures(1, &prev_frame[i].texture);
2018-05-10 23:47:55 +00:00
}
glDeleteBuffers(1, &vbo);
2018-05-11 22:08:13 +00:00
param.clear();
2018-05-10 23:47:55 +00:00
pass.clear();
lut.clear();
prev_frame.clear();
}