mirror of
https://github.com/libretro/RetroArch.git
synced 2025-01-31 05:56:07 +00:00
An initial take on bsnes CPU filters.
This commit is contained in:
parent
96b978d55a
commit
f94dc125e5
6
Makefile
6
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)
|
||||
|
13
audio/sdl.c
13
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;
|
||||
|
@ -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())
|
||||
|
96
driver.c
96
driver.c
@ -20,8 +20,8 @@
|
||||
#include "general.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "hqflt/filters.h"
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
#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),
|
||||
|
70
dynamic.c
70
dynamic.c
@ -25,31 +25,24 @@
|
||||
|
||||
#include <libsnes.hpp>
|
||||
|
||||
#ifdef HAVE_DYNAMIC
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#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 <dlfcn.h>
|
||||
#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
|
||||
}
|
||||
|
||||
|
10
dynamic.h
10
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);
|
||||
|
29
general.h
29
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
|
||||
|
||||
|
||||
|
47
gfx/gl.c
47
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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include "conf/config_file.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "hqflt/filters.h"
|
||||
#include "config.def.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
11
ssnes.c
11
ssnes.c
@ -25,7 +25,6 @@
|
||||
#include <time.h>
|
||||
#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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user