Slang Subframe Shaders Feature (#16209)

Adds support for sub-frame shaders to vulkan/glcore/dx10-11-12.

Builds on the concept already present for frame duplication in use for BFI, to present multiple 'sub' frames per real frame to the shaders, so they can run at a higher framerate than the content framerate. Must be enabled via subframe shaders setting under synchronization settings to be active.

Will allow BFI to be implemented inside of the shaders, among any other use for the higher framerate shader authors can devise.

CurrentSubFrame and TotalSubFrames have been available inside the shaders to track what they want to do on an given subframe. TotalSubFrames will always be 1 when the setting is disabled (and when in menu/ff/pause). Framecount will not increment on sub-frames, as it does not for injected bfi frames now. Should not interfere with any existing shaders that do not check for subframes.
This commit is contained in:
Ophidon 2024-02-09 06:12:55 -05:00 committed by GitHub
parent 997c7453a8
commit 7b711214a7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 702 additions and 38 deletions

View file

@ -1715,7 +1715,7 @@ void rcheevos_validate_config_settings(void)
}
/* this causes N blank frames to be rendered between real frames, thus
* slowing down the actual number of rendered frames per second. */
* can slow down the actual number of rendered frames per second. */
if (settings->uints.video_black_frame_insertion > 0) {
const char* error = "Hardcore paused. Black frame insertion not allowed.";
CHEEVOS_LOG(RCHEEVOS_TAG "%s\n", error);
@ -1726,6 +1726,20 @@ void rcheevos_validate_config_settings(void)
return;
}
/* this causes N dupe frames to be rendered between real frames, for
the purposes of shaders that update faster than content. Thus
* can slow down the actual number of rendered frames per second. */
if (settings->uints.video_shader_subframes > 1) {
const char* error = "Hardcore paused. Shader subframes not allowed.";
CHEEVOS_LOG(RCHEEVOS_TAG "%s\n", error);
rcheevos_pause_hardcore();
runloop_msg_queue_push(error, 0, 4 * 60, false, NULL,
MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_WARNING);
return;
}
if (!(disallowed_settings
= rc_libretro_get_disallowed_settings(sysinfo->library_name)))
return;

View file

@ -394,6 +394,13 @@
/* Try to sleep the spare time after frame is presented in order to reduce vsync CPU usage. */
#define DEFAULT_FRAME_REST false
/* Duplicates frames for the purposes of running Shaders at a higher framerate
* than content framerate. Requires running screen at multiple of 60hz, and
* don't combine with Swap_interval > 1, or BFI. (Though BFI can be done in a shader
* with multi-frame shaders.)
*/
#define DEFAULT_SHADER_SUBFRAMES 1
/* Inserts black frame(s) inbetween frames.
* Useful for Higher Hz monitors (set to multiples of 60 Hz) who want to play 60 Hz
* material with CRT-like motion clarity.

View file

@ -2396,6 +2396,7 @@ static struct config_uint_setting *populate_settings_uint(
SETTING_UINT("video_max_frame_latency", &settings->uints.video_max_frame_latency, true, DEFAULT_MAX_FRAME_LATENCY, false);
SETTING_UINT("video_black_frame_insertion", &settings->uints.video_black_frame_insertion, true, DEFAULT_BLACK_FRAME_INSERTION, false);
SETTING_UINT("video_bfi_dark_frames", &settings->uints.video_bfi_dark_frames, true, DEFAULT_BFI_DARK_FRAMES, false);
SETTING_UINT("video_shader_subframes", &settings->uints.video_shader_subframes, true, DEFAULT_SHADER_SUBFRAMES, false);
SETTING_UINT("video_swap_interval", &settings->uints.video_swap_interval, true, DEFAULT_SWAP_INTERVAL, false);
SETTING_UINT("video_rotation", &settings->uints.video_rotation, true, ORIENTATION_NORMAL, false);
SETTING_UINT("screen_orientation", &settings->uints.screen_orientation, true, ORIENTATION_NORMAL, false);

View file

@ -346,6 +346,7 @@ typedef struct settings
unsigned core_updater_auto_backup_history_size;
unsigned video_black_frame_insertion;
unsigned video_bfi_dark_frames;
unsigned video_shader_subframes;
unsigned video_autoswitch_refresh_rate;
unsigned quit_on_close_content;

View file

@ -216,6 +216,8 @@ typedef struct
uint32_t frame_count;
int32_t frame_direction;
uint32_t rotation;
uint32_t total_subframes;
uint32_t current_subframe;
} pass[GFX_MAX_SHADERS];
struct video_shader* shader_preset;

View file

@ -292,6 +292,8 @@ typedef struct
uint32_t frame_count;
int32_t frame_direction;
uint32_t rotation;
uint32_t total_subframes;
uint32_t current_subframe;
} pass[GFX_MAX_SHADERS];
struct video_shader* shader_preset;

View file

@ -329,6 +329,8 @@ typedef struct
uint32_t frame_count;
int32_t frame_direction;
uint32_t rotation;
uint32_t total_subframes;
uint32_t current_subframe;
D3D12_GPU_DESCRIPTOR_HANDLE textures;
D3D12_GPU_DESCRIPTOR_HANDLE samplers;
} pass[GFX_MAX_SHADERS];

View file

@ -2345,10 +2345,21 @@ void win32_set_style(MONITORINFOEX *current_mon, HMONITOR *hm_to_use,
* an integer, so video_refresh_rate needs to be rounded. Also, account
* for black frame insertion using video_refresh_rate set to a portion
* of the display refresh rate, as well as higher vsync swap intervals. */
float video_refresh = settings->floats.video_refresh_rate;
float refresh_rate = settings->floats.video_refresh_rate;
unsigned bfi = settings->uints.video_black_frame_insertion;
float refresh_mod = bfi + 1.0f;
float refresh_rate = video_refresh * refresh_mod;
unsigned swap_interval = settings->uints.video_swap_interval;
unsigned
shader_subframes = settings->uints.video_shader_subframes;
/* if refresh_rate is <=60hz, adjust for modifiers, if it is higher
assume modifiers already factored into setting. Multiplying by
modifiers will still leave result at original value when they
are not set. Swap interval 0 is automatic, but at automatic
we should default to checking for normal SI 1 for rate change*/
if (swap_interval == 0)
++swap_interval;
if ((int)refresh_rate <= 60)
refresh_rate = refresh_rate * (bfi + 1) * swap_interval * shader_subframes;
if (windowed_full)
{

View file

@ -1334,6 +1334,8 @@ static bool d3d10_gfx_set_shader(void* data,
&d3d10->pass[i].frame_count, /* FrameCount */
&d3d10->pass[i].frame_direction, /* FrameDirection */
&d3d10->pass[i].rotation, /* Rotation */
&d3d10->pass[i].total_subframes, /* TotalSubFrames */
&d3d10->pass[i].current_subframe,/* CurrentSubFrame */
}
};
/* clang-format on */
@ -2153,7 +2155,7 @@ static bool d3d10_gfx_frame(
const char* msg,
video_frame_info_t* video_info)
{
unsigned i;
unsigned i, k, m;
UINT offset = 0, stride = 0;
d3d10_texture_t* texture = NULL;
d3d10_video_t * d3d10 = (d3d10_video_t*)data;
@ -2327,6 +2329,22 @@ static bool d3d10_gfx_frame(
d3d10->pass[i].rotation = retroarch_get_rotation();
/* Sub-frame info for multiframe shaders (per real content frame).
Should always be 1 for non-use of subframes */
if (!(d3d10->flags & D3D10_ST_FLAG_FRAME_DUPE_LOCK))
{
if ( black_frame_insertion
|| nonblock_state
|| runloop_is_slowmotion
|| runloop_is_paused
|| (d3d10->flags & D3D10_ST_FLAG_MENU_ENABLE))
d3d10->pass[i].total_subframes = 1;
else
d3d10->pass[i].total_subframes = video_info->shader_subframes;
d3d10->pass[i].current_subframe = 1;
}
for (j = 0; j < SLANG_CBUFFER_MAX; j++)
{
D3D10Buffer buffer = d3d10->pass[i].buffers[j];
@ -2554,7 +2572,8 @@ static bool d3d10_gfx_frame(
&& !(d3d10->flags & D3D10_ST_FLAG_MENU_ENABLE)
&& !nonblock_state
&& !runloop_is_slowmotion
&& !runloop_is_paused)
&& !runloop_is_paused
&& (!(video_info->shader_subframes > 1)))
{
if (video_info->bfi_dark_frames > video_info->black_frame_insertion)
video_info->bfi_dark_frames = video_info->black_frame_insertion;
@ -2590,6 +2609,37 @@ static bool d3d10_gfx_frame(
}
}
/* Frame duping for Shader Subframes, don't combine with swap_interval > 1, BFI.
Also, a major logical use of shader sub-frames will still be shader implemented BFI
or even rolling scan bfi, so we need to protect the menu/ff/etc from bad flickering
from improper settings, and unnecessary performance overhead for ff, screenshots etc. */
if ( (video_info->shader_subframes > 1)
&& !black_frame_insertion
&& !nonblock_state
&& !runloop_is_slowmotion
&& !runloop_is_paused
&& (!(d3d10->flags & D3D10_ST_FLAG_MENU_ENABLE))
&& (!(d3d10->flags & D3D10_ST_FLAG_FRAME_DUPE_LOCK)))
{
d3d10->flags |= D3D10_ST_FLAG_FRAME_DUPE_LOCK;
for (k = 1; k < video_info->shader_subframes; k++)
{
if (d3d10->shader_preset)
for (m = 0; m < d3d10->shader_preset->passes; m++)
{
d3d10->pass[m].total_subframes = video_info->shader_subframes;
d3d10->pass[m].current_subframe = k+1;
}
if (!d3d10_gfx_frame(d3d10, NULL, 0, 0, frame_count, 0, msg,
video_info))
{
d3d10->flags &= ~D3D10_ST_FLAG_FRAME_DUPE_LOCK;
return false;
}
}
d3d10->flags &= ~D3D10_ST_FLAG_FRAME_DUPE_LOCK;
}
return true;
}
@ -2815,6 +2865,7 @@ static uint32_t d3d10_get_flags(void *data)
BIT32_SET(flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION);
#if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS)
BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG);
BIT32_SET(flags, GFX_CTX_FLAGS_SUBFRAME_SHADERS);
#endif
return flags;

