RetroArch/gfx/shader_glsl.c

1573 lines
42 KiB
C
Raw Normal View History

2012-04-21 21:13:50 +00:00
/* RetroArch - A frontend for libretro.
2013-01-01 00:37:37 +00:00
* Copyright (C) 2010-2013 - Hans-Kristian Arntzen
2011-01-05 18:07:12 +00:00
*
2012-04-21 21:13:50 +00:00
* RetroArch is free software: you can redistribute it and/or modify it under the terms
2011-01-05 18:07:12 +00:00
* 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.
*
2012-04-21 21:13:50 +00:00
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
2011-01-05 18:07:12 +00:00
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
2012-04-21 21:31:57 +00:00
* You should have received a copy of the GNU General Public License along with RetroArch.
2011-01-05 18:07:12 +00:00
* If not, see <http://www.gnu.org/licenses/>.
*/
2011-12-24 12:46:12 +00:00
#include "../boolean.h"
2011-01-05 18:07:12 +00:00
#include <string.h>
2011-11-30 17:31:09 +00:00
#include "../general.h"
2011-03-16 20:48:44 +00:00
#include "shader_glsl.h"
2012-03-16 22:26:57 +00:00
#include "../compat/strl.h"
#include "../compat/posix_string.h"
2012-04-07 10:17:40 +00:00
#include "state_tracker.h"
2011-11-30 17:31:09 +00:00
#include "../dynamic.h"
#include "../file.h"
2011-05-18 18:22:27 +00:00
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
2012-10-01 20:48:19 +00:00
#if defined(__APPLE__) // Because they like to be "oh, so, special".
2011-02-04 12:46:51 +00:00
#include <OpenGL/gl.h>
#include <OpenGL/glext.h>
#elif defined(HAVE_PSGL)
#include <PSGL/psgl.h>
#include <PSGL/psglu.h>
#include <GLES/glext.h>
#elif defined(HAVE_OPENGL_MODERN)
#include <EGL/egl.h>
#include <GL3/gl3.h>
#include <GL3/gl3ext.h>
#elif defined(HAVE_OPENGLES2)
#include <GLES2/gl2.h>
#elif defined(HAVE_OPENGLES1)
#include <GLES/gl.h>
#include <GLES/glext.h>
2011-02-04 12:46:51 +00:00
#else
#define GL_GLEXT_PROTOTYPES
2011-01-05 18:07:12 +00:00
#include <GL/gl.h>
2011-02-04 12:46:51 +00:00
#include <GL/glext.h>
#endif
#include "gfx_context.h"
2011-01-05 18:07:12 +00:00
#include <stdlib.h>
2011-12-26 00:32:19 +00:00
#ifdef HAVE_LIBXML2
2011-01-05 18:07:12 +00:00
#include <libxml/parser.h>
#include <libxml/tree.h>
#else
#define RXML_LIBXML2_COMPAT
#include "../compat/rxml/rxml.h"
#endif
#include "gl_common.h"
2011-05-18 18:22:27 +00:00
#include "image.h"
2012-10-01 20:48:19 +00:00
#if defined(HAVE_OPENGLES2) || defined(HAVE_OPENGL_MODERN) || defined(__APPLE__)
2012-09-15 13:17:34 +00:00
#define pglCreateProgram glCreateProgram
#define pglUseProgram glUseProgram
#define pglCreateShader glCreateShader
#define pglDeleteShader glDeleteShader
#define pglShaderSource glShaderSource
#define pglCompileShader glCompileShader
#define pglAttachShader glAttachShader
#define pglDetachShader glDetachShader
#define pglLinkProgram glLinkProgram
#define pglGetUniformLocation glGetUniformLocation
#define pglUniform1i glUniform1i
#define pglUniform1f glUniform1f
#define pglUniform2fv glUniform2fv
#define pglUniform4fv glUniform4fv
#define pglUniformMatrix4fv glUniformMatrix4fv
#define pglGetShaderiv glGetShaderiv
#define pglGetShaderInfoLog glGetShaderInfoLog
#define pglGetProgramiv glGetProgramiv
#define pglGetProgramInfoLog glGetProgramInfoLog
#define pglDeleteProgram glDeleteProgram
#define pglGetAttachedShaders glGetAttachedShaders
#define pglGetAttribLocation glGetAttribLocation
#define pglEnableVertexAttribArray glEnableVertexAttribArray
#define pglDisableVertexAttribArray glDisableVertexAttribArray
#define pglVertexAttribPointer glVertexAttribPointer
#else
2012-10-21 00:58:51 +00:00
static PFNGLCREATEPROGRAMPROC pglCreateProgram;
static PFNGLUSEPROGRAMPROC pglUseProgram;
static PFNGLCREATESHADERPROC pglCreateShader;
static PFNGLDELETESHADERPROC pglDeleteShader;
static PFNGLSHADERSOURCEPROC pglShaderSource;
static PFNGLCOMPILESHADERPROC pglCompileShader;
static PFNGLATTACHSHADERPROC pglAttachShader;
static PFNGLDETACHSHADERPROC pglDetachShader;
static PFNGLLINKPROGRAMPROC pglLinkProgram;
static PFNGLGETUNIFORMLOCATIONPROC pglGetUniformLocation;
static PFNGLUNIFORM1IPROC pglUniform1i;
static PFNGLUNIFORM1FPROC pglUniform1f;
static PFNGLUNIFORM2FVPROC pglUniform2fv;
static PFNGLUNIFORM4FVPROC pglUniform4fv;
static PFNGLUNIFORMMATRIX4FVPROC pglUniformMatrix4fv;
static PFNGLGETSHADERIVPROC pglGetShaderiv;
static PFNGLGETSHADERINFOLOGPROC pglGetShaderInfoLog;
static PFNGLGETPROGRAMIVPROC pglGetProgramiv;
static PFNGLGETPROGRAMINFOLOGPROC pglGetProgramInfoLog;
static PFNGLDELETEPROGRAMPROC pglDeleteProgram;
static PFNGLGETATTACHEDSHADERSPROC pglGetAttachedShaders;
static PFNGLGETATTRIBLOCATIONPROC pglGetAttribLocation;
static PFNGLENABLEVERTEXATTRIBARRAYPROC pglEnableVertexAttribArray;
static PFNGLDISABLEVERTEXATTRIBARRAYPROC pglDisableVertexAttribArray;
static PFNGLVERTEXATTRIBPOINTERPROC pglVertexAttribPointer;
2012-09-15 13:17:34 +00:00
#endif
#ifdef HAVE_OPENGLES2
#define BORDER_FUNC GL_CLAMP_TO_EDGE
#else
#define BORDER_FUNC GL_CLAMP_TO_BORDER
#endif
2011-01-05 18:07:12 +00:00
#define MAX_VARIABLES 256
#define MAX_TEXTURES 8
2011-11-12 13:18:10 +00:00
#define PREV_TEXTURES 7
2011-03-12 14:30:57 +00:00
2011-03-14 20:28:30 +00:00
enum filter_type
{
2012-04-21 21:25:32 +00:00
RARCH_GL_NOFORCE,
RARCH_GL_LINEAR,
RARCH_GL_NEAREST
2011-03-14 20:28:30 +00:00
};
2012-10-21 00:58:51 +00:00
static bool glsl_enable;
static bool glsl_modern;
static GLuint gl_program[RARCH_GLSL_MAX_SHADERS];
static enum filter_type gl_filter_type[RARCH_GLSL_MAX_SHADERS];
static struct gl_fbo_scale gl_scale[RARCH_GLSL_MAX_SHADERS];
2012-10-21 00:58:51 +00:00
static unsigned gl_num_programs;
static unsigned active_index;
2011-01-05 18:07:12 +00:00
2011-05-18 18:22:27 +00:00
static GLuint gl_teximage[MAX_TEXTURES];
2012-10-21 00:58:51 +00:00
static unsigned gl_teximage_cnt;
2011-05-18 18:22:27 +00:00
static char gl_teximage_uniforms[MAX_TEXTURES][64];
2012-10-21 00:58:51 +00:00
static state_tracker_t *gl_state_tracker;
2012-04-07 10:17:40 +00:00
static struct state_tracker_uniform_info gl_tracker_info[MAX_VARIABLES];
2012-10-21 00:58:51 +00:00
static unsigned gl_tracker_info_cnt;
2011-06-06 16:50:36 +00:00
static char gl_tracker_script_class[64];
2012-10-21 00:58:51 +00:00
static char *gl_script_program;
static GLint gl_attribs[PREV_TEXTURES + 1 + 4 + RARCH_GLSL_MAX_SHADERS];
2012-10-21 00:58:51 +00:00
static unsigned gl_attrib_index;
2012-09-24 23:26:22 +00:00
static gfx_ctx_proc_t (*glsl_get_proc_address)(const char*);
2011-03-12 14:30:57 +00:00
struct shader_program
{
char *vertex;
char *fragment;
2011-03-14 20:28:30 +00:00
enum filter_type filter;
float scale_x;
float scale_y;
2011-03-27 18:29:47 +00:00
unsigned abs_x;
unsigned abs_y;
enum gl_scale_type type_x;
enum gl_scale_type type_y;
bool valid_scale;
2011-03-12 14:30:57 +00:00
};
2012-10-21 00:58:51 +00:00
struct shader_uniforms_frame
{
int texture;
int input_size;
int texture_size;
int tex_coord;
};
struct shader_uniforms
{
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[MAX_TEXTURES];
struct shader_uniforms_frame orig;
struct shader_uniforms_frame pass[RARCH_GLSL_MAX_SHADERS];
2012-10-21 00:58:51 +00:00
struct shader_uniforms_frame prev[PREV_TEXTURES];
};
static struct shader_uniforms gl_uniforms[RARCH_GLSL_MAX_SHADERS];
2012-10-21 00:58:51 +00:00
static const char *stock_vertex_legacy =
2011-11-12 14:26:24 +00:00
"varying vec4 color;\n"
"void main() {\n"
" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
" gl_TexCoord[0] = gl_MultiTexCoord0;\n"
" color = gl_Color;\n"
"}";
static const char *stock_fragment_legacy =
2011-11-12 14:26:24 +00:00
"uniform sampler2D rubyTexture;\n"
"varying vec4 color;\n"
"void main() {\n"
" gl_FragColor = color * texture2D(rubyTexture, gl_TexCoord[0].xy);\n"
"}";
static const char *stock_vertex_modern =
"attribute vec2 rubyTexCoord;\n"
"attribute vec2 rubyVertexCoord;\n"
"attribute vec4 rubyColor;\n"
"uniform mat4 rubyMVPMatrix;\n"
"varying vec2 tex_coord;\n"
"varying vec4 color;\n"
"void main() {\n"
" gl_Position = rubyMVPMatrix * vec4(rubyVertexCoord, 0.0, 1.0);\n"
" tex_coord = rubyTexCoord;\n"
" color = rubyColor;\n"
"}";
static const char *stock_fragment_modern =
"#ifdef GL_ES\n"
"precision mediump float;\n"
"#endif\n"
"uniform sampler2D rubyTexture;\n"
"varying vec2 tex_coord;\n"
"varying vec4 color;\n"
"void main() {\n"
" gl_FragColor = color * texture2D(rubyTexture, tex_coord);\n"
"}";
static bool xml_get_prop(char *buf, size_t size, xmlNodePtr node, const char *prop)
{
if (!size)
return false;
xmlChar *p = xmlGetProp(node, (const xmlChar*)prop);
if (p)
{
bool ret = strlcpy(buf, (const char*)p, size) < size;
xmlFree(p);
return ret;
}
else
{
*buf = '\0';
return false;
}
}
static char *xml_get_content(xmlNodePtr node)
{
xmlChar *content = xmlNodeGetContent(node);
if (!content)
return NULL;
char *ret = strdup((const char*)content);
xmlFree(content);
return ret;
}
static char *xml_replace_if_file(char *content, const char *path, xmlNodePtr node, const char *src_prop)
{
char prop[64];
if (!xml_get_prop(prop, sizeof(prop), node, src_prop))
return content;
free(content);
content = NULL;
char shader_path[PATH_MAX];
fill_pathname_resolve_relative(shader_path, path, (const char*)prop, sizeof(shader_path));
RARCH_LOG("Loading external source from \"%s\".\n", shader_path);
if (read_file(shader_path, (void**)&content) >= 0)
return content;
else
return NULL;
}
static bool get_xml_attrs(struct shader_program *prog, xmlNodePtr ptr)
{
prog->scale_x = 1.0;
prog->scale_y = 1.0;
2012-04-21 21:25:32 +00:00
prog->type_x = prog->type_y = RARCH_SCALE_INPUT;
prog->valid_scale = false;
// Check if shader forces a certain texture filtering.
char attr[64];
if (xml_get_prop(attr, sizeof(attr), ptr, "filter"))
{
if (strcmp(attr, "nearest") == 0)
{
2012-04-21 21:25:32 +00:00
prog->filter = RARCH_GL_NEAREST;
RARCH_LOG("XML: Shader forces GL_NEAREST.\n");
}
else if (strcmp(attr, "linear") == 0)
{
2012-04-21 21:25:32 +00:00
prog->filter = RARCH_GL_LINEAR;
RARCH_LOG("XML: Shader forces GL_LINEAR.\n");
}
else
2012-04-21 21:25:32 +00:00
RARCH_WARN("XML: Invalid property for filter.\n");
}
else
2012-04-21 21:25:32 +00:00
prog->filter = RARCH_GL_NOFORCE;
2011-03-27 18:29:47 +00:00
// Check for scaling attributes *lots of code <_<*
char attr_scale[64], attr_scale_x[64], attr_scale_y[64];
char attr_size[64], attr_size_x[64], attr_size_y[64];
char attr_outscale[64], attr_outscale_x[64], attr_outscale_y[64];
xml_get_prop(attr_scale, sizeof(attr_scale), ptr, "scale");
xml_get_prop(attr_scale_x, sizeof(attr_scale_x), ptr, "scale_x");
xml_get_prop(attr_scale_y, sizeof(attr_scale_y), ptr, "scale_y");
xml_get_prop(attr_size, sizeof(attr_size), ptr, "size");
xml_get_prop(attr_size_x, sizeof(attr_size_x), ptr, "size_x");
xml_get_prop(attr_size_y, sizeof(attr_size_y), ptr, "size_y");
xml_get_prop(attr_outscale, sizeof(attr_outscale), ptr, "outscale");
xml_get_prop(attr_outscale_x, sizeof(attr_outscale_x), ptr, "outscale_x");
xml_get_prop(attr_outscale_y, sizeof(attr_outscale_y), ptr, "outscale_y");
2011-03-14 22:59:31 +00:00
unsigned x_attr_cnt = 0, y_attr_cnt = 0;
if (*attr_scale)
{
float scale = strtod(attr_scale, NULL);
prog->scale_x = scale;
prog->scale_y = scale;
prog->valid_scale = true;
2012-04-21 21:25:32 +00:00
prog->type_x = prog->type_y = RARCH_SCALE_INPUT;
RARCH_LOG("Got scale attr: %.1f\n", scale);
x_attr_cnt++;
y_attr_cnt++;
}
2011-03-14 22:59:31 +00:00
if (*attr_scale_x)
{
float scale = strtod(attr_scale_x, NULL);
prog->scale_x = scale;
prog->valid_scale = true;
2012-04-21 21:25:32 +00:00
prog->type_x = RARCH_SCALE_INPUT;
RARCH_LOG("Got scale_x attr: %.1f\n", scale);
x_attr_cnt++;
}
2011-03-14 22:59:31 +00:00
if (*attr_scale_y)
{
float scale = strtod(attr_scale_y, NULL);
prog->scale_y = scale;
prog->valid_scale = true;
2012-04-21 21:25:32 +00:00
prog->type_y = RARCH_SCALE_INPUT;
RARCH_LOG("Got scale_y attr: %.1f\n", scale);
y_attr_cnt++;
}
if (*attr_size)
2011-03-27 18:29:47 +00:00
{
prog->abs_x = prog->abs_y = strtoul(attr_size, NULL, 0);
2011-03-27 18:29:47 +00:00
prog->valid_scale = true;
2012-04-21 21:25:32 +00:00
prog->type_x = prog->type_y = RARCH_SCALE_ABSOLUTE;
RARCH_LOG("Got size attr: %u\n", prog->abs_x);
x_attr_cnt++;
y_attr_cnt++;
2011-03-27 18:29:47 +00:00
}
if (*attr_size_x)
2011-03-27 18:29:47 +00:00
{
prog->abs_x = strtoul(attr_size_x, NULL, 0);
2011-03-27 18:29:47 +00:00
prog->valid_scale = true;
2012-04-21 21:25:32 +00:00
prog->type_x = RARCH_SCALE_ABSOLUTE;
RARCH_LOG("Got size_x attr: %u\n", prog->abs_x);
x_attr_cnt++;
2011-03-27 18:29:47 +00:00
}
if (*attr_size_y)
2011-03-27 18:29:47 +00:00
{
prog->abs_y = strtoul(attr_size_y, NULL, 0);
2011-03-27 18:29:47 +00:00
prog->valid_scale = true;
2012-04-21 21:25:32 +00:00
prog->type_y = RARCH_SCALE_ABSOLUTE;
RARCH_LOG("Got size_y attr: %u\n", prog->abs_y);
y_attr_cnt++;
2011-03-27 18:29:47 +00:00
}
if (*attr_outscale)
2011-03-27 18:29:47 +00:00
{
float scale = strtod(attr_outscale, NULL);
2011-03-27 18:29:47 +00:00
prog->scale_x = scale;
prog->scale_y = scale;
prog->valid_scale = true;
2012-04-21 21:25:32 +00:00
prog->type_x = prog->type_y = RARCH_SCALE_VIEWPORT;
RARCH_LOG("Got outscale attr: %.1f\n", scale);
x_attr_cnt++;
y_attr_cnt++;
2011-03-27 18:29:47 +00:00
}
if (*attr_outscale_x)
2011-03-27 18:29:47 +00:00
{
float scale = strtod(attr_outscale_x, NULL);
2011-03-27 18:29:47 +00:00
prog->scale_x = scale;
prog->valid_scale = true;
2012-04-21 21:25:32 +00:00
prog->type_x = RARCH_SCALE_VIEWPORT;
RARCH_LOG("Got outscale_x attr: %.1f\n", scale);
x_attr_cnt++;
2011-03-27 18:29:47 +00:00
}
if (*attr_outscale_y)
2011-03-27 18:29:47 +00:00
{
float scale = strtod(attr_outscale_y, NULL);
2011-03-27 18:29:47 +00:00
prog->scale_y = scale;
prog->valid_scale = true;
2012-04-21 21:25:32 +00:00
prog->type_y = RARCH_SCALE_VIEWPORT;
RARCH_LOG("Got outscale_y attr: %.1f\n", scale);
y_attr_cnt++;
2011-03-27 18:29:47 +00:00
}
if (x_attr_cnt > 1)
return false;
if (y_attr_cnt > 1)
return false;
return true;
}
2011-05-18 18:22:27 +00:00
static bool get_texture_image(const char *shader_path, xmlNodePtr ptr)
{
if (gl_teximage_cnt >= MAX_TEXTURES)
{
2012-04-21 21:25:32 +00:00
RARCH_WARN("Too many texture images. Ignoring ...\n");
return true;
}
2011-05-18 18:22:27 +00:00
bool linear = true;
char filename[PATH_MAX];
char filter[64];
char id[64];
xml_get_prop(filename, sizeof(filename), ptr, "file");
xml_get_prop(filter, sizeof(filter), ptr, "filter");
xml_get_prop(id, sizeof(id), ptr, "id");
2011-12-24 12:46:12 +00:00
struct texture_image img;
2011-05-18 18:22:27 +00:00
if (!*id)
2011-05-18 18:22:27 +00:00
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Could not find ID in texture.\n");
return false;
2011-05-18 18:22:27 +00:00
}
if (!*filename)
2011-05-18 18:22:27 +00:00
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Could not find filename in texture.\n");
return false;
2011-05-18 18:22:27 +00:00
}
if (strcmp(filter, "nearest") == 0)
2011-05-18 18:22:27 +00:00
linear = false;
2012-01-02 12:32:25 +00:00
char tex_path[PATH_MAX];
fill_pathname_resolve_relative(tex_path, shader_path, (const char*)filename, sizeof(tex_path));
2011-05-18 18:22:27 +00:00
2012-04-21 21:25:32 +00:00
RARCH_LOG("Loading texture image from: \"%s\" ...\n", tex_path);
2011-05-18 18:22:27 +00:00
if (!texture_image_load(tex_path, &img))
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Failed to load texture image from: \"%s\"\n", tex_path);
return false;
2011-05-18 18:22:27 +00:00
}
strlcpy(gl_teximage_uniforms[gl_teximage_cnt], (const char*)id, sizeof(gl_teximage_uniforms[0]));
glGenTextures(1, &gl_teximage[gl_teximage_cnt]);
2011-05-18 20:11:34 +00:00
pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 1);
2011-05-18 18:22:27 +00:00
glBindTexture(GL_TEXTURE_2D, gl_teximage[gl_teximage_cnt]);
2012-09-15 13:17:34 +00:00
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, BORDER_FUNC);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, BORDER_FUNC);
2011-05-18 18:22:27 +00:00
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexImage2D(GL_TEXTURE_2D,
0, driver.gfx_use_rgba ? GL_RGBA : RARCH_GL_INTERNAL_FORMAT32,
img.width, img.height, 0, driver.gfx_use_rgba ? GL_RGBA : RARCH_GL_TEXTURE_TYPE32,
RARCH_GL_FORMAT32, img.pixels);
2011-05-18 18:22:27 +00:00
2011-05-18 20:11:34 +00:00
pglActiveTexture(GL_TEXTURE0);
2011-05-18 18:22:27 +00:00
glBindTexture(GL_TEXTURE_2D, 0);
free(img.pixels);
gl_teximage_cnt++;
return true;
}
2011-06-07 14:28:58 +00:00
#ifdef HAVE_PYTHON
static bool get_script(const char *path, xmlNodePtr ptr)
2011-06-07 13:33:29 +00:00
{
2012-11-18 20:23:34 +00:00
if (gl_script_program)
2011-06-07 13:33:29 +00:00
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Script already imported.\n");
2011-06-07 13:33:29 +00:00
return false;
}
char script_class[64];
xml_get_prop(script_class, sizeof(script_class), ptr, "class");
if (*script_class)
strlcpy(gl_tracker_script_class, script_class, sizeof(gl_tracker_script_class));
2011-06-07 13:33:29 +00:00
char language[64];
xml_get_prop(language, sizeof(language), ptr, "language");
if (strcmp(language, "python") != 0)
2011-06-07 13:33:29 +00:00
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Script language is not Python.\n");
2011-06-07 13:33:29 +00:00
return false;
}
char *script = xml_get_content(ptr);
if (!script)
return false;
2011-06-07 13:33:29 +00:00
2012-11-18 20:23:34 +00:00
gl_script_program = xml_replace_if_file(script, path, ptr, "src");
if (!gl_script_program)
2011-06-07 13:33:29 +00:00
{
RARCH_ERR("Cannot find Python script.\n");
return false;
2011-06-07 13:33:29 +00:00
}
2011-06-07 14:28:58 +00:00
2011-06-07 13:33:29 +00:00
return true;
}
2011-06-07 14:28:58 +00:00
#endif
2011-06-07 13:33:29 +00:00
2011-06-07 14:28:58 +00:00
static bool get_import_value(xmlNodePtr ptr)
{
2011-05-25 18:55:38 +00:00
if (gl_tracker_info_cnt >= MAX_VARIABLES)
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Too many import variables ...\n");
2011-05-25 18:55:38 +00:00
return false;
}
char id[64], semantic[64], wram[64], input[64], bitmask[64], bitequal[64];
xml_get_prop(id, sizeof(id), ptr, "id");
xml_get_prop(semantic, sizeof(semantic), ptr, "semantic");
xml_get_prop(wram, sizeof(wram), ptr, "wram");
xml_get_prop(input, sizeof(input), ptr, "input_slot");
xml_get_prop(bitmask, sizeof(bitmask), ptr, "mask");
xml_get_prop(bitequal, sizeof(bitequal), ptr, "equal");
2011-12-24 12:46:12 +00:00
unsigned memtype;
2012-04-07 10:17:40 +00:00
enum state_tracker_type tracker_type;
2012-04-21 21:25:32 +00:00
enum state_ram_type ram_type = RARCH_STATE_NONE;
2011-12-24 12:46:12 +00:00
uint32_t addr = 0;
unsigned mask_value = 0;
2012-02-12 17:05:33 +00:00
unsigned mask_equal = 0;
2011-12-24 12:46:12 +00:00
if (!*semantic || !*id)
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("No semantic or ID for import value.\n");
return false;
}
2011-06-06 16:50:36 +00:00
if (strcmp(semantic, "capture") == 0)
2012-04-21 21:25:32 +00:00
tracker_type = RARCH_STATE_CAPTURE;
else if (strcmp(semantic, "capture_previous") == 0)
2012-04-21 21:25:32 +00:00
tracker_type = RARCH_STATE_CAPTURE_PREV;
else if (strcmp(semantic, "transition") == 0)
2012-04-21 21:25:32 +00:00
tracker_type = RARCH_STATE_TRANSITION;
else if (strcmp(semantic, "transition_count") == 0)
2012-04-21 21:25:32 +00:00
tracker_type = RARCH_STATE_TRANSITION_COUNT;
else if (strcmp(semantic, "transition_previous") == 0)
2012-04-21 21:25:32 +00:00
tracker_type = RARCH_STATE_TRANSITION_PREV;
2011-06-06 16:50:36 +00:00
#ifdef HAVE_PYTHON
else if (strcmp(semantic, "python") == 0)
2012-04-21 21:25:32 +00:00
tracker_type = RARCH_STATE_PYTHON;
2011-06-06 16:50:36 +00:00
#endif
else
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Invalid semantic for import value.\n");
return false;
}
2011-06-06 16:50:36 +00:00
#ifdef HAVE_PYTHON
2012-04-21 21:25:32 +00:00
if (tracker_type != RARCH_STATE_PYTHON)
2011-06-06 16:50:36 +00:00
#endif
{
if (*input)
2011-06-11 18:02:17 +00:00
{
unsigned slot = strtoul(input, NULL, 0);
2011-06-11 18:02:17 +00:00
switch (slot)
{
case 1:
2012-04-21 21:25:32 +00:00
ram_type = RARCH_STATE_INPUT_SLOT1;
2011-06-11 18:02:17 +00:00
break;
case 2:
2012-04-21 21:25:32 +00:00
ram_type = RARCH_STATE_INPUT_SLOT2;
2011-06-11 18:02:17 +00:00
break;
default:
2012-04-21 21:25:32 +00:00
RARCH_ERR("Invalid input slot for import.\n");
return false;
2011-06-11 18:02:17 +00:00
}
}
else if (*wram)
2012-04-07 10:17:40 +00:00
{
addr = strtoul(wram, NULL, 16);
2012-04-21 21:25:32 +00:00
ram_type = RARCH_STATE_WRAM;
2012-04-07 10:17:40 +00:00
}
2011-06-06 16:50:36 +00:00
else
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("No RAM address specificed for import value.\n");
return false;
2011-06-06 16:50:36 +00:00
}
}
switch (ram_type)
{
2012-04-21 21:25:32 +00:00
case RARCH_STATE_WRAM:
2012-04-07 10:17:40 +00:00
memtype = RETRO_MEMORY_SYSTEM_RAM;
break;
2011-06-06 16:50:36 +00:00
default:
memtype = -1u;
}
2012-04-07 10:17:40 +00:00
if ((memtype != -1u) && (addr >= pretro_get_memory_size(memtype)))
2011-05-28 13:06:46 +00:00
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Address out of bounds.\n");
return false;
2011-05-28 13:06:46 +00:00
}
if (*bitmask)
mask_value = strtoul(bitmask, NULL, 16);
if (*bitequal)
mask_equal = strtoul(bitequal, NULL, 16);
2011-05-29 21:58:04 +00:00
strlcpy(gl_tracker_info[gl_tracker_info_cnt].id, id, sizeof(gl_tracker_info[0].id));
gl_tracker_info[gl_tracker_info_cnt].addr = addr;
gl_tracker_info[gl_tracker_info_cnt].type = tracker_type;
gl_tracker_info[gl_tracker_info_cnt].ram_type = ram_type;
2011-05-29 21:58:04 +00:00
gl_tracker_info[gl_tracker_info_cnt].mask = mask_value;
2012-02-12 17:05:33 +00:00
gl_tracker_info[gl_tracker_info_cnt].equal = mask_equal;
gl_tracker_info_cnt++;
return true;
}
2011-03-12 14:30:57 +00:00
static unsigned get_xml_shaders(const char *path, struct shader_program *prog, size_t size)
2011-01-05 18:07:12 +00:00
{
LIBXML_TEST_VERSION;
xmlParserCtxtPtr ctx = xmlNewParserCtxt();
if (!ctx)
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Failed to load libxml2 context.\n");
2011-01-05 18:07:12 +00:00
return false;
}
2012-04-21 21:25:32 +00:00
RARCH_LOG("Loading XML shader: %s\n", path);
2011-01-05 18:07:12 +00:00
xmlDocPtr doc = xmlCtxtReadFile(ctx, path, NULL, 0);
2011-12-24 12:46:12 +00:00
xmlNodePtr head = NULL;
xmlNodePtr cur = NULL;
unsigned num = 0;
2011-01-05 18:07:12 +00:00
if (!doc)
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Failed to parse XML file: %s\n", path);
2011-01-05 18:07:12 +00:00
goto error;
}
#ifdef HAVE_LIBXML2
2011-01-05 18:07:12 +00:00
if (ctx->valid == 0)
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Cannot validate XML shader: %s\n", path);
2011-01-05 18:07:12 +00:00
goto error;
}
#endif
2011-01-05 18:07:12 +00:00
2011-12-24 12:46:12 +00:00
head = xmlDocGetRootElement(doc);
2011-01-05 18:07:12 +00:00
for (cur = head; cur; cur = cur->next)
{
if (cur->type != XML_ELEMENT_NODE)
continue;
if (strcmp((const char*)cur->name, "shader") != 0)
continue;
char attr[64];
xml_get_prop(attr, sizeof(attr), cur, "language");
if (strcmp(attr, "GLSL") != 0)
continue;
xml_get_prop(attr, sizeof(attr), cur, "style");
glsl_modern = strcmp(attr, "GLES2") == 0;
if (glsl_modern)
RARCH_LOG("[GL]: Shader reports a GLES2 style shader.\n");
break;
2011-01-05 18:07:12 +00:00
}
if (!cur) // We couldn't find any GLSL shader :(
goto error;
2011-03-12 14:30:57 +00:00
memset(prog, 0, sizeof(struct shader_program) * size);
2011-01-05 18:07:12 +00:00
// Iterate to check if we find fragment and/or vertex shaders.
2011-03-12 14:30:57 +00:00
for (cur = cur->children; cur && num < size; cur = cur->next)
2011-01-05 18:07:12 +00:00
{
if (cur->type != XML_ELEMENT_NODE)
continue;
char *content = xml_get_content(cur);
2011-01-05 18:07:12 +00:00
if (!content)
continue;
2011-03-12 14:30:57 +00:00
if (strcmp((const char*)cur->name, "vertex") == 0)
2011-01-05 18:07:12 +00:00
{
2011-03-12 14:30:57 +00:00
if (prog[num].vertex)
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Cannot have more than one vertex shader in a program.\n");
free(content);
2011-03-12 14:30:57 +00:00
goto error;
}
content = xml_replace_if_file(content, path, cur, "src");
if (!content)
{
RARCH_ERR("Shader source file was provided, but failed to read.\n");
goto error;
}
prog[num].vertex = content;
2011-01-05 18:07:12 +00:00
}
2011-03-12 14:30:57 +00:00
else if (strcmp((const char*)cur->name, "fragment") == 0)
2011-01-05 18:07:12 +00:00
{
2012-09-12 18:46:38 +00:00
if (glsl_modern && !prog[num].vertex)
{
RARCH_ERR("Modern GLSL was chosen and vertex shader was not provided. This is an error.\n");
free(content);
goto error;
}
content = xml_replace_if_file(content, path, cur, "src");
if (!content)
{
RARCH_ERR("Shader source file was provided, but failed to read.\n");
2012-09-12 18:46:38 +00:00
goto error;
}
prog[num].fragment = content;
if (!get_xml_attrs(&prog[num], cur))
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("XML shader attributes do not comply with specifications.\n");
goto error;
}
2011-03-12 14:30:57 +00:00
num++;
2011-01-05 18:07:12 +00:00
}
2011-05-18 18:22:27 +00:00
else if (strcmp((const char*)cur->name, "texture") == 0)
{
free(content);
2011-05-18 18:22:27 +00:00
if (!get_texture_image(path, cur))
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Texture image failed to load.\n");
2011-05-18 18:22:27 +00:00
goto error;
}
}
else if (strcmp((const char*)cur->name, "import") == 0)
{
free(content);
2011-06-07 14:28:58 +00:00
if (!get_import_value(cur))
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Import value is invalid.\n");
goto error;
}
}
2011-06-07 14:28:58 +00:00
#ifdef HAVE_PYTHON
2011-06-07 13:33:29 +00:00
else if (strcmp((const char*)cur->name, "script") == 0)
{
free(content);
2011-06-07 14:28:58 +00:00
if (!get_script(path, cur))
2011-06-07 13:33:29 +00:00
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Script is invalid.\n");
2011-06-07 13:33:29 +00:00
goto error;
}
}
2011-06-07 14:28:58 +00:00
#endif
2011-01-05 18:07:12 +00:00
}
2011-03-12 14:30:57 +00:00
if (num == 0)
2011-01-05 18:07:12 +00:00
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Couldn't find vertex shader nor fragment shader in XML file.\n");
2011-01-05 18:07:12 +00:00
goto error;
}
xmlFreeDoc(doc);
xmlFreeParserCtxt(ctx);
2011-03-12 14:30:57 +00:00
return num;
2011-01-05 18:07:12 +00:00
error:
2012-04-21 21:25:32 +00:00
RARCH_ERR("Failed to load XML shader ...\n");
2011-01-05 18:07:12 +00:00
if (doc)
xmlFreeDoc(doc);
xmlFreeParserCtxt(ctx);
2011-03-12 14:30:57 +00:00
return 0;
2011-01-05 18:07:12 +00:00
}
static void print_shader_log(GLuint obj)
{
2011-06-29 01:26:21 +00:00
GLint info_len = 0;
GLint max_len;
pglGetShaderiv(obj, GL_INFO_LOG_LENGTH, &max_len);
2011-12-24 12:46:12 +00:00
if (max_len == 0)
return;
char *info_log = (char*)malloc(max_len);
if (!info_log)
return;
pglGetShaderInfoLog(obj, max_len, &info_len, info_log);
if (info_len > 0)
2012-04-21 21:25:32 +00:00
RARCH_LOG("Shader log: %s\n", info_log);
2011-12-24 12:46:12 +00:00
free(info_log);
}
static void print_linker_log(GLuint obj)
{
2011-06-29 01:26:21 +00:00
GLint info_len = 0;
GLint max_len;
pglGetProgramiv(obj, GL_INFO_LOG_LENGTH, &max_len);
2011-12-24 12:46:12 +00:00
if (max_len == 0)
return;
char *info_log = (char*)malloc(max_len);
if (!info_log)
return;
pglGetProgramInfoLog(obj, max_len, &info_len, info_log);
if (info_len > 0)
2012-04-21 21:25:32 +00:00
RARCH_LOG("Linker log: %s\n", info_log);
2011-12-24 12:46:12 +00:00
free(info_log);
}
2011-06-02 11:49:22 +00:00
static bool compile_shader(GLuint shader, const char *program)
{
pglShaderSource(shader, 1, &program, 0);
pglCompileShader(shader);
GLint status;
pglGetShaderiv(shader, GL_COMPILE_STATUS, &status);
print_shader_log(shader);
return status == GL_TRUE;
}
2011-06-07 13:58:30 +00:00
static bool link_program(GLuint prog)
{
pglLinkProgram(prog);
GLint status;
pglGetProgramiv(prog, GL_LINK_STATUS, &status);
print_linker_log(prog);
if (status == GL_TRUE)
{
pglUseProgram(prog);
return true;
}
else
return false;
}
2011-03-29 22:10:16 +00:00
static bool compile_programs(GLuint *gl_prog, struct shader_program *progs, size_t num)
2011-01-05 18:07:12 +00:00
{
bool ret = true;
2011-03-12 14:30:57 +00:00
for (unsigned i = 0; i < num; i++)
{
gl_prog[i] = pglCreateProgram();
2011-06-07 13:58:30 +00:00
if (gl_prog[i] == 0)
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Failed to create GL program #%u.\n", i);
ret = false;
goto end;
}
2011-03-12 14:30:57 +00:00
if (progs[i].vertex)
{
2012-04-21 21:25:32 +00:00
RARCH_LOG("Found GLSL vertex shader.\n");
2011-03-12 14:30:57 +00:00
GLuint shader = pglCreateShader(GL_VERTEX_SHADER);
2011-06-02 11:49:22 +00:00
if (!compile_shader(shader, progs[i].vertex))
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Failed to compile vertex shader #%u\n", i);
ret = false;
goto end;
2011-06-02 11:49:22 +00:00
}
2011-03-12 14:30:57 +00:00
pglAttachShader(gl_prog[i], shader);
}
2011-03-12 15:33:01 +00:00
if (progs[i].fragment)
2011-03-12 14:30:57 +00:00
{
2012-04-21 21:25:32 +00:00
RARCH_LOG("Found GLSL fragment shader.\n");
2011-03-12 14:30:57 +00:00
GLuint shader = pglCreateShader(GL_FRAGMENT_SHADER);
2011-06-02 11:49:22 +00:00
if (!compile_shader(shader, progs[i].fragment))
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Failed to compile fragment shader #%u\n", i);
ret = false;
goto end;
2011-06-02 11:49:22 +00:00
}
2011-03-12 14:30:57 +00:00
pglAttachShader(gl_prog[i], shader);
}
if (progs[i].vertex || progs[i].fragment)
{
2012-04-21 21:25:32 +00:00
RARCH_LOG("Linking GLSL program.\n");
2011-06-07 13:58:30 +00:00
if (!link_program(gl_prog[i]))
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Failed to link program #%u\n", i);
ret = false;
goto end;
2011-06-07 13:58:30 +00:00
}
2011-03-12 14:30:57 +00:00
GLint location = pglGetUniformLocation(gl_prog[i], "rubyTexture");
pglUniform1i(location, 0);
pglUseProgram(0);
}
}
2011-03-29 22:10:16 +00:00
end:
for (unsigned i = 0; i < num; i++)
{
free(progs[i].vertex);
free(progs[i].fragment);
progs[i].vertex = NULL;
progs[i].fragment = NULL;
}
return ret;
2011-03-12 14:30:57 +00:00
}
2011-02-22 22:43:00 +00:00
static void gl_glsl_reset_attrib(void)
{
for (unsigned i = 0; i < gl_attrib_index; i++)
pglDisableVertexAttribArray(gl_attribs[i]);
gl_attrib_index = 0;
}
2012-10-21 00:58:51 +00:00
static void find_uniforms_frame(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];
snprintf(texture, sizeof(texture), "%s%s", base, "Texture");
snprintf(texture_size, sizeof(texture_size), "%s%s", base, "TextureSize");
snprintf(input_size, sizeof(input_size), "%s%s", base, "InputSize");
snprintf(tex_coord, sizeof(tex_coord), "%s%s", base, "TexCoord");
frame->texture = pglGetUniformLocation(prog, texture);
frame->texture_size = pglGetUniformLocation(prog, texture_size);
frame->input_size = pglGetUniformLocation(prog, input_size);
frame->tex_coord = pglGetAttribLocation(prog, tex_coord);
}
static void find_uniforms(GLuint prog, struct shader_uniforms *uni)
{
pglUseProgram(prog);
uni->mvp = pglGetUniformLocation(prog, "rubyMVPMatrix");
uni->tex_coord = pglGetAttribLocation(prog, "rubyTexCoord");
uni->vertex_coord = pglGetAttribLocation(prog, "rubyVertexCoord");
uni->color = pglGetAttribLocation(prog, "rubyColor");
uni->lut_tex_coord = pglGetAttribLocation(prog, "rubyLUTTexCoord");
uni->input_size = pglGetUniformLocation(prog, "rubyInputSize");
uni->output_size = pglGetUniformLocation(prog, "rubyOutputSize");
uni->texture_size = pglGetUniformLocation(prog, "rubyTextureSize");
uni->frame_count = pglGetUniformLocation(prog, "rubyFrameCount");
uni->frame_direction = pglGetUniformLocation(prog, "rubyFrameDirection");
for (unsigned i = 0; i < gl_teximage_cnt; i++)
uni->lut_texture[i] = pglGetUniformLocation(prog, gl_teximage_uniforms[i]);
find_uniforms_frame(prog, &uni->orig, "rubyOrig");
char frame_base[64];
for (unsigned i = 0; i < RARCH_GLSL_MAX_SHADERS; i++)
2012-10-21 00:58:51 +00:00
{
snprintf(frame_base, sizeof(frame_base), "rubyPass%u", i + 1);
find_uniforms_frame(prog, &uni->pass[i], frame_base);
}
find_uniforms_frame(prog, &uni->prev[0], "rubyPrev");
for (unsigned i = 1; i < PREV_TEXTURES; i++)
{
snprintf(frame_base, sizeof(frame_base), "rubyPrev%u", i);
find_uniforms_frame(prog, &uni->prev[i], frame_base);
}
pglUseProgram(0);
}
static void gl_glsl_delete_shader(GLuint prog)
{
GLsizei count;
GLuint shaders[2] = {0};
pglGetAttachedShaders(prog, 2, &count, shaders);
for (GLsizei i = 0; i < count; i++)
{
pglDetachShader(prog, shaders[i]);
pglDeleteShader(shaders[i]);
}
pglDeleteProgram(prog);
}
static bool gl_glsl_load_shader(unsigned index, const char *path)
{
pglUseProgram(0);
if (gl_program[index] != gl_program[0])
{
gl_glsl_delete_shader(gl_program[index]);
gl_program[index] = 0;
}
if (path)
{
struct shader_program prog = {0};
unsigned progs = get_xml_shaders(path, &prog, 1);
if (progs != 1)
return false;
if (!compile_programs(&gl_program[index], &prog, 1))
{
RARCH_ERR("Failed to compile shader: %s.\n", path);
return false;
}
find_uniforms(gl_program[index], &gl_uniforms[index]);
}
else
{
gl_program[index] = gl_program[0];
gl_uniforms[index] = gl_uniforms[0];
}
pglUseProgram(gl_program[active_index]);
return true;
}
// Platforms with broken get_proc_address.
// Assume functions are available without proc_address.
#undef LOAD_GL_SYM
#define LOAD_GL_SYM(SYM) if (!pgl##SYM) { \
2012-09-24 23:26:22 +00:00
gfx_ctx_proc_t sym = glsl_get_proc_address("gl" #SYM); \
memcpy(&(pgl##SYM), &sym, sizeof(sym)); \
}
2011-03-12 14:30:57 +00:00
bool gl_glsl_init(const char *path)
{
2012-10-12 19:24:23 +00:00
#if !defined(HAVE_OPENGLES2) && !defined(HAVE_OPENGL_MODERN) && !defined(__APPLE__)
2011-01-05 18:07:12 +00:00
// Load shader functions.
LOAD_GL_SYM(CreateProgram);
LOAD_GL_SYM(UseProgram);
LOAD_GL_SYM(CreateShader);
LOAD_GL_SYM(DeleteShader);
LOAD_GL_SYM(ShaderSource);
LOAD_GL_SYM(CompileShader);
LOAD_GL_SYM(AttachShader);
LOAD_GL_SYM(DetachShader);
LOAD_GL_SYM(LinkProgram);
LOAD_GL_SYM(GetUniformLocation);
LOAD_GL_SYM(Uniform1i);
LOAD_GL_SYM(Uniform1f);
LOAD_GL_SYM(Uniform2fv);
LOAD_GL_SYM(Uniform4fv);
LOAD_GL_SYM(UniformMatrix4fv);
LOAD_GL_SYM(GetShaderiv);
LOAD_GL_SYM(GetShaderInfoLog);
LOAD_GL_SYM(GetProgramiv);
LOAD_GL_SYM(GetProgramInfoLog);
LOAD_GL_SYM(DeleteProgram);
LOAD_GL_SYM(GetAttachedShaders);
LOAD_GL_SYM(GetAttribLocation);
LOAD_GL_SYM(EnableVertexAttribArray);
LOAD_GL_SYM(DisableVertexAttribArray);
LOAD_GL_SYM(VertexAttribPointer);
2011-01-05 18:07:12 +00:00
2012-04-21 21:25:32 +00:00
RARCH_LOG("Checking GLSL shader support ...\n");
2011-01-07 20:02:46 +00:00
bool shader_support = pglCreateProgram && pglUseProgram && pglCreateShader
&& pglDeleteShader && pglShaderSource && pglCompileShader && pglAttachShader
&& pglDetachShader && pglLinkProgram && pglGetUniformLocation
&& pglUniform1i && pglUniform1f && pglUniform2fv && pglUniform4fv && pglUniformMatrix4fv
&& pglGetShaderiv && pglGetShaderInfoLog && pglGetProgramiv && pglGetProgramInfoLog
&& pglDeleteProgram && pglGetAttachedShaders
&& pglGetAttribLocation && pglEnableVertexAttribArray && pglDisableVertexAttribArray
&& pglVertexAttribPointer;
2011-01-05 18:07:12 +00:00
if (!shader_support)
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("GLSL shaders aren't supported by your OpenGL driver.\n");
2011-01-05 18:07:12 +00:00
return false;
}
2012-09-15 13:17:34 +00:00
#endif
2011-01-05 18:07:12 +00:00
2012-09-15 13:37:08 +00:00
unsigned num_progs = 0;
struct shader_program progs[RARCH_GLSL_MAX_SHADERS] = {{0}};
2012-09-15 13:37:08 +00:00
if (path)
2011-01-05 18:07:12 +00:00
{
num_progs = get_xml_shaders(path, progs, RARCH_GLSL_MAX_SHADERS - 1);
2012-09-15 13:37:08 +00:00
if (num_progs == 0)
{
RARCH_ERR("Couldn't find any valid shaders in XML file.\n");
return false;
}
2011-03-12 14:30:57 +00:00
}
2012-09-15 13:37:08 +00:00
else
{
RARCH_WARN("[GL]: Stock GLSL shaders will be used.\n");
num_progs = 1;
progs[0].vertex = strdup(stock_vertex_modern);
progs[0].fragment = strdup(stock_fragment_modern);
glsl_modern = true;
}
2011-01-05 18:07:12 +00:00
2012-09-15 13:46:02 +00:00
#ifdef HAVE_OPENGLES2
if (!glsl_modern)
{
RARCH_ERR("[GL]: GLES context is used, but shader is not modern. Cannot use it.\n");
return false;
}
#endif
struct shader_program stock_prog = {0};
stock_prog.vertex = strdup(glsl_modern ? stock_vertex_modern : stock_vertex_legacy);
stock_prog.fragment = strdup(glsl_modern ? stock_fragment_modern : stock_fragment_legacy);
if (!compile_programs(&gl_program[0], &stock_prog, 1))
{
RARCH_ERR("GLSL stock programs failed to compile.\n");
return false;
}
2011-03-14 20:28:30 +00:00
for (unsigned i = 0; i < num_progs; i++)
{
gl_filter_type[i + 1] = progs[i].filter;
gl_scale[i + 1].type_x = progs[i].type_x;
gl_scale[i + 1].type_y = progs[i].type_y;
gl_scale[i + 1].scale_x = progs[i].scale_x;
gl_scale[i + 1].scale_y = progs[i].scale_y;
gl_scale[i + 1].abs_x = progs[i].abs_x;
gl_scale[i + 1].abs_y = progs[i].abs_y;
gl_scale[i + 1].valid = progs[i].valid_scale;
}
2011-03-14 20:28:30 +00:00
2011-03-29 22:10:16 +00:00
if (!compile_programs(&gl_program[1], progs, num_progs))
return false;
2011-03-06 18:56:35 +00:00
2012-04-21 21:13:50 +00:00
// RetroArch custom two-pass with two different files.
if (num_progs == 1 && *g_settings.video.second_pass_shader && g_settings.video.render_to_texture)
2011-03-12 14:30:57 +00:00
{
unsigned secondary_progs = get_xml_shaders(g_settings.video.second_pass_shader, progs, 1);
if (secondary_progs == 1)
2011-03-06 18:56:35 +00:00
{
if (!compile_programs(&gl_program[2], progs, 1))
{
RARCH_ERR("Failed to compile second pass shader.\n");
return false;
}
2011-03-12 14:30:57 +00:00
num_progs++;
2011-03-06 18:56:35 +00:00
}
2011-03-12 14:30:57 +00:00
else
2011-03-06 18:56:35 +00:00
{
RARCH_ERR("Did not find exactly one valid shader in secondary shader file.\n");
2011-03-12 14:30:57 +00:00
return false;
2011-03-06 18:56:35 +00:00
}
2011-01-05 18:07:12 +00:00
}
2012-10-21 00:58:51 +00:00
for (unsigned i = 0; i <= num_progs; i++)
find_uniforms(gl_program[i], &gl_uniforms[i]);
#ifdef GLSL_DEBUG
if (!gl_check_error())
2012-10-21 00:58:51 +00:00
RARCH_WARN("Detected GL error in GLSL.\n");
#endif
if (gl_tracker_info_cnt > 0)
{
2012-04-07 10:17:40 +00:00
struct state_tracker_info info = {0};
info.wram = (uint8_t*)pretro_get_memory_data(RETRO_MEMORY_SYSTEM_RAM);
info.info = gl_tracker_info;
2011-12-24 12:46:12 +00:00
info.info_elem = gl_tracker_info_cnt;
2011-06-07 13:33:29 +00:00
2011-06-06 16:50:36 +00:00
#ifdef HAVE_PYTHON
2012-11-18 20:23:34 +00:00
info.script = gl_script_program;
info.script_class = *gl_tracker_script_class ? gl_tracker_script_class : NULL;
2012-11-18 20:23:34 +00:00
info.script_is_file = false;
2011-06-06 16:50:36 +00:00
#endif
2011-06-07 13:33:29 +00:00
2012-04-07 10:17:40 +00:00
gl_state_tracker = state_tracker_init(&info);
if (!gl_state_tracker)
2012-04-21 21:25:32 +00:00
RARCH_WARN("Failed to init state tracker.\n");
}
2012-10-21 00:58:51 +00:00
glsl_enable = true;
gl_num_programs = num_progs;
gl_program[gl_num_programs + 1] = gl_program[0];
gl_uniforms[gl_num_programs + 1] = gl_uniforms[0];
gl_glsl_reset_attrib();
2011-01-05 18:07:12 +00:00
return true;
}
void gl_glsl_deinit(void)
{
if (glsl_enable)
{
pglUseProgram(0);
2011-12-24 12:46:12 +00:00
for (unsigned i = 0; i <= gl_num_programs; i++)
{
if (gl_program[i] == 0 || (i && gl_program[i] == gl_program[0]))
2011-11-12 14:26:24 +00:00
continue;
gl_glsl_delete_shader(gl_program[i]);
}
2011-05-18 18:22:27 +00:00
glDeleteTextures(gl_teximage_cnt, gl_teximage);
gl_teximage_cnt = 0;
memset(gl_teximage_uniforms, 0, sizeof(gl_teximage_uniforms));
}
memset(gl_program, 0, sizeof(gl_program));
glsl_enable = false;
active_index = 0;
gl_tracker_info_cnt = 0;
memset(gl_tracker_info, 0, sizeof(gl_tracker_info));
2011-06-06 16:50:36 +00:00
memset(gl_tracker_script_class, 0, sizeof(gl_tracker_script_class));
2012-11-18 20:23:34 +00:00
free(gl_script_program);
gl_script_program = NULL;
2012-04-07 10:17:40 +00:00
if (gl_state_tracker)
{
2012-04-07 10:17:40 +00:00
state_tracker_free(gl_state_tracker);
gl_state_tracker = NULL;
}
gl_glsl_reset_attrib();
}
2011-01-05 18:07:12 +00:00
void gl_glsl_set_params(unsigned width, unsigned height,
unsigned tex_width, unsigned tex_height,
unsigned out_width, unsigned out_height,
unsigned frame_count,
const struct gl_tex_info *info,
const struct gl_tex_info *prev_info,
const struct gl_tex_info *fbo_info, unsigned fbo_info_cnt)
2011-01-05 18:07:12 +00:00
{
// We enforce a certain layout for our various texture types in the texunits.
2012-09-15 21:15:19 +00:00
// - Regular frame (rubyTexture) (always bound).
2011-11-12 13:18:10 +00:00
// - LUT textures (always bound).
// - Original texture (always bound if meaningful).
// - FBO textures (always bound if available).
// - Previous textures.
2011-11-12 13:18:10 +00:00
if (!glsl_enable || (gl_program[active_index] == 0))
return;
2011-01-05 18:07:12 +00:00
2012-10-21 00:58:51 +00:00
const struct shader_uniforms *uni = &gl_uniforms[active_index];
float input_size[2] = {(float)width, (float)height};
float output_size[2] = {(float)out_width, (float)out_height};
float texture_size[2] = {(float)tex_width, (float)tex_height};
2011-01-05 18:07:12 +00:00
2012-10-21 00:58:51 +00:00
if (uni->input_size >= 0)
pglUniform2fv(uni->input_size, 1, input_size);
2011-01-05 18:07:12 +00:00
2012-10-21 00:58:51 +00:00
if (uni->output_size >= 0)
pglUniform2fv(uni->output_size, 1, output_size);
2012-10-21 00:58:51 +00:00
if (uni->texture_size >= 0)
pglUniform2fv(uni->texture_size, 1, texture_size);
2011-05-18 18:22:27 +00:00
2012-10-21 00:58:51 +00:00
if (uni->frame_count >= 0)
pglUniform1i(uni->frame_count, frame_count);
2012-10-21 00:58:51 +00:00
if (uni->frame_direction >= 0)
pglUniform1i(uni->frame_direction, g_extern.frame_is_reverse ? -1 : 1);
2011-11-12 13:18:10 +00:00
for (unsigned i = 0; i < gl_teximage_cnt; i++)
{
2012-10-21 00:58:51 +00:00
if (uni->lut_texture[i] >= 0)
pglUniform1i(uni->lut_texture[i], i + 1);
2011-11-12 13:18:10 +00:00
}
2011-11-12 13:18:10 +00:00
unsigned texunit = gl_teximage_cnt + 1;
2011-11-12 13:18:10 +00:00
// Set original texture unless we're in first pass (pointless).
if (active_index > 1)
{
2012-10-21 00:58:51 +00:00
if (uni->orig.texture >= 0)
{
// Bind original texture.
pglActiveTexture(GL_TEXTURE0 + texunit);
pglUniform1i(uni->orig.texture, texunit);
glBindTexture(GL_TEXTURE_2D, info->tex);
}
texunit++;
2011-11-12 13:18:10 +00:00
2012-10-21 00:58:51 +00:00
if (uni->orig.texture_size >= 0)
pglUniform2fv(uni->orig.texture_size, 1, info->tex_size);
2011-11-12 13:18:10 +00:00
2012-10-21 00:58:51 +00:00
if (uni->orig.input_size >= 0)
pglUniform2fv(uni->orig.input_size, 1, info->input_size);
// Pass texture coordinates.
2012-10-21 00:58:51 +00:00
if (uni->orig.tex_coord >= 0)
{
2012-10-21 00:58:51 +00:00
int loc = uni->orig.tex_coord;
pglEnableVertexAttribArray(loc);
pglVertexAttribPointer(loc, 2, GL_FLOAT, GL_FALSE, 0, info->coord);
gl_attribs[gl_attrib_index++] = loc;
}
2011-11-12 13:18:10 +00:00
// Bind new texture in the chain.
if (fbo_info_cnt > 0)
{
2011-11-12 13:18:10 +00:00
pglActiveTexture(GL_TEXTURE0 + texunit + fbo_info_cnt - 1);
glBindTexture(GL_TEXTURE_2D, fbo_info[fbo_info_cnt - 1].tex);
}
2011-11-12 13:18:10 +00:00
// Bind FBO textures.
for (unsigned i = 0; i < fbo_info_cnt; i++)
{
2012-10-21 00:58:51 +00:00
if (uni->pass[i].texture)
pglUniform1i(uni->pass[i].texture, texunit);
2012-10-21 00:58:51 +00:00
texunit++;
2012-10-21 00:58:51 +00:00
if (uni->pass[i].texture_size >= 0)
pglUniform2fv(uni->pass[i].texture_size, 1, fbo_info[i].tex_size);
2012-10-21 00:58:51 +00:00
if (uni->pass[i].input_size >= 0)
pglUniform2fv(uni->pass[i].input_size, 1, fbo_info[i].input_size);
2012-10-21 00:58:51 +00:00
if (uni->pass[i].tex_coord >= 0)
{
2012-10-21 00:58:51 +00:00
int loc = uni->pass[i].tex_coord;
pglEnableVertexAttribArray(loc);
pglVertexAttribPointer(loc, 2, GL_FLOAT, GL_FALSE, 0, fbo_info[i].coord);
gl_attribs[gl_attrib_index++] = loc;
}
}
2011-11-12 13:18:10 +00:00
}
else
{
// First pass, so unbind everything to avoid collitions.
// Unbind ORIG.
pglActiveTexture(GL_TEXTURE0 + texunit);
glBindTexture(GL_TEXTURE_2D, 0);
GLuint base_tex = texunit + 1;
// Unbind any lurking FBO passes.
// Rendering to a texture that is bound to a texture unit
// sounds very shaky ... ;)
for (unsigned i = 0; i < gl_num_programs; i++)
{
2011-11-12 13:18:10 +00:00
pglActiveTexture(GL_TEXTURE0 + base_tex + i);
glBindTexture(GL_TEXTURE_2D, 0);
2011-11-12 13:18:10 +00:00
}
}
2011-11-12 14:51:37 +00:00
// Set previous textures. Only bind if they're actually used.
2011-11-12 13:18:10 +00:00
for (unsigned i = 0; i < PREV_TEXTURES; i++)
{
2012-10-21 00:58:51 +00:00
if (uni->prev[i].texture >= 0)
2011-11-12 13:18:10 +00:00
{
pglActiveTexture(GL_TEXTURE0 + texunit);
glBindTexture(GL_TEXTURE_2D, prev_info[i].tex);
2012-10-21 00:58:51 +00:00
pglUniform1i(uni->prev[i].texture, texunit++);
}
2012-10-21 00:58:51 +00:00
texunit++;
if (uni->prev[i].texture_size >= 0)
pglUniform2fv(uni->prev[i].texture_size, 1, prev_info[i].tex_size);
if (uni->prev[i].input_size >= 0)
pglUniform2fv(uni->prev[i].input_size, 1, prev_info[i].input_size);
2011-11-12 13:18:10 +00:00
// Pass texture coordinates.
2012-10-21 00:58:51 +00:00
if (uni->prev[i].tex_coord >= 0)
{
2012-10-21 00:58:51 +00:00
int loc = uni->prev[i].tex_coord;
pglEnableVertexAttribArray(loc);
pglVertexAttribPointer(loc, 2, GL_FLOAT, GL_FALSE, 0, prev_info[i].coord);
gl_attribs[gl_attrib_index++] = loc;
2011-11-12 13:18:10 +00:00
}
}
2011-11-12 13:18:10 +00:00
pglActiveTexture(GL_TEXTURE0);
2012-04-07 10:17:40 +00:00
if (gl_state_tracker)
2011-11-12 13:18:10 +00:00
{
2012-04-07 10:17:40 +00:00
static struct state_tracker_uniform info[MAX_VARIABLES];
2011-11-12 13:18:10 +00:00
static unsigned cnt = 0;
if (active_index == 1)
2012-04-07 10:17:40 +00:00
cnt = state_get_uniform(gl_state_tracker, info, MAX_VARIABLES, frame_count);
2011-11-12 13:18:10 +00:00
for (unsigned i = 0; i < cnt; i++)
{
2012-10-21 00:58:51 +00:00
int location = pglGetUniformLocation(gl_program[active_index], info[i].id);
2011-11-12 13:18:10 +00:00
pglUniform1f(location, info[i].value);
}
2011-01-05 18:07:12 +00:00
}
}
bool gl_glsl_set_mvp(const math_matrix *mat)
{
if (!glsl_enable || !glsl_modern)
return false;
2012-10-21 00:58:51 +00:00
int loc = gl_uniforms[active_index].mvp;
if (loc >= 0)
pglUniformMatrix4fv(loc, 1, GL_FALSE, mat->data);
2012-10-21 00:58:51 +00:00
return true;
}
bool gl_glsl_set_coords(const struct gl_coords *coords)
{
if (!glsl_enable || !glsl_modern)
return false;
2012-10-21 00:58:51 +00:00
const struct shader_uniforms *uni = &gl_uniforms[active_index];
if (uni->tex_coord >= 0)
{
2012-10-21 00:58:51 +00:00
int loc = uni->tex_coord;
pglEnableVertexAttribArray(loc);
pglVertexAttribPointer(loc, 2, GL_FLOAT, GL_FALSE, 0, coords->tex_coord);
gl_attribs[gl_attrib_index++] = loc;
}
2012-10-21 00:58:51 +00:00
if (uni->vertex_coord >= 0)
{
2012-10-21 00:58:51 +00:00
int loc = uni->vertex_coord;
pglEnableVertexAttribArray(loc);
pglVertexAttribPointer(loc, 2, GL_FLOAT, GL_FALSE, 0, coords->vertex);
gl_attribs[gl_attrib_index++] = loc;
}
2012-10-21 00:58:51 +00:00
if (uni->color >= 0)
{
2012-10-21 00:58:51 +00:00
int loc = uni->color;
pglEnableVertexAttribArray(loc);
pglVertexAttribPointer(loc, 4, GL_FLOAT, GL_FALSE, 0, coords->color);
gl_attribs[gl_attrib_index++] = loc;
}
2012-10-21 00:58:51 +00:00
if (uni->lut_tex_coord >= 0)
{
2012-10-21 00:58:51 +00:00
int loc = uni->lut_tex_coord;
pglEnableVertexAttribArray(loc);
pglVertexAttribPointer(loc, 2, GL_FLOAT, GL_FALSE, 0, coords->lut_tex_coord);
gl_attribs[gl_attrib_index++] = loc;
}
return true;
}
2011-03-06 18:56:35 +00:00
void gl_glsl_use(unsigned index)
{
if (glsl_enable)
2011-03-06 18:56:35 +00:00
{
gl_glsl_reset_attrib();
2011-03-06 18:56:35 +00:00
active_index = index;
pglUseProgram(gl_program[index]);
}
}
2011-03-12 14:30:57 +00:00
unsigned gl_glsl_num(void)
{
return gl_num_programs;
}
2011-03-14 20:28:30 +00:00
bool gl_glsl_filter_type(unsigned index, bool *smooth)
{
if (!glsl_enable)
return false;
switch (gl_filter_type[index])
{
2012-04-21 21:25:32 +00:00
case RARCH_GL_NOFORCE:
2011-03-14 20:28:30 +00:00
return false;
2012-04-21 21:25:32 +00:00
case RARCH_GL_NEAREST:
2011-03-14 20:28:30 +00:00
*smooth = false;
return true;
2012-04-21 21:25:32 +00:00
case RARCH_GL_LINEAR:
2011-03-14 20:28:30 +00:00
*smooth = true;
return true;
default:
return false;
}
}
void gl_glsl_shader_scale(unsigned index, struct gl_fbo_scale *scale)
{
if (glsl_enable)
*scale = gl_scale[index];
else
scale->valid = false;
}
2012-09-24 23:26:22 +00:00
void gl_glsl_set_get_proc_address(gfx_ctx_proc_t (*proc)(const char*))
{
glsl_get_proc_address = proc;
}
const gl_shader_backend_t gl_glsl_backend = {
gl_glsl_init,
gl_glsl_deinit,
gl_glsl_set_params,
gl_glsl_use,
gl_glsl_num,
gl_glsl_filter_type,
gl_glsl_shader_scale,
gl_glsl_set_coords,
gl_glsl_set_mvp,
gl_glsl_load_shader,
RARCH_SHADER_GLSL,
};