diff --git a/Makefile b/Makefile index 1ec3db790e..09c6457fde 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,8 @@ TARGET = ssnes OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o record/ffemu.o LIBS = -lsamplerate -lavformat -lavutil -lavcodec -lswscale +LIBS = -lsamplerate +DEFINES = ifeq ($(HAVE_RSOUND), 1) OBJ += audio/rsound.o @@ -36,9 +38,16 @@ ifeq ($(HAVE_GLFW), 1) endif ifeq ($(HAVE_CG), 1) + OBJ += gfx/shader_cg.o LIBS += -lCg -lCgGL endif +ifeq ($(HAVE_XML), 1) + OBJ += gfx/shader_glsl.o + LIBS += $(XML_LIBS) + DEFINES += $(XML_CFLAGS) +endif + ifeq ($(HAVE_FILTER), 1) OBJ += hqflt/hq.o OBJ += hqflt/grayscale.o @@ -65,7 +74,7 @@ ssnes: $(OBJ) $(CXX) -o $@ $(OBJ) $(LIBS) $(CFLAGS) %.o: %.c config.h config.mk - $(CC) $(CFLAGS) -c -o $@ $< + $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< install: $(TARGET) install -m755 $(TARGET) $(DESTDIR)/$(PREFIX)/bin diff --git a/config.def.h b/config.def.h index d2ce444381..477d637d9b 100644 --- a/config.def.h +++ b/config.def.h @@ -127,7 +127,7 @@ static const struct snes_keybind snes_keybinds_1[] = { { SNES_DEVICE_ID_JOYPAD_DOWN, GLFW_KEY_DOWN, 14, AXIS_NEG(1) }, { SNES_DEVICE_ID_JOYPAD_START, GLFW_KEY_ENTER, 7, AXIS_NONE }, { SNES_DEVICE_ID_JOYPAD_SELECT, GLFW_KEY_RSHIFT, 6, AXIS_NONE }, - { SNES_FAST_FORWARD_KEY, GLFW_KEY_SPACE, 10, AXIS_NONE }, + { SSNES_FAST_FORWARD_KEY, GLFW_KEY_SPACE, 10, AXIS_NONE }, { -1 } }; diff --git a/driver.h b/driver.h index f695746cd3..0deb3672d7 100644 --- a/driver.h +++ b/driver.h @@ -24,7 +24,7 @@ #include #include -#define SNES_FAST_FORWARD_KEY 0x666 // Hurr, durr +#define SSNES_FAST_FORWARD_KEY 0x666 // Hurr, durr void set_fast_forward_button(bool state); struct snes_keybind diff --git a/general.h b/general.h index 6ecd3bf543..e6b7d550e8 100644 --- a/general.h +++ b/general.h @@ -42,6 +42,7 @@ struct settings bool smooth; bool force_aspect; char cg_shader_path[256]; + char bsnes_shader_path[256]; unsigned filter; } video; @@ -100,4 +101,8 @@ extern struct global g_extern; fprintf(stderr, "SSNES [ERROR] :: " msg, ##args); \ } while(0) +#define SSNES_WARN(msg, args...) do { \ + fprintf(stderr, "SSNES [WARN] :: " msg, ##args); \ + } while(0) + #endif diff --git a/gfx/gl.c b/gfx/gl.c index 3e9b38c351..04d8606c2e 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -30,8 +30,11 @@ #ifdef HAVE_CG -#include -#include +#include "shader_cg.h" +#endif + +#ifdef HAVE_XML +#include "shader_glsl.h" #endif static const GLfloat vertexes[] = { @@ -49,23 +52,10 @@ static const GLfloat tex_coords[] = { }; static bool keep_aspect = true; -#ifdef HAVE_CG -static CGparameter cg_mvp_matrix; -static bool cg_active = false; -#endif static GLuint gl_width = 0, gl_height = 0; typedef struct gl { bool vsync; -#ifdef HAVE_CG - CGcontext cgCtx; - CGprogram cgFPrg; - CGprogram cgVPrg; - CGprofile cgFProf; - CGprofile cgVProf; - CGparameter cg_video_size, cg_texture_size, cg_output_size; - CGparameter cg_Vvideo_size, cg_Vtexture_size, cg_Voutput_size; // Vertexes -#endif GLuint texture; GLuint tex_filter; @@ -160,7 +150,7 @@ static int16_t glfw_input_state(void *data, const struct snes_keybind **binds, b // Checks if button is pressed, and sets fast-forwarding state bool pressed = false; for ( int i = 0; snes_keybinds[i].id != -1; i++ ) - if ( snes_keybinds[i].id == SNES_FAST_FORWARD_KEY ) + if ( snes_keybinds[i].id == SSNES_FAST_FORWARD_KEY ) set_fast_forward_button(glfw_is_pressed(port_num, &snes_keybinds[i], buttons, axes)); else if ( !pressed && snes_keybinds[i].id == (int)id ) pressed = glfw_is_pressed(port_num, &snes_keybinds[i], buttons, axes); @@ -180,6 +170,61 @@ static const input_driver_t input_glfw = { .ident = "glfw" }; +static inline bool gl_shader_init(void) +{ + if (strlen(g_settings.video.cg_shader_path) > 0 && strlen(g_settings.video.bsnes_shader_path) > 0) + SSNES_WARN("Both Cg and bSNES XML shader are defined in config file. Cg shader will be selected by default.\n"); + +#ifdef HAVE_CG + if (strlen(g_settings.video.cg_shader_path) > 0) + return gl_cg_init(g_settings.video.cg_shader_path); +#endif + +#ifdef HAVE_XML + if (strlen(g_settings.video.bsnes_shader_path) > 0) + return gl_glsl_init(g_settings.video.bsnes_shader_path); +#endif + + return true; +} + +static inline void gl_shader_deinit(void) +{ +#ifdef HAVE_CG + gl_cg_deinit(); +#endif + +#ifdef HAVE_XML + gl_glsl_deinit(); +#endif +} + +static inline void gl_shader_set_proj_matrix(void) +{ +#ifdef HAVE_CG + gl_cg_set_proj_matrix(); +#endif + +#ifdef HAVE_XML + gl_glsl_set_proj_matrix(); +#endif +} + +static inline void gl_shader_set_params(unsigned width, unsigned height, + unsigned tex_width, unsigned tex_height, + unsigned out_width, unsigned out_height) +{ +#ifdef HAVE_CG + gl_cg_set_params(width, height, tex_width, tex_height, out_width, out_height); +#endif + +#ifdef HAVE_XML + gl_glsl_set_params(width, height, tex_width, tex_height, out_width, out_height); +#endif +} + +#define SNES_ASPECT_RATIO (4.0/3) + static void GLFWCALL resize(int width, int height) { glMatrixMode(GL_PROJECTION); @@ -188,7 +233,7 @@ static void GLFWCALL resize(int width, int height) if ( keep_aspect ) { - float desired_aspect = 4.0/3; + float desired_aspect = SNES_ASPECT_RATIO; float device_aspect = (float)width / height; // If the aspect ratios of screen and desired aspect ratio are sufficiently equal (floating point stuff), @@ -215,10 +260,9 @@ static void GLFWCALL resize(int width, int height) glOrtho(0, 1, 0, 1, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); -#ifdef HAVE_CG - if (cg_active) - cgGLSetStateMatrixParameter(cg_mvp_matrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY); -#endif + + gl_shader_set_proj_matrix(); + gl_width = out_width; gl_height = out_height; } @@ -260,18 +304,7 @@ static bool gl_frame(void *data, const uint16_t* frame, int width, int height, i glClear(GL_COLOR_BUFFER_BIT); -#if HAVE_CG - if (cg_active) - { - cgGLSetParameter2f(gl->cg_video_size, width, height); - cgGLSetParameter2f(gl->cg_texture_size, gl->tex_w, gl->tex_h); - cgGLSetParameter2f(gl->cg_output_size, gl_width, gl_height); - - cgGLSetParameter2f(gl->cg_Vvideo_size, width, height); - cgGLSetParameter2f(gl->cg_Vtexture_size, gl->tex_w, gl->tex_h); - cgGLSetParameter2f(gl->cg_Voutput_size, gl_width, gl_height); - } -#endif + gl_shader_set_params(width, height, gl->tex_w, gl->tex_h, gl_width, gl_height); if (width != gl->last_width || height != gl->last_height) // res change. need to clear out texture. { @@ -310,10 +343,8 @@ static bool gl_frame(void *data, const uint16_t* frame, int width, int height, i static void gl_free(void *data) { gl_t *gl = data; -#ifdef HAVE_CG - if (cg_active) - cgDestroyContext(gl->cgCtx); -#endif + + gl_shader_deinit(); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDeleteTextures(1, &gl->texture); @@ -352,6 +383,7 @@ static void* gl_init(video_info_t *video, const input_driver_t **input) if (!res) { glfwTerminate(); + free(gl); return NULL; } @@ -378,8 +410,8 @@ static void* gl_init(video_info_t *video, const input_driver_t **input) glBindTexture(GL_TEXTURE_2D, gl->texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl->tex_filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl->tex_filter); @@ -400,60 +432,10 @@ static void* gl_init(video_info_t *video, const input_driver_t **input) gl->last_width = gl->tex_w; gl->last_height = gl->tex_h; -#ifdef HAVE_CG - cg_active = false; - if (strlen(g_settings.video.cg_shader_path) > 0) - { - SSNES_LOG("Loading Cg file: %s\n", g_settings.video.cg_shader_path); - gl->cgCtx = cgCreateContext(); - if (gl->cgCtx == NULL) - { - fprintf(stderr, "Failed to create Cg context\n"); - goto error; - } - gl->cgFProf = cgGLGetLatestProfile(CG_GL_FRAGMENT); - gl->cgVProf = cgGLGetLatestProfile(CG_GL_VERTEX); - if (gl->cgFProf == CG_PROFILE_UNKNOWN || gl->cgVProf == CG_PROFILE_UNKNOWN) - { - fprintf(stderr, "Invalid profile type\n"); - goto error; - } - cgGLSetOptimalOptions(gl->cgFProf); - cgGLSetOptimalOptions(gl->cgVProf); - gl->cgFPrg = cgCreateProgramFromFile(gl->cgCtx, CG_SOURCE, g_settings.video.cg_shader_path, gl->cgFProf, "main_fragment", 0); - gl->cgVPrg = cgCreateProgramFromFile(gl->cgCtx, CG_SOURCE, g_settings.video.cg_shader_path, gl->cgVProf, "main_vertex", 0); - if (gl->cgFPrg == NULL || gl->cgVPrg == NULL) - { - CGerror err = cgGetError(); - fprintf(stderr, "CG error: %s\n", cgGetErrorString(err)); - goto error; - } - cgGLLoadProgram(gl->cgFPrg); - cgGLLoadProgram(gl->cgVPrg); - cgGLEnableProfile(gl->cgFProf); - cgGLEnableProfile(gl->cgVProf); - cgGLBindProgram(gl->cgFPrg); - cgGLBindProgram(gl->cgVPrg); - - gl->cg_video_size = cgGetNamedParameter(gl->cgFPrg, "IN.video_size"); - gl->cg_texture_size = cgGetNamedParameter(gl->cgFPrg, "IN.texture_size"); - gl->cg_output_size = cgGetNamedParameter(gl->cgFPrg, "IN.output_size"); - gl->cg_Vvideo_size = cgGetNamedParameter(gl->cgVPrg, "IN.video_size"); - gl->cg_Vtexture_size = cgGetNamedParameter(gl->cgVPrg, "IN.texture_size"); - gl->cg_Voutput_size = cgGetNamedParameter(gl->cgVPrg, "IN.output_size"); - cg_mvp_matrix = cgGetNamedParameter(gl->cgVPrg, "modelViewProj"); - cgGLSetStateMatrixParameter(cg_mvp_matrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY); - cg_active = true; - } -#endif + gl_shader_init(); *input = &input_glfw; return gl; -#ifdef HAVE_CG -error: - free(gl); - return NULL; -#endif } const video_driver_t video_gl = { diff --git a/gfx/shader_cg.c b/gfx/shader_cg.c new file mode 100644 index 0000000000..1c1005a794 --- /dev/null +++ b/gfx/shader_cg.c @@ -0,0 +1,104 @@ +/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010 - Hans-Kristian Arntzen + * + * 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 . + */ + +#include "shader_cg.h" +#include +#include +#include "general.h" + +static CGcontext cgCtx; +static CGprogram cgFPrg; +static CGprogram cgVPrg; +static CGprofile cgFProf; +static CGprofile cgVProf; +static CGparameter cg_video_size, cg_texture_size, cg_output_size; +static CGparameter cg_Vvideo_size, cg_Vtexture_size, cg_Voutput_size; // Vertexes +static CGparameter cg_mvp_matrix; +static bool cg_active = false; + +void gl_cg_set_proj_matrix(void) +{ + if (cg_active) + cgGLSetStateMatrixParameter(cg_mvp_matrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY); +} + +void gl_cg_set_params(unsigned width, unsigned height, + unsigned tex_width, unsigned tex_height, + unsigned out_width, unsigned out_height) +{ + if (cg_active) + { + cgGLSetParameter2f(cg_video_size, width, height); + cgGLSetParameter2f(cg_texture_size, tex_width, tex_height); + cgGLSetParameter2f(cg_output_size, out_width, out_height); + + cgGLSetParameter2f(cg_Vvideo_size, width, height); + cgGLSetParameter2f(cg_Vtexture_size, tex_width, tex_height); + cgGLSetParameter2f(cg_Voutput_size, out_width, out_height); + } +} + +void gl_cg_deinit(void) +{ + if (cg_active) + cgDestroyContext(cgCtx); +} + +bool gl_cg_init(const char *path) +{ + SSNES_LOG("Loading Cg file: %s\n", path); + cgCtx = cgCreateContext(); + if (cgCtx == NULL) + { + SSNES_ERR("Failed to create Cg context\n"); + return false; + } + cgFProf = cgGLGetLatestProfile(CG_GL_FRAGMENT); + cgVProf = cgGLGetLatestProfile(CG_GL_VERTEX); + if (cgFProf == CG_PROFILE_UNKNOWN || cgVProf == CG_PROFILE_UNKNOWN) + { + SSNES_ERR("Invalid profile type\n"); + return false; + } + cgGLSetOptimalOptions(cgFProf); + cgGLSetOptimalOptions(cgVProf); + cgFPrg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, path, cgFProf, "main_fragment", 0); + cgVPrg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, path, cgVProf, "main_vertex", 0); + if (cgFPrg == NULL || cgVPrg == NULL) + { + CGerror err = cgGetError(); + SSNES_ERR("CG error: %s\n", cgGetErrorString(err)); + return false; + } + cgGLLoadProgram(cgFPrg); + cgGLLoadProgram(cgVPrg); + cgGLEnableProfile(cgFProf); + cgGLEnableProfile(cgVProf); + cgGLBindProgram(cgFPrg); + cgGLBindProgram(cgVPrg); + + cg_video_size = cgGetNamedParameter(cgFPrg, "IN.video_size"); + cg_texture_size = cgGetNamedParameter(cgFPrg, "IN.texture_size"); + cg_output_size = cgGetNamedParameter(cgFPrg, "IN.output_size"); + cg_Vvideo_size = cgGetNamedParameter(cgVPrg, "IN.video_size"); + cg_Vtexture_size = cgGetNamedParameter(cgVPrg, "IN.texture_size"); + cg_Voutput_size = cgGetNamedParameter(cgVPrg, "IN.output_size"); + cg_mvp_matrix = cgGetNamedParameter(cgVPrg, "modelViewProj"); + cgGLSetStateMatrixParameter(cg_mvp_matrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY); + cg_active = true; + return true; +} diff --git a/gfx/shader_cg.h b/gfx/shader_cg.h new file mode 100644 index 0000000000..f788466c0a --- /dev/null +++ b/gfx/shader_cg.h @@ -0,0 +1,34 @@ +/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010 - Hans-Kristian Arntzen + * + * 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 . + */ + + +#ifndef __SSNES_CG_H +#define __SSNES_CG_H + +#include + +bool gl_cg_init(const char *path); + +void gl_cg_deinit(void); + +void gl_cg_set_proj_matrix(void); + +void gl_cg_set_params(unsigned width, unsigned height, + unsigned tex_width, unsigned tex_height, + unsigned out_width, unsigned out_height); + +#endif diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c new file mode 100644 index 0000000000..5e2ea1a8e3 --- /dev/null +++ b/gfx/shader_glsl.c @@ -0,0 +1,224 @@ +/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010 - Hans-Kristian Arntzen + * + * 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 . + */ + +// +// GLSL code here is mostly copypasted from bSNES. +// + +#include +#include +#include "general.h" + +#include +#include +#include +#include +#include + +static PFNGLCREATEPROGRAMPROC glCreateProgram = NULL; +static PFNGLUSEPROGRAMPROC glUseProgram = NULL; +static PFNGLCREATESHADERPROC glCreateShader = NULL; +static PFNGLDELETESHADERPROC glDeleteShader = NULL; +static PFNGLSHADERSOURCEPROC glShaderSource = NULL; +static PFNGLCOMPILESHADERPROC glCompileShader = NULL; +static PFNGLATTACHSHADERPROC glAttachShader = NULL; +static PFNGLDETACHSHADERPROC glDetachShader = NULL; +static PFNGLLINKPROGRAMPROC glLinkProgram = NULL; +static PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = NULL; +static PFNGLUNIFORM1IPROC glUniform1i = NULL; +static PFNGLUNIFORM2FVPROC glUniform2fv = NULL; +static PFNGLUNIFORM4FVPROC glUniform4fv = NULL; + +static bool glsl_enable = false; +static GLuint gl_program; +static GLuint fragment_shader; +static GLuint vertex_shader; + +static bool get_xml_shaders(const char *path, char **vertex_shader, char **fragment_shader) +{ + 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; + + bool vertex_found = false; + bool fragment_found = false; + // Iterate to check if we find fragment and/or vertex shaders. + for (cur = cur->children; cur; cur = cur->next) + { + if (cur->type != XML_ELEMENT_NODE) + continue; + + xmlChar *content = xmlNodeGetContent(cur); + if (!content) + continue; + + if (strcmp((const char*)cur->name, "vertex") == 0 && !vertex_found) + { + *vertex_shader = malloc(xmlStrlen(content) + 1); + strcpy(*vertex_shader, (const char*)content); + vertex_found = true; + } + else if (strcmp((const char*)cur->name, "fragment") == 0 && !fragment_found) + { + *fragment_shader = malloc(xmlStrlen(content) + 1); + strcpy(*fragment_shader, (const char*)content); + fragment_found = true; + } + } + + if (!vertex_found && !fragment_found) + { + SSNES_ERR("Couldn't find vertex shader nor fragment shader in XML file.\n"); + goto error; + } + + + xmlFreeDoc(doc); + xmlFreeParserCtxt(ctx); + return true; + +error: + if (doc) + xmlFreeDoc(doc); + xmlFreeParserCtxt(ctx); + return false; +} + +bool gl_glsl_init(const char *path) +{ + // Load shader functions. + glCreateProgram = glfwGetProcAddress("glCreateProgram"); + glUseProgram = glfwGetProcAddress("glUseProgram"); + glCreateShader = glfwGetProcAddress("glCreateShader"); + glDeleteShader = glfwGetProcAddress("glDeleteShader"); + glShaderSource = glfwGetProcAddress("glShaderSource"); + glCompileShader = glfwGetProcAddress("glCompileShader"); + glAttachShader = glfwGetProcAddress("glAttachShader"); + glDetachShader = glfwGetProcAddress("glDetachShader"); + glLinkProgram = glfwGetProcAddress("glLinkProgram"); + glGetUniformLocation = glfwGetProcAddress("glGetUniformLocation"); + glUniform1i = glfwGetProcAddress("glUniform1i"); + glUniform2fv = glfwGetProcAddress("glUniform2fv"); + glUniform4fv = glfwGetProcAddress("glUniform4fv"); + + SSNES_LOG("Checking GLSL shader support ...\n"); + bool shader_support = glCreateProgram && glUseProgram && glCreateShader + && glDeleteShader && glShaderSource && glCompileShader && glAttachShader + && glDetachShader && glLinkProgram && glGetUniformLocation + && glUniform1i && glUniform2fv && glUniform4fv; + + if (!shader_support) + { + SSNES_ERR("GLSL shaders aren't supported by your GL driver.\n"); + return false; + } + + gl_program = glCreateProgram(); + + char *vertex_prog = NULL; + char *fragment_prog = NULL; + if (!get_xml_shaders(path, &vertex_prog, &fragment_prog)) + return false; + + if (vertex_prog) + { + vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_shader, 1, (const char**)&vertex_prog, 0); + glCompileShader(vertex_shader); + glAttachShader(gl_program, vertex_shader); + free(vertex_prog); + } + if (fragment_prog) + { + fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader, 1, (const char**)&fragment_prog, 0); + glCompileShader(fragment_shader); + glAttachShader(gl_program, fragment_shader); + free(fragment_prog); + } + + if (vertex_prog || fragment_prog) + { + glLinkProgram(gl_program); + glUseProgram(gl_program); + } + + glsl_enable = true; + 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) +{ + if (glsl_enable) + { + GLint location; + + float inputSize[2] = {width, height}; + location = glGetUniformLocation(gl_program, "rubyInputSize"); + glUniform2fv(location, 1, inputSize); + + float outputSize[2] = {out_width, out_height}; + location = glGetUniformLocation(gl_program, "rubyOutputSize"); + glUniform2fv(location, 1, outputSize); + + float textureSize[2] = {tex_width, tex_height}; + location = glGetUniformLocation(gl_program, "rubyTextureSize"); + glUniform2fv(location, 1, textureSize); + } +} + +void gl_glsl_set_proj_matrix(void) +{} diff --git a/gfx/shader_glsl.h b/gfx/shader_glsl.h new file mode 100644 index 0000000000..4b0ad8761e --- /dev/null +++ b/gfx/shader_glsl.h @@ -0,0 +1,34 @@ +/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes. + * Copyright (C) 2010 - Hans-Kristian Arntzen + * + * 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 . + */ + + +#ifndef __SSNES_GLSL_H +#define __SSNES_GLSL_H + +#include + +bool gl_glsl_init(const char *path); + +void gl_glsl_deinit(void); + +void gl_glsl_set_proj_matrix(void); + +void gl_glsl_set_params(unsigned width, unsigned height, + unsigned tex_width, unsigned tex_height, + unsigned out_width, unsigned out_height); + +#endif diff --git a/qb/config.libs.sh b/qb/config.libs.sh index a01eaf03d9..cf0bfb0774 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -20,13 +20,14 @@ check_lib GLFW -lglfw glfwInit check_critical GLFW "Cannot find GLFW library." check_lib CG -lCg cgCreateContext +check_pkgconf XML libxml-2.0 check_lib SRC -lsamplerate src_callback_new check_lib DYNAMIC -ldl dlopen # Creates config.mk. -VARS="ALSA OSS AL RSOUND ROAR JACK GLFW FILTER CG DYNAMIC" +VARS="ALSA OSS AL RSOUND ROAR JACK GLFW FILTER CG XML DYNAMIC" create_config_make config.mk $VARS create_config_header config.h $VARS diff --git a/qb/config.params.sh b/qb/config.params.sh index 34dc9a88d2..9e3ea58c8d 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -10,7 +10,8 @@ PACKAGE_VERSION=0.1 add_command_line_enable DYNAMIC "Enable dynamic loading of libsnes library." no add_command_line_string LIBSNES "libsnes library used" "-lsnes" add_command_line_enable FILTER "Disable CPU filter support" yes -add_command_line_enable CG "Enable CG shader support" auto +add_command_line_enable CG "Enable Cg shader support" auto +add_command_line_enable XML "Enable bSNES-style XML shader support" auto add_command_line_enable ALSA "Enable ALSA support" auto add_command_line_enable OSS "Enable OSS support" auto add_command_line_enable RSOUND "Enable RSound support" auto diff --git a/settings.c b/settings.c index 0288dd3dd6..71b7c1be82 100644 --- a/settings.c +++ b/settings.c @@ -177,6 +177,12 @@ void parse_config(void) free(tmp_str); } + if (config_get_string(conf, "video_bsnes_shader", &tmp_str)) + { + strncpy(g_settings.video.bsnes_shader_path, tmp_str, sizeof(g_settings.video.bsnes_shader_path)); + free(tmp_str); + } + #ifdef HAVE_FILTER if (config_get_string(conf, "video_filter", &tmp_str)) { @@ -263,8 +269,6 @@ void parse_config(void) read_keybinds(conf); - // TODO: Keybinds. - config_file_free(conf); } @@ -291,7 +295,7 @@ static const struct bind_map bind_maps[2][13] = { { "input_player1_right", "input_player1_right_btn", "input_player1_right_axis", SNES_DEVICE_ID_JOYPAD_RIGHT }, { "input_player1_up", "input_player1_up_btn", "input_player1_up_axis", SNES_DEVICE_ID_JOYPAD_UP }, { "input_player1_down", "input_player1_down_btn", "input_player1_down_axis", SNES_DEVICE_ID_JOYPAD_DOWN }, - { "input_toggle_fast_forward", "input_toggle_fast_forward_btn", NULL, SNES_FAST_FORWARD_KEY } + { "input_toggle_fast_forward", "input_toggle_fast_forward_btn", NULL, SSNES_FAST_FORWARD_KEY } }, { { "input_player2_a", "input_player2_a_btn", NULL, SNES_DEVICE_ID_JOYPAD_A }, @@ -306,7 +310,7 @@ static const struct bind_map bind_maps[2][13] = { { "input_player2_right", "input_player2_right_btn", "input_player2_right_axis", SNES_DEVICE_ID_JOYPAD_RIGHT }, { "input_player2_up", "input_player2_up_btn", "input_player2_up_axis", SNES_DEVICE_ID_JOYPAD_UP }, { "input_player2_down", "input_player2_down_btn", "input_player2_down_axis", SNES_DEVICE_ID_JOYPAD_DOWN }, - { "input_toggle_fast_forward", "input_toggle_fast_forward_btn", NULL, SNES_FAST_FORWARD_KEY } + { "input_toggle_fast_forward", "input_toggle_fast_forward_btn", NULL, SSNES_FAST_FORWARD_KEY } } }; diff --git a/ssnes.cfg b/ssnes.cfg index b9d5f9aaba..894e7379f6 100644 --- a/ssnes.cfg +++ b/ssnes.cfg @@ -28,6 +28,9 @@ # Path to Cg shader. If enabled # video_cg_shader = "/path/to/cg/shader.cg" +# Path to bSNES-style XML shader. If both Cg shader path and XML shader path are defined, Cg shader will take priority. +# video_bsnes_shader = "/path/to/bsnes/xml/shader.shader" + # CPU-based filter. Valid ones are: hq2x, hq4x, grayscale, bleed, ntsc. # video_filter = ntsc