2011-01-17 19:54:58 +00:00
|
|
|
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
2011-01-23 19:29:28 +00:00
|
|
|
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
2011-01-05 18:07:12 +00:00
|
|
|
*
|
|
|
|
* Some code herein may be based on code found in BSNES.
|
|
|
|
*
|
|
|
|
* SSNES 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.
|
|
|
|
*
|
|
|
|
* SSNES 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 SSNES.
|
|
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "general.h"
|
|
|
|
|
2011-02-04 12:46:51 +00:00
|
|
|
#ifdef __APPLE__
|
|
|
|
#include <OpenGL/gl.h>
|
|
|
|
#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
|
|
|
|
|
|
|
|
|
|
|
|
#define NO_SDL_GLEXT
|
2011-01-07 17:11:06 +00:00
|
|
|
#include "SDL.h"
|
|
|
|
#include "SDL_opengl.h"
|
2011-01-05 18:07:12 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <libxml/parser.h>
|
|
|
|
#include <libxml/tree.h>
|
2011-01-07 21:33:21 +00:00
|
|
|
|
2011-01-11 21:13:55 +00:00
|
|
|
#include "gl_common.h"
|
|
|
|
|
2011-02-22 22:43:00 +00:00
|
|
|
#ifdef __APPLE__
|
|
|
|
#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 pglUniform2fv glUniform2fv
|
|
|
|
#define pglUniform4fv glUniform4fv
|
|
|
|
#define pglGetShaderiv glGetShaderiv
|
|
|
|
#define pglGetShaderInfoLog glGetShaderInfoLog
|
|
|
|
#define pglGetProgramiv glGetProgramiv
|
|
|
|
#define pglGetProgramInfoLog glGetProgramInfoLog
|
|
|
|
#else
|
2011-01-07 20:02:46 +00:00
|
|
|
static PFNGLCREATEPROGRAMPROC pglCreateProgram = NULL;
|
|
|
|
static PFNGLUSEPROGRAMPROC pglUseProgram = NULL;
|
|
|
|
static PFNGLCREATESHADERPROC pglCreateShader = NULL;
|
|
|
|
static PFNGLDELETESHADERPROC pglDeleteShader = NULL;
|
|
|
|
static PFNGLSHADERSOURCEPROC pglShaderSource = NULL;
|
|
|
|
static PFNGLCOMPILESHADERPROC pglCompileShader = NULL;
|
|
|
|
static PFNGLATTACHSHADERPROC pglAttachShader = NULL;
|
|
|
|
static PFNGLDETACHSHADERPROC pglDetachShader = NULL;
|
|
|
|
static PFNGLLINKPROGRAMPROC pglLinkProgram = NULL;
|
|
|
|
static PFNGLGETUNIFORMLOCATIONPROC pglGetUniformLocation = NULL;
|
|
|
|
static PFNGLUNIFORM1IPROC pglUniform1i = NULL;
|
|
|
|
static PFNGLUNIFORM2FVPROC pglUniform2fv = NULL;
|
|
|
|
static PFNGLUNIFORM4FVPROC pglUniform4fv = NULL;
|
2011-01-07 20:41:11 +00:00
|
|
|
static PFNGLGETSHADERIVPROC pglGetShaderiv = NULL;
|
|
|
|
static PFNGLGETSHADERINFOLOGPROC pglGetShaderInfoLog = NULL;
|
2011-01-11 21:13:55 +00:00
|
|
|
static PFNGLGETPROGRAMIVPROC pglGetProgramiv = NULL;
|
|
|
|
static PFNGLGETPROGRAMINFOLOGPROC pglGetProgramInfoLog = NULL;
|
2011-02-22 22:43:00 +00:00
|
|
|
#endif
|
2011-01-05 18:07:12 +00:00
|
|
|
|
2011-03-12 14:30:57 +00:00
|
|
|
#define MAX_PROGRAMS 16
|
|
|
|
|
2011-01-05 18:07:12 +00:00
|
|
|
static bool glsl_enable = false;
|
2011-03-12 14:30:57 +00:00
|
|
|
static GLuint gl_program[MAX_PROGRAMS] = {0};
|
|
|
|
static unsigned gl_num_programs = 0;
|
2011-03-06 18:56:35 +00:00
|
|
|
static unsigned active_index = 0;
|
2011-01-05 18:07:12 +00:00
|
|
|
|
2011-03-12 14:30:57 +00:00
|
|
|
struct shader_program
|
|
|
|
{
|
|
|
|
char *vertex;
|
|
|
|
char *fragment;
|
|
|
|
};
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
SSNES_ERR("Failed to load libxml2 context.\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
SSNES_LOG("Loading XML shader: %s\n", path);
|
|
|
|
xmlDocPtr doc = xmlCtxtReadFile(ctx, path, NULL, 0);
|
|
|
|
if (!doc)
|
|
|
|
{
|
|
|
|
SSNES_ERR("Failed to parse XML file: %s\n", path);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx->valid == 0)
|
|
|
|
{
|
|
|
|
SSNES_ERR("Cannot validate XML shader: %s\n", path);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
xmlNodePtr head = xmlDocGetRootElement(doc);
|
|
|
|
xmlNodePtr cur = NULL;
|
|
|
|
for (cur = head; cur; cur = cur->next)
|
|
|
|
{
|
|
|
|
if (cur->type == XML_ELEMENT_NODE && strcmp((const char*)cur->name, "shader") == 0)
|
|
|
|
{
|
|
|
|
xmlChar *attr;
|
|
|
|
if ((attr = xmlGetProp(cur, (const xmlChar*)"language")) && strcmp((const char*)attr, "GLSL") == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cur) // We couldn't find any GLSL shader :(
|
|
|
|
goto error;
|
|
|
|
|
2011-03-12 14:30:57 +00:00
|
|
|
unsigned num = 0;
|
|
|
|
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;
|
|
|
|
|
|
|
|
xmlChar *content = xmlNodeGetContent(cur);
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
SSNES_ERR("Cannot have more than one vertex shader in a program.\n");
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
prog[num].vertex = strdup((const char*)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
|
|
|
{
|
2011-03-12 14:30:57 +00:00
|
|
|
prog[num].fragment = strdup((const char*)content);
|
|
|
|
num++;
|
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
|
|
|
{
|
|
|
|
SSNES_ERR("Couldn't find vertex shader nor fragment shader in XML file.\n");
|
|
|
|
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:
|
2011-01-07 17:35:44 +00:00
|
|
|
SSNES_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
|
|
|
}
|
|
|
|
|
2011-01-07 20:41:11 +00:00
|
|
|
static void print_shader_log(GLuint obj)
|
|
|
|
{
|
|
|
|
int info_len = 0;
|
|
|
|
int max_len;
|
|
|
|
|
|
|
|
pglGetShaderiv(obj, GL_INFO_LOG_LENGTH, &max_len);
|
|
|
|
|
|
|
|
char info_log[max_len];
|
|
|
|
pglGetShaderInfoLog(obj, max_len, &info_len, info_log);
|
|
|
|
|
|
|
|
if (info_len > 0)
|
|
|
|
SSNES_LOG("Shader log: %s\n", info_log);
|
|
|
|
}
|
|
|
|
|
2011-01-11 21:13:55 +00:00
|
|
|
static void print_linker_log(GLuint obj)
|
|
|
|
{
|
|
|
|
int info_len = 0;
|
|
|
|
int max_len;
|
|
|
|
|
|
|
|
pglGetProgramiv(obj, GL_INFO_LOG_LENGTH, &max_len);
|
|
|
|
|
|
|
|
char info_log[max_len];
|
|
|
|
pglGetProgramInfoLog(obj, max_len, &info_len, info_log);
|
|
|
|
|
|
|
|
if (info_len > 0)
|
|
|
|
SSNES_LOG("Linker log: %s\n", info_log);
|
|
|
|
}
|
|
|
|
|
2011-03-12 14:30:57 +00:00
|
|
|
static void compile_programs(GLuint *gl_prog, struct shader_program *progs, size_t num)
|
2011-01-05 18:07:12 +00:00
|
|
|
{
|
2011-03-12 14:30:57 +00:00
|
|
|
for (unsigned i = 0; i < num; i++)
|
|
|
|
{
|
|
|
|
gl_prog[i] = pglCreateProgram();
|
|
|
|
|
|
|
|
if (progs[i].vertex)
|
|
|
|
{
|
2011-03-12 15:33:01 +00:00
|
|
|
SSNES_LOG("Found GLSL vertex shader.\n");
|
2011-03-12 14:30:57 +00:00
|
|
|
GLuint shader = pglCreateShader(GL_VERTEX_SHADER);
|
|
|
|
pglShaderSource(shader, 1, (const char**)&progs[i].vertex, 0);
|
|
|
|
pglCompileShader(shader);
|
|
|
|
print_shader_log(shader);
|
|
|
|
|
|
|
|
pglAttachShader(gl_prog[i], shader);
|
|
|
|
free(progs[i].vertex);
|
|
|
|
}
|
|
|
|
|
2011-03-12 15:33:01 +00:00
|
|
|
if (progs[i].fragment)
|
2011-03-12 14:30:57 +00:00
|
|
|
{
|
2011-03-12 15:33:01 +00:00
|
|
|
SSNES_LOG("Found GLSL fragment shader.\n");
|
2011-03-12 14:30:57 +00:00
|
|
|
GLuint shader = pglCreateShader(GL_FRAGMENT_SHADER);
|
|
|
|
pglShaderSource(shader, 1, (const char**)&progs[i].fragment, 0);
|
|
|
|
pglCompileShader(shader);
|
|
|
|
print_shader_log(shader);
|
|
|
|
|
|
|
|
pglAttachShader(gl_prog[i], shader);
|
|
|
|
free(progs[i].fragment);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (progs[i].vertex || progs[i].fragment)
|
|
|
|
{
|
2011-03-12 15:33:01 +00:00
|
|
|
SSNES_LOG("Linking GLSL program.\n");
|
2011-03-12 14:30:57 +00:00
|
|
|
pglLinkProgram(gl_prog[i]);
|
|
|
|
pglUseProgram(gl_prog[i]);
|
|
|
|
print_linker_log(gl_prog[i]);
|
|
|
|
|
|
|
|
GLint location = pglGetUniformLocation(gl_prog[i], "rubyTexture");
|
|
|
|
pglUniform1i(location, 0);
|
|
|
|
pglUseProgram(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-02-22 22:43:00 +00:00
|
|
|
|
2011-03-12 14:30:57 +00:00
|
|
|
bool gl_glsl_init(const char *path)
|
|
|
|
{
|
2011-02-22 22:43:00 +00:00
|
|
|
#ifndef __APPLE__
|
2011-01-05 18:07:12 +00:00
|
|
|
// Load shader functions.
|
2011-01-07 20:02:46 +00:00
|
|
|
pglCreateProgram = SDL_GL_GetProcAddress("glCreateProgram");
|
|
|
|
pglUseProgram = SDL_GL_GetProcAddress("glUseProgram");
|
|
|
|
pglCreateShader = SDL_GL_GetProcAddress("glCreateShader");
|
|
|
|
pglDeleteShader = SDL_GL_GetProcAddress("glDeleteShader");
|
|
|
|
pglShaderSource = SDL_GL_GetProcAddress("glShaderSource");
|
|
|
|
pglCompileShader = SDL_GL_GetProcAddress("glCompileShader");
|
|
|
|
pglAttachShader = SDL_GL_GetProcAddress("glAttachShader");
|
|
|
|
pglDetachShader = SDL_GL_GetProcAddress("glDetachShader");
|
|
|
|
pglLinkProgram = SDL_GL_GetProcAddress("glLinkProgram");
|
|
|
|
pglGetUniformLocation = SDL_GL_GetProcAddress("glGetUniformLocation");
|
|
|
|
pglUniform1i = SDL_GL_GetProcAddress("glUniform1i");
|
|
|
|
pglUniform2fv = SDL_GL_GetProcAddress("glUniform2fv");
|
|
|
|
pglUniform4fv = SDL_GL_GetProcAddress("glUniform4fv");
|
2011-01-07 20:41:11 +00:00
|
|
|
pglGetShaderiv = SDL_GL_GetProcAddress("glGetShaderiv");
|
|
|
|
pglGetShaderInfoLog = SDL_GL_GetProcAddress("glGetShaderInfoLog");
|
2011-01-11 21:13:55 +00:00
|
|
|
pglGetProgramiv = SDL_GL_GetProcAddress("glGetProgramiv");
|
|
|
|
pglGetProgramInfoLog = SDL_GL_GetProcAddress("glGetProgramInfoLog");
|
2011-02-22 22:43:00 +00:00
|
|
|
#endif
|
2011-01-05 18:07:12 +00:00
|
|
|
|
|
|
|
SSNES_LOG("Checking GLSL shader support ...\n");
|
2011-02-22 22:43:00 +00:00
|
|
|
#ifdef __APPLE__
|
|
|
|
const bool shader_support = true;
|
|
|
|
#else
|
2011-01-07 20:02:46 +00:00
|
|
|
bool shader_support = pglCreateProgram && pglUseProgram && pglCreateShader
|
|
|
|
&& pglDeleteShader && pglShaderSource && pglCompileShader && pglAttachShader
|
|
|
|
&& pglDetachShader && pglLinkProgram && pglGetUniformLocation
|
2011-01-07 20:41:11 +00:00
|
|
|
&& pglUniform1i && pglUniform2fv && pglUniform4fv
|
2011-01-11 21:13:55 +00:00
|
|
|
&& pglGetShaderiv && pglGetShaderInfoLog && pglGetProgramiv && pglGetProgramInfoLog;
|
2011-02-22 22:43:00 +00:00
|
|
|
#endif
|
2011-01-05 18:07:12 +00:00
|
|
|
|
|
|
|
if (!shader_support)
|
|
|
|
{
|
|
|
|
SSNES_ERR("GLSL shaders aren't supported by your GL driver.\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-03-12 14:30:57 +00:00
|
|
|
struct shader_program progs[MAX_PROGRAMS];
|
|
|
|
unsigned num_progs = get_xml_shaders(path, progs, MAX_PROGRAMS - 1);
|
2011-01-05 18:07:12 +00:00
|
|
|
|
2011-03-12 14:30:57 +00:00
|
|
|
if (num_progs == 0)
|
2011-01-05 18:07:12 +00:00
|
|
|
{
|
2011-03-12 14:30:57 +00:00
|
|
|
SSNES_ERR("Couldn't find any valid shaders in XML file.\n");
|
|
|
|
return false;
|
|
|
|
}
|
2011-01-05 18:07:12 +00:00
|
|
|
|
2011-03-12 14:30:57 +00:00
|
|
|
compile_programs(&gl_program[1], progs, num_progs);
|
2011-03-06 18:56:35 +00:00
|
|
|
|
2011-03-12 14:30:57 +00:00
|
|
|
// SSNES custom two-pass with two different files.
|
|
|
|
if (num_progs == 1 && *g_settings.video.second_pass_shader)
|
|
|
|
{
|
|
|
|
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
|
|
|
{
|
2011-03-12 14:30:57 +00:00
|
|
|
compile_programs(&gl_program[2], progs, 1);
|
|
|
|
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
|
|
|
{
|
2011-03-12 14:30:57 +00:00
|
|
|
SSNES_ERR("Did not find valid shader in secondary shader file.\n");
|
|
|
|
return false;
|
2011-03-06 18:56:35 +00:00
|
|
|
}
|
2011-01-05 18:07:12 +00:00
|
|
|
}
|
|
|
|
|
2011-01-11 21:13:55 +00:00
|
|
|
if (!gl_check_error())
|
|
|
|
return false;
|
|
|
|
|
2011-01-05 18:07:12 +00:00
|
|
|
glsl_enable = true;
|
2011-03-12 14:30:57 +00:00
|
|
|
gl_num_programs = num_progs;
|
2011-01-05 18:07:12 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void gl_glsl_deinit(void)
|
|
|
|
{}
|
|
|
|
|
|
|
|
void gl_glsl_set_params(unsigned width, unsigned height,
|
|
|
|
unsigned tex_width, unsigned tex_height,
|
|
|
|
unsigned out_width, unsigned out_height)
|
|
|
|
{
|
2011-03-06 18:56:35 +00:00
|
|
|
if (glsl_enable && gl_program[active_index] > 0)
|
2011-01-05 18:07:12 +00:00
|
|
|
{
|
|
|
|
GLint location;
|
|
|
|
|
|
|
|
float inputSize[2] = {width, height};
|
2011-03-06 18:56:35 +00:00
|
|
|
location = pglGetUniformLocation(gl_program[active_index], "rubyInputSize");
|
2011-01-07 20:02:46 +00:00
|
|
|
pglUniform2fv(location, 1, inputSize);
|
2011-01-05 18:07:12 +00:00
|
|
|
|
|
|
|
float outputSize[2] = {out_width, out_height};
|
2011-03-06 18:56:35 +00:00
|
|
|
location = pglGetUniformLocation(gl_program[active_index], "rubyOutputSize");
|
2011-01-07 20:02:46 +00:00
|
|
|
pglUniform2fv(location, 1, outputSize);
|
2011-01-05 18:07:12 +00:00
|
|
|
|
|
|
|
float textureSize[2] = {tex_width, tex_height};
|
2011-03-06 18:56:35 +00:00
|
|
|
location = pglGetUniformLocation(gl_program[active_index], "rubyTextureSize");
|
2011-01-07 20:02:46 +00:00
|
|
|
pglUniform2fv(location, 1, textureSize);
|
2011-01-05 18:07:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void gl_glsl_set_proj_matrix(void)
|
|
|
|
{}
|
2011-01-22 23:48:47 +00:00
|
|
|
|
2011-03-06 18:56:35 +00:00
|
|
|
void gl_glsl_use(unsigned index)
|
2011-01-22 23:48:47 +00:00
|
|
|
{
|
|
|
|
if (glsl_enable)
|
2011-03-06 18:56:35 +00:00
|
|
|
{
|
|
|
|
active_index = index;
|
|
|
|
pglUseProgram(gl_program[index]);
|
|
|
|
}
|
2011-01-22 23:48:47 +00:00
|
|
|
}
|
2011-03-12 14:30:57 +00:00
|
|
|
|
|
|
|
unsigned gl_glsl_num(void)
|
|
|
|
{
|
|
|
|
return gl_num_programs;
|
|
|
|
}
|