diff --git a/command.h b/command.h index 8fa57ec7b3..6f614fabd7 100644 --- a/command.h +++ b/command.h @@ -488,6 +488,7 @@ static const struct cmd_map map[] = { { "DISK_PREV", RARCH_DISK_PREV }, { "SHADER_TOGGLE", RARCH_SHADER_TOGGLE }, + { "SHADER_HOLD", RARCH_SHADER_HOLD }, { "SHADER_NEXT", RARCH_SHADER_NEXT }, { "SHADER_PREV", RARCH_SHADER_PREV }, diff --git a/config.def.keybinds.h b/config.def.keybinds.h index f4d6a6bb28..c83b7059c7 100644 --- a/config.def.keybinds.h +++ b/config.def.keybinds.h @@ -472,6 +472,13 @@ static const struct retro_keybind retro_keybinds_1[] = { RARCH_SHADER_TOGGLE, NO_BTN, NO_BTN, 0, true }, + { + NULL, NULL, + AXIS_NONE, AXIS_NONE, + MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_HOLD, RETROK_UNKNOWN, + RARCH_SHADER_HOLD, NO_BTN, NO_BTN, 0, + true + }, { NULL, NULL, AXIS_NONE, AXIS_NONE, @@ -1114,6 +1121,13 @@ static const struct retro_keybind retro_keybinds_1[] = { RARCH_SHADER_TOGGLE, NO_BTN, NO_BTN, 0, true }, + { + NULL, NULL, + AXIS_NONE, AXIS_NONE, + MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_HOLD, RETROK_UNKNOWN, + RARCH_SHADER_HOLD, NO_BTN, NO_BTN, 0, + true + }, { NULL, NULL, AXIS_NONE, AXIS_NONE, @@ -1766,6 +1780,13 @@ static const struct retro_keybind retro_keybinds_1[] = { RARCH_SHADER_TOGGLE, NO_BTN, NO_BTN, 0, true }, + { + NULL, NULL, + AXIS_NONE, AXIS_NONE, + MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_HOLD, RETROK_UNKNOWN, + RARCH_SHADER_HOLD, NO_BTN, NO_BTN, 0, + true + }, { NULL, NULL, AXIS_NONE, AXIS_NONE, diff --git a/configuration.c b/configuration.c index cba24cabf5..fc547dfdb1 100644 --- a/configuration.c +++ b/configuration.c @@ -373,6 +373,7 @@ const struct input_bind_map input_config_bind_map[RARCH_BIND_LIST_END_NULL] = { DECLARE_META_BIND(2, disk_prev, RARCH_DISK_PREV, MENU_ENUM_LABEL_VALUE_INPUT_META_DISK_PREV), DECLARE_META_BIND(2, shader_toggle, RARCH_SHADER_TOGGLE, MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_TOGGLE), + DECLARE_META_BIND(2, shader_hold, RARCH_SHADER_HOLD, MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_HOLD), DECLARE_META_BIND(2, shader_next, RARCH_SHADER_NEXT, MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_NEXT), DECLARE_META_BIND(2, shader_prev, RARCH_SHADER_PREV, MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_PREV), diff --git a/gfx/common/gl3_defines.h b/gfx/common/gl3_defines.h index 2a90ac3283..c3ed727490 100644 --- a/gfx/common/gl3_defines.h +++ b/gfx/common/gl3_defines.h @@ -73,6 +73,7 @@ typedef struct gl3 const gfx_ctx_driver_t *ctx_driver; void *ctx_data; gl3_filter_chain_t *filter_chain; + gl3_filter_chain_t *filter_chain_default; GLuint *overlay_tex; float *overlay_vertex_coord; float *overlay_tex_coord; diff --git a/gfx/common/vulkan_common.h b/gfx/common/vulkan_common.h index 3a6dc9dd91..a83dff025c 100644 --- a/gfx/common/vulkan_common.h +++ b/gfx/common/vulkan_common.h @@ -551,6 +551,7 @@ struct vk_draw_triangles typedef struct vk { vulkan_filter_chain_t *filter_chain; + vulkan_filter_chain_t *filter_chain_default; vulkan_context_t *context; void *ctx_data; const gfx_ctx_driver_t *ctx_driver; diff --git a/gfx/drivers/d3d11.c b/gfx/drivers/d3d11.c index 29e81d591d..5ea83039a0 100644 --- a/gfx/drivers/d3d11.c +++ b/gfx/drivers/d3d11.c @@ -1004,6 +1004,7 @@ static uint32_t d3d11_get_flags(void *data) #if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS) BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG); BIT32_SET(flags, GFX_CTX_FLAGS_SUBFRAME_SHADERS); + BIT32_SET(flags, GFX_CTX_FLAGS_FAST_TOGGLE_SHADERS); #endif return flags; @@ -3056,7 +3057,7 @@ static bool d3d11_gfx_frame( texture = d3d11->frame.texture; - if (d3d11->shader_preset) + if (d3d11->shader_preset && video_info->shader_active) { for (i = 0; i < d3d11->shader_preset->passes; i++) { diff --git a/gfx/drivers/d3d12.c b/gfx/drivers/d3d12.c index b9dcbe9923..82c5664b69 100644 --- a/gfx/drivers/d3d12.c +++ b/gfx/drivers/d3d12.c @@ -1152,6 +1152,7 @@ static uint32_t d3d12_get_flags(void *data) #if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS) BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG); BIT32_SET(flags, GFX_CTX_FLAGS_SUBFRAME_SHADERS); + BIT32_SET(flags, GFX_CTX_FLAGS_FAST_TOGGLE_SHADERS); #endif return flags; @@ -3565,7 +3566,7 @@ static bool d3d12_gfx_frame( texture = d3d12->frame.texture; - if (d3d12->shader_preset) + if (d3d12->shader_preset && video_info->shader_active) { cmd->lpVtbl->SetGraphicsRootSignature(cmd, d3d12->desc.sl_rootSignature); diff --git a/gfx/drivers/gl3.c b/gfx/drivers/gl3.c index 5d7bef3332..7c1c907786 100644 --- a/gfx/drivers/gl3.c +++ b/gfx/drivers/gl3.c @@ -1332,6 +1332,10 @@ static void gl3_destroy_resources(gl3_t *gl) gl3_filter_chain_free(gl->filter_chain); gl->filter_chain = NULL; + if (gl->filter_chain_default) + gl3_filter_chain_free(gl->filter_chain_default); + gl->filter_chain_default = NULL; + glBindVertexArray(0); if (gl->vao != 0) glDeleteVertexArrays(1, &gl->vao); @@ -1710,14 +1714,17 @@ static bool gl3_init_default_filter_chain(gl3_t *gl) if (!gl->ctx_driver) return false; - gl->filter_chain = gl3_filter_chain_create_default( + if (gl->filter_chain_default) + return true; + + gl->filter_chain_default = gl3_filter_chain_create_default( gl->video_info.smooth ? GLSLANG_FILTER_CHAIN_LINEAR : GLSLANG_FILTER_CHAIN_NEAREST); - if (!gl->filter_chain) + if (!gl->filter_chain_default) { - RARCH_ERR("Failed to create filter chain.\n"); + RARCH_ERR("[GLCore]: Failed to create default filter chain.\n"); return false; } @@ -1726,6 +1733,9 @@ static bool gl3_init_default_filter_chain(gl3_t *gl) static bool gl3_init_filter_chain_preset(gl3_t *gl, const char *shader_path) { + if (!gl->ctx_driver) + return false; + gl->filter_chain = gl3_filter_chain_create_from_preset( shader_path, gl->video_info.smooth @@ -2107,7 +2117,7 @@ static void *gl3_init(const video_info_t *video, if (!gl_check_error(&error_string)) { - RARCH_ERR("%s\n", error_string); + RARCH_ERR("[GLCore]: %s\n", error_string); free(error_string); goto error; } @@ -2650,6 +2660,7 @@ static bool gl3_frame(void *data, const void *frame, { struct gl3_filter_chain_texture texture; struct gl3_streamed_texture *streamed = NULL; + gl3_filter_chain_t *filter_chain = NULL; gl3_t *gl = (gl3_t*)data; unsigned width = video_info->width; unsigned height = video_info->height; @@ -2734,26 +2745,44 @@ static bool gl3_frame(void *data, const void *frame, texture.padded_width = streamed->width; texture.padded_height = streamed->height; } - gl3_filter_chain_set_frame_count(gl->filter_chain, frame_count); + + /* Fast toggle shader filter chain logic */ + filter_chain = gl->filter_chain; + + if (!video_info->shader_active && gl->filter_chain != gl->filter_chain_default) + { + if (!gl->filter_chain_default) + gl3_init_default_filter_chain(gl); + + if (gl->filter_chain_default) + filter_chain = gl->filter_chain_default; + else + return false; + } + + if (!filter_chain && gl->filter_chain_default) + filter_chain = gl->filter_chain_default; + + gl3_filter_chain_set_frame_count(filter_chain, frame_count); #ifdef HAVE_REWIND - gl3_filter_chain_set_frame_direction(gl->filter_chain, state_manager_frame_is_reversed() ? -1 : 1); + gl3_filter_chain_set_frame_direction(filter_chain, state_manager_frame_is_reversed() ? -1 : 1); #else - gl3_filter_chain_set_frame_direction(gl->filter_chain, 1); + gl3_filter_chain_set_frame_direction(filter_chain, 1); #endif - gl3_filter_chain_set_frame_time_delta(gl->filter_chain, (uint32_t)video_driver_get_frame_time_delta_usec()); + gl3_filter_chain_set_frame_time_delta(filter_chain, (uint32_t)video_driver_get_frame_time_delta_usec()); - gl3_filter_chain_set_original_fps(gl->filter_chain, video_driver_get_original_fps()); + gl3_filter_chain_set_original_fps(filter_chain, video_driver_get_original_fps()); - gl3_filter_chain_set_rotation(gl->filter_chain, retroarch_get_rotation()); + gl3_filter_chain_set_rotation(filter_chain, retroarch_get_rotation()); - gl3_filter_chain_set_core_aspect(gl->filter_chain, video_driver_get_core_aspect()); + gl3_filter_chain_set_core_aspect(filter_chain, video_driver_get_core_aspect()); /* OriginalAspectRotated: return 1/aspect for 90 and 270 rotated content */ uint32_t rot = retroarch_get_rotation(); float core_aspect_rot = video_driver_get_core_aspect(); if (rot == 1 || rot == 3) core_aspect_rot = 1/core_aspect_rot; - gl3_filter_chain_set_core_aspect_rot(gl->filter_chain, core_aspect_rot); + gl3_filter_chain_set_core_aspect_rot(filter_chain, core_aspect_rot); /* Sub-frame info for multiframe shaders (per real content frame). Should always be 1 for non-use of subframes*/ @@ -2765,13 +2794,13 @@ static bool gl3_frame(void *data, const void *frame, || video_info->runloop_is_paused || (gl->flags & GL3_FLAG_MENU_TEXTURE_ENABLE)) gl3_filter_chain_set_shader_subframes( - gl->filter_chain, 1); + filter_chain, 1); else gl3_filter_chain_set_shader_subframes( - gl->filter_chain, video_info->shader_subframes); + filter_chain, video_info->shader_subframes); gl3_filter_chain_set_current_shader_subframe( - gl->filter_chain, 1); + filter_chain, 1); } #ifdef GL3_ROLLING_SCANLINE_SIMULATION @@ -2783,25 +2812,25 @@ static bool gl3_frame(void *data, const void *frame, && !video_info->runloop_is_paused && (!(gl->flags & GL3_FLAG_MENU_TEXTURE_ENABLE))) gl3_filter_chain_set_simulate_scanline( - gl->filter_chain, true); + filter_chain, true); else gl3_filter_chain_set_simulate_scanline( - gl->filter_chain, false); + filter_chain, false); #endif /* GL3_ROLLING_SCANLINE_SIMULATION */ - gl3_filter_chain_set_input_texture(gl->filter_chain, &texture); - gl3_filter_chain_build_offscreen_passes(gl->filter_chain, + gl3_filter_chain_set_input_texture(filter_chain, &texture); + gl3_filter_chain_build_offscreen_passes(filter_chain, &gl->filter_chain_vp); glBindFramebuffer(GL_FRAMEBUFFER, 0); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); - gl3_filter_chain_build_viewport_pass(gl->filter_chain, + gl3_filter_chain_build_viewport_pass(filter_chain, &gl->filter_chain_vp, (gl->flags & GL3_FLAG_HW_RENDER_BOTTOM_LEFT) ? gl->mvp.data : gl->mvp_yflip.data); - gl3_filter_chain_end_frame(gl->filter_chain); + gl3_filter_chain_end_frame(filter_chain); #ifdef HAVE_OVERLAY if ((gl->flags & GL3_FLAG_OVERLAY_ENABLE) && overlay_behind_menu) @@ -2940,9 +2969,9 @@ static bool gl3_frame(void *data, const void *frame, for (i = 1; i < (int) video_info->shader_subframes; i++) { gl3_filter_chain_set_shader_subframes( - gl->filter_chain, video_info->shader_subframes); + filter_chain, video_info->shader_subframes); gl3_filter_chain_set_current_shader_subframe( - gl->filter_chain, i+1); + filter_chain, i+1); if (!gl3_frame(gl, NULL, 0, 0, frame_count, 0, msg, video_info)) @@ -2975,6 +3004,7 @@ static uint32_t gl3_get_flags(void *data) BIT32_SET(flags, GFX_CTX_FLAGS_SCREENSHOTS_SUPPORTED); BIT32_SET(flags, GFX_CTX_FLAGS_OVERLAY_BEHIND_MENU_SUPPORTED); BIT32_SET(flags, GFX_CTX_FLAGS_SUBFRAME_SHADERS); + BIT32_SET(flags, GFX_CTX_FLAGS_FAST_TOGGLE_SHADERS); return flags; } diff --git a/gfx/drivers/vulkan.c b/gfx/drivers/vulkan.c index 7063b3e872..0a80a93459 100644 --- a/gfx/drivers/vulkan.c +++ b/gfx/drivers/vulkan.c @@ -2829,6 +2829,9 @@ static bool vulkan_init_default_filter_chain(vk_t *vk) if (!vk->context) return false; + if (vk->filter_chain_default) + return true; + info.device = vk->context->device; info.gpu = vk->context->gpu; info.memory_properties = &vk->context->memory_properties; @@ -2844,15 +2847,15 @@ static bool vulkan_init_default_filter_chain(vk_t *vk) info.swapchain.render_pass = vk->render_pass; info.swapchain.num_indices = vk->context->num_swapchain_images; - vk->filter_chain = vulkan_filter_chain_create_default( + vk->filter_chain_default = vulkan_filter_chain_create_default( &info, vk->video.smooth ? GLSLANG_FILTER_CHAIN_LINEAR : GLSLANG_FILTER_CHAIN_NEAREST); - if (!vk->filter_chain) + if (!vk->filter_chain_default) { - RARCH_ERR("Failed to create filter chain.\n"); + RARCH_ERR("[Vulkan]: Failed to create default filter chain.\n"); return false; } @@ -2901,6 +2904,9 @@ static bool vulkan_init_filter_chain_preset(vk_t *vk, const char *shader_path) { struct vulkan_filter_chain_create_info info; + if (!vk->context) + return false; + info.device = vk->context->device; info.gpu = vk->context->gpu; info.memory_properties = &vk->context->memory_properties; @@ -3106,6 +3112,9 @@ static void vulkan_free(void *data) if (vk->filter_chain) vulkan_filter_chain_free((vulkan_filter_chain_t*)vk->filter_chain); + if (vk->filter_chain_default) + vulkan_filter_chain_free((vulkan_filter_chain_t*)vk->filter_chain_default); + #ifdef VULKAN_HDR_SWAPCHAIN if (vk->context->flags & VK_CTX_FLAG_HDR_SUPPORT) { @@ -3647,10 +3656,10 @@ static void vulkan_check_swapchain(vk_t *vk) filter_info.num_indices = vk->context->num_swapchain_images; if ( !vulkan_filter_chain_update_swapchain_info( - (vulkan_filter_chain_t*)vk->filter_chain, + (vk->filter_chain) ? vk->filter_chain : vk->filter_chain_default, &filter_info) ) - RARCH_ERR("Failed to update filter chain info. This will probably lead to a crash ...\n"); + RARCH_ERR("[Vulkan]: Failed to update filter chain info.\n"); } static void vulkan_set_nonblock_state(void *data, bool state, @@ -4365,6 +4374,7 @@ static bool vulkan_frame(void *data, const void *frame, VkCommandBufferBeginInfo begin_info; VkSemaphore signal_semaphores[2]; vk_t *vk = (vk_t*)data; + vulkan_filter_chain_t *filter_chain = NULL; bool waits_for_semaphores = false; unsigned width = video_info->width; unsigned height = video_info->height; @@ -4391,11 +4401,29 @@ static bool vulkan_frame(void *data, const void *frame, unsigned swapchain_index = vk->context->current_swapchain_index; bool overlay_behind_menu = video_info->overlay_behind_menu; + bool use_main_buffer = true; + + /* Fast toggle shader filter chain logic */ + filter_chain = vk->filter_chain; + + if (!video_info->shader_active && vk->filter_chain != vk->filter_chain_default) + { + if (!vk->filter_chain_default) + vulkan_init_default_filter_chain(vk); + + if (vk->filter_chain_default) + filter_chain = vk->filter_chain_default; + else + return false; + } + + if (!filter_chain && vk->filter_chain_default) + filter_chain = vk->filter_chain_default; #ifdef VULKAN_HDR_SWAPCHAIN - bool use_main_buffer = + use_main_buffer = ( vk->context->flags & VK_CTX_FLAG_HDR_ENABLE) - && (!vk->filter_chain || !vulkan_filter_chain_emits_hdr10(vk->filter_chain)); + && (!filter_chain || !vulkan_filter_chain_emits_hdr10(filter_chain)); #endif /* VULKAN_HDR_SWAPCHAIN */ /* Bookkeeping on start of frame. */ @@ -4516,9 +4544,9 @@ static bool vulkan_frame(void *data, const void *frame, /* Notify filter chain about the new sync index. */ vulkan_filter_chain_notify_sync_index( - (vulkan_filter_chain_t*)vk->filter_chain, frame_index); + (vulkan_filter_chain_t*)filter_chain, frame_index); vulkan_filter_chain_set_frame_count( - (vulkan_filter_chain_t*)vk->filter_chain, frame_count); + (vulkan_filter_chain_t*)filter_chain, frame_count); /* Sub-frame info for multiframe shaders (per real content frame). Should always be 1 for non-use of subframes*/ @@ -4531,13 +4559,13 @@ static bool vulkan_frame(void *data, const void *frame, || (vk->context->swap_interval > 1) || (vk->flags & VK_FLAG_MENU_ENABLE)) vulkan_filter_chain_set_shader_subframes( - (vulkan_filter_chain_t*)vk->filter_chain, 1); + (vulkan_filter_chain_t*)filter_chain, 1); else vulkan_filter_chain_set_shader_subframes( - (vulkan_filter_chain_t*)vk->filter_chain, video_info->shader_subframes); + (vulkan_filter_chain_t*)filter_chain, video_info->shader_subframes); vulkan_filter_chain_set_current_shader_subframe( - (vulkan_filter_chain_t*)vk->filter_chain, 1); + (vulkan_filter_chain_t*)filter_chain, 1); } #ifdef VULKAN_ROLLING_SCANLINE_SIMULATION @@ -4551,32 +4579,32 @@ static bool vulkan_frame(void *data, const void *frame, && (!(vk->flags & VK_FLAG_MENU_ENABLE)) && !(vk->context->swap_interval > 1)) vulkan_filter_chain_set_simulate_scanline( - (vulkan_filter_chain_t*)vk->filter_chain, true); + (vulkan_filter_chain_t*)filter_chain, true); else vulkan_filter_chain_set_simulate_scanline( - (vulkan_filter_chain_t*)vk->filter_chain, false); + (vulkan_filter_chain_t*)filter_chain, false); #endif /* VULKAN_ROLLING_SCANLINE_SIMULATION */ #ifdef HAVE_REWIND vulkan_filter_chain_set_frame_direction( - (vulkan_filter_chain_t*)vk->filter_chain, + (vulkan_filter_chain_t*)filter_chain, state_manager_frame_is_reversed() ? -1 : 1); #else vulkan_filter_chain_set_frame_direction( - (vulkan_filter_chain_t*)vk->filter_chain, + (vulkan_filter_chain_t*)filter_chain, 1); #endif vulkan_filter_chain_set_frame_time_delta( - (vulkan_filter_chain_t*)vk->filter_chain, (uint32_t)video_driver_get_frame_time_delta_usec()); + (vulkan_filter_chain_t*)filter_chain, (uint32_t)video_driver_get_frame_time_delta_usec()); vulkan_filter_chain_set_original_fps( - (vulkan_filter_chain_t*)vk->filter_chain, video_driver_get_original_fps()); + (vulkan_filter_chain_t*)filter_chain, video_driver_get_original_fps()); vulkan_filter_chain_set_rotation( - (vulkan_filter_chain_t*)vk->filter_chain, retroarch_get_rotation()); + (vulkan_filter_chain_t*)filter_chain, retroarch_get_rotation()); vulkan_filter_chain_set_core_aspect( - (vulkan_filter_chain_t*)vk->filter_chain, video_driver_get_core_aspect()); + (vulkan_filter_chain_t*)filter_chain, video_driver_get_core_aspect()); /* OriginalAspectRotated: return 1/aspect for 90 and 270 rotated content */ uint32_t rot = retroarch_get_rotation(); @@ -4584,7 +4612,7 @@ static bool vulkan_frame(void *data, const void *frame, if (rot == 1 || rot == 3) core_aspect_rot = 1/core_aspect_rot; vulkan_filter_chain_set_core_aspect_rot( - (vulkan_filter_chain_t*)vk->filter_chain, core_aspect_rot); + (vulkan_filter_chain_t*)filter_chain, core_aspect_rot); /* Render offscreen filter chain passes. */ { @@ -4648,13 +4676,13 @@ static bool vulkan_frame(void *data, const void *frame, } vulkan_filter_chain_set_input_texture((vulkan_filter_chain_t*) - vk->filter_chain, &input); + filter_chain, &input); } vulkan_set_viewport(vk, width, height, false, true); vulkan_filter_chain_build_offscreen_passes( - (vulkan_filter_chain_t*)vk->filter_chain, + (vulkan_filter_chain_t*)filter_chain, vk->cmd, &vk->vk_vp); #if defined(HAVE_MENU) @@ -4712,7 +4740,7 @@ static bool vulkan_frame(void *data, const void *frame, vkCmdBeginRenderPass(vk->cmd, &rp_info, VK_SUBPASS_CONTENTS_INLINE); vulkan_filter_chain_build_viewport_pass( - (vulkan_filter_chain_t*)vk->filter_chain, vk->cmd, + (vulkan_filter_chain_t*)filter_chain, vk->cmd, &vk->vk_vp, vk->mvp.data); #ifdef HAVE_OVERLAY @@ -4802,7 +4830,7 @@ static bool vulkan_frame(void *data, const void *frame, /* End the filter chain frame. * This must happen outside a render pass. */ - vulkan_filter_chain_end_frame((vulkan_filter_chain_t*)vk->filter_chain, vk->cmd); + vulkan_filter_chain_end_frame((vulkan_filter_chain_t*)filter_chain, vk->cmd); if ( (backbuffer->image != VK_NULL_HANDLE) @@ -5115,9 +5143,9 @@ static bool vulkan_frame(void *data, const void *frame, for (j = 1; j < (int) video_info->shader_subframes; j++) { vulkan_filter_chain_set_shader_subframes( - (vulkan_filter_chain_t*)vk->filter_chain, video_info->shader_subframes); + (vulkan_filter_chain_t*)filter_chain, video_info->shader_subframes); vulkan_filter_chain_set_current_shader_subframe( - (vulkan_filter_chain_t*)vk->filter_chain, j+1); + (vulkan_filter_chain_t*)filter_chain, j+1); if (!vulkan_frame(vk, NULL, 0, 0, frame_count, 0, msg, video_info)) { @@ -5466,6 +5494,7 @@ static uint32_t vulkan_get_flags(void *data) BIT32_SET(flags, GFX_CTX_FLAGS_SCREENSHOTS_SUPPORTED); BIT32_SET(flags, GFX_CTX_FLAGS_OVERLAY_BEHIND_MENU_SUPPORTED); BIT32_SET(flags, GFX_CTX_FLAGS_SUBFRAME_SHADERS); + BIT32_SET(flags, GFX_CTX_FLAGS_FAST_TOGGLE_SHADERS); return flags; } diff --git a/gfx/video_defines.h b/gfx/video_defines.h index 4a5845b3b0..34ebb43bc4 100644 --- a/gfx/video_defines.h +++ b/gfx/video_defines.h @@ -248,7 +248,8 @@ enum display_flags GFX_CTX_FLAGS_SCREENSHOTS_SUPPORTED, GFX_CTX_FLAGS_OVERLAY_BEHIND_MENU_SUPPORTED, GFX_CTX_FLAGS_CRT_SWITCHRES, - GFX_CTX_FLAGS_SUBFRAME_SHADERS + GFX_CTX_FLAGS_SUBFRAME_SHADERS, + GFX_CTX_FLAGS_FAST_TOGGLE_SHADERS }; enum shader_uniform_type diff --git a/gfx/video_driver.c b/gfx/video_driver.c index 7fb5f83c65..9efecff999 100644 --- a/gfx/video_driver.c +++ b/gfx/video_driver.c @@ -2818,6 +2818,7 @@ void video_driver_build_info(video_frame_info_t *video_info) #ifdef HAVE_MENU struct menu_state *menu_st = menu_state_get_ptr(); #endif + uint8_t menu_shdr_flags = 0; #ifdef HAVE_GFX_WIDGETS dispgfx_widget_t *p_dispwidget = dispwidget_get_ptr(); #endif @@ -2826,6 +2827,12 @@ void video_driver_build_info(video_frame_info_t *video_info) VIDEO_DRIVER_IS_THREADED_INTERNAL(video_st); VIDEO_DRIVER_THREADED_LOCK(video_st, is_threaded); +#endif + +#ifdef HAVE_MENU +#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) + menu_shdr_flags = menu_shader_get()->flags; +#endif #endif custom_vp = &settings->video_vp_custom; #ifdef HAVE_GFX_WIDGETS @@ -2896,6 +2903,7 @@ void video_driver_build_info(video_frame_info_t *video_info) video_info->scale_width = video_st->scale_width; video_info->scale_height = video_st->scale_height; + video_info->shader_active = !(menu_shdr_flags & SHDR_FLAG_DISABLED) ? true : false; video_info->hdr_enable = settings->bools.video_hdr_enable; video_info->libretro_running = false; diff --git a/gfx/video_driver.h b/gfx/video_driver.h index a5cb80d986..7492155403 100644 --- a/gfx/video_driver.h +++ b/gfx/video_driver.h @@ -494,6 +494,7 @@ typedef struct video_frame_info bool hdr_enable; bool overlay_behind_menu; bool scan_subframes; + bool shader_active; } video_frame_info_t; typedef void (*update_window_title_cb)(void*); diff --git a/gfx/video_shader_parse.c b/gfx/video_shader_parse.c index c66d19f249..37b85e022f 100644 --- a/gfx/video_shader_parse.c +++ b/gfx/video_shader_parse.c @@ -3140,27 +3140,41 @@ const char *video_shader_get_current_shader_preset(void) return NULL; } -void video_shader_toggle(settings_t *settings) +void video_shader_toggle(settings_t *settings, bool write) { - bool toggle = !settings->bools.video_shader_enable; + bool enabled = settings->bools.video_shader_enable; #ifdef HAVE_MENU struct video_shader *menu_shdr = menu_shader_get(); struct menu_state *menu_st = menu_state_get_ptr(); - menu_shdr->flags |= SHDR_FLAG_MODIFIED; - if (toggle) + menu_shdr->flags &= ~SHDR_FLAG_MODIFIED; + menu_shdr->flags &= ~SHDR_FLAG_TEMPORARY; +#endif + + /* Cold start from hotkey requires enabling shaders initially */ + if (!write && !enabled) + { + write = true; + enabled = true; + configuration_set_bool(settings, settings->bools.video_shader_enable, true); + } +#ifdef HAVE_MENU + else if (!write) + enabled = (menu_shdr->flags & SHDR_FLAG_DISABLED); +#endif + +#ifdef HAVE_MENU + if (enabled) menu_shdr->flags &= ~SHDR_FLAG_DISABLED; else menu_shdr->flags |= SHDR_FLAG_DISABLED; + if (!write && video_driver_test_all_flags(GFX_CTX_FLAGS_FAST_TOGGLE_SHADERS)) + menu_shdr->flags |= SHDR_FLAG_TEMPORARY; + menu_st->flags |= MENU_ST_FLAG_ENTRIES_NEED_REFRESH | MENU_ST_FLAG_PREVENT_POPULATE; #endif command_event(CMD_EVENT_SHADERS_APPLY_CHANGES, NULL); - - /* TODO/FIXME: Due to general_write_handler being called twice, - * this has be done in this order in order to truly disable */ - if (!toggle) - configuration_set_bool(settings, settings->bools.video_shader_enable, toggle); } diff --git a/gfx/video_shader_parse.h b/gfx/video_shader_parse.h index 3a35463647..d95878f073 100644 --- a/gfx/video_shader_parse.h +++ b/gfx/video_shader_parse.h @@ -88,7 +88,8 @@ enum video_shader_flags /* Indicative of whether shader was modified - * for instance from the menus */ SHDR_FLAG_MODIFIED = (1 << 1), - SHDR_FLAG_DISABLED = (1 << 2) + SHDR_FLAG_DISABLED = (1 << 2), + SHDR_FLAG_TEMPORARY = (1 << 3) }; enum gfx_wrap_type @@ -289,7 +290,7 @@ bool video_shader_apply_shader( const char *video_shader_get_preset_extension(enum rarch_shader_type type); -void video_shader_toggle(settings_t *settings); +void video_shader_toggle(settings_t *settings, bool write); RETRO_END_DECLS diff --git a/input/input_defines.h b/input/input_defines.h index 7c03962b2a..cb47fe4d6b 100644 --- a/input/input_defines.h +++ b/input/input_defines.h @@ -155,6 +155,7 @@ enum RARCH_DISK_PREV, RARCH_SHADER_TOGGLE, + RARCH_SHADER_HOLD, RARCH_SHADER_NEXT, RARCH_SHADER_PREV, diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index c52def956d..7d766fc49d 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -4039,6 +4039,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_INPUT_META_SHADER_TOGGLE, "Switches the currently selected shader on/off." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_HOLD, + "Shaders (Hold)" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_INPUT_META_SHADER_HOLD, + "Keeps the currently selected shader on/off while key is pressed." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_NEXT, "Next Shader" diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 4d45a099de..94193a84a4 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -451,6 +451,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_meta_disk_next, ME DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_meta_disk_prev, MENU_ENUM_SUBLABEL_INPUT_META_DISK_PREV) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_meta_shader_toggle, MENU_ENUM_SUBLABEL_INPUT_META_SHADER_TOGGLE) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_meta_shader_hold, MENU_ENUM_SUBLABEL_INPUT_META_SHADER_HOLD) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_meta_shader_next, MENU_ENUM_SUBLABEL_INPUT_META_SHADER_NEXT) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_meta_shader_prev, MENU_ENUM_SUBLABEL_INPUT_META_SHADER_PREV) #ifdef HAVE_CHEATS @@ -2383,6 +2384,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case RARCH_SHADER_TOGGLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_meta_shader_toggle); return 0; + case RARCH_SHADER_HOLD: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_meta_shader_hold); + return 0; case RARCH_CHEAT_TOGGLE: #ifdef HAVE_CHEATS diff --git a/menu/menu_driver.c b/menu/menu_driver.c index 86596d3937..7a2c823277 100644 --- a/menu/menu_driver.c +++ b/menu/menu_driver.c @@ -2873,15 +2873,31 @@ void menu_shader_manager_apply_changes( const char *dir_menu_config) { enum rarch_shader_type type = RARCH_SHADER_NONE; + settings_t *settings = config_get_ptr(); if (!shader) return; type = menu_shader_manager_get_type(shader); + /* Allow cold start from hotkey */ + if ( type == RARCH_SHADER_NONE + && settings->bools.video_shader_enable + && !(shader->flags & SHDR_FLAG_DISABLED)) + { + const char *preset = video_shader_get_current_shader_preset(); + enum rarch_shader_type type = video_shader_parse_type(preset); + video_shader_apply_shader(settings, type, preset, false); + return; + } + + /* Temporary state does not save anything */ + if (shader->flags & SHDR_FLAG_TEMPORARY) + return; + if ( shader->passes - && (type != RARCH_SHADER_NONE) - && (!(shader->flags & SHDR_FLAG_DISABLED))) + && type != RARCH_SHADER_NONE + && !(shader->flags & SHDR_FLAG_DISABLED)) { menu_shader_manager_save_preset(shader, NULL, dir_video_shader, dir_menu_config, true); @@ -2889,6 +2905,9 @@ void menu_shader_manager_apply_changes( } menu_shader_manager_set_preset(NULL, type, NULL, true); + + /* Reinforce disabled state on failure */ + configuration_set_bool(settings, settings->bools.video_shader_enable, false); } static bool menu_shader_manager_save_preset_internal( diff --git a/menu/menu_setting.c b/menu/menu_setting.c index b8953eab9d..82b990e8fe 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -8136,7 +8136,8 @@ static void general_write_handler(rarch_setting_t *setting) { case MENU_ENUM_LABEL_VIDEO_SHADERS_ENABLE: #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) - video_shader_toggle(settings); + video_shader_toggle(settings, true); + configuration_set_bool(settings, settings->bools.video_shader_enable, *setting->value.target.boolean); #endif break; case MENU_ENUM_LABEL_VIDEO_THREADED: diff --git a/msg_hash.h b/msg_hash.h index 6273a1c125..d4afd2092a 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1140,6 +1140,7 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_INPUT_META_DISK_PREV, MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_TOGGLE, + MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_HOLD, MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_NEXT, MENU_ENUM_LABEL_VALUE_INPUT_META_SHADER_PREV, @@ -1240,6 +1241,7 @@ enum msg_hash_enums MENU_ENUM_SUBLABEL_INPUT_META_DISK_PREV, MENU_ENUM_SUBLABEL_INPUT_META_SHADER_TOGGLE, + MENU_ENUM_SUBLABEL_INPUT_META_SHADER_HOLD, MENU_ENUM_SUBLABEL_INPUT_META_SHADER_NEXT, MENU_ENUM_SUBLABEL_INPUT_META_SHADER_PREV, diff --git a/retroarch.c b/retroarch.c index 927a754121..e23408e7d5 100644 --- a/retroarch.c +++ b/retroarch.c @@ -3091,9 +3091,23 @@ bool command_event(enum event_command cmd, void *data) break; case CMD_EVENT_SHADER_TOGGLE: #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) - video_shader_toggle(settings); + video_shader_toggle(settings, false); #endif break; + case CMD_EVENT_SHADER_PRESET_LOADED: + ui_companion_event_command(cmd); + break; + case CMD_EVENT_SHADERS_APPLY_CHANGES: +#ifdef HAVE_MENU +#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) + menu_shader_manager_apply_changes(menu_shader_get(), + settings->paths.directory_video_shader, + settings->paths.directory_menu_config + ); +#endif +#endif + ui_companion_event_command(cmd); + break; case CMD_EVENT_AI_SERVICE_TOGGLE: { #ifdef HAVE_TRANSLATE @@ -3645,6 +3659,16 @@ bool command_event(enum event_command cmd, void *data) runloop_st->runtime_shader_preset_path[0] = '\0'; #endif +#ifdef HAVE_MENU +#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) + /* Restore shader option state after temporary fast toggling */ + if (menu_shader_get()->flags & SHDR_FLAG_TEMPORARY) + { + bool enabled = !(menu_shader_get()->flags & SHDR_FLAG_DISABLED); + configuration_set_bool(settings, settings->bools.video_shader_enable, enabled); + } +#endif +#endif video_driver_restore_cached(settings); if ( (flags & CONTENT_ST_FLAG_IS_INITED) @@ -4621,20 +4645,6 @@ bool command_event(enum event_command cmd, void *data) return false; #endif break; - case CMD_EVENT_SHADER_PRESET_LOADED: - ui_companion_event_command(cmd); - break; - case CMD_EVENT_SHADERS_APPLY_CHANGES: -#ifdef HAVE_MENU -#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) - menu_shader_manager_apply_changes(menu_shader_get(), - settings->paths.directory_video_shader, - settings->paths.directory_menu_config - ); -#endif -#endif - ui_companion_event_command(cmd); - break; case CMD_EVENT_PAUSE_TOGGLE: { bool paused = (runloop_st->flags & RUNLOOP_FLAG_PAUSED) ? true : false; @@ -8538,6 +8548,17 @@ bool retroarch_main_quit(void) } #endif +#ifdef HAVE_MENU +#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) + /* Restore shader option state after temporary fast toggling */ + if (menu_shader_get()->flags & SHDR_FLAG_TEMPORARY) + { + bool enabled = !(menu_shader_get()->flags & SHDR_FLAG_DISABLED); + configuration_set_bool(settings, settings->bools.video_shader_enable, enabled); + } +#endif +#endif + /* Save configs before quitting * as for UWP depending on `OnSuspending` is not important as we can call it directly here * specifically we need to get width,height which requires UI thread and it will not be available on exit diff --git a/runloop.c b/runloop.c index 68393a588d..07959cce6a 100644 --- a/runloop.c +++ b/runloop.c @@ -6793,6 +6793,18 @@ static enum runloop_state_enum runloop_check_state( RARCH_SHADER_PREV, CMD_EVENT_SHADER_PREV, RARCH_SHADER_TOGGLE, CMD_EVENT_SHADER_TOGGLE); + { + /* Check shader hold hotkey */ + static bool old_shader_hold_button_state = false; + bool new_shader_hold_button_state = BIT256_GET( + current_bits, RARCH_SHADER_HOLD); + + if (old_shader_hold_button_state != new_shader_hold_button_state) + command_event(CMD_EVENT_SHADER_TOGGLE, NULL); + + old_shader_hold_button_state = new_shader_hold_button_state; + } + if (settings->bools.video_shader_watch_files) { static rarch_timer_t timer = {0};