View file

@ -1000,6 +1000,7 @@ static uint32_t d3d11_get_flags(void *data)
BIT32_SET(flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION);
#if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS)
BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG);
BIT32_SET(flags, GFX_CTX_FLAGS_SUBFRAME_SHADERS);
#endif
return flags;
@ -1534,6 +1535,8 @@ static bool d3d11_gfx_set_shader(void* data, enum rarch_shader_type type, const
&d3d11->pass[i].frame_count, /* FrameCount */
&d3d11->pass[i].frame_direction, /* FrameDirection */
&d3d11->pass[i].rotation, /* Rotation */
&d3d11->pass[i].total_subframes, /* TotalSubFrames */
&d3d11->pass[i].current_subframe,/* CurrentSubFrame */
}
};
/* clang-format on */
@ -2778,7 +2781,7 @@ static bool d3d11_gfx_frame(
const char* msg,
video_frame_info_t* video_info)
{
unsigned i;
unsigned i, k, m;
d3d11_texture_t* texture = NULL;
D3D11RenderTargetView rtv = NULL;
d3d11_video_t* d3d11 = (d3d11_video_t*)data;
@ -3074,6 +3077,22 @@ static bool d3d11_gfx_frame(
d3d11->pass[i].rotation = retroarch_get_rotation();
/* Sub-frame info for multiframe shaders (per real content frame).
Should always be 1 for non-use of subframes */
if (!(d3d11->flags & D3D11_ST_FLAG_FRAME_DUPE_LOCK))
{
if ( black_frame_insertion
|| nonblock_state
|| runloop_is_slowmotion
|| runloop_is_paused
|| (d3d11->flags & D3D11_ST_FLAG_MENU_ENABLE))
d3d11->pass[i].total_subframes = 1;
else
d3d11->pass[i].total_subframes = video_info->shader_subframes;
d3d11->pass[i].current_subframe = 1;
}
for (j = 0; j < SLANG_CBUFFER_MAX; j++)
{
D3D11Buffer buffer = d3d11->pass[i].buffers[j];
@ -3387,7 +3406,8 @@ static bool d3d11_gfx_frame(
&& !(d3d11->flags & D3D11_ST_FLAG_MENU_ENABLE)
&& !nonblock_state
&& !runloop_is_slowmotion
&& !runloop_is_paused)
&& !runloop_is_paused
&& (!(video_info->shader_subframes > 1)))
{
if (video_info->bfi_dark_frames > video_info->black_frame_insertion)
video_info->bfi_dark_frames = video_info->black_frame_insertion;
@ -3423,6 +3443,37 @@ static bool d3d11_gfx_frame(
}
}
/* Frame duping for Shader Subframes, don't combine with swap_interval > 1, BFI.
Also, a major logical use of shader sub-frames will still be shader implemented BFI
or even rolling scan bfi, so we need to protect the menu/ff/etc from bad flickering
from improper settings, and unnecessary performance overhead for ff, screenshots etc. */
if ( (video_info->shader_subframes > 1)
&& !black_frame_insertion
&& !nonblock_state
&& !runloop_is_slowmotion
&& !runloop_is_paused
&& (!(d3d11->flags & D3D11_ST_FLAG_MENU_ENABLE))
&& (!(d3d11->flags & D3D11_ST_FLAG_FRAME_DUPE_LOCK)))
{
d3d11->flags |= D3D11_ST_FLAG_FRAME_DUPE_LOCK;
for (k = 1; k < video_info->shader_subframes; k++)
{
if (d3d11->shader_preset)
for (m = 0; m < d3d11->shader_preset->passes; m++)
{
d3d11->pass[m].total_subframes = video_info->shader_subframes;
d3d11->pass[m].current_subframe = k+1;
}
if (!d3d11_gfx_frame(d3d11, NULL, 0, 0, frame_count, 0, msg,
video_info))
{
d3d11->flags &= ~D3D11_ST_FLAG_FRAME_DUPE_LOCK;
return false;
}
}
d3d11->flags &= ~D3D11_ST_FLAG_FRAME_DUPE_LOCK;
}
Release(rtv);
return true;

View file

@ -1148,6 +1148,7 @@ static uint32_t d3d12_get_flags(void *data)
BIT32_SET(flags, GFX_CTX_FLAGS_BLACK_FRAME_INSERTION);
#if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS)
BIT32_SET(flags, GFX_CTX_FLAGS_SHADERS_SLANG);
BIT32_SET(flags, GFX_CTX_FLAGS_SUBFRAME_SHADERS);
#endif
return flags;
@ -1722,6 +1723,8 @@ static bool d3d12_gfx_set_shader(void* data, enum rarch_shader_type type, const
&d3d12->pass[i].frame_count, /* FrameCount */
&d3d12->pass[i].frame_direction, /* FrameDirection */
&d3d12->pass[i].rotation, /* Rotation */
&d3d12->pass[i].total_subframes, /* TotalSubFrames */
&d3d12->pass[i].current_subframe,/* CurrentSubFrame */
}
};
/* clang-format on */
@ -3242,7 +3245,7 @@ static bool d3d12_gfx_frame(
const char* msg,
video_frame_info_t* video_info)
{
unsigned i;
unsigned i, k, m;
d3d12_texture_t* texture = NULL;
d3d12_video_t* d3d12 = (d3d12_video_t*)data;
bool vsync = (d3d12->flags & D3D12_ST_FLAG_VSYNC) ? true : false;
@ -3590,6 +3593,22 @@ static bool d3d12_gfx_frame(
d3d12->pass[i].rotation = retroarch_get_rotation();
/* Sub-frame info for multiframe shaders (per real content frame).
Should always be 1 for non-use of subframes */
if (!(d3d12->flags & D3D12_ST_FLAG_FRAME_DUPE_LOCK))
{
if ( black_frame_insertion
|| nonblock_state
|| runloop_is_slowmotion
|| runloop_is_paused
|| (d3d12->flags & D3D12_ST_FLAG_MENU_ENABLE))
d3d12->pass[i].total_subframes = 1;
else
d3d12->pass[i].total_subframes = video_info->shader_subframes;
d3d12->pass[i].current_subframe = 1;
}
for (j = 0; j < SLANG_CBUFFER_MAX; j++)
{
cbuffer_sem_t* buffer_sem = &d3d12->pass[i].semantics.cbuffers[j];
@ -3977,7 +3996,8 @@ static bool d3d12_gfx_frame(
&& !(d3d12->flags & D3D12_ST_FLAG_MENU_ENABLE)
&& !nonblock_state
&& !runloop_is_slowmotion
&& !runloop_is_paused)
&& !runloop_is_paused
&& (!(video_info->shader_subframes > 1)))
{
if (video_info->bfi_dark_frames > video_info->black_frame_insertion)
video_info->bfi_dark_frames = video_info->black_frame_insertion;
@ -4011,6 +4031,37 @@ static bool d3d12_gfx_frame(
}
}
/* Frame duping for Shader Subframes, don't combine with swap_interval > 1, BFI.
Also, a major logical use of shader sub-frames will still be shader implemented BFI
or even rolling scan bfi, so we need to protect the menu/ff/etc from bad flickering
from improper settings, and unnecessary performance overhead for ff, screenshots etc. */
if ( (video_info->shader_subframes > 1)
&& !black_frame_insertion
&& !nonblock_state
&& !runloop_is_slowmotion
&& !runloop_is_paused
&& (!(d3d12->flags & D3D12_ST_FLAG_MENU_ENABLE))
&& (!(d3d12->flags & D3D12_ST_FLAG_FRAME_DUPE_LOCK)))
{
d3d12->flags |= D3D12_ST_FLAG_FRAME_DUPE_LOCK;
for (k = 1; k < video_info->shader_subframes; k++)
{
if (d3d12->shader_preset)
for (m = 0; m < d3d12->shader_preset->passes; m++)
{
d3d12->pass[m].total_subframes = video_info->shader_subframes;
d3d12->pass[m].current_subframe = k+1;
}
if (!d3d12_gfx_frame(d3d12, NULL, 0, 0, frame_count, 0, msg,
video_info))
{
d3d12->flags &= ~D3D12_ST_FLAG_FRAME_DUPE_LOCK;
return false;
}
}
d3d12->flags &= ~D3D12_ST_FLAG_FRAME_DUPE_LOCK;
}
return true;
}

View file

@ -2504,6 +2504,7 @@ static bool gl3_frame(void *data, const void *frame,
bool msg_bgcolor_enable = video_info->msg_bgcolor_enable;
#endif
int bfi_light_frames;
int i;
unsigned n;
unsigned hard_sync_frames = video_info->hard_sync_frames;
bool input_driver_nonblock_state = video_info->input_driver_nonblock_state;
@ -2583,6 +2584,26 @@ static bool gl3_frame(void *data, const void *frame,
gl3_filter_chain_set_frame_direction(gl->filter_chain, 1);
#endif
gl3_filter_chain_set_rotation(gl->filter_chain, retroarch_get_rotation());
/* Sub-frame info for multiframe shaders (per real content frame).
Should always be 1 for non-use of subframes*/
if (!(gl->flags & GL3_FLAG_FRAME_DUPE_LOCK))
{
if ( video_info->black_frame_insertion
|| video_info->input_driver_nonblock_state
|| video_info->runloop_is_slowmotion
|| video_info->runloop_is_paused
|| (gl->flags & GL3_FLAG_MENU_TEXTURE_ENABLE))
gl3_filter_chain_set_shader_subframes(
gl->filter_chain, 1);
else
gl3_filter_chain_set_shader_subframes(
gl->filter_chain, video_info->shader_subframes);
gl3_filter_chain_set_current_shader_subframe(
gl->filter_chain, 1);
}
gl3_filter_chain_set_input_texture(gl->filter_chain, &texture);
gl3_filter_chain_build_offscreen_passes(gl->filter_chain,
&gl->filter_chain_vp);
@ -2716,6 +2737,36 @@ static bool gl3_frame(void *data, const void *frame,
}
#endif
/* Frame duping for Shader Subframes, don't combine with swap_interval > 1, BFI.
Also, a major logical use of shader sub-frames will still be shader implemented BFI
or even rolling scan bfi, so we need to protect the menu/ff/etc from bad flickering
from improper settings, and unnecessary performance overhead for ff, screenshots etc. */
if ( (video_info->shader_subframes > 1)
&& !video_info->black_frame_insertion
&& !video_info->input_driver_nonblock_state
&& !video_info->runloop_is_slowmotion
&& !video_info->runloop_is_paused
&& (!(gl->flags & GL3_FLAG_MENU_TEXTURE_ENABLE))
&& (!(gl->flags & GL3_FLAG_FRAME_DUPE_LOCK)))
{
gl->flags |= GL3_FLAG_FRAME_DUPE_LOCK;
for (i = 1; i < (int) video_info->shader_subframes; i++)
{
gl3_filter_chain_set_shader_subframes(
gl->filter_chain, video_info->shader_subframes);
gl3_filter_chain_set_current_shader_subframe(
gl->filter_chain, i+1);
if (!gl3_frame(gl, NULL, 0, 0, frame_count, 0, msg,
video_info))
{
gl->flags &= ~GL3_FLAG_FRAME_DUPE_LOCK;
return false;
}
}
gl->flags &= ~GL3_FLAG_FRAME_DUPE_LOCK;
}
if ( hard_sync
&& !input_driver_nonblock_state
)
@ -2736,6 +2787,7 @@ static uint32_t gl3_get_flags(void *data)
BIT32_SET(flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING);
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);
return flags;
}

View file

@ -4051,7 +4051,7 @@ static bool vulkan_frame(void *data, const void *frame,
uint64_t frame_count,
unsigned pitch, const char *msg, video_frame_info_t *video_info)
{
int i;
int i, j, k;
VkSubmitInfo submit_info;
VkClearValue clear_color;
VkRenderPassBeginInfo rp_info;
@ -4212,6 +4212,26 @@ static bool vulkan_frame(void *data, const void *frame,
(vulkan_filter_chain_t*)vk->filter_chain, frame_index);
vulkan_filter_chain_set_frame_count(
(vulkan_filter_chain_t*)vk->filter_chain, frame_count);
/* Sub-frame info for multiframe shaders (per real content frame).
Should always be 1 for non-use of subframes*/
if (!(vk->context->flags & VK_CTX_FLAG_SWAP_INTERVAL_EMULATION_LOCK))
{
if ( black_frame_insertion
|| input_driver_nonblock_state
|| runloop_is_slowmotion
|| runloop_is_paused
|| (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);
else
vulkan_filter_chain_set_shader_subframes(
(vulkan_filter_chain_t*)vk->filter_chain, video_info->shader_subframes);
vulkan_filter_chain_set_current_shader_subframe(
(vulkan_filter_chain_t*)vk->filter_chain, 1);
}
#ifdef HAVE_REWIND
vulkan_filter_chain_set_frame_direction(
(vulkan_filter_chain_t*)vk->filter_chain,
@ -4880,6 +4900,8 @@ static bool vulkan_frame(void *data, const void *frame,
&& !input_driver_nonblock_state
&& !runloop_is_slowmotion
&& !runloop_is_paused
&& !(vk->context->swap_interval > 1)
&& !(video_info->shader_subframes > 1)
&& (!(vk->flags & VK_FLAG_MENU_ENABLE)))
{
if (video_info->bfi_dark_frames > video_info->black_frame_insertion)
@ -4916,14 +4938,50 @@ static bool vulkan_frame(void *data, const void *frame,
}
}
/* Vulkan doesn't directly support swap_interval > 1,
* so we fake it by duping out more frames. */
if ( (vk->context->swap_interval > 1)
&& (!(vk->context->flags & VK_CTX_FLAG_SWAP_INTERVAL_EMULATION_LOCK)))
/* Frame duping for Shader Subframes, don't combine with swap_interval > 1, BFI.
Also, a major logical use of shader sub-frames will still be shader implemented BFI
or even rolling scan bfi, so we need to protect the menu/ff/etc from bad flickering
from improper settings, and unnecessary performance overhead for ff, screenshots etc. */
if ( (video_info->shader_subframes > 1)
&& (backbuffer->image != VK_NULL_HANDLE)
&& (vk->context->flags & VK_CTX_FLAG_HAS_ACQUIRED_SWAPCHAIN)
&& !black_frame_insertion
&& !input_driver_nonblock_state
&& !runloop_is_slowmotion
&& !runloop_is_paused
&& (!(vk->flags & VK_FLAG_MENU_ENABLE))
&& !(vk->context->swap_interval > 1)
&& (!(vk->context->flags & VK_CTX_FLAG_SWAP_INTERVAL_EMULATION_LOCK)))
{
int i;
vk->context->flags |= VK_CTX_FLAG_SWAP_INTERVAL_EMULATION_LOCK;
for (i = 1; i < (int) vk->context->swap_interval; i++)
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_set_current_shader_subframe(
(vulkan_filter_chain_t*)vk->filter_chain, j+1);
if (!vulkan_frame(vk, NULL, 0, 0, frame_count, 0, msg,
video_info))
{
vk->context->flags &= ~VK_CTX_FLAG_SWAP_INTERVAL_EMULATION_LOCK;
return false;
}
}
vk->context->flags &= ~VK_CTX_FLAG_SWAP_INTERVAL_EMULATION_LOCK;
}
/* Vulkan doesn't directly support swap_interval > 1,
* so we fake it by duping out more frames. Shader subframes
uses same concept but split above so sub_frame logic the
same as the other apis that do support real swap_interval */
if ( (vk->context->swap_interval > 1)
&& !(video_info->shader_subframes > 1)
&& !black_frame_insertion
&& (!(vk->context->flags & VK_CTX_FLAG_SWAP_INTERVAL_EMULATION_LOCK)))
{
vk->context->flags |= VK_CTX_FLAG_SWAP_INTERVAL_EMULATION_LOCK;
for (k = 1; k < (int) vk->context->swap_interval; k++)
{
if (!vulkan_frame(vk, NULL, 0, 0, frame_count, 0, msg,
video_info))
@ -5249,6 +5307,7 @@ static uint32_t vulkan_get_flags(void *data)
BIT32_SET(flags, GFX_CTX_FLAGS_MENU_FRAME_FILTERING);
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);
return flags;
}

View file

@ -925,6 +925,16 @@ public:
rotation = rot;
}
void set_shader_subframes(uint32_t tot_subframes)
{
total_subframes = tot_subframes;
}
void set_current_shader_subframe(uint32_t cur_subframe)
{
current_subframe = cur_subframe;
}
void set_name(const char *name)
{
pass_name = name;
@ -1024,6 +1034,8 @@ private:
int32_t frame_direction = 1;
uint32_t rotation = 0;
unsigned pass_number = 0;
uint32_t total_subframes = 1;
uint32_t current_subframe = 1;
size_t ubo_offset = 0;
std::string pass_name;
@ -1221,6 +1233,8 @@ bool Pass::init_pipeline()
reflect_parameter("FrameCount", reflection.semantics[SLANG_SEMANTIC_FRAME_COUNT]);
reflect_parameter("FrameDirection", reflection.semantics[SLANG_SEMANTIC_FRAME_DIRECTION]);
reflect_parameter("Rotation", reflection.semantics[SLANG_SEMANTIC_ROTATION]);
reflect_parameter("TotalSubFrames", reflection.semantics[SLANG_SEMANTIC_TOTAL_SUBFRAMES]);
reflect_parameter("CurrentSubFrame", reflection.semantics[SLANG_SEMANTIC_CURRENT_SUBFRAME]);
reflect_parameter("OriginalSize", reflection.semantic_textures[SLANG_TEXTURE_SEMANTIC_ORIGINAL][0]);
reflect_parameter("SourceSize", reflection.semantic_textures[SLANG_TEXTURE_SEMANTIC_SOURCE][0]);
@ -1654,6 +1668,11 @@ void Pass::build_semantics(uint8_t *buffer,
build_semantic_uint(buffer, SLANG_SEMANTIC_ROTATION,
rotation);
build_semantic_uint(buffer, SLANG_SEMANTIC_TOTAL_SUBFRAMES,
total_subframes);
build_semantic_uint(buffer, SLANG_SEMANTIC_CURRENT_SUBFRAME,
current_subframe);
/* Standard inputs */
build_semantic_texture(buffer, SLANG_TEXTURE_SEMANTIC_ORIGINAL, original);
build_semantic_texture(buffer, SLANG_TEXTURE_SEMANTIC_SOURCE, source);
@ -1849,6 +1868,8 @@ public:
void set_frame_count_period(unsigned pass, unsigned period);
void set_frame_direction(int32_t direction);
void set_rotation(uint32_t rot);
void set_shader_subframes(uint32_t tot_subframes);
void set_current_shader_subframe(uint32_t cur_subframe);
void set_pass_name(unsigned pass, const char *name);
void add_static_texture(std::unique_ptr<gl3_shader::StaticTexture> texture);
@ -2338,6 +2359,20 @@ void gl3_filter_chain::set_rotation(uint32_t rot)
passes[i]->set_rotation(rot);
}
void gl3_filter_chain::set_shader_subframes(uint32_t tot_subframes)
{
unsigned i;
for (i = 0; i < passes.size(); i++)
passes[i]->set_shader_subframes(tot_subframes);
}
void gl3_filter_chain::set_current_shader_subframe(uint32_t cur_subframe)
{
unsigned i;
for (i = 0; i < passes.size(); i++)
passes[i]->set_current_shader_subframe(cur_subframe);
}
void gl3_filter_chain::set_pass_name(unsigned pass, const char *name)
{
passes[pass]->set_name(name);
@ -2758,6 +2793,20 @@ void gl3_filter_chain_set_rotation(
chain->set_rotation(rot);
}
void gl3_filter_chain_set_shader_subframes(
gl3_filter_chain_t *chain,
uint32_t tot_subframes)
{
chain->set_shader_subframes(tot_subframes);
}
void gl3_filter_chain_set_current_shader_subframe(
gl3_filter_chain_t *chain,
uint32_t cur_subframe)
{
chain->set_current_shader_subframe(cur_subframe);
}
void gl3_filter_chain_set_frame_count_period(
gl3_filter_chain_t *chain,
unsigned pass,

View file

@ -117,6 +117,14 @@ void gl3_filter_chain_set_rotation(
gl3_filter_chain_t *chain,
uint32_t rotation);
void gl3_filter_chain_set_shader_subframes(
gl3_filter_chain_t *chain,
uint32_t tot_subframes);
void gl3_filter_chain_set_current_shader_subframe(
gl3_filter_chain_t *chain,
uint32_t cur_subframe);
void gl3_filter_chain_set_pass_name(
gl3_filter_chain_t *chain,
unsigned pass,

View file

@ -546,6 +546,8 @@ class Pass
void notify_sync_index(unsigned index) { sync_index = index; }
void set_frame_count(uint64_t count) { frame_count = count; }
void set_frame_count_period(unsigned p) { frame_count_period = p; }
void set_shader_subframes(uint32_t ts) { total_subframes = ts; }
void set_current_shader_subframe(uint32_t cs) { current_subframe = cs; }
void set_frame_direction(int32_t dir) { frame_direction = dir; }
void set_rotation(uint32_t rot) { rotation = rot; }
void set_name(const char *name) { pass_name = name; }
@ -636,6 +638,8 @@ class Pass
uint32_t rotation = 0;
unsigned frame_count_period = 0;
unsigned pass_number = 0;
uint32_t total_subframes = 1;
uint32_t current_subframe = 1;
size_t ubo_offset = 0;
std::string pass_name;
@ -693,6 +697,8 @@ struct vulkan_filter_chain
void set_frame_count(uint64_t count);
void set_frame_count_period(unsigned pass, unsigned period);
void set_shader_subframes(uint32_t total_subframes);
void set_current_shader_subframe(uint32_t current_subframe);
void set_frame_direction(int32_t direction);
void set_rotation(uint32_t rot);
void set_pass_name(unsigned pass, const char *name);
@ -1687,6 +1693,20 @@ void vulkan_filter_chain::set_frame_count_period(
passes[pass]->set_frame_count_period(period);
}
void vulkan_filter_chain::set_shader_subframes(uint32_t total_subframes)
{
unsigned i;
for (i = 0; i < passes.size(); i++)
passes[i]->set_shader_subframes(total_subframes);
}
void vulkan_filter_chain::set_current_shader_subframe(uint32_t current_subframe)
{
unsigned i;
for (i = 0; i < passes.size(); i++)
passes[i]->set_current_shader_subframe(current_subframe);
}
void vulkan_filter_chain::set_frame_direction(int32_t direction)
{
unsigned i;
@ -2595,6 +2615,12 @@ void Pass::build_semantics(VkDescriptorSet set, uint8_t *buffer,
build_semantic_int(buffer, SLANG_SEMANTIC_FRAME_DIRECTION,
frame_direction);
build_semantic_uint(buffer, SLANG_SEMANTIC_TOTAL_SUBFRAMES,
total_subframes);
build_semantic_uint(buffer, SLANG_SEMANTIC_CURRENT_SUBFRAME,
current_subframe);
build_semantic_uint(buffer, SLANG_SEMANTIC_ROTATION,
rotation);
@ -3363,6 +3389,20 @@ void vulkan_filter_chain_set_frame_count_period(
chain->set_frame_count_period(pass, period);
}
void vulkan_filter_chain_set_shader_subframes(
vulkan_filter_chain_t *chain,
uint32_t tot_subframes)
{
chain->set_shader_subframes(tot_subframes);
}
void vulkan_filter_chain_set_current_shader_subframe(
vulkan_filter_chain_t *chain,
uint32_t cur_subframe)
{
chain->set_current_shader_subframe(cur_subframe);
}
void vulkan_filter_chain_set_frame_direction(
vulkan_filter_chain_t *chain,
int32_t direction)

View file

@ -120,6 +120,12 @@ void vulkan_filter_chain_set_frame_count_period(vulkan_filter_chain_t *chain,
unsigned pass,
unsigned period);
void vulkan_filter_chain_set_shader_subframes(vulkan_filter_chain_t *chain,
uint32_t tot_subframes);
void vulkan_filter_chain_set_current_shader_subframe(vulkan_filter_chain_t *chain,
uint32_t cur_subframe);
void vulkan_filter_chain_set_frame_direction(vulkan_filter_chain_t *chain,
int32_t direction);

View file

@ -161,6 +161,8 @@ static bool slang_process_reflection(
"FrameCount",
"FrameDirection",
"Rotation",
"TotalSubFrames",
"CurrentSubFrame",
};
int size = sizeof(names) / sizeof(*names);
if (semantic < size)

View file

@ -53,6 +53,8 @@ static const char *semantic_uniform_names[] = {
"FrameCount",
"FrameDirection",
"Rotation",
"TotalSubFrames",
"CurrentSubFrame",
};
static slang_texture_semantic slang_name_to_texture_semantic(
@ -244,6 +246,12 @@ static bool validate_type_for_semantic(const SPIRType &type, slang_semantic sem)
case SLANG_SEMANTIC_FRAME_COUNT:
return type.basetype == SPIRType::UInt && type.vecsize == 1 && type.columns == 1;
/* int */
case SLANG_SEMANTIC_TOTAL_SUBFRAMES:
return type.basetype == SPIRType::UInt && type.vecsize == 1 && type.columns == 1;
/* int */
case SLANG_SEMANTIC_CURRENT_SUBFRAME:
return type.basetype == SPIRType::UInt && type.vecsize == 1 && type.columns == 1;
/* int */
case SLANG_SEMANTIC_FRAME_DIRECTION:
return type.basetype == SPIRType::Int && type.vecsize == 1 && type.columns == 1;
/* uint */

View file

@ -61,24 +61,28 @@ enum slang_texture_semantic
enum slang_semantic
{
/* mat4, MVP */
SLANG_SEMANTIC_MVP = 0,
SLANG_SEMANTIC_MVP = 0,
/* vec4, viewport size of current pass */
SLANG_SEMANTIC_OUTPUT = 1,
SLANG_SEMANTIC_OUTPUT = 1,
/* vec4, viewport size of final pass */
SLANG_SEMANTIC_FINAL_VIEWPORT = 2,
SLANG_SEMANTIC_FINAL_VIEWPORT = 2,
/* uint, frame count with modulo */
SLANG_SEMANTIC_FRAME_COUNT = 3,
SLANG_SEMANTIC_FRAME_COUNT = 3,
/* int, frame direction */
SLANG_SEMANTIC_FRAME_DIRECTION = 4,
SLANG_SEMANTIC_FRAME_DIRECTION = 4,
/* uint, rotation */
SLANG_SEMANTIC_ROTATION = 5,
SLANG_SEMANTIC_ROTATION = 5,
/* uint, sub frames per content frame */
SLANG_SEMANTIC_TOTAL_SUBFRAMES = 6,
/* uint, current sub frame */
SLANG_SEMANTIC_CURRENT_SUBFRAME = 7,
SLANG_NUM_BASE_SEMANTICS,
/* float, user defined parameter, arrayed */
SLANG_SEMANTIC_FLOAT_PARAMETER = 6,
SLANG_SEMANTIC_FLOAT_PARAMETER = 8,
SLANG_NUM_SEMANTICS,
SLANG_INVALID_SEMANTIC = -1
SLANG_INVALID_SEMANTIC = -1
};
enum slang_stage

View file

@ -228,7 +228,8 @@ enum display_flags
GFX_CTX_FLAGS_SHADERS_SLANG,
GFX_CTX_FLAGS_SCREENSHOTS_SUPPORTED,
GFX_CTX_FLAGS_OVERLAY_BEHIND_MENU_SUPPORTED,
GFX_CTX_FLAGS_CRT_SWITCHRES
GFX_CTX_FLAGS_CRT_SWITCHRES,
GFX_CTX_FLAGS_SUBFRAME_SHADERS
};
enum shader_uniform_type

View file

@ -1240,6 +1240,7 @@ void video_switch_refresh_rate_maybe(
unsigned video_swap_interval = runloop_get_video_swap_interval(
settings->uints.video_swap_interval);
unsigned video_bfi = settings->uints.video_black_frame_insertion;
unsigned shader_subframes = settings->uints.video_shader_subframes;
bool vrr_runloop_enable = settings->bools.vrr_runloop_enable;
bool exclusive_fullscreen = settings->bools.video_fullscreen && !settings->bools.video_windowed_fullscreen;
bool windowed_fullscreen = settings->bools.video_fullscreen && settings->bools.video_windowed_fullscreen;
@ -1254,7 +1255,7 @@ void video_switch_refresh_rate_maybe(
refresh_rate = 60.00f;
/* Black frame insertion + swap interval multiplier */
refresh_rate = (refresh_rate * (video_bfi + 1.0f) * video_swap_interval);
refresh_rate = (refresh_rate * (video_bfi + 1.0f) * video_swap_interval * shader_subframes);
/* Fallback when target refresh rate is not exposed or when below standards */
if (!video_display_server_has_refresh_rate(refresh_rate) || refresh_rate < 50)
@ -2552,6 +2553,7 @@ void video_driver_build_info(video_frame_info_t *video_info)
video_info->crt_switch_hires_menu = settings->bools.crt_switch_hires_menu;
video_info->black_frame_insertion = settings->uints.video_black_frame_insertion;
video_info->bfi_dark_frames = settings->uints.video_bfi_dark_frames;
video_info->shader_subframes = settings->uints.video_shader_subframes;
video_info->hard_sync = settings->bools.video_hard_sync;
video_info->hard_sync_frames = settings->uints.video_hard_sync_frames;
video_info->runahead = settings->bools.run_ahead_enabled;
@ -4044,6 +4046,7 @@ void video_frame_delay(video_driver_state_t *video_st,
uint8_t video_swap_interval = runloop_get_video_swap_interval(
settings->uints.video_swap_interval);
uint8_t video_bfi = settings->uints.video_black_frame_insertion;
uint8_t shader_subframes = settings->uints.video_shader_subframes;
uint8_t frame_time_interval = 8;
static uint8_t skip_update = 0;
static bool skip_delay_prev = false;
@ -4075,7 +4078,7 @@ void video_frame_delay(video_driver_state_t *video_st,
frame_time_update = false;
/* Black frame insertion + swap interval multiplier */
refresh_rate = (refresh_rate / (video_bfi + 1.0f) / video_swap_interval);
refresh_rate = (refresh_rate / (video_bfi + 1.0f) / video_swap_interval / shader_subframes);
/* Set target moderately as half frame time with 0 (Auto) delay */
if (video_frame_delay == 0)

View file

@ -416,6 +416,7 @@ typedef struct video_frame_info
unsigned custom_vp_full_height;
unsigned black_frame_insertion;
unsigned bfi_dark_frames;
unsigned shader_subframes;
unsigned fps_update_interval;
unsigned memory_update_interval;
unsigned msg_queue_delay;

View file

@ -4043,6 +4043,10 @@ MSG_HASH(
MENU_ENUM_LABEL_VIDEO_BFI_DARK_FRAMES,
"video_bfi_dark_frames"
)
MSG_HASH(
MENU_ENUM_LABEL_VIDEO_SHADER_SUBFRAMES,
"video_shader_subframes"
)
MSG_HASH(
MENU_ENUM_LABEL_VIDEO_CROP_OVERSCAN,
"video_crop_overscan"

View file

@ -425,6 +425,9 @@ int msg_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len)
case MENU_ENUM_LABEL_VIDEO_BFI_DARK_FRAMES:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_HELP_VIDEO_BFI_DARK_FRAMES), len);
break;
case MENU_ENUM_LABEL_VIDEO_SHADER_SUBFRAMES:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_HELP_VIDEO_SHADER_SUBFRAMES), len);
break;
case MENU_ENUM_LABEL_SAVEFILE_DIRECTORY:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_HELP_SAVEFILE_DIRECTORY), len);
break;

View file

@ -1932,11 +1932,11 @@ MSG_HASH(
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VIDEO_BLACK_FRAME_INSERTION,
"Insert black frame(s) between frames. Can greatly reduce motion blur by emulating CRT scan out, but at cost of brightness. Do not combine with Swap Interval > 1 (Auto is ok), Frame Delay, or Sync to Exact Content Framerate."
"Insert black frame(s) between frames. Can greatly reduce motion blur by emulating CRT scan out, but at cost of brightness. Do not combine with Swap Interval > 1, sub-frames, Frame Delay, or Sync to Exact Content Framerate."
)
MSG_HASH(
MENU_ENUM_LABEL_HELP_VIDEO_BLACK_FRAME_INSERTION,
"Inserts black frame(s) inbetween frames for enhanced motion clarity. Only use option designated for your current display refresh rate. Not for use at refresh rates that are non-multiples of 60Hz such as 144Hz, 165Hz, etc. Do not combine with Swap Interval > 1 (Auto is ok), Frame Delay, or Sync to Exact Content Framerate. Leaving system VRR on is ok, just not that setting. If you notice -any- temporary image retention, you should disable at 120hz, and for higher hz adjust the dark frames setting below."
"Inserts black frame(s) inbetween frames for enhanced motion clarity. Only use option designated for your current display refresh rate. Not for use at refresh rates that are non-multiples of 60Hz such as 144Hz, 165Hz, etc. Do not combine with Swap Interval > 1, sub-frames, Frame Delay, or Sync to Exact Content Framerate. Leaving system VRR on is ok, just not that setting. If you notice -any- temporary image retention, you should disable at 120hz, and for higher hz adjust the dark frames setting below."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_BLACK_FRAME_INSERTION_VALUE_OFF,
@ -2014,6 +2014,82 @@ MSG_HASH(
MENU_ENUM_LABEL_HELP_VIDEO_BFI_DARK_FRAMES,
"Adjusts the number of frames displayed in the bfi sequence that are black. More black frames increases motion clarity but reduces brightness. Not applicable at 120hz as there is only one total extra 60hz frame, so it must be black otherwise BFI would not be active at all."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES,
"Shader Sub-frames"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VIDEO_SHADER_SUBFRAMES,
"Insert extra shader frame(s) between frames. Allows shaders to do effects that run at a higher fps than actual content rate. Should be set to current screen Hz. Do not combine with Swap Interval > 1, BFI, Frame Delay, or Sync to Exact Content Framerate."
)
MSG_HASH(
MENU_ENUM_LABEL_HELP_VIDEO_SHADER_SUBFRAMES,
"Inserts extra shader frame(s) inbetween frames for any possible shader effects that are designed to run faster than content rate. Only use option designated for your current display refresh rate. Not for use at refresh rates that are non-multiples of 60Hz such as 144Hz, 165Hz, etc. Do not combine with Swap Interval > 1, BFI, Frame Delay, or Sync to Exact Content Framerate. Leaving system VRR on is ok, just not that setting."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_OFF,
"Off"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_120,
"2 - For 120Hz Display Refresh Rate"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_180,
"3 - For 180Hz Display Refresh Rate"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_240,
"4 - For 240Hz Display Refresh Rate"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_300,
"5 - For 300Hz Display Refresh Rate"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_360,
"6 - For 360Hz Display Refresh Rate"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_420,
"7 - For 420Hz Display Refresh Rate"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_480,
"8 - For 480Hz Display Refresh Rate"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_540,
"9 - For 540Hz Display Refresh Rate"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_600,
"10 - For 600Hz Display Refresh Rate"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_660,
"11 - For 660Hz Display Refresh Rate"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_720,
"12 - For 720Hz Display Refresh Rate"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_780,
"13 - For 780Hz Display Refresh Rate"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_840,
"14 - For 840Hz Display Refresh Rate"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_900,
"15 - For 900Hz Display Refresh Rate"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_960,
"16 - For 960Hz Display Refresh Rate"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_GPU_SCREENSHOT,
"GPU Screenshot"

View file

@ -495,6 +495,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_frame_delay_auto, MENU_
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_shader_delay, MENU_ENUM_SUBLABEL_VIDEO_SHADER_DELAY)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_black_frame_insertion, MENU_ENUM_SUBLABEL_VIDEO_BLACK_FRAME_INSERTION)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_bfi_dark_frames, MENU_ENUM_SUBLABEL_VIDEO_BFI_DARK_FRAMES)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_shader_subframes, MENU_ENUM_SUBLABEL_VIDEO_SHADER_SUBFRAMES)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_toggle_gamepad_combo, MENU_ENUM_SUBLABEL_INPUT_MENU_ENUM_TOGGLE_GAMEPAD_COMBO)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_quit_gamepad_combo, MENU_ENUM_SUBLABEL_INPUT_QUIT_GAMEPAD_COMBO)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_show_hidden_files, MENU_ENUM_SUBLABEL_SHOW_HIDDEN_FILES)
@ -4761,6 +4762,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_VIDEO_BFI_DARK_FRAMES:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_bfi_dark_frames);
break;
case MENU_ENUM_LABEL_VIDEO_SHADER_SUBFRAMES:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_shader_subframes);
break;
case MENU_ENUM_LABEL_VIDEO_FRAME_DELAY:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_frame_delay);
break;

