diff --git a/general.h b/general.h index 52bade4117..c801ece649 100644 --- a/general.h +++ b/general.h @@ -185,6 +185,16 @@ struct settings float refresh_rate; bool threaded; + bool render_to_texture; + + struct + { + float scale_x; + float scale_y; + } fbo; + + char second_pass_shader[PATH_MAX]; + bool second_pass_smooth; char shader_dir[PATH_MAX]; char font_path[PATH_MAX]; diff --git a/gfx/gl.c b/gfx/gl.c index d27f6e5bc3..420a21f105 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -534,7 +534,7 @@ static void gl_create_fbo_textures(void *data) glGenTextures(gl->fbo_pass, gl->fbo_texture); - GLuint base_filt = gl->tex_filter; + GLuint base_filt = g_settings.video.second_pass_smooth ? GL_LINEAR : GL_NEAREST; for (int i = 0; i < gl->fbo_pass; i++) { glBindTexture(GL_TEXTURE_2D, gl->fbo_texture[i]); @@ -662,7 +662,7 @@ void gl_init_fbo(void *data, unsigned width, unsigned height) // No need to use FBOs. #ifndef RARCH_CONSOLE /* we always want FBO to be at least initialized on startup for consoles */ - if (gl_shader_num_func(gl) == 0) + if (!g_settings.video.render_to_texture && gl_shader_num_func(gl) == 0) return; #endif @@ -673,7 +673,7 @@ void gl_init_fbo(void *data, unsigned width, unsigned height) // No need to use FBOs. #ifndef RARCH_CONSOLE /* we always want FBO to be at least initialized on startup for consoles */ - if (gl_shader_num_func(gl) == 1 && !scale.valid) + if (gl_shader_num_func(gl) == 1 && !scale.valid && !g_settings.video.render_to_texture) return; #endif @@ -692,8 +692,8 @@ void gl_init_fbo(void *data, unsigned width, unsigned height) if (!scale.valid) { - scale.scale_x = 1.0f; - scale.scale_y = 1.0f; + scale.scale_x = g_settings.video.fbo.scale_x; + scale.scale_y = g_settings.video.fbo.scale_y; scale.type_x = scale.type_y = RARCH_SCALE_INPUT; scale.valid = true; } diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c index 588fefee73..5fed3263ed 100644 --- a/gfx/shader_glsl.c +++ b/gfx/shader_glsl.c @@ -1248,6 +1248,27 @@ bool gl_glsl_init(const char *path) if (!compile_programs(&gl_program[1], progs, num_progs)) return false; + // RetroArch custom two-pass with two different files. + if (num_progs == 1 && *g_settings.video.second_pass_shader && g_settings.video.render_to_texture) + { + unsigned secondary_progs = get_xml_shaders(g_settings.video.second_pass_shader, progs + 1, 1); + if (secondary_progs == 1) + { + if (!compile_programs(&gl_program[2], progs + 1, 1)) + { + RARCH_ERR("Failed to compile second pass shader.\n"); + return false; + } + + num_progs++; + } + else + { + RARCH_ERR("Did not find exactly one valid shader in secondary shader file.\n"); + return false; + } + } + for (unsigned i = 0; i <= num_progs; i++) find_uniforms(gl_program[i], &gl_uniforms[i]); diff --git a/retroarch.cfg b/retroarch.cfg index 2314307df8..d1610d4d50 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -110,6 +110,20 @@ # Defines a directory where XML shaders are kept. # video_shader_dir = +# Render to texture first. Useful when doing multi-pass shaders or control the output of shaders better. +# video_render_to_texture = false + +# Defines the video scale of render-to-texture. +# The output FBO size is scaled by these amounts against the input size (typically 256 * 224 for SNES). +# video_fbo_scale_x = 2.0 +# video_fbo_scale_y = 2.0 + +# Define shader to use for second pass (needs render-to-texture). +# video_second_pass_shader = "/path/to/second/shader.{cg,shader}" + +# Defines if bilinear filtering is used during second pass (needs render-to-texture). +# video_second_pass_smooth = true + # CPU-based filter. Path to a bSNES CPU filter (*.filter) # video_filter = diff --git a/settings.c b/settings.c index caa10ee2a4..77f7ef4b27 100644 --- a/settings.c +++ b/settings.c @@ -183,6 +183,13 @@ void config_set_defaults(void) g_settings.video.msg_color_g = ((message_color >> 8) & 0xff) / 255.0f; g_settings.video.msg_color_b = ((message_color >> 0) & 0xff) / 255.0f; +#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_HLSL) + g_settings.video.render_to_texture = render_to_texture; + g_settings.video.second_pass_smooth = second_pass_smooth; + g_settings.video.fbo.scale_x = fbo_scale_x; + g_settings.video.fbo.scale_y = fbo_scale_y; +#endif + g_settings.video.refresh_rate = refresh_rate; g_settings.video.post_filter_record = post_filter_record; g_settings.video.gpu_record = gpu_record; @@ -246,6 +253,7 @@ void config_set_defaults(void) g_extern.lifecycle_mode_state |= ((1ULL << MODE_INFO_DRAW) | (1ULL << MODE_MENU)); #if defined(HAVE_CG) || defined(HAVE_HLSL) || defined(HAVE_GLSL) strlcpy(g_settings.video.cg_shader_path, default_paths.shader_file, sizeof(g_settings.video.cg_shader_path)); + strlcpy(g_settings.video.second_pass_shader, default_paths.shader_file, sizeof(g_settings.video.second_pass_shader)); #endif strlcpy(g_settings.system_directory, default_paths.system_dir, sizeof(g_settings.system_directory)); @@ -447,6 +455,11 @@ bool config_load_file(const char *path) CONFIG_GET_PATH(video.cg_shader_path, "video_cg_shader"); CONFIG_GET_PATH(video.xml_shader_path, "video_xml_shader"); + CONFIG_GET_PATH(video.second_pass_shader, "video_second_pass_shader"); + CONFIG_GET_BOOL(video.render_to_texture, "video_render_to_texture"); + CONFIG_GET_FLOAT(video.fbo.scale_x, "video_fbo_scale_x"); + CONFIG_GET_FLOAT(video.fbo.scale_y, "video_fbo_scale_y"); + CONFIG_GET_BOOL(video.second_pass_smooth, "video_second_pass_smooth"); CONFIG_GET_BOOL(video.allow_rotate, "video_allow_rotate"); CONFIG_GET_PATH(video.font_path, "video_font_path"); @@ -1173,6 +1186,13 @@ bool config_save_file(const char *path) config_set_int(conf, "rewind_granularity", g_settings.rewind_granularity); config_set_string(conf, "video_cg_shader", g_settings.video.cg_shader_path); config_set_float(conf, "video_aspect_ratio", g_extern.system.aspect_ratio); +#ifdef HAVE_FBO + config_set_float(conf, "video_fbo_scale_x", g_settings.video.fbo.scale_x); + config_set_float(conf, "video_fbo_scale_y", g_settings.video.fbo.scale_y); + config_set_string(conf, "video_second_pass_shader", g_settings.video.second_pass_shader); + config_set_bool(conf, "video_render_to_texture", g_settings.video.render_to_texture); + config_set_bool(conf, "video_second_pass_smooth", g_settings.video.second_pass_smooth); +#endif config_set_bool(conf, "video_smooth", g_settings.video.smooth); config_set_bool(conf, "video_vsync", g_settings.video.vsync); config_set_int(conf, "aspect_ratio_index", g_settings.video.aspect_ratio_idx); @@ -1348,6 +1368,9 @@ void settings_set(uint64_t settings) if (settings & (1ULL << S_HW_TEXTURE_FILTER)) g_settings.video.smooth = !g_settings.video.smooth; + if (settings & (1ULL << S_HW_TEXTURE_FILTER_2)) + g_settings.video.second_pass_smooth = !g_settings.video.second_pass_smooth; + if (settings & (1ULL << S_OVERSCAN_DECREMENT)) { g_extern.console.screen.overscan_amount -= 0.01f; @@ -1409,6 +1432,21 @@ void settings_set(uint64_t settings) if (settings & (1ULL << S_SAVESTATE_INCREMENT)) g_extern.state_slot++; + if (settings & (1ULL << S_SCALE_ENABLED)) + g_settings.video.render_to_texture = !g_settings.video.render_to_texture; + + if (settings & (1ULL << S_SCALE_FACTOR_DECREMENT)) + { + g_settings.video.fbo.scale_x -= 1.0f; + g_settings.video.fbo.scale_y -= 1.0f; + } + + if (settings & (1ULL << S_SCALE_FACTOR_INCREMENT)) + { + g_settings.video.fbo.scale_x += 1.0f; + g_settings.video.fbo.scale_y += 1.0f; + } + if (settings & (1ULL << S_THROTTLE)) { if(!g_extern.system.force_nonblock) @@ -1465,6 +1503,9 @@ void settings_set(uint64_t settings) if (settings & (1ULL << S_DEF_HW_TEXTURE_FILTER)) g_settings.video.smooth = video_smooth; + if (settings & (1ULL << S_DEF_HW_TEXTURE_FILTER_2)) + g_settings.video.second_pass_smooth = second_pass_smooth; + if (settings & (1ULL << S_DEF_OVERSCAN)) { g_extern.console.screen.overscan_amount = 0.0f; @@ -1486,6 +1527,19 @@ void settings_set(uint64_t settings) if (settings & (1ULL << S_DEF_SAVE_STATE)) g_extern.state_slot = 0; + if (settings & (1ULL << S_DEF_SCALE_ENABLED)) + { + g_settings.video.render_to_texture = render_to_texture; + g_settings.video.fbo.scale_x = fbo_scale_x; + g_settings.video.fbo.scale_y = fbo_scale_y; + } + + if (settings & (1ULL << S_DEF_SCALE_FACTOR)) + { + g_settings.video.fbo.scale_x = fbo_scale_x; + g_settings.video.fbo.scale_y = fbo_scale_y; + } + if (settings & (1ULL << S_DEF_REFRESH_RATE)) g_settings.video.refresh_rate = refresh_rate;