RetroArch/gfx/drivers_shader/shader_glsl.c
2023-01-09 00:51:05 +01:00

1775 lines
51 KiB
C

/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
* Copyright (C) 2011-2017 - Daniel De Matteis
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#include <compat/strl.h>
#include <compat/posix_string.h>
#include <file/file_path.h>
#include <streams/file_stream.h>
#include <string/stdstring.h>
#ifdef HAVE_CONFIG_H
#include "../../config.h"
#endif
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES)
#include <gfx/gl_capabilities.h>
#include "../common/gl2_common.h"
#endif
#include "shader_glsl.h"
#ifdef HAVE_REWIND
#include "../../state_manager.h"
#endif
#include "../../core.h"
#include "../../verbosity.h"
#if defined(ORBIS)
#include "../../deps/xxHash/xxhash.h"
#endif
#define PREV_TEXTURES (GFX_MAX_TEXTURES - 1)
/* Cache the VBO. */
struct cache_vbo
{
GLuint vbo_primary;
GLuint vbo_secondary;
size_t size_primary;
size_t size_secondary;
GLfloat *buffer_primary;
GLfloat *buffer_secondary;
};
struct shader_program_glsl_data
{
GLuint vprg;
GLuint fprg;
GLuint id;
};
struct glsl_attrib
{
GLint loc;
GLsizei size;
GLsizei offset;
};
struct shader_uniforms_frame
{
int texture;
int input_size;
int texture_size;
int tex_coord;
};
struct shader_uniforms
{
#if defined(VITA)
int time;
#endif
int mvp;
int tex_coord;
int vertex_coord;
int color;
int lut_tex_coord;
int input_size;
int output_size;
int texture_size;
int frame_count;
int frame_direction;
int lut_texture[GFX_MAX_TEXTURES];
unsigned frame_count_mod;
struct shader_uniforms_frame orig;
struct shader_uniforms_frame feedback;
struct shader_uniforms_frame pass[GFX_MAX_SHADERS];
struct shader_uniforms_frame prev[PREV_TEXTURES];
};
static const char *glsl_prefixes[] = {
"",
"ruby",
};
#if defined(VITA)
#include "../drivers/gl_shaders/modern_opaque.cg.vert.h"
#include "../drivers/gl_shaders/modern_opaque.cg.frag.h"
#include "../drivers/gl_shaders/modern_alpha_blend.cg.vert.h"
#include "../drivers/gl_shaders/modern_alpha_blend.cg.frag.h"
#ifdef HAVE_SHADERPIPELINE
#include "../drivers/gl_shaders/modern_pipeline_xmb_ribbon_simple.cg.vert.h"
#include "../drivers/gl_shaders/pipeline_xmb_ribbon_simple.cg.frag.h"
#include "../drivers/gl_shaders/modern_pipeline_xmb_ribbon.cg.vert.h"
#include "../drivers/gl_shaders/pipeline_xmb_ribbon.cg.frag.h"
#endif
#else
#include "../drivers/gl_shaders/modern_opaque.glsl.vert.h"
#include "../drivers/gl_shaders/modern_opaque.glsl.frag.h"
#include "../drivers/gl_shaders/core_opaque.glsl.vert.h"
#include "../drivers/gl_shaders/core_opaque.glsl.frag.h"
#include "../drivers/gl_shaders/legacy_opaque.glsl.vert.h"
#include "../drivers/gl_shaders/legacy_opaque.glsl.frag.h"
#include "../drivers/gl_shaders/modern_alpha_blend.glsl.vert.h"
#include "../drivers/gl_shaders/modern_alpha_blend.glsl.frag.h"
#include "../drivers/gl_shaders/core_alpha_blend.glsl.vert.h"
#include "../drivers/gl_shaders/core_alpha_blend.glsl.frag.h"
#ifdef HAVE_SHADERPIPELINE
#include "../drivers/gl_shaders/core_pipeline_snow.glsl.frag.h"
#include "../drivers/gl_shaders/core_pipeline_snow_simple.glsl.frag.h"
#include "../drivers/gl_shaders/core_pipeline_xmb_ribbon.glsl.frag.h"
#include "../drivers/gl_shaders/core_pipeline_xmb_ribbon_simple.glsl.frag.h"
#include "../drivers/gl_shaders/core_pipeline_bokeh.glsl.frag.h"
#include "../drivers/gl_shaders/core_pipeline_snowflake.glsl.frag.h"
#include "../drivers/gl_shaders/legacy_pipeline_xmb_ribbon_simple.glsl.vert.h"
#include "../drivers/gl_shaders/modern_pipeline_xmb_ribbon_simple.glsl.vert.h"
#include "../drivers/gl_shaders/pipeline_xmb_ribbon_simple.glsl.frag.h"
#include "../drivers/gl_shaders/pipeline_snow.glsl.frag.h"
#include "../drivers/gl_shaders/pipeline_snow.glsl.vert.h"
#include "../drivers/gl_shaders/pipeline_snow_core.glsl.vert.h"
#include "../drivers/gl_shaders/pipeline_snow_simple.glsl.frag.h"
#include "../drivers/gl_shaders/legacy_pipeline_snow.glsl.vert.h"
#include "../drivers/gl_shaders/legacy_pipeline_xmb_ribbon.glsl.vert.h"
#include "../drivers/gl_shaders/modern_pipeline_xmb_ribbon.glsl.vert.h"
#include "../drivers/gl_shaders/pipeline_xmb_ribbon.glsl.frag.h"
#include "../drivers/gl_shaders/pipeline_bokeh.glsl.frag.h"
#include "../drivers/gl_shaders/pipeline_snowflake.glsl.frag.h"
#endif
#endif
typedef struct glsl_shader_data
{
char alias_define[1024];
GLint attribs_elems[32 * PREV_TEXTURES + 2 + 4 + GFX_MAX_SHADERS];
unsigned attribs_index;
unsigned active_idx;
unsigned current_idx;
GLuint lut_textures[GFX_MAX_TEXTURES];
float current_mat_data[GFX_MAX_SHADERS];
float* current_mat_data_pointer[GFX_MAX_SHADERS];
struct shader_uniforms uniforms[GFX_MAX_SHADERS];
struct cache_vbo vbo[GFX_MAX_SHADERS];
struct shader_program_glsl_data prg[GFX_MAX_SHADERS];
struct video_shader *shader;
} glsl_shader_data_t;
/* TODO/FIXME - static globals */
static bool glsl_core;
static unsigned glsl_major;
static unsigned glsl_minor;
static GLint gl_glsl_get_uniform(glsl_shader_data_t *glsl,
GLuint prog, const char *base)
{
unsigned i;
GLint loc;
char buf[80];
strlcpy(buf, glsl->shader->prefix, sizeof(buf));
strlcat(buf, base, sizeof(buf));
if ((loc = glGetUniformLocation(prog, buf)) >= 0)
return loc;
for (i = 0; i < ARRAY_SIZE(glsl_prefixes); i++)
{
buf[0] = '\0';
strlcpy(buf, glsl_prefixes[i], sizeof(buf));
strlcat(buf, base, sizeof(buf));
if ((loc = glGetUniformLocation(prog, buf)) >= 0)
return loc;
}
return -1;
}
static GLint gl_glsl_get_attrib(glsl_shader_data_t *glsl,
GLuint prog, const char *base)
{
unsigned i;
GLint loc;
char buf[80];
strlcpy(buf, glsl->shader->prefix, sizeof(buf));
strlcat(buf, base, sizeof(buf));
if ((loc = glGetUniformLocation(prog, buf)) >= 0)
return loc;
for (i = 0; i < ARRAY_SIZE(glsl_prefixes); i++)
{
strlcpy(buf, glsl_prefixes[i], sizeof(buf));
strlcat(buf, base, sizeof(buf));
if ((loc = glGetAttribLocation(prog, buf)) >= 0)
return loc;
}
return -1;
}
static void gl_glsl_print_shader_log(GLuint obj)
{
char *info_log = NULL;
GLint max_len, info_len = 0;
glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &max_len);
if (max_len == 0)
return;
info_log = (char*)malloc(max_len);
if (!info_log)
return;
glGetShaderInfoLog(obj, max_len, &info_len, info_log);
if (info_len > 0)
RARCH_LOG("Shader log: %s\n", info_log);
free(info_log);
}
static void gl_glsl_print_linker_log(GLuint obj)
{
char *info_log = NULL;
GLint max_len, info_len = 0;
glGetProgramiv(obj, GL_INFO_LOG_LENGTH, &max_len);
if (max_len == 0)
return;
info_log = (char*)malloc(max_len);
if (!info_log)
return;
glGetProgramInfoLog(obj, max_len, &info_len, info_log);
if (info_len > 0)
RARCH_LOG("Linker log: %s\n", info_log);
free(info_log);
}
#if defined(ORBIS)
void glPigletGetShaderBinarySCE(GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, void* binary);
static const XXH64_hash_t gl_glsl_hash_shader(
const char **source, const int source_length)
{
int n;
XXH64_state_t* const state = XXH64_createState();
XXH64_reset(state, 0xAABBCCDDu);
for(n = 0; n < source_length; n++)
{
XXH64_update(state, source[n], strlen(source[n]));
}
XXH64_hash_t const hash = XXH64_digest(state);
XXH64_freeState(state);
return hash;
}
static bool gl_glsl_load_binary_shader(GLuint shader, char *save_path)
{
GLsizei shader_size;
GLint status;
FILE *shader_binary = fopen(save_path, "rb" );
if(shader_binary)
{
char *shader_data = NULL;
fseek (shader_binary, 0, SEEK_END);
shader_size=ftell (shader_binary);
fseek(shader_binary, 0, SEEK_SET);
shader_data = malloc(shader_size);
fread(shader_data, shader_size, 1, shader_binary);
fclose(shader_binary);
glShaderBinary(1, &shader, 2, shader_data, shader_size);
free(shader_data);
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
return status == GL_TRUE;
}
return false;
}
#if 0
static void gl_glsl_dump_shader(GLuint shader, char *save_path)
{
FILE * fShader;
GLint length;
GLenum format;
GLsizei shader_size;
GLsizei bufferSize;
void *shaderBinary = NULL;
glGetShaderiv(shader, 0x8b89, &length);
bufferSize = length;
shaderBinary = malloc(bufferSize);
memset(shaderBinary, 0, bufferSize);
glPigletGetShaderBinarySCE(shader, bufferSize, &shader_size, &format, shaderBinary);
fShader = fopen(save_path, "wb");
fwrite(shaderBinary, shader_size, 1, fShader);
fclose(fShader);
}
#endif
#endif
static bool gl_glsl_compile_shader(glsl_shader_data_t *glsl,
GLuint shader,
const char *define, const char *program)
{
GLint status;
const char *source[4];
char version[32];
const char *existing_version = strstr(program, "#version");
version[0] = '\0';
if (existing_version)
{
const char* version_extra = "";
unsigned version_no = (unsigned)
strtoul(existing_version + 8, (char**)&program, 10);
#ifdef HAVE_OPENGLES
if (version_no < 130)
version_no = 100;
else
{
version_extra = " es";
version_no = 300;
}
#endif
snprintf(version,
sizeof(version), "#version %u%s\n", version_no, version_extra);
RARCH_LOG("[GLSL]: Using GLSL version %u%s.\n", version_no, version_extra);
}
else if (glsl_core)
{
unsigned version_no = 0;
unsigned gl_ver = glsl_major * 100 + glsl_minor * 10;
switch (gl_ver)
{
case 300:
version_no = 130;
break;
case 310:
version_no = 140;
break;
case 320:
version_no = 150;
break;
default:
version_no = gl_ver;
break;
}
snprintf(version, sizeof(version), "#version %u\n", version_no);
RARCH_LOG("[GLSL]: Using GLSL version %u.\n", version_no);
}
source[0] = version;
source[1] = define;
source[2] = glsl->alias_define;
source[3] = program;
#if defined(ORBIS)
{
char save_path[250];
XXH64_hash_t const hash =
gl_glsl_hash_shader(source, ARRAY_SIZE(source));
snprintf(save_path, sizeof(save_path),
"/data/retroarch/temp/%lx.sb", hash);
if(gl_glsl_load_binary_shader(shader, save_path))
return true;
}
#endif
glShaderSource(shader, ARRAY_SIZE(source), source, NULL);
glCompileShader(shader);
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
gl_glsl_print_shader_log(shader);
#if 0
#if defined(ORBIS)
if(status == GL_TRUE)
gl_glsl_dump_shader(shader, save_path);
#endif
#endif
return status == GL_TRUE;
}
static bool gl_glsl_link_program(GLuint prog)
{
GLint status;
glLinkProgram(prog);
glGetProgramiv(prog, GL_LINK_STATUS, &status);
gl_glsl_print_linker_log(prog);
if (status != GL_TRUE)
return false;
glUseProgram(prog);
return true;
}
static bool gl_glsl_compile_program(
void *data,
unsigned idx,
void *program_data,
struct shader_program_info *program_info)
{
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
struct shader_program_glsl_data *program =
(struct shader_program_glsl_data*)program_data;
GLuint prog = glCreateProgram();
if (!program)
program = &glsl->prg[idx];
if (!prog)
goto error;
if (program_info->vertex)
{
RARCH_LOG("[GLSL]: Found GLSL vertex shader.\n");
program->vprg = glCreateShader(GL_VERTEX_SHADER);
if (!gl_glsl_compile_shader(
glsl,
program->vprg,
"#define VERTEX\n#define PARAMETER_UNIFORM\n", program_info->vertex))
{
RARCH_ERR("Failed to compile vertex shader #%u\n", idx);
goto error;
}
glAttachShader(prog, program->vprg);
}
if (program_info->fragment)
{
RARCH_LOG("[GLSL]: Found GLSL fragment shader.\n");
program->fprg = glCreateShader(GL_FRAGMENT_SHADER);
if (!gl_glsl_compile_shader(glsl, program->fprg,
"#define FRAGMENT\n#define PARAMETER_UNIFORM\n",
program_info->fragment))
{
RARCH_ERR("Failed to compile fragment shader #%u\n", idx);
goto error;
}
glAttachShader(prog, program->fprg);
}
if (program_info->vertex || program_info->fragment)
{
RARCH_LOG("[GLSL]: Linking GLSL program.\n");
if (!gl_glsl_link_program(prog))
goto error;
/* Clean up dead memory. We're not going to relink the program.
* Detaching first seems to kill some mobile drivers
* (according to the intertubes anyways). */
if (program->vprg)
glDeleteShader(program->vprg);
if (program->fprg)
glDeleteShader(program->fprg);
program->vprg = 0;
program->fprg = 0;
glUseProgram(prog);
#if defined(VITA)
glUniform1i(gl_glsl_get_uniform(glsl, prog, "vTexture"), 0);
#else
glUniform1i(gl_glsl_get_uniform(glsl, prog, "Texture"), 0);
#endif
glUseProgram(0);
}
program->id = prog;
return true;
error:
RARCH_ERR("Failed to link program #%u.\n", idx);
program->id = 0;
return false;
}
static void gl_glsl_strip_parameter_pragmas(char *source, const char *str)
{
/* #pragma parameter lines tend to have " characters in them,
* which is not legal GLSL. */
char *s = strstr(source, str);
while (s)
{
/* #pragmas have to be on a single line,
* so we can just replace the entire line with spaces. */
while (*s != '\0' && *s != '\n')
*s++ = ' ';
s = strstr(s, str);
}
}
static bool gl_glsl_load_source_path(struct video_shader_pass *pass,
const char *path)
{
int64_t len = 0;
int64_t nitems = pass ? filestream_read_file(path,
(void**)&pass->source.string.vertex, &len) : 0;
if (nitems <= 0 || len <= 0)
return false;
gl_glsl_strip_parameter_pragmas(pass->source.string.vertex,
"#pragma parameter");
pass->source.string.fragment = strdup(pass->source.string.vertex);
return pass->source.string.fragment && pass->source.string.vertex;
}
static bool gl_glsl_compile_programs(
glsl_shader_data_t *glsl,
struct shader_program_glsl_data *program)
{
unsigned i;
for (i = 0; i < glsl->shader->passes; i++)
{
struct shader_program_info shader_prog_info;
const char *vertex = NULL;
const char *fragment = NULL;
struct video_shader_pass *pass = (struct video_shader_pass*)
&glsl->shader->pass[i];
if (!pass)
continue;
/* If we load from GLSLP (preset),
* load the file here.
*/
if ( !string_is_empty(pass->source.path)
&& !gl_glsl_load_source_path(pass, pass->source.path))
{
RARCH_ERR("Failed to load GLSL shader: %s.\n",
pass->source.path);
return false;
}
vertex = pass->source.string.vertex;
fragment = pass->source.string.fragment;
shader_prog_info.vertex = vertex;
shader_prog_info.fragment = fragment;
shader_prog_info.is_file = false;
if (!gl_glsl_compile_program(glsl, i,
&program[i],
&shader_prog_info))
{
RARCH_ERR("Failed to create GL program #%u.\n", i);
return false;
}
}
return true;
}
static void gl_glsl_reset_attrib(glsl_shader_data_t *glsl)
{
unsigned i;
for (i = 0; i < glsl->attribs_index; i++)
glDisableVertexAttribArray(glsl->attribs_elems[i]);
glsl->attribs_index = 0;
}
static void gl_glsl_set_vbo(GLfloat **buffer, size_t *buffer_elems,
const GLfloat *data, size_t elems)
{
if (elems > *buffer_elems)
{
GLfloat *new_buffer = (GLfloat*)
realloc(*buffer, elems * sizeof(GLfloat));
*buffer = new_buffer;
}
memcpy(*buffer, data, elems * sizeof(GLfloat));
glBufferData(GL_ARRAY_BUFFER, elems * sizeof(GLfloat),
data, GL_STATIC_DRAW);
*buffer_elems = elems;
}
static INLINE void gl_glsl_set_attribs(glsl_shader_data_t *glsl,
GLuint vbo,
GLfloat **buffer, size_t *buffer_elems,
const GLfloat *data, size_t elems,
const struct glsl_attrib *attrs, size_t num_attrs)
{
size_t i;
glBindBuffer(GL_ARRAY_BUFFER, vbo);
if (elems != *buffer_elems ||
memcmp(data, *buffer, elems * sizeof(GLfloat)))
gl_glsl_set_vbo(buffer, buffer_elems, data, elems);
for (i = 0; i < num_attrs; i++)
{
if (glsl->attribs_index < ARRAY_SIZE(glsl->attribs_elems))
{
GLint loc = attrs[i].loc;
glEnableVertexAttribArray(loc);
glVertexAttribPointer(loc, attrs[i].size, GL_FLOAT, GL_FALSE, 0,
(const GLvoid*)(uintptr_t)attrs[i].offset);
glsl->attribs_elems[glsl->attribs_index++] = loc;
}
else
RARCH_WARN("Attrib array buffer was overflown!\n");
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
static void gl_glsl_clear_uniforms_frame(
struct shader_uniforms_frame *frame)
{
frame->texture = -1;
frame->texture_size = -1;
frame->input_size = -1;
frame->tex_coord = -1;
}
static void gl_glsl_find_uniforms_frame(glsl_shader_data_t *glsl,
GLuint prog,
struct shader_uniforms_frame *frame, const char *base)
{
char texture[64];
char texture_size[64];
char input_size[64];
char tex_coord[64];
strlcpy(texture, base, sizeof(texture));
strlcat(texture, "Texture", sizeof(texture));
strlcpy(texture_size, base, sizeof(texture_size));
strlcat(texture_size, "TextureSize", sizeof(texture_size));
strlcpy(input_size, base, sizeof(input_size));
strlcat(input_size, "InputSize", sizeof(input_size));
strlcpy(tex_coord, base, sizeof(tex_coord));
strlcat(tex_coord, "TexCoord", sizeof(tex_coord));
if (frame->texture < 0)
frame->texture = gl_glsl_get_uniform(glsl, prog, texture);
if (frame->texture_size < 0)
frame->texture_size = gl_glsl_get_uniform(glsl, prog, texture_size);
if (frame->input_size < 0)
frame->input_size = gl_glsl_get_uniform(glsl, prog, input_size);
if (frame->tex_coord < 0)
frame->tex_coord = gl_glsl_get_attrib(glsl, prog, tex_coord);
}
static void gl_glsl_find_uniforms(glsl_shader_data_t *glsl,
unsigned pass, GLuint prog,
struct shader_uniforms *uni)
{
unsigned i;
char frame_base[64];
frame_base[0] = '\0';
glUseProgram(prog);
#if defined(VITA)
uni->time = gl_glsl_get_uniform(glsl, prog, "Time");
#endif
uni->mvp = gl_glsl_get_uniform(glsl, prog, "MVPMatrix");
uni->tex_coord = gl_glsl_get_attrib(glsl, prog, "TexCoord");
uni->vertex_coord = gl_glsl_get_attrib(glsl, prog, "VertexCoord");
uni->color = gl_glsl_get_attrib(glsl, prog, "Color");
uni->lut_tex_coord = gl_glsl_get_attrib(glsl, prog, "LUTTexCoord");
uni->input_size = gl_glsl_get_uniform(glsl, prog, "InputSize");
uni->output_size = gl_glsl_get_uniform(glsl, prog, "OutputSize");
uni->texture_size = gl_glsl_get_uniform(glsl, prog, "TextureSize");
uni->frame_count = gl_glsl_get_uniform(glsl, prog, "FrameCount");
uni->frame_direction = gl_glsl_get_uniform(glsl, prog, "FrameDirection");
for (i = 0; i < glsl->shader->luts; i++)
uni->lut_texture[i] = glGetUniformLocation(prog, glsl->shader->lut[i].id);
gl_glsl_clear_uniforms_frame(&uni->orig);
gl_glsl_find_uniforms_frame(glsl, prog, &uni->orig, "Orig");
gl_glsl_clear_uniforms_frame(&uni->feedback);
gl_glsl_find_uniforms_frame(glsl, prog, &uni->feedback, "Feedback");
if (pass > 1)
{
snprintf(frame_base, sizeof(frame_base), "PassPrev%u", pass);
gl_glsl_find_uniforms_frame(glsl, prog, &uni->orig, frame_base);
}
for (i = 0; i + 1 < pass; i++)
{
snprintf(frame_base, sizeof(frame_base), "Pass%u", i + 1);
gl_glsl_clear_uniforms_frame(&uni->pass[i]);
gl_glsl_find_uniforms_frame(glsl, prog, &uni->pass[i], frame_base);
snprintf(frame_base, sizeof(frame_base), "PassPrev%u", pass - (i + 1));
gl_glsl_find_uniforms_frame(glsl, prog, &uni->pass[i], frame_base);
if (*glsl->shader->pass[i].alias)
gl_glsl_find_uniforms_frame(glsl, prog, &uni->pass[i], glsl->shader->pass[i].alias);
}
gl_glsl_clear_uniforms_frame(&uni->prev[0]);
gl_glsl_find_uniforms_frame(glsl, prog, &uni->prev[0], "Prev");
for (i = 1; i < PREV_TEXTURES; i++)
{
snprintf(frame_base, sizeof(frame_base), "Prev%u", i);
gl_glsl_clear_uniforms_frame(&uni->prev[i]);
gl_glsl_find_uniforms_frame(glsl, prog, &uni->prev[i], frame_base);
}
glUseProgram(0);
}
static void gl_glsl_deinit_shader(glsl_shader_data_t *glsl)
{
unsigned i;
if (!glsl || !glsl->shader)
return;
for (i = 0; i < glsl->shader->passes; i++)
{
free(glsl->shader->pass[i].source.string.vertex);
free(glsl->shader->pass[i].source.string.fragment);
}
free(glsl->shader);
glsl->shader = NULL;
}
static void gl_glsl_destroy_resources(glsl_shader_data_t *glsl)
{
unsigned i;
if (!glsl)
return;
glsl->current_idx = 0;
glUseProgram(0);
for (i = 0; i < GFX_MAX_SHADERS; i++)
{
if (glsl->prg[i].id == 0 || (i && glsl->prg[i].id == glsl->prg[0].id))
continue;
if (!glIsProgram(glsl->prg[i].id))
continue;
glDeleteProgram(glsl->prg[i].id);
glsl->prg[i].id = 0;
}
if (glsl->shader && glsl->shader->luts)
glDeleteTextures(glsl->shader->luts, glsl->lut_textures);
memset(glsl->prg, 0, sizeof(glsl->prg));
memset(glsl->uniforms, 0, sizeof(glsl->uniforms));
glsl->active_idx = 0;
gl_glsl_deinit_shader(glsl);
gl_glsl_reset_attrib(glsl);
for (i = 0; i < GFX_MAX_SHADERS; i++)
{
if (glsl->vbo[i].vbo_primary)
glDeleteBuffers(1, &glsl->vbo[i].vbo_primary);
if (glsl->vbo[i].vbo_secondary)
glDeleteBuffers(1, &glsl->vbo[i].vbo_secondary);
free(glsl->vbo[i].buffer_primary);
free(glsl->vbo[i].buffer_secondary);
}
memset(&glsl->vbo, 0, sizeof(glsl->vbo));
}
static void gl_glsl_deinit(void *data)
{
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
if (!glsl)
return;
gl_glsl_destroy_resources(glsl);
free(glsl);
}
static void gl_glsl_init_menu_shaders(void *data)
{
#ifdef HAVE_SHADERPIPELINE
struct shader_program_info shader_prog_info;
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
if (!glsl)
return;
#ifdef HAVE_OPENGLES
#if defined(VITA)
shader_prog_info.vertex = stock_vertex_xmb_ribbon_modern;
shader_prog_info.fragment = stock_fragment_xmb;
#else
if (gl_query_extension("GL_OES_standard_derivatives"))
{
shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_ribbon_modern : stock_vertex_xmb_ribbon_legacy;
shader_prog_info.fragment = glsl_core ? core_stock_fragment_xmb : stock_fragment_xmb;
}
else
{
shader_prog_info.vertex = stock_vertex_xmb_ribbon_simple_legacy;
shader_prog_info.fragment = stock_fragment_xmb_ribbon_simple;
}
#endif
#else
shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_ribbon_modern : stock_vertex_xmb_ribbon_legacy;
shader_prog_info.fragment = glsl_core ? core_stock_fragment_xmb : stock_fragment_xmb;
#endif
shader_prog_info.is_file = false;
RARCH_LOG("[GLSL]: Compiling ribbon shader..\n");
gl_glsl_compile_program(
glsl,
VIDEO_SHADER_MENU,
&glsl->prg[VIDEO_SHADER_MENU],
&shader_prog_info);
gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU].id,
&glsl->uniforms[VIDEO_SHADER_MENU]);
#if defined(VITA)
shader_prog_info.vertex = stock_vertex_xmb_simple_modern;
shader_prog_info.fragment = stock_fragment_xmb_ribbon_simple;
#else
shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_simple_modern : stock_vertex_xmb_ribbon_simple_legacy;
shader_prog_info.fragment = glsl_core ? stock_fragment_xmb_ribbon_simple_core : stock_fragment_xmb_ribbon_simple;
#endif
RARCH_LOG("[GLSL]: Compiling simple ribbon shader..\n");
gl_glsl_compile_program(
glsl,
VIDEO_SHADER_MENU_2,
&glsl->prg[VIDEO_SHADER_MENU_2],
&shader_prog_info);
gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_2].id,
&glsl->uniforms[VIDEO_SHADER_MENU_2]);
#if !defined(VITA)
#if defined(HAVE_OPENGLES)
shader_prog_info.vertex = stock_vertex_xmb_snow;
shader_prog_info.fragment = stock_fragment_xmb_simple_snow;
#else
shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_core : stock_vertex_xmb_snow_legacy;
shader_prog_info.fragment = glsl_core ? stock_fragment_xmb_simple_snow_core : stock_fragment_xmb_simple_snow;
#endif
RARCH_LOG("[GLSL]: Compiling snow shader..\n");
gl_glsl_compile_program(
glsl,
VIDEO_SHADER_MENU_3,
&glsl->prg[VIDEO_SHADER_MENU_3],
&shader_prog_info);
gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_3].id,
&glsl->uniforms[VIDEO_SHADER_MENU_3]);
#if defined(HAVE_OPENGLES)
shader_prog_info.vertex = stock_vertex_xmb_snow;
shader_prog_info.fragment = stock_fragment_xmb_snow;
#else
shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_core : stock_vertex_xmb_snow_legacy;
shader_prog_info.fragment = glsl_core ? stock_fragment_xmb_snow_core : stock_fragment_xmb_snow;
#endif
RARCH_LOG("[GLSL]: Compiling modern snow shader..\n");
gl_glsl_compile_program(
glsl,
VIDEO_SHADER_MENU_4,
&glsl->prg[VIDEO_SHADER_MENU_4],
&shader_prog_info);
gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_4].id,
&glsl->uniforms[VIDEO_SHADER_MENU_4]);
#if defined(HAVE_OPENGLES)
shader_prog_info.vertex = stock_vertex_xmb_snow;
shader_prog_info.fragment = stock_fragment_xmb_bokeh;
#else
shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_core : stock_vertex_xmb_snow_legacy;
shader_prog_info.fragment = glsl_core ? stock_fragment_xmb_bokeh_core : stock_fragment_xmb_bokeh;
#endif
RARCH_LOG("[GLSL]: Compiling bokeh shader..\n");
gl_glsl_compile_program(
glsl,
VIDEO_SHADER_MENU_5,
&glsl->prg[VIDEO_SHADER_MENU_5],
&shader_prog_info);
gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_5].id,
&glsl->uniforms[VIDEO_SHADER_MENU_5]);
#if defined(HAVE_OPENGLES)
shader_prog_info.vertex = stock_vertex_xmb_snow;
shader_prog_info.fragment = stock_fragment_xmb_snowflake;
#else
shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_core : stock_vertex_xmb_snow_legacy;
shader_prog_info.fragment = glsl_core ? stock_fragment_xmb_snowflake_core : stock_fragment_xmb_snowflake;
#endif
RARCH_LOG("[GLSL]: Compiling snowflake shader..\n");
gl_glsl_compile_program(
glsl,
VIDEO_SHADER_MENU_6,
&glsl->prg[VIDEO_SHADER_MENU_6],
&shader_prog_info);
gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_6].id,
&glsl->uniforms[VIDEO_SHADER_MENU_6]);
#endif
#endif
}
static void *gl_glsl_init(void *data, const char *path)
{
unsigned i;
struct shader_program_info shader_prog_info;
bool shader_support = false;
#ifdef GLSL_DEBUG
char *error_string = NULL;
#endif
const char *stock_vertex = NULL;
const char *stock_fragment = NULL;
glsl_shader_data_t *glsl = (glsl_shader_data_t*)
calloc(1, sizeof(glsl_shader_data_t));
if (!glsl)
return NULL;
(void)shader_support;
#ifndef HAVE_OPENGLES
RARCH_LOG("[GLSL]: Checking GLSL shader support ...\n");
shader_support = glCreateProgram && glUseProgram && glCreateShader
&& glDeleteShader && glShaderSource && glCompileShader && glAttachShader
&& glDetachShader && glLinkProgram && glGetUniformLocation
&& glUniform1i && glUniform1f && glUniform2fv && glUniform4fv
&& glUniformMatrix4fv
&& glGetShaderiv && glGetShaderInfoLog && glGetProgramiv
&& glGetProgramInfoLog
&& glDeleteProgram && glGetAttachedShaders
&& glGetAttribLocation && glEnableVertexAttribArray
&& glDisableVertexAttribArray
&& glVertexAttribPointer
&& glGenBuffers && glBufferData && glDeleteBuffers && glBindBuffer;
if (!shader_support)
{
RARCH_ERR("GLSL shaders aren't supported by your OpenGL driver.\n");
goto error;
}
#endif
glsl->shader = (struct video_shader*)calloc(1, sizeof(*glsl->shader));
if (!glsl->shader)
goto error;
{
bool is_preset;
enum rarch_shader_type type =
video_shader_get_type_from_ext(path_get_extension(path), &is_preset);
if (!string_is_empty(path) && type != RARCH_SHADER_GLSL)
{
RARCH_ERR("[GL]: Invalid shader type, falling back to stock.\n");
path = NULL;
}
if (!string_is_empty(path))
{
bool ret = false;
if (is_preset)
{
ret = video_shader_load_preset_into_shader(path, glsl->shader);
glsl->shader->flags |= SHDR_FLAG_MODERN;
}
else
{
strlcpy(glsl->shader->pass[0].source.path, path,
sizeof(glsl->shader->pass[0].source.path));
glsl->shader->passes = 1;
glsl->shader->flags |= SHDR_FLAG_MODERN;
ret = true;
}
if (!ret)
{
RARCH_ERR("[GL]: Failed to parse GLSL shader.\n");
goto error;
}
}
else
{
RARCH_WARN("[GL]: Stock GLSL shaders will be used.\n");
glsl->shader->passes = 1;
#if defined(VITA)
glsl->shader->pass[0].source.string.vertex = strdup(stock_vertex_modern);
glsl->shader->pass[0].source.string.fragment = strdup(stock_fragment_modern);
#else
glsl->shader->pass[0].source.string.vertex =
strdup(glsl_core ? stock_vertex_core : stock_vertex_modern);
glsl->shader->pass[0].source.string.fragment =
strdup(glsl_core ? stock_fragment_core : stock_fragment_modern);
#endif
glsl->shader->flags |= SHDR_FLAG_MODERN;
}
}
#if defined(VITA)
stock_vertex = stock_vertex_modern;
stock_fragment = stock_fragment_modern;
#else
stock_vertex = (glsl->shader->flags & SHDR_FLAG_MODERN) ?
stock_vertex_modern : stock_vertex_legacy;
stock_fragment = (glsl->shader->flags & SHDR_FLAG_MODERN) ?
stock_fragment_modern : stock_fragment_legacy;
if (glsl_core)
{
stock_vertex = stock_vertex_core;
stock_fragment = stock_fragment_core;
}
#endif
#ifdef HAVE_OPENGLES
if (!(glsl->shader->flags & SHDR_FLAG_MODERN))
{
RARCH_ERR("[GL]: GLES context is used, but shader is not modern. Cannot use it.\n");
goto error;
}
#else
if ( glsl_core
&& (!(glsl->shader->flags & SHDR_FLAG_MODERN)))
{
RARCH_ERR("[GL]: GL core context is used, but shader is not core compatible. Cannot use it.\n");
goto error;
}
#endif
/* Find all aliases we use in our GLSLP and add #defines for them so
* that a shader can choose a fallback if we are not using a preset. */
*glsl->alias_define = '\0';
for (i = 0; i < glsl->shader->passes; i++)
{
if (*glsl->shader->pass[i].alias)
{
char define[128];
define[0] = '\0';
snprintf(define, sizeof(define), "#define %s_ALIAS\n",
glsl->shader->pass[i].alias);
strlcat(glsl->alias_define, define, sizeof(glsl->alias_define));
}
}
shader_prog_info.vertex = stock_vertex;
shader_prog_info.fragment = stock_fragment;
shader_prog_info.is_file = false;
if (!gl_glsl_compile_program(glsl, 0, &glsl->prg[0], &shader_prog_info))
{
RARCH_ERR("GLSL stock programs failed to compile.\n");
goto error;
}
if (!gl_glsl_compile_programs(glsl, &glsl->prg[1]))
goto error;
if (!gl2_load_luts(glsl->shader, glsl->lut_textures))
{
RARCH_ERR("[GL]: Failed to load LUTs.\n");
goto error;
}
for (i = 0; i <= glsl->shader->passes; i++)
gl_glsl_find_uniforms(glsl, i, glsl->prg[i].id, &glsl->uniforms[i]);
#ifdef GLSL_DEBUG
if (!gl_check_error(&error_string))
{
RARCH_ERR("%s\n", error_string);
free(error_string);
RARCH_WARN("Detected GL error in GLSL.\n");
}
#endif
glsl->prg[glsl->shader->passes + 1] = glsl->prg[0];
glsl->uniforms[glsl->shader->passes + 1] = glsl->uniforms[0];
if (glsl->shader->flags & SHDR_FLAG_MODERN)
{
#if defined(VITA)
shader_prog_info.vertex = stock_vertex_modern_blend;
shader_prog_info.fragment = stock_fragment_modern_blend;
#else
shader_prog_info.vertex =
glsl_core ?
stock_vertex_core_blend : stock_vertex_modern_blend;
shader_prog_info.fragment =
glsl_core ?
stock_fragment_core_blend : stock_fragment_modern_blend;
#endif
shader_prog_info.is_file = false;
gl_glsl_compile_program(
glsl,
VIDEO_SHADER_STOCK_BLEND,
&glsl->prg[VIDEO_SHADER_STOCK_BLEND],
&shader_prog_info
);
gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_STOCK_BLEND].id,
&glsl->uniforms[VIDEO_SHADER_STOCK_BLEND]);
}
else
{
glsl->prg[VIDEO_SHADER_STOCK_BLEND] = glsl->prg[0];
glsl->uniforms[VIDEO_SHADER_STOCK_BLEND] = glsl->uniforms[0];
}
gl_glsl_reset_attrib(glsl);
for (i = 0; i < GFX_MAX_SHADERS; i++)
{
glGenBuffers(1, &glsl->vbo[i].vbo_primary);
glGenBuffers(1, &glsl->vbo[i].vbo_secondary);
}
return glsl;
error:
gl_glsl_destroy_resources(glsl);
if (glsl)
free(glsl);
return NULL;
}
static void gl_glsl_set_uniform_parameter(
void *data,
struct uniform_info *param,
void *uniform_data)
{
GLint location = 0;
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
if (!glsl || !param)
return;
if (param->lookup.enable)
location = glGetUniformLocation(glsl->prg[param->lookup.idx].id, param->lookup.ident);
else
location = param->location;
switch (param->type)
{
case UNIFORM_1F:
glUniform1f(location, param->result.f.v0);
break;
case UNIFORM_2F:
glUniform2f(location, param->result.f.v0,
param->result.f.v1);
break;
case UNIFORM_3F:
glUniform3f(location, param->result.f.v0,
param->result.f.v1, param->result.f.v2);
break;
case UNIFORM_4F:
glUniform4f(location, param->result.f.v0,
param->result.f.v1, param->result.f.v2,
param->result.f.v3);
break;
case UNIFORM_1FV:
glUniform1fv(location, 1, param->result.floatv);
break;
case UNIFORM_2FV:
glUniform2fv(location, 1, param->result.floatv);
break;
case UNIFORM_3FV:
glUniform3fv(location, 1, param->result.floatv);
break;
case UNIFORM_4FV:
glUniform4fv(location, 1, param->result.floatv);
break;
case UNIFORM_1I:
glUniform1i(location, (GLint)param->result.integer.v0);
break;
}
}
static void gl_glsl_set_params(void *dat, void *shader_data)
{
unsigned i;
GLfloat buffer[512];
struct glsl_attrib attribs[32];
float input_size[2], output_size[2], texture_size[2];
video_shader_ctx_params_t *params = (video_shader_ctx_params_t*)dat;
unsigned width = params->width;
unsigned height = params->height;
unsigned tex_width = params->tex_width;
unsigned tex_height = params->tex_height;
unsigned out_width = params->out_width;
unsigned out_height = params->out_height;
unsigned frame_count = params->frame_counter;
const void *_info = params->info;
const void *_prev_info = params->prev_info;
const void *_feedback_info = params->feedback_info;
const void *_fbo_info = params->fbo_info;
unsigned fbo_info_cnt = params->fbo_info_cnt;
unsigned texunit = 1;
const struct shader_uniforms *uni = NULL;
size_t size = 0;
size_t attribs_size = 0;
const struct video_tex_info *info = (const struct video_tex_info*)_info;
const struct video_tex_info *prev_info = (const struct video_tex_info*)_prev_info;
const struct video_tex_info *feedback_info = (const struct video_tex_info*)_feedback_info;
const struct video_tex_info *fbo_info = (const struct video_tex_info*)_fbo_info;
struct glsl_attrib *attr = (struct glsl_attrib*)attribs;
glsl_shader_data_t *glsl = (glsl_shader_data_t*)shader_data;
if (!glsl)
return;
uni = (const struct shader_uniforms*)&glsl->uniforms[glsl->active_idx];
if (glsl->prg[glsl->active_idx].id == 0)
return;
input_size [0] = (float)width;
input_size [1] = (float)height;
output_size[0] = (float)out_width;
output_size[1] = (float)out_height;
texture_size[0] = (float)tex_width;
texture_size[1] = (float)tex_height;
if (uni->input_size >= 0)
glUniform2fv(uni->input_size, 1, input_size);
if (uni->output_size >= 0)
glUniform2fv(uni->output_size, 1, output_size);
if (uni->texture_size >= 0)
glUniform2fv(uni->texture_size, 1, texture_size);
if (uni->frame_count >= 0 && glsl->active_idx)
{
unsigned modulo = glsl->shader->pass[glsl->active_idx - 1].frame_count_mod;
if (modulo)
frame_count %= modulo;
glUniform1i(uni->frame_count, frame_count);
}
if (uni->frame_direction >= 0)
{
#ifdef HAVE_REWIND
if (state_manager_frame_is_reversed())
glUniform1i(uni->frame_direction, -1);
else
#endif
glUniform1i(uni->frame_direction, 1);
}
/* Set lookup textures. */
for (i = 0; i < glsl->shader->luts; i++)
{
if (uni->lut_texture[i] < 0)
continue;
/* Have to rebind as HW render could override this. */
glActiveTexture(GL_TEXTURE0 + texunit);
glBindTexture(GL_TEXTURE_2D, glsl->lut_textures[i]);
glUniform1i(uni->lut_texture[i], texunit);
texunit++;
}
if (glsl->active_idx)
{
/* Set original texture. */
if (uni->orig.texture >= 0)
{
/* Bind original texture. */
glActiveTexture(GL_TEXTURE0 + texunit);
glUniform1i(uni->orig.texture, texunit);
glBindTexture(GL_TEXTURE_2D, info->tex);
texunit++;
}
if (uni->orig.texture_size >= 0)
glUniform2fv(uni->orig.texture_size, 1, info->tex_size);
if (uni->orig.input_size >= 0)
glUniform2fv(uni->orig.input_size, 1, info->input_size);
/* Pass texture coordinates. */
if (uni->orig.tex_coord >= 0)
{
attr->loc = uni->orig.tex_coord;
attr->size = 2;
attr->offset = (GLsizei)(size * sizeof(GLfloat));
attribs_size++;
attr++;
buffer[size ] = info->coord[0];
buffer[size + 1] = info->coord[1];
buffer[size + 2] = info->coord[2];
buffer[size + 3] = info->coord[3];
buffer[size + 4] = info->coord[4];
buffer[size + 5] = info->coord[5];
buffer[size + 6] = info->coord[6];
buffer[size + 7] = info->coord[7];
size += 8;
}
/* Set feedback texture. */
if (uni->feedback.texture >= 0)
{
/* Bind original texture. */
glActiveTexture(GL_TEXTURE0 + texunit);
glUniform1i(uni->feedback.texture, texunit);
glBindTexture(GL_TEXTURE_2D, feedback_info->tex);
texunit++;
}
if (uni->feedback.texture_size >= 0)
glUniform2fv(uni->feedback.texture_size, 1, feedback_info->tex_size);
if (uni->feedback.input_size >= 0)
glUniform2fv(uni->feedback.input_size, 1, feedback_info->input_size);
/* Pass texture coordinates. */
if (uni->feedback.tex_coord >= 0)
{
attr->loc = uni->feedback.tex_coord;
attr->size = 2;
attr->offset = (GLsizei)(size * sizeof(GLfloat));
attribs_size++;
attr++;
buffer[size ] = feedback_info->coord[0];
buffer[size + 1] = feedback_info->coord[1];
buffer[size + 2] = feedback_info->coord[2];
buffer[size + 3] = feedback_info->coord[3];
buffer[size + 4] = feedback_info->coord[4];
buffer[size + 5] = feedback_info->coord[5];
buffer[size + 6] = feedback_info->coord[6];
buffer[size + 7] = feedback_info->coord[7];
size += 8;
}
/* Bind FBO textures. */
for (i = 0; i < fbo_info_cnt; i++)
{
if (uni->pass[i].texture)
{
glActiveTexture(GL_TEXTURE0 + texunit);
glBindTexture(GL_TEXTURE_2D, fbo_info[i].tex);
glUniform1i(uni->pass[i].texture, texunit);
texunit++;
}
if (uni->pass[i].texture_size >= 0)
glUniform2fv(uni->pass[i].texture_size, 1, fbo_info[i].tex_size);
if (uni->pass[i].input_size >= 0)
glUniform2fv(uni->pass[i].input_size, 1, fbo_info[i].input_size);
if (uni->pass[i].tex_coord >= 0)
{
attr->loc = uni->pass[i].tex_coord;
attr->size = 2;
attr->offset = (GLsizei)(size * sizeof(GLfloat));
attribs_size++;
attr++;
buffer[size ] = fbo_info[i].coord[0];
buffer[size + 1] = fbo_info[i].coord[1];
buffer[size + 2] = fbo_info[i].coord[2];
buffer[size + 3] = fbo_info[i].coord[3];
buffer[size + 4] = fbo_info[i].coord[4];
buffer[size + 5] = fbo_info[i].coord[5];
buffer[size + 6] = fbo_info[i].coord[6];
buffer[size + 7] = fbo_info[i].coord[7];
size += 8;
}
}
}
/* Set previous textures. Only bind if they're actually used. */
for (i = 0; i < PREV_TEXTURES; i++)
{
if (uni->prev[i].texture >= 0)
{
glActiveTexture(GL_TEXTURE0 + texunit);
glBindTexture(GL_TEXTURE_2D, prev_info[i].tex);
glUniform1i(uni->prev[i].texture, texunit);
texunit++;
}
if (uni->prev[i].texture_size >= 0)
glUniform2fv(uni->prev[i].texture_size, 1, prev_info[i].tex_size);
if (uni->prev[i].input_size >= 0)
glUniform2fv(uni->prev[i].input_size, 1, prev_info[i].input_size);
/* Pass texture coordinates. */
if (uni->prev[i].tex_coord >= 0)
{
attr->loc = uni->prev[i].tex_coord;
attr->size = 2;
attr->offset = (GLsizei)(size * sizeof(GLfloat));
attribs_size++;
attr++;
buffer[size ] = prev_info[i].coord[0];
buffer[size + 1] = prev_info[i].coord[1];
buffer[size + 2] = prev_info[i].coord[2];
buffer[size + 3] = prev_info[i].coord[3];
buffer[size + 4] = prev_info[i].coord[4];
buffer[size + 5] = prev_info[i].coord[5];
buffer[size + 6] = prev_info[i].coord[6];
buffer[size + 7] = prev_info[i].coord[7];
size += 8;
}
}
if (size)
gl_glsl_set_attribs(glsl, glsl->vbo[glsl->active_idx].vbo_secondary,
&glsl->vbo[glsl->active_idx].buffer_secondary,
&glsl->vbo[glsl->active_idx].size_secondary,
buffer, size, attribs, attribs_size);
glActiveTexture(GL_TEXTURE0);
/* #pragma parameters. */
for (i = 0; i < glsl->shader->num_parameters; i++)
{
int location = glGetUniformLocation(
glsl->prg[glsl->active_idx].id,
glsl->shader->parameters[i].id);
glUniform1f(location, glsl->shader->parameters[i].current);
}
}
static bool gl_glsl_set_mvp(void *shader_data, const void *mat_data)
{
int loc;
glsl_shader_data_t *glsl = (glsl_shader_data_t*)shader_data;
if ( !glsl
|| (!(glsl->shader->flags & SHDR_FLAG_MODERN)))
return false;
loc = glsl->uniforms[glsl->active_idx].mvp;
if (loc >= 0)
{
const math_matrix_4x4 *mat = (const math_matrix_4x4*)mat_data;
if ( (glsl->current_idx != glsl->active_idx) ||
(mat->data != glsl->current_mat_data_pointer[glsl->active_idx]) ||
(*mat->data != glsl->current_mat_data[glsl->active_idx]))
{
glUniformMatrix4fv(loc, 1, GL_FALSE, mat->data);
glsl->current_idx = glsl->active_idx;
glsl->current_mat_data_pointer[glsl->active_idx] = (float*)mat->data;
glsl->current_mat_data[glsl->active_idx] = *mat->data;
}
}
return true;
}
#define gl_glsl_set_coord_array(attribs, coord1, coord2, coords, size, multiplier) \
unsigned y; \
attribs[attribs_size].loc = (GLint)coord1; \
attribs[attribs_size].size = (GLsizei)multiplier; \
attribs[attribs_size].offset = (GLsizei)(size * sizeof(GLfloat)); \
for (y = 0; y < (multiplier * coords->vertices); y++) \
buffer[y + size] = coord2[y]; \
size += multiplier * coords->vertices; \
static bool gl_glsl_set_coords(void *shader_data,
const struct video_coords *coords)
{
GLfloat short_buffer[4 * (2 + 2 + 4 + 2)];
struct glsl_attrib attribs[4];
size_t attribs_size = 0;
size_t size = 0;
GLfloat *buffer = short_buffer;
glsl_shader_data_t *glsl = (glsl_shader_data_t*)shader_data;
const struct shader_uniforms *uni = glsl
? &glsl->uniforms[glsl->active_idx] : NULL;
if ( !glsl
|| (!(glsl->shader->flags & SHDR_FLAG_MODERN))
|| !coords)
{
if (coords)
return false;
return true;
}
if (coords->vertices > 4)
{
/* Avoid hitting malloc on every single regular quad draw. */
size_t elems = 0;
elems += (uni->color >= 0) * 4;
elems += (uni->tex_coord >= 0) * 2;
elems += (uni->vertex_coord >= 0) * 2;
elems += (uni->lut_tex_coord >= 0) * 2;
elems *= coords->vertices * sizeof(GLfloat);
buffer = (GLfloat*)malloc(elems);
if (!buffer)
return false;
}
#if defined(VITA)
if (uni->time >= 0) {
float t = (sceKernelGetSystemTimeWide()) / (scePowerGetArmClockFrequency() * 1000.0);
glUniform1f(uni->time, t);
}
#endif
if (uni->tex_coord >= 0)
{
gl_glsl_set_coord_array(attribs, uni->tex_coord,
coords->tex_coord, coords, size, 2);
attribs_size++;
}
if (uni->vertex_coord >= 0)
{
gl_glsl_set_coord_array(attribs, uni->vertex_coord,
coords->vertex, coords, size, 2);
attribs_size++;
}
if (uni->color >= 0)
{
gl_glsl_set_coord_array(attribs, uni->color,
coords->color, coords, size, 4);
attribs_size++;
}
if (uni->lut_tex_coord >= 0)
{
gl_glsl_set_coord_array(attribs, uni->lut_tex_coord,
coords->lut_tex_coord, coords, size, 2);
attribs_size++;
}
if (size)
gl_glsl_set_attribs(glsl,
glsl->vbo[glsl->active_idx].vbo_primary,
&glsl->vbo[glsl->active_idx].buffer_primary,
&glsl->vbo[glsl->active_idx].size_primary,
buffer, size,
attribs, attribs_size);
if (buffer != short_buffer)
free(buffer);
return true;
}
static void gl_glsl_use(void *data, void *shader_data, unsigned idx, bool set_active)
{
GLuint id;
if (set_active)
{
glsl_shader_data_t *glsl = (glsl_shader_data_t*)shader_data;
if (!glsl)
return;
gl_glsl_reset_attrib(glsl);
glsl->active_idx = idx;
id = glsl->prg[idx].id;
}
else
id = (GLuint)idx;
glUseProgram(id);
}
static unsigned gl_glsl_num(void *data)
{
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
if (glsl && glsl->shader)
return glsl->shader->passes;
return 0;
}
static bool gl_glsl_filter_type(void *data, unsigned idx, bool *smooth)
{
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
if (glsl && idx
&& (glsl->shader->pass[idx - 1].filter != RARCH_FILTER_UNSPEC)
)
{
*smooth = (glsl->shader->pass[idx - 1].filter == RARCH_FILTER_LINEAR);
return true;
}
return false;
}
static enum gfx_wrap_type gl_glsl_wrap_type(void *data, unsigned idx)
{
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
if (glsl && idx)
return glsl->shader->pass[idx - 1].wrap;
return RARCH_WRAP_BORDER;
}
static void gl_glsl_shader_scale(void *data, unsigned idx, struct gfx_fbo_scale *scale)
{
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
if (glsl && idx)
*scale = glsl->shader->pass[idx - 1].fbo;
else
scale->flags &= ~FBO_SCALE_FLAG_VALID;
}
static unsigned gl_glsl_get_prev_textures(void *data)
{
unsigned i, j;
unsigned max_prev = 0;
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
if (!glsl)
return 0;
for (i = 1; i <= glsl->shader->passes; i++)
for (j = 0; j < PREV_TEXTURES; j++)
if (glsl->uniforms[i].prev[j].texture >= 0)
max_prev = MAX(j + 1, max_prev);
return max_prev;
}
static bool gl_glsl_mipmap_input(void *data, unsigned idx)
{
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
if (glsl && idx)
return glsl->shader->pass[idx - 1].mipmap;
return false;
}
static bool gl_glsl_get_feedback_pass(void *data, unsigned *index)
{
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
if (!glsl || glsl->shader->feedback_pass < 0)
return false;
*index = glsl->shader->feedback_pass;
return true;
}
static struct video_shader *gl_glsl_get_current_shader(void *data)
{
glsl_shader_data_t *glsl = (glsl_shader_data_t*)data;
if (!glsl)
return NULL;
return glsl->shader;
}
static void gl_glsl_get_flags(uint32_t *flags)
{
BIT32_SET(*flags, GFX_CTX_FLAGS_SHADERS_GLSL);
}
void gl_glsl_set_context_type(bool core_profile,
unsigned major, unsigned minor)
{
glsl_core = core_profile;
glsl_major = major;
glsl_minor = minor;
}
const shader_backend_t gl_glsl_backend = {
gl_glsl_init,
gl_glsl_init_menu_shaders,
gl_glsl_deinit,
gl_glsl_set_params,
gl_glsl_set_uniform_parameter,
gl_glsl_compile_program,
gl_glsl_use,
gl_glsl_num,
gl_glsl_filter_type,
gl_glsl_wrap_type,
gl_glsl_shader_scale,
gl_glsl_set_coords,
gl_glsl_set_mvp,
gl_glsl_get_prev_textures,
gl_glsl_get_feedback_pass,
gl_glsl_mipmap_input,
gl_glsl_get_current_shader,
gl_glsl_get_flags,
RARCH_SHADER_GLSL,
"glsl"
};