View file

@ -9384,6 +9384,13 @@ unsigned menu_displaylist_build_list(
MENU_ENUM_LABEL_VIDEO_SWAP_INTERVAL,
PARSE_ONLY_UINT, false) == 0)
count++;
if (video_driver_test_all_flags(GFX_CTX_FLAGS_SUBFRAME_SHADERS))
{
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
MENU_ENUM_LABEL_VIDEO_SHADER_SUBFRAMES,
PARSE_ONLY_UINT, false) == 0)
count++;
}
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
MENU_ENUM_LABEL_VIDEO_BLACK_FRAME_INSERTION,
PARSE_ONLY_UINT, false) == 0)

View file

@ -6451,6 +6451,65 @@ static void setting_get_string_representation_black_frame_insertion(rarch_settin
}
}
static void setting_get_string_representation_shader_subframes(rarch_setting_t *setting,
char *s, size_t len)
{
if (!setting)
return;
switch (*setting->value.target.unsigned_integer)
{
case 1:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_OFF), len);
break;
case 2:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_120), len);
break;
case 3:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_180), len);
break;
case 4:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_240), len);
break;
case 5:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_300), len);
break;
case 6:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_360), len);
break;
case 7:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_420), len);
break;
case 8:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_480), len);
break;
case 9:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_540), len);
break;
case 10:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_600), len);
break;
case 11:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_660), len);
break;
case 12:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_720), len);
break;
case 13:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_780), len);
break;
case 14:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_840), len);
break;
case 15:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_900), len);
break;
case 16:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_960), len);
break;
}
}
static void setting_get_string_representation_video_frame_delay(rarch_setting_t *setting,
char *s, size_t len)
{
@ -8181,6 +8240,9 @@ static void general_write_handler(rarch_setting_t *setting)
configuration_set_bool(settings,
settings->bools.vrr_runloop_enable,
0);
configuration_set_uint(settings,
settings->uints.video_shader_subframes,
1);
/* Set reasonable default for dark frames for current BFI value.
Even results OR odd 60hz multiples should be mostly immune to lcd voltage retention.
@ -8215,6 +8277,31 @@ static void general_write_handler(rarch_setting_t *setting)
}
#ifdef HAVE_CHEEVOS
rcheevos_validate_config_settings();
#endif
break;
case MENU_ENUM_LABEL_VIDEO_SHADER_SUBFRAMES:
/* If enabling BFI, auto disable other sync settings
that do not work together with subframes */
if (*setting->value.target.unsigned_integer > 1)
{
configuration_set_uint(settings,
settings->uints.video_swap_interval,
0);
configuration_set_uint(settings,
settings->uints.video_frame_delay,
0);
configuration_set_bool(settings,
settings->bools.video_frame_delay_auto,
0);
configuration_set_bool(settings,
settings->bools.vrr_runloop_enable,
0);
configuration_set_uint(settings,
settings->uints.video_black_frame_insertion,
0);
}
#ifdef HAVE_CHEEVOS
rcheevos_validate_config_settings();
#endif
break;
case MENU_ENUM_LABEL_VIDEO_BFI_DARK_FRAMES:
@ -8228,10 +8315,13 @@ static void general_write_handler(rarch_setting_t *setting)
case MENU_ENUM_LABEL_VIDEO_FRAME_DELAY_AUTO:
case MENU_ENUM_LABEL_VIDEO_SWAP_INTERVAL:
case MENU_ENUM_LABEL_VRR_RUNLOOP_ENABLE:
/* BFI doesn't play nice with any of these */
/* BFI or shader subframes doesn't play nice with any of these */
configuration_set_bool(settings,
settings->uints.video_black_frame_insertion,
0);
configuration_set_uint(settings,
settings->uints.video_shader_subframes,
1);
#ifdef HAVE_CHEEVOS
rcheevos_validate_config_settings();
#endif
@ -13357,6 +13447,26 @@ static bool setting_append_list(
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_CMD_APPLY_AUTO);
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_LAKKA_ADVANCED);
CONFIG_UINT(
list, list_info,
&settings->uints.video_shader_subframes,
MENU_ENUM_LABEL_VIDEO_SHADER_SUBFRAMES,
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES,
DEFAULT_SHADER_SUBFRAMES,
&group_info,
&subgroup_info,
parent_group,
general_write_handler,
general_read_handler);
(*list)[list_info->index - 1].action_ok = &setting_action_ok_uint;
(*list)[list_info->index - 1].get_string_representation =
&setting_get_string_representation_shader_subframes;
(*list)[list_info->index - 1].offset_by = 1;
menu_settings_list_current_add_range(list, list_info, 1, 16, 1, true, true);
MENU_SETTINGS_LIST_CURRENT_ADD_CMD(list, list_info, CMD_EVENT_REINIT);
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_CMD_APPLY_AUTO);
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_LAKKA_ADVANCED);
CONFIG_UINT(
list, list_info,
&settings->uints.video_max_swapchain_images,
@ -15753,6 +15863,8 @@ static bool setting_append_list(
(*list)[list_info->index - 1].action_ok = &setting_bool_action_left_with_refresh;
(*list)[list_info->index - 1].action_left = &setting_bool_action_left_with_refresh;
(*list)[list_info->index - 1].action_right = &setting_bool_action_right_with_refresh;
MENU_SETTINGS_LIST_CURRENT_ADD_CMD(list, list_info, CMD_EVENT_REINIT);
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_CMD_APPLY_AUTO);
CONFIG_BOOL(
list, list_info,

View file

@ -1349,6 +1349,7 @@ enum msg_hash_enums
MENU_LABEL(VIDEO_GPU_SCREENSHOT),
MENU_LBL_H(VIDEO_BLACK_FRAME_INSERTION),
MENU_LBL_H(VIDEO_BFI_DARK_FRAMES),
MENU_LBL_H(VIDEO_SHADER_SUBFRAMES),
MENU_LBL_H(VIDEO_FRAME_DELAY),
MENU_LBL_H(VIDEO_FRAME_DELAY_AUTO),
MENU_ENUM_LABEL_VALUE_VIDEO_FRAME_DELAY_AUTOMATIC,
@ -1431,6 +1432,23 @@ enum msg_hash_enums
MENU_ENUM_LABEL_VALUE_VIDEO_BLACK_FRAME_INSERTION_VALUE_900,
MENU_ENUM_LABEL_VALUE_VIDEO_BLACK_FRAME_INSERTION_VALUE_960,
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_OFF,
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_120,
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_180,
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_240,
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_300,
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_360,
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_420,
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_480,
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_540,
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_600,
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_660,
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_720,
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_780,
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_840,
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_900,
MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_SUBFRAMES_VALUE_960,
MENU_LABEL(VIDEO_FULLSCREEN),
MENU_LBL_H(VIDEO_MONITOR_INDEX),
MENU_LABEL(VIDEO_WIIU_PREFER_DRC),

View file

@ -1380,6 +1380,7 @@ static float audio_driver_monitor_adjust_system_rates(
float video_refresh_rate,
unsigned video_swap_interval,
unsigned black_frame_insertion,
unsigned shader_subframes,
float audio_max_timing_skew)
{
float inp_sample_rate = input_sample_rate;
@ -1394,7 +1395,7 @@ static float audio_driver_monitor_adjust_system_rates(
float timing_skew = 0.0f;
if (refresh_closest_multiple > 1)
target_video_sync_rate /= (((float)black_frame_insertion + 1.0f) * (float)video_swap_interval);
target_video_sync_rate /= (((float)black_frame_insertion + 1.0f) * (float)video_swap_interval * (float)shader_subframes);
timing_skew =
fabs(1.0f - input_fps / target_video_sync_rate);
@ -1411,6 +1412,7 @@ static bool video_driver_monitor_adjust_system_rates(
float audio_max_timing_skew,
unsigned video_swap_interval,
unsigned black_frame_insertion,
unsigned shader_subframes,
double input_fps)
{
float target_video_sync_rate = timing_skew_hz;
@ -1421,7 +1423,7 @@ static bool video_driver_monitor_adjust_system_rates(
float timing_skew = 0.0f;
if (refresh_closest_multiple > 1)
target_video_sync_rate /= (((float)black_frame_insertion + 1.0f) * (float)video_swap_interval);
target_video_sync_rate /= (((float)black_frame_insertion + 1.0f) * (float)video_swap_interval * (float)shader_subframes);
if (!vrr_runloop_enable)
{
@ -1448,7 +1450,8 @@ static void driver_adjust_system_rates(
float audio_max_timing_skew,
bool video_adaptive_vsync,
unsigned video_swap_interval,
unsigned black_frame_insertion)
unsigned black_frame_insertion,
unsigned shader_subframes)
{
struct retro_system_av_info *av_info = &video_st->av_info;
const struct retro_system_timing *info =
@ -1463,6 +1466,7 @@ static void driver_adjust_system_rates(
(video_st->flags & VIDEO_FLAG_CRT_SWITCHING_ACTIVE) ? true : false,
video_swap_interval,
black_frame_insertion,
shader_subframes,
audio_max_timing_skew,
video_refresh_rate,
input_fps);
@ -1482,6 +1486,7 @@ static void driver_adjust_system_rates(
video_refresh_rate,
video_swap_interval,
black_frame_insertion,
shader_subframes,
audio_max_timing_skew);
RARCH_LOG("[Audio]: Set audio input rate to: %.2f Hz.\n",
@ -1506,6 +1511,7 @@ static void driver_adjust_system_rates(
audio_max_timing_skew,
video_swap_interval,
black_frame_insertion,
shader_subframes,
input_fps))
{
/* We won't be able to do VSync reliably
@ -1626,7 +1632,8 @@ void drivers_init(
settings->floats.audio_max_timing_skew,
settings->bools.video_adaptive_vsync,
settings->uints.video_swap_interval,
settings->uints.video_black_frame_insertion
settings->uints.video_black_frame_insertion,
settings->uints.video_shader_subframes
);
/* Initialize video driver */
@ -2063,6 +2070,7 @@ bool driver_ctl(enum driver_ctl_state state, void *data)
unsigned video_swap_interval = settings->uints.video_swap_interval;
unsigned
black_frame_insertion = settings->uints.video_black_frame_insertion;
unsigned shader_subframes = settings->uints.video_shader_subframes;
video_monitor_set_refresh_rate(*hz);
@ -2077,7 +2085,8 @@ bool driver_ctl(enum driver_ctl_state state, void *data)
audio_max_timing_skew,
video_adaptive_vsync,
video_swap_interval,
black_frame_insertion
black_frame_insertion,
shader_subframes
);
}
break;

View file

@ -4424,6 +4424,7 @@ void runloop_set_video_swap_interval(
bool crt_switching_active,
unsigned swap_interval_config,
unsigned black_frame_insertion,
unsigned shader_subframes,
float audio_max_timing_skew,
float video_refresh_rate,
double input_fps)
@ -4451,12 +4452,14 @@ void runloop_set_video_swap_interval(
* set swap interval to 1
* > If core fps or display refresh rate are zero,
* set swap interval to 1
* > If BFI is active set swap interval to 1 */
* > If BFI is active set swap interval to 1
* > If Shader Subframes active, set swap interval to 1 */
if ( (vrr_runloop_enable)
|| (core_hz > timing_hz)
|| (core_hz <= 0.0f)
|| (timing_hz <= 0.0f)
|| (black_frame_insertion))
|| (black_frame_insertion)
|| (shader_subframes > 1))
{
runloop_st->video_swap_interval_auto = 1;
return;

View file

@ -397,6 +397,7 @@ void runloop_set_video_swap_interval(
bool crt_switching_active,
unsigned swap_interval_config,
unsigned black_frame_insertion,
unsigned shader_subframes,
float audio_max_timing_skew,
float video_refresh_rate,
double input_fps);

View file

@ -1374,6 +1374,7 @@ QWidget *VideoPage::widget()
windowedGroup->add(MENU_ENUM_LABEL_UI_MENUBAR_ENABLE);
vSyncGroup->add(MENU_ENUM_LABEL_VIDEO_SWAP_INTERVAL);
vSyncGroup->add(MENU_ENUM_LABEL_VIDEO_SHADER_SUBFRAMES);
vSyncGroup->add(MENU_ENUM_LABEL_VIDEO_BLACK_FRAME_INSERTION);
vSyncGroup->add(MENU_ENUM_LABEL_VIDEO_ADAPTIVE_VSYNC);
vSyncGroup->add(MENU_ENUM_LABEL_VIDEO_FRAME_DELAY);