diff --git a/Makefile b/Makefile index f50632ab74..b8cb007756 100644 --- a/Makefile +++ b/Makefile @@ -84,11 +84,7 @@ ifeq ($(HAVE_XML), 1) endif ifeq ($(HAVE_FILTER), 1) - OBJ += hqflt/hq.o - OBJ += hqflt/grayscale.o - OBJ += hqflt/bleed.o - OBJ += hqflt/ntsc.o - OBJ += hqflt/snes_ntsc/snes_ntsc.o + LIBS += -ldl endif ifeq ($(HAVE_FREETYPE), 1) diff --git a/audio/sdl.c b/audio/sdl.c index 94c936c624..389facf5f2 100644 --- a/audio/sdl.c +++ b/audio/sdl.c @@ -51,19 +51,6 @@ static void sdl_audio_cb(void *data, Uint8 *stream, int len) memset(stream + write_size, 0, len - write_size); } -// Interesting hack from http://www-graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 -static inline uint32_t next_pow2(uint32_t v) -{ - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; - return v; -} - static inline int find_num_frames(int rate, int latency) { int frames = (rate * latency) / 1000; diff --git a/audio/xaudio.c b/audio/xaudio.c index 563bf20c59..7b70364adc 100644 --- a/audio/xaudio.c +++ b/audio/xaudio.c @@ -67,19 +67,6 @@ static bool init_lib(void) return true; } -// Interesting hack from http://www-graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 -static inline uint32_t next_pow2(uint32_t v) -{ - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; - return v; -} - static void* __xa_init(const char* device, int rate, int latency) { if (!init_lib()) diff --git a/driver.c b/driver.c index 3632175111..4af2baa36e 100644 --- a/driver.c +++ b/driver.c @@ -20,8 +20,8 @@ #include "general.h" #include #include -#include "hqflt/filters.h" #include +#include #ifdef HAVE_CONFIG_H #include "config.h" @@ -212,28 +212,90 @@ void uninit_audio(void) free(g_extern.audio_data.conv_outsamples); g_extern.audio_data.conv_outsamples = NULL; } +#ifdef HAVE_FILTER +static void init_filter(void) +{ + if (g_extern.filter.active) + return; + if (*g_settings.video.filter_path == '\0') + return; + + g_extern.filter.lib = dylib_load(g_settings.video.filter_path); + if (!g_extern.filter.lib) + { + SSNES_ERR("Failed to load filter \"%s\"\n", g_settings.video.filter_path); + return; + } + + g_extern.filter.psize = dylib_proc(g_extern.filter.lib, "video_size"); + g_extern.filter.prender = dylib_proc(g_extern.filter.lib, "video_render"); + if (!g_extern.filter.psize || !g_extern.filter.prender) + { + SSNES_ERR("Failed to find functions in filter...\n"); + dylib_close(g_extern.filter.lib); + g_extern.filter.lib = NULL; + return; + } + + g_extern.filter.active = true; + + unsigned width = 512; + unsigned height = 512; + g_extern.filter.psize(&width, &height); + + unsigned pow2_x = next_pow2(ceil(width)); + unsigned pow2_y = next_pow2(ceil(height)); + unsigned maxsize = pow2_x > pow2_y ? pow2_x : pow2_y; + g_extern.filter.scale = maxsize / 256; + + g_extern.filter.buffer = malloc(256 * 256 * g_extern.filter.scale * g_extern.filter.scale * sizeof(uint32_t)); + g_extern.filter.pitch = 256 * g_extern.filter.scale * sizeof(uint32_t); + assert(g_extern.filter.buffer); + + g_extern.filter.colormap = malloc(32768 * sizeof(uint32_t)); + assert(g_extern.filter.colormap); + + // TODO: Taken from bSNES source. WTF does this do? + for (int i = 0; i < 32768; i++) + { + unsigned r = (i >> 10) & 31; + unsigned g = (i >> 5) & 31; + unsigned b = (i >> 0) & 31; + + r = (r << 3) | (r >> 2); + g = (g << 3) | (g >> 2); + b = (b << 3) | (b >> 2); + g_extern.filter.colormap[i] = (r << 16) | (g << 8) | (b << 0); + } +} +#endif + +static void deinit_filter(void) +{ + if (g_extern.filter.active) + { + g_extern.filter.active = false; + dylib_close(g_extern.filter.lib); + g_extern.filter.lib = NULL; + free(g_extern.filter.buffer); + free(g_extern.filter.colormap); + } +} + void init_video_input(void) { - int scale = 2; +#ifdef HAVE_FILTER + init_filter(); +#endif + + // We use at least 512x512 textures to accomodate for hi-res games. + unsigned scale = 2; find_video_driver(); find_input_driver(); - // We multiply scales with 2 to allow for hi-res games. -#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 + if (g_extern.filter.active) + scale = g_extern.filter.scale; video_info_t video = { .width = (g_settings.video.fullscreen) ? g_settings.video.fullscreen_x : (296 * g_settings.video.xscale), diff --git a/dynamic.c b/dynamic.c index aeb3a8c333..33d8be2118 100644 --- a/dynamic.c +++ b/dynamic.c @@ -25,31 +25,24 @@ #include -#ifdef HAVE_DYNAMIC - #ifdef _WIN32 #include -#define SYM(x) do { \ - p##x = ((void*)GetProcAddress(lib_handle, #x)); \ - if (p##x == NULL) { SSNES_ERR("Failed to load symbol: \"%s\"\n", #x); exit(1); } \ -} while(0) #else #include -#define SYM(x) do { \ - p##x = dlsym(lib_handle, #x); \ - if (p##x == NULL) { SSNES_ERR("Failed to load symbol: \"%s\"\n", #x); exit(1); } \ -} while(0) -#endif // _WIN32 -#endif // HAVE_DYNAMIC +#endif #ifdef HAVE_DYNAMIC -#ifdef _WIN32 -static HMODULE lib_handle; -#else -static void *lib_handle = NULL; -#endif +#define DLSYM(lib, x) dylib_proc(lib, #x) + +#define SYM(x) do { \ + p##x = DLSYM(lib_handle, x); \ + if (p##x == NULL) { SSNES_ERR("Failed to load symbol: \"%s\"\n", #x); exit(1); } \ +} while(0) + +static dylib_t lib_handle = NULL; #endif + void (*psnes_init)(void); void (*psnes_set_video_refresh)(snes_video_refresh_t); @@ -97,11 +90,7 @@ void (*psnes_term)(void); static void load_dynamic(void) { SSNES_LOG("Loading dynamic libsnes from: \"%s\"\n", g_settings.libsnes); -#ifdef _WIN32 - lib_handle = LoadLibrary(g_settings.libsnes); -#else - lib_handle = dlopen(g_settings.libsnes, RTLD_LAZY); -#endif + lib_handle = dylib_load(g_settings.libsnes); if (!lib_handle) { SSNES_ERR("Failed to open dynamic library: \"%s\"\n", g_settings.libsnes); @@ -186,12 +175,35 @@ void uninit_dlsym(void) { #ifdef HAVE_DYNAMIC if (lib_handle) - { -#ifdef _WIN32 - FreeLibrary(lib_handle); -#else - dlclose(lib_handle); -#endif - } + dylib_close(lib_handle); #endif } + +// Platform independent dylib loading. +dylib_t dylib_load(const char *path) +{ +#ifdef _WIN32 + return LoadLibrary(path); +#else + return dlopen(path, RTLD_LAZY); +#endif +} + +void* dylib_proc(dylib_t lib, const char *proc) +{ +#ifdef _WIN32 + return GetProcAddress(lib, proc); +#else + return dlsym(lib, proc); +#endif +} + +void dylib_close(dylib_t lib) +{ +#ifdef _WIN32 + FreeLibrary(lib); +#else + dlclose(lib); +#endif +} + diff --git a/dynamic.h b/dynamic.h index 2f738ef978..9073c55b3a 100644 --- a/dynamic.h +++ b/dynamic.h @@ -24,6 +24,16 @@ void init_dlsym(void); void uninit_dlsym(void); +#ifdef _WIN32 +typedef HMODULE dylib_t; +#else +typedef void* dylib_t; +#endif + +dylib_t dylib_load(const char *path); +void dylib_close(dylib_t lib); +void* dylib_proc(dylib_t lib, const char *proc); + extern void (*psnes_init)(void); extern void (*psnes_set_video_refresh)(snes_video_refresh_t); diff --git a/general.h b/general.h index 098c26d841..6f7c2dc111 100644 --- a/general.h +++ b/general.h @@ -28,6 +28,7 @@ #include "movie.h" #include "autosave.h" #include "netplay.h" +#include "dynamic.h" #ifdef HAVE_CONFIG_H #include "config.h" @@ -184,6 +185,20 @@ struct global int16_t *conv_outsamples; } audio_data; + struct + { + bool active; + uint32_t *buffer; + uint32_t *colormap; + unsigned pitch; + dylib_t lib; + unsigned scale; + + void (*psize)(unsigned *width, unsigned *height); + void (*prender)(uint32_t *colormap, uint32_t *output, unsigned outpitch, + const uint16_t *input, unsigned pitch, unsigned width, unsigned height); + } filter; + msg_queue_t *msg_queue; // Rewind support. @@ -240,4 +255,18 @@ extern struct global g_extern; fflush(stderr); \ } while(0) +static inline uint32_t next_pow2(uint32_t v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + #endif + + diff --git a/gfx/gl.c b/gfx/gl.c index 1c1d342846..ef711e8a32 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -141,6 +141,10 @@ typedef struct gl GLfloat tex_coords[8]; GLfloat fbo_tex_coords[8]; + GLenum texture_type; // XBGR1555 or RGBA + GLenum texture_fmt; + unsigned base_size; // 2 or 4 + #ifdef HAVE_FREETYPE font_renderer_t *font; GLuint font_tex; @@ -263,18 +267,6 @@ static inline void gl_init_font(gl_t *gl, const char *font_path, unsigned font_s #endif } -static inline unsigned next_pow_2(unsigned v) -{ - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; - return v; -} - static void gl_init_fbo(gl_t *gl, unsigned width, unsigned height) { if (!g_settings.video.render_to_texture) @@ -288,8 +280,8 @@ static void gl_init_fbo(gl_t *gl, unsigned width, unsigned height) float scale_x = g_settings.video.fbo_scale_x; float scale_y = g_settings.video.fbo_scale_y; - unsigned xscale = next_pow_2(ceil(scale_x)); - unsigned yscale = next_pow_2(ceil(scale_y)); + unsigned xscale = next_pow2(ceil(scale_x)); + unsigned yscale = next_pow2(ceil(scale_y)); SSNES_LOG("Internal FBO scale: (%u, %u)\n", xscale, yscale); gl->fbo_width = width * xscale; @@ -485,7 +477,7 @@ static void show_fps(void) frames++; } -static bool gl_frame(void *data, const uint16_t* frame, unsigned width, unsigned height, unsigned pitch, const char *msg) +static bool gl_frame(void *data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg) { gl_t *gl = data; @@ -517,10 +509,11 @@ static bool gl_frame(void *data, const uint16_t* frame, unsigned width, unsigned gl->last_height = height; glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(pitch)); glPixelStorei(GL_UNPACK_ROW_LENGTH, gl->tex_w); - uint8_t *tmp = calloc(1, gl->tex_w * gl->tex_h * sizeof(uint16_t)); + + void *tmp = calloc(1, gl->tex_w * gl->tex_h * gl->base_size); glTexSubImage2D(GL_TEXTURE_2D, - 0, 0, 0, gl->tex_w, gl->tex_h, GL_BGRA, - GL_UNSIGNED_SHORT_1_5_5_5_REV, tmp); + 0, 0, 0, gl->tex_w, gl->tex_h, gl->texture_type, + gl->texture_fmt, tmp); free(tmp); gl->tex_coords[0] = 0; @@ -534,10 +527,10 @@ static bool gl_frame(void *data, const uint16_t* frame, unsigned width, unsigned } - glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch >> 1); + glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch / gl->base_size); glTexSubImage2D(GL_TEXTURE_2D, - 0, 0, 0, width, height, GL_BGRA, - GL_UNSIGNED_SHORT_1_5_5_5_REV, frame); + 0, 0, 0, width, height, gl->texture_type, + gl->texture_fmt, frame); glFlush(); glDrawArrays(GL_QUADS, 0, 4); @@ -681,6 +674,10 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i else gl->tex_filter = GL_NEAREST; + gl->texture_type = video->rgb32 ? GL_RGBA : GL_BGRA; + gl->texture_fmt = video->rgb32 ? GL_UNSIGNED_INT_8_8_8_8 : GL_UNSIGNED_SHORT_1_5_5_5_REV; + gl->base_size = video->rgb32 ? sizeof(uint32_t) : sizeof(uint16_t); + glEnable(GL_TEXTURE_2D); glDisable(GL_DITHER); glDisable(GL_DEPTH_TEST); @@ -710,11 +707,13 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i gl->tex_w = 256 * video->input_scale; gl->tex_h = 256 * video->input_scale; - uint8_t *tmp = calloc(1, gl->tex_w * gl->tex_h * sizeof(uint32_t)); + + void *tmp = calloc(1, gl->tex_w * gl->tex_h * gl->base_size); glTexImage2D(GL_TEXTURE_2D, - 0, GL_RGBA, gl->tex_w, gl->tex_h, 0, GL_BGRA, - GL_UNSIGNED_SHORT_1_5_5_5_REV, tmp); + 0, GL_RGBA, gl->tex_w, gl->tex_h, 0, gl->texture_type, + gl->texture_fmt, tmp); free(tmp); + gl->last_width = gl->tex_w; gl->last_height = gl->tex_h; diff --git a/qb/config.params.sh b/qb/config.params.sh index 46fc194511..33c5aa4151 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -10,7 +10,7 @@ PACKAGE_VERSION=0.3.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 FFMPEG "Enable FFmpeg recording support" no -add_command_line_enable FILTER "Enable CPU filter support" no +add_command_line_enable FILTER "Disable CPU filter support" yes add_command_line_enable SRC "Enable libsamplerate support" no add_command_line_enable CONFIGFILE "Disable support for config file" yes add_command_line_enable CG "Enable Cg shader support" auto diff --git a/settings.c b/settings.c index ffc8b1d341..0dd6d719bc 100644 --- a/settings.c +++ b/settings.c @@ -19,7 +19,6 @@ #include "conf/config_file.h" #include #include -#include "hqflt/filters.h" #include "config.def.h" #ifdef HAVE_CONFIG_H diff --git a/ssnes.c b/ssnes.c index 9dff2bf224..a75d2f61a1 100644 --- a/ssnes.c +++ b/ssnes.c @@ -25,7 +25,6 @@ #include #include "driver.h" #include "file.h" -#include "filters.h" #include "general.h" #include "dynamic.h" #include "record/ffemu.h" @@ -100,12 +99,14 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height) const char *msg = msg_queue_pull(g_extern.msg_queue); #ifdef HAVE_FILTER - unsigned owidth = width; - unsigned oheight = height; - video_filter_size(&owidth, &oheight); if (g_extern.filter.active) { - video_filter_render(g_extern.filter.buffer, g_extern.filter.pitch >> 2, data, (height == 448 || height == 478) ? 512 : 1024, width, height); + unsigned owidth = width; + unsigned oheight = height; + g_extern.filter.psize(&owidth, &oheight); + + g_extern.filter.prender(g_extern.filter.colormap, g_extern.filter.buffer, + g_extern.filter.pitch >> 2, data, (height == 448 || height == 478) ? 512 : 1024, width, height); if (!driver.video->frame(driver.video_data, g_extern.filter.buffer, owidth, oheight, g_extern.filter.pitch, msg)) g_extern.video_active = false; }