diff --git a/Makefile b/Makefile index b1277c42c0..072e048b5e 100644 --- a/Makefile +++ b/Makefile @@ -50,6 +50,7 @@ ifeq ($(BUILD_FILTER), 1) OBJ += hqflt/bleed.o OBJ += hqflt/ntsc.o OBJ += hqflt/snes_ntsc/snes_ntsc.o + DEFINES += -DHAVE_FILTER endif CFLAGS = -Wall -O0 -g -std=gnu99 -Wno-unused-variable -I. $(DEFINES) diff --git a/config.h.def b/config.h.def index f61d7d2a8f..a044335aec 100644 --- a/config.h.def +++ b/config.h.def @@ -39,7 +39,6 @@ #define AUDIO_AL 5 //////////////////////// -// Chooses which video and audio subsystem to use. Remember to update config.mk if you change these. #define VIDEO_DEFAULT_DRIVER VIDEO_GL #define AUDIO_DEFAULT_DRIVER AUDIO_ALSA @@ -63,26 +62,24 @@ static const bool vsync = true; // Smooths picture static const bool video_smooth = true; -// Path to custom Cg shader. If using custom shaders, it is recommended to disable video_smooth. -#ifdef HAVE_CG -#define DEFAULT_CG_SHADER "hqflt/cg/quad.cg" -#endif - // On resize and fullscreen, rendering area will stay 4:3 static const bool force_aspect = true; /////////// Video filters (CPU based) -#define FILTER_NONE 0 +#ifdef HAVE_FILTER #define FILTER_HQ2X 1 #define FILTER_HQ4X 2 #define FILTER_GRAYSCALE 3 #define FILTER_BLEED 4 #define FILTER_NTSC 5 +#define FILTER_HQ2X_STR "hq2x" +#define FILTER_HQ4X_STR "hq4x" +#define FILTER_GRAYSCALE_STR "grayscale" +#define FILTER_BLEED_STR "bleed" +#define FILTER_NTSC_STR "ntsc" +#endif //////////////////////// -// If you change this to something other than FILTER_NONE, make sure that you build the filter module in config.mk. -#define VIDEO_FILTER FILTER_NONE - //////////////// // Audio diff --git a/driver.c b/driver.c index f8b758c0a0..6e03733a04 100644 --- a/driver.c +++ b/driver.c @@ -133,29 +133,25 @@ void uninit_audio(void) void init_video_input(void) { - int scale; + int scale = 2; find_video_driver(); // We multiply scales with 2 to allow for hi-res games. -#if 0 -#if VIDEO_FILTER == FILTER_NONE - scale = 2; -#elif VIDEO_FILTER == FILTER_HQ2X - scale = 4; -#elif VIDEO_FILTER == FILTER_HQ4X - scale = 8; -#elif VIDEO_FILTER == FILTER_NTSC - scale = 8; -#elif VIDEO_FILTER == FILTER_GRAYSCALE - scale = 2; -#elif VIDEO_FILTER == FILTER_BLEED - scale = 2; -#else - scale = 2; +#if HAVE_FILTER + switch (g_settings.video.filter) + { + case FILTER_HQ2X: + scale = 4; + break; + case FILTER_HQ4X: + case FILTER_NTSC: + scale = 8; + break; + default: + break; + } #endif -#endif - scale = 2; video_info_t video = { .width = (g_settings.video.fullscreen) ? g_settings.video.fullscreen_x : (296 * g_settings.video.xscale), @@ -168,7 +164,7 @@ void init_video_input(void) }; const input_driver_t *tmp = driver.input; - driver.video_data = driver.video->init(&video, &(driver.input)); + driver.video_data = driver.video->init(&video, &driver.input); if ( driver.video_data == NULL ) { diff --git a/general.h b/general.h index bd8ab93893..3b86a831cd 100644 --- a/general.h +++ b/general.h @@ -41,7 +41,7 @@ struct settings bool smooth; bool force_aspect; char cg_shader_path[256]; - char video_filter[64]; + unsigned filter; } video; struct @@ -76,7 +76,6 @@ struct global FILE *rom_file; char savefile_name_srm[256]; - char cg_shader_path[256]; char config_path[256]; }; diff --git a/gfx/gl.c b/gfx/gl.c index 96c482bb1a..da29fb4f9e 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -50,6 +50,7 @@ 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 @@ -214,7 +215,8 @@ static void GLFWCALL resize(int width, int height) glMatrixMode(GL_MODELVIEW); glLoadIdentity(); #ifdef HAVE_CG - cgGLSetStateMatrixParameter(cg_mvp_matrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY); + if (cg_active) + cgGLSetStateMatrixParameter(cg_mvp_matrix, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY); #endif gl_width = out_width; gl_height = out_height; @@ -258,13 +260,16 @@ static bool gl_frame(void *data, const uint16_t* frame, int width, int height, i glClear(GL_COLOR_BUFFER_BIT); #if HAVE_CG - 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); + 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); + 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 if (width != gl->last_width || height != gl->last_height) // res change. need to clear out texture. @@ -305,7 +310,8 @@ static void gl_free(void *data) { gl_t *gl = data; #ifdef HAVE_CG - cgDestroyContext(gl->cgCtx); + if (cg_active) + cgDestroyContext(gl->cgCtx); #endif glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); @@ -327,7 +333,7 @@ static void gl_set_nonblock_state(void *data, bool state) static void* gl_init(video_info_t *video, const input_driver_t **input) { - gl_t *gl = malloc(sizeof(gl_t)); + gl_t *gl = calloc(1, sizeof(gl_t)); if ( gl == NULL ) return NULL; @@ -394,44 +400,50 @@ static void* gl_init(video_info_t *video, const input_driver_t **input) gl->last_height = gl->tex_h; #ifdef HAVE_CG - gl->cgCtx = cgCreateContext(); - if (gl->cgCtx == NULL) + cg_active = false; + if (strlen(g_settings.video.cg_shader_path) > 0) { - 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->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); + puts(g_settings.video.cg_shader_path); + 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); + 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 *input = &input_glfw; diff --git a/hqflt/filters.h b/hqflt/filters.h new file mode 100644 index 0000000000..e379695352 --- /dev/null +++ b/hqflt/filters.h @@ -0,0 +1,23 @@ +#ifndef __FILTERS_H +#define __FILTERS_H + +#ifdef HAVE_FILTER + +#include "pastlib.h" +#include "grayscale.h" +#include "bleed.h" +#include "ntsc.h" + +#define FILTER_HQ2X 1 +#define FILTER_HQ4X 2 +#define FILTER_GRAYSCALE 3 +#define FILTER_BLEED 4 +#define FILTER_NTSC 5 +#define FILTER_HQ2X_STR "hq2x" +#define FILTER_HQ4X_STR "hq4x" +#define FILTER_GRAYSCALE_STR "grayscale" +#define FILTER_BLEED_STR "bleed" +#define FILTER_NTSC_STR "ntsc" +#endif + +#endif diff --git a/settings.c b/settings.c index d0012c603f..5c3b0fb40f 100644 --- a/settings.c +++ b/settings.c @@ -56,10 +56,6 @@ static void set_defaults(void) g_settings.video.vsync = vsync; g_settings.video.smooth = video_smooth; g_settings.video.force_aspect = force_aspect; -#if HAVE_CG - strncpy(g_settings.video.cg_shader_path, DEFAULT_CG_SHADER, sizeof(g_settings.video.cg_shader_path) - 1); -#endif - strncpy(g_settings.video.video_filter, "foo", sizeof(g_settings.video.video_filter) - 1); g_settings.audio.enable = audio_enable; g_settings.audio.out_rate = out_rate; @@ -86,25 +82,35 @@ void parse_config(void) memset(&g_settings, 0, sizeof(struct settings)); config_file_t *conf = NULL; - const char *xdg = getenv("XDG_CONFIG_HOME"); - if (xdg) + if (strlen(g_extern.config_path) > 0) { - char conf_path[strlen(xdg) + strlen("/ssnes ")]; - strcpy(conf_path, xdg); - strcat(conf_path, "/ssnes"); - conf = config_file_new(conf_path); + conf = config_file_new(g_extern.config_path); + if (!conf) + { + SSNES_ERR("Couldn't find config at path: \"%s\"\n", g_extern.config_path); + exit(1); + } } else { + const char *xdg = getenv("XDG_CONFIG_HOME"); const char *home = getenv("HOME"); - - if (home) + if (xdg) + { + char conf_path[strlen(xdg) + strlen("/ssnes ")]; + strcpy(conf_path, xdg); + strcat(conf_path, "/ssnes"); + conf = config_file_new(conf_path); + } + else if (home) { char conf_path[strlen(home) + strlen("/.ssnesrc ")]; strcpy(conf_path, xdg); strcat(conf_path, "/.ssnesrc"); conf = config_file_new(conf_path); } + else // Try /etc/ssnes.conf as a final test ... + conf = config_file_new("/etc/ssnes.conf"); } set_defaults(); @@ -138,17 +144,43 @@ void parse_config(void) if (config_get_bool(conf, "video_force_aspect", &tmp_bool)) g_settings.video.force_aspect = tmp_bool; - if (config_get_string(conf, "video_cg_shader_path", &tmp_str)) + if (config_get_string(conf, "video_cg_shader", &tmp_str)) { strncpy(g_settings.video.cg_shader_path, tmp_str, sizeof(g_settings.video.cg_shader_path) - 1); free(tmp_str); } - if (config_get_string(conf, "video_video_filter", &tmp_str)) +#ifdef HAVE_FILTER + if (config_get_string(conf, "video_filter", &tmp_str)) { - strncpy(g_settings.video.video_filter, tmp_str, sizeof(g_settings.video.video_filter) - 1); + unsigned filter = 0; + if (strcasecmp(FILTER_HQ2X_STR, tmp_str) == 0) + filter = FILTER_HQ2X; + else if (strcasecmp(FILTER_HQ4X_STR, tmp_str) == 0) + filter = FILTER_HQ4X; + else if (strcasecmp(FILTER_GRAYSCALE_STR, tmp_str) == 0) + filter = FILTER_GRAYSCALE; + else if (strcasecmp(FILTER_BLEED_STR, tmp_str) == 0) + filter = FILTER_BLEED; + else if (strcasecmp(FILTER_NTSC_STR, tmp_str) == 0) + filter = FILTER_NTSC; + else + { + SSNES_ERR( + "Invalid filter... Valid filters are:\n" + "\t%s\n" + "\t%s\n" + "\t%s\n" + "\t%s\n" + "\t%s\n", + FILTER_HQ2X_STR, FILTER_HQ4X_STR, FILTER_GRAYSCALE_STR, + FILTER_BLEED, FILTER_NTSC); + exit(1); + } + free(tmp_str); } +#endif // Input Settings. if (config_get_double(conf, "input_axis_threshold", &tmp_double)) @@ -178,8 +210,8 @@ void parse_config(void) if (config_get_int(conf, "audio_src_quality", &tmp_int)) { - int quals[] = {SRC_ZERO_ORDER_HOLD, SRC_LINEAR, SRC_SINC_FASTEST, - SRC_SINC_MEDIUM_QUALITY, SRC_SINC_BEST_QUALITY}; + int quals[] = { SRC_ZERO_ORDER_HOLD, SRC_LINEAR, SRC_SINC_FASTEST, + SRC_SINC_MEDIUM_QUALITY, SRC_SINC_BEST_QUALITY }; if (tmp_int > 0 && tmp_int < 6) g_settings.audio.src_quality = quals[tmp_int]; diff --git a/ssnes.c b/ssnes.c index 7a8b5650ad..eebbe6ca81 100644 --- a/ssnes.c +++ b/ssnes.c @@ -26,10 +26,7 @@ #include #include "driver.h" #include "file.h" -#include "hqflt/pastlib.h" -#include "hqflt/grayscale.h" -#include "hqflt/bleed.h" -#include "hqflt/ntsc.h" +#include "hqflt/filters.h" #include "general.h" struct global g_extern = { @@ -61,7 +58,7 @@ void set_fast_forward_button(bool new_button_state) old_button_state = new_button_state; } -#if VIDEO_FILTER != FILTER_NONE +#ifdef HAVE_FILTER static inline void process_frame (uint16_t * restrict out, const uint16_t * restrict in, unsigned width, unsigned height) { int pitch = 1024; @@ -85,48 +82,46 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height) if ( !g_extern.video_active ) return; -#if 0 -#if VIDEO_FILTER == FILTER_HQ2X - uint16_t outputHQ2x[width * height * 2 * 2]; -#elif VIDEO_FILTER == FILTER_HQ4X - uint16_t outputHQ4x[width * height * 4 * 4]; -#elif VIDEO_FILTER == FILTER_NTSC - uint16_t output_ntsc[SNES_NTSC_OUT_WIDTH(width) * height]; -#endif - -#if VIDEO_FILTER != FILTER_NONE +#ifdef HAVE_FILTER + uint16_t output_filter[width * height * 4 * 4]; uint16_t output[width * height]; process_frame(output, data, width, height); -#endif -#if VIDEO_FILTER == FILTER_HQ2X - ProcessHQ2x(output, outputHQ2x); - if ( !driver.video->frame(driver.video_data, outputHQ2x, width << 1, height << 1, width << 2) ) - video_active = false; -#elif VIDEO_FILTER == FILTER_HQ4X - ProcessHQ4x(output, outputHQ4x); - if ( !driver.video->frame(driver.video_data, outputHQ4x, width << 2, height << 2, width << 3) ) - video_active = false; -#elif VIDEO_FILTER == FILTER_GRAYSCALE - grayscale_filter(output, width, height); - if ( !driver.video->frame(driver.video_data, output, width, height, width << 1) ) - video_active = false; -#elif VIDEO_FILTER == FILTER_BLEED - bleed_filter(output, width, height); - if ( !driver.video->frame(driver.video_data, output, width, height, width << 1) ) - video_active = false; -#elif VIDEO_FILTER == FILTER_NTSC - ntsc_filter(output_ntsc, output, width, height); - if ( !driver.video->frame(driver.video_data, output_ntsc, SNES_NTSC_OUT_WIDTH(width), height, SNES_NTSC_OUT_WIDTH(width) << 1) ) - video_active = false; + switch (g_settings.video.filter) + { + case FILTER_HQ2X: + ProcessHQ2x(output, output_filter); + if ( !driver.video->frame(driver.video_data, output_filter, width << 1, height << 1, width << 2) ) + video_active = false; + break; + case FILTER_HQ4X: + ProcessHQ4x(output, output_filter); + if ( !driver.video->frame(driver.video_data, output_filter, width << 2, height << 2, width << 3) ) + video_active = false; + break; + case FILTER_GRAYSCALE: + grayscale_filter(output, width, height); + if ( !driver.video->frame(driver.video_data, output, width, height, width << 1) ) + video_active = false; + break; + case FILTER_BLEED: + bleed_filter(output, width, height); + if ( !driver.video->frame(driver.video_data, output, width, height, width << 1) ) + video_active = false; + break; + case FILTER_NTSC: + ntsc_filter(output_filter, output, width, height); + if ( !driver.video->frame(driver.video_data, output_filter, SNES_NTSC_OUT_WIDTH(width), height, SNES_NTSC_OUT_WIDTH(width) << 1) ) + video_active = false; + break; + default: + if ( !driver.video->frame(driver.video_data, data, width, height, (height == 448 || height == 478) ? 1024 : 2048) ) + g_extern.video_active = false; + } #else if ( !driver.video->frame(driver.video_data, data, width, height, (height == 448 || height == 478) ? 1024 : 2048) ) g_extern.video_active = false; #endif -#endif - - if ( !driver.video->frame(driver.video_data, data, width, height, (height == 448 || height == 478) ? 1024 : 2048) ) - g_extern.video_active = false; } static void audio_sample(uint16_t left, uint16_t right) @@ -200,9 +195,6 @@ static void print_help(void) puts("\t-h/--help: Show this help message"); puts("\t-s/--save: Path for save file (*.srm). Required when rom is input from stdin"); puts("\t-c/--config: Path for config file. Defaults to $XDG_CONFIG_HOME/ssnes"); -#ifdef HAVE_CG - puts("\t-f/--shader: Path to Cg shader. Will be compiled at runtime.\n"); -#endif puts("\t-v/--verbose: Verbose logging"); } @@ -219,18 +211,11 @@ static void parse_input(int argc, char *argv[]) { "save", 1, NULL, 's' }, { "verbose", 0, NULL, 'v' }, { "config", 0, NULL, 'c' }, -#ifdef HAVE_CG - { "shader", 1, NULL, 'f' }, -#endif { NULL, 0, NULL, 0 } }; int option_index = 0; -#ifdef HAVE_CG - char optstring[] = "hs:vf:c:"; -#else char optstring[] = "hs:vc:"; -#endif for(;;) { int c = getopt_long(argc, argv, optstring, opts, &option_index); @@ -249,12 +234,6 @@ static void parse_input(int argc, char *argv[]) g_extern.savefile_name_srm[sizeof(g_extern.savefile_name_srm)-1] = '\0'; break; -#ifdef HAVE_CG - case 'f': - strncpy(g_extern.cg_shader_path, optarg, sizeof(g_extern.cg_shader_path) - 1); - break; -#endif - case 'v': g_extern.verbose = true; break; @@ -307,8 +286,8 @@ static void parse_input(int argc, char *argv[]) int main(int argc, char *argv[]) { snes_init(); - parse_config(); parse_input(argc, argv); + parse_config(); void *rom_buf; ssize_t rom_len = 0;