Added rolling scan line simulation based on the shader subframe feature. This is implemented with a scrolling scissor rect rather than in the shader itself as this is more efficient although may not work for every shader pass - we may need an option to exclude certain passes. The implementation simply divides the screen up by the number of sub frames and then moves the scissor rect down over the screen over the number of sub frames. (#16282)

This commit is contained in:
MajorPainTheCactus 2024-03-19 15:59:36 +00:00 committed by GitHub
parent 5452999b2a
commit eef34e9461
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 517 additions and 23 deletions

View file

@ -401,6 +401,11 @@
*/
#define DEFAULT_SHADER_SUBFRAMES 1
/* Divides implements basic rolling scanning of sub frames - does this simply by scrolling a
* a scissor rect down the screen according to how many sub frames there are
*/
#define DEFAULT_SCAN_SUBFRAMES false
/* 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

@ -1809,6 +1809,8 @@ static struct config_bool_setting *populate_settings_bool(
/* Let implementation decide if automatic, or 1:1 PAR. */
SETTING_BOOL("video_aspect_ratio_auto", &settings->bools.video_aspect_ratio_auto, true, DEFAULT_ASPECT_RATIO_AUTO, false);
SETTING_BOOL("video_scan_subframes", &settings->bools.video_scan_subframes, true, DEFAULT_SCAN_SUBFRAMES, false);
SETTING_BOOL("video_allow_rotate", &settings->bools.video_allow_rotate, true, DEFAULT_ALLOW_ROTATE, false);
SETTING_BOOL("video_windowed_fullscreen", &settings->bools.video_windowed_fullscreen, true, DEFAULT_WINDOWED_FULLSCREEN, false);
SETTING_BOOL("video_crop_overscan", &settings->bools.video_crop_overscan, true, DEFAULT_CROP_OVERSCAN, false);

View file

@ -611,6 +611,7 @@ typedef struct settings
bool video_shader_watch_files;
bool video_shader_remember_last_dir;
bool video_shader_preset_save_reference_enable;
bool video_scan_subframes;
bool video_threaded;
bool video_font_enable;
bool video_disable_composition;

View file

@ -60,6 +60,8 @@
#error "UWP does not support D3D10"
#endif
#define D3D10_ROLLING_SCANLINE_SIMULATION
typedef struct
{
d3d10_texture_t texture;
@ -2417,6 +2419,41 @@ static bool d3d10_gfx_frame(
context->lpVtbl->RSSetViewports(context, 1,
&d3d10->pass[i].viewport);
#ifdef D3D10_ROLLING_SCANLINE_SIMULATION
if ( (video_info->shader_subframes > 1)
&& (video_info->scan_subframes)
&& !black_frame_insertion
&& !nonblock_state
&& !runloop_is_slowmotion
&& !runloop_is_paused
&& (!(d3d10->flags & D3D10_ST_FLAG_MENU_ENABLE)))
{
D3D10_RECT scissor_rect;
scissor_rect.left = 0;
scissor_rect.top = (unsigned int)(((float)d3d10->pass[i].viewport.Height / (float)video_info->shader_subframes)
* (float)video_info->current_subframe);
scissor_rect.right = d3d10->pass[i].viewport.Width ;
scissor_rect.bottom = (unsigned int)(((float)d3d10->pass[i].viewport.Height / (float)video_info->shader_subframes)
* (float)(video_info->current_subframe + 1));
d3d10->device->lpVtbl->RSSetScissorRects(d3d10->device, 1,
&scissor_rect);
}
else
{
D3D10_RECT scissor_rect;
scissor_rect.left = 0;
scissor_rect.top = 0;
scissor_rect.right = d3d10->pass[i].viewport.Width;
scissor_rect.bottom = d3d10->pass[i].viewport.Height;
d3d10->device->lpVtbl->RSSetScissorRects(d3d10->device, 1,
&scissor_rect);
}
#endif // D3D10_ROLLING_SCANLINE_SIMULATION
context->lpVtbl->Draw(context, 4, 0);
texture = &d3d10->pass[i].rt;
}
@ -2448,6 +2485,29 @@ static bool d3d10_gfx_frame(
d3d10->clearcolor);
context->lpVtbl->RSSetViewports(context, 1, &d3d10->frame.viewport);
#ifdef D3D10_ROLLING_SCANLINE_SIMULATION
if ( (video_info->shader_subframes > 1)
&& (video_info->scan_subframes)
&& !black_frame_insertion
&& !nonblock_state
&& !runloop_is_slowmotion
&& !runloop_is_paused
&& (!(d3d10->flags & D3D10_ST_FLAG_MENU_ENABLE)))
{
D3D10_RECT scissor_rect;
scissor_rect.left = 0;
scissor_rect.top = (unsigned int)(((float)video_height / (float)video_info->shader_subframes)
* (float)video_info->current_subframe);
scissor_rect.right = video_width ;
scissor_rect.bottom = (unsigned int)(((float)video_height / (float)video_info->shader_subframes)
* (float)(video_info->current_subframe + 1));
d3d10->device->lpVtbl->RSSetScissorRects(d3d10->device, 1,
&scissor_rect);
}
else
#endif // D3D10_ROLLING_SCANLINE_SIMULATION
{
D3D10_RECT scissor_rect;
@ -2464,6 +2524,20 @@ static bool d3d10_gfx_frame(
context->lpVtbl->OMSetBlendState(context, d3d10->blend_enable, NULL,
D3D10_DEFAULT_SAMPLE_MASK);
#ifdef D3D10_ROLLING_SCANLINE_SIMULATION
{
D3D10_RECT scissor_rect;
scissor_rect.left = 0;
scissor_rect.top = 0;
scissor_rect.right = video_width;
scissor_rect.bottom = video_height;
d3d10->device->lpVtbl->RSSetScissorRects(d3d10->device, 1,
&scissor_rect);
}
#endif // D3D10_ROLLING_SCANLINE_SIMULATION
if ( (d3d10->flags & D3D10_ST_FLAG_MENU_ENABLE)
&& d3d10->menu.texture.handle)
{
@ -2622,8 +2696,13 @@ static bool d3d10_gfx_frame(
&& (!(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++)
{
#ifdef D3D10_ROLLING_SCANLINE_SIMULATION
video_info->current_subframe = k;
#endif // D3D10_ROLLING_SCANLINE_SIMULATION
if (d3d10->shader_preset)
for (m = 0; m < d3d10->shader_preset->passes; m++)
{

View file

@ -76,6 +76,8 @@ const GUID DECLSPEC_SELECTANY libretro_IID_IDXGIFactory5 = { 0x7632e1f5,0xee65,0
#endif
#endif
#define D3D11_ROLLING_SCANLINE_SIMULATION
/* Temporary workaround for d3d11 not being able to poll flags during init */
static gfx_ctx_driver_t d3d11_fake_context;
@ -3025,7 +3027,23 @@ static bool d3d11_gfx_frame(
context, width, height, pitch, d3d11->format, frame, &d3d11->frame.texture[0]);
}
context->lpVtbl->RSSetState(context, d3d11->scissor_disabled);
#ifdef D3D11_ROLLING_SCANLINE_SIMULATION
if ( (video_info->shader_subframes > 1)
&& (video_info->scan_subframes)
&& !black_frame_insertion
&& !nonblock_state
&& !runloop_is_slowmotion
&& !runloop_is_paused
&& (!(d3d11->flags & D3D11_ST_FLAG_MENU_ENABLE)))
{
context->lpVtbl->RSSetState(context, d3d11->scissor_enabled);
}
else
#endif // D3D11_ROLLING_SCANLINE_SIMULATION
{
context->lpVtbl->RSSetState(context, d3d11->scissor_disabled);
}
d3d11->context->lpVtbl->OMSetBlendState(
d3d11->context, d3d11->blend_disable,
NULL, D3D11_DEFAULT_SAMPLE_MASK);
@ -3158,6 +3176,41 @@ static bool d3d11_gfx_frame(
&d3d11->pass[i].rt.rt_view, NULL);
context->lpVtbl->RSSetViewports(context, 1, &d3d11->pass[i].viewport);
#ifdef D3D11_ROLLING_SCANLINE_SIMULATION
if ( (video_info->shader_subframes > 1)
&& (video_info->scan_subframes)
&& !black_frame_insertion
&& !nonblock_state
&& !runloop_is_slowmotion
&& !runloop_is_paused
&& (!(d3d11->flags & D3D11_ST_FLAG_MENU_ENABLE)))
{
D3D11_RECT scissor_rect;
scissor_rect.left = 0;
scissor_rect.top = (unsigned int)(((float)d3d11->pass[i].viewport.Height / (float)video_info->shader_subframes)
* (float)video_info->current_subframe);
scissor_rect.right = d3d11->pass[i].viewport.Width ;
scissor_rect.bottom = (unsigned int)(((float)d3d11->pass[i].viewport.Height / (float)video_info->shader_subframes)
* (float)(video_info->current_subframe + 1));
d3d11->context->lpVtbl->RSSetScissorRects(d3d11->context, 1,
&scissor_rect);
}
else
{
D3D11_RECT scissor_rect;
scissor_rect.left = 0;
scissor_rect.top = 0;
scissor_rect.right = d3d11->pass[i].viewport.Width;
scissor_rect.bottom = d3d11->pass[i].viewport.Height;
d3d11->context->lpVtbl->RSSetScissorRects(d3d11->context, 1,
&scissor_rect);
}
#endif // D3D11_ROLLING_SCANLINE_SIMULATION
if (i == d3d11->shader_preset->passes - 1)
context->lpVtbl->Draw(context, 4, 0);
else
@ -3204,6 +3257,33 @@ static bool d3d11_gfx_frame(
context->lpVtbl->VSSetConstantBuffers(context, 0, 1, &d3d11->frame.ubo);
}
#ifdef D3D11_ROLLING_SCANLINE_SIMULATION
if ( (video_info->shader_subframes > 1)
&& (video_info->scan_subframes)
&& !black_frame_insertion
&& !nonblock_state
&& !runloop_is_slowmotion
&& !runloop_is_paused
&& (!(d3d11->flags & D3D11_ST_FLAG_MENU_ENABLE)))
{
D3D11_RECT scissor_rect;
scissor_rect.left = 0;
scissor_rect.top = (unsigned int)(((float)video_height / (float)video_info->shader_subframes)
* (float)video_info->current_subframe);
scissor_rect.right = video_width ;
scissor_rect.bottom = (unsigned int)(((float)video_height / (float)video_info->shader_subframes)
* (float)(video_info->current_subframe + 1));
d3d11->context->lpVtbl->RSSetScissorRects(d3d11->context, 1,
&scissor_rect);
}
else
{
d3d11->context->lpVtbl->RSSetScissorRects(d3d11->context, 1, &d3d11->scissor);
}
#endif // D3D11_ROLLING_SCANLINE_SIMULATION
context->lpVtbl->Draw(context, 4, 0);
context->lpVtbl->RSSetState(context, d3d11->scissor_enabled);
d3d11->context->lpVtbl->RSSetScissorRects(d3d11->context, 1, &d3d11->scissor);
@ -3458,6 +3538,10 @@ static bool d3d11_gfx_frame(
d3d11->flags |= D3D11_ST_FLAG_FRAME_DUPE_LOCK;
for (k = 1; k < video_info->shader_subframes; k++)
{
#ifdef D3D11_ROLLING_SCANLINE_SIMULATION
video_info->current_subframe = k;
#endif // D3D11_ROLLING_SCANLINE_SIMULATION
if (d3d11->shader_preset)
for (m = 0; m < d3d11->shader_preset->passes; m++)
{

View file

@ -79,6 +79,8 @@
} \
}
#define D3D12_ROLLING_SCANLINE_SIMULATION
typedef struct
{
d3d12_texture_t texture;
@ -3748,8 +3750,33 @@ static bool d3d12_gfx_frame(
#endif
cmd->lpVtbl->RSSetViewports(cmd, 1,
&d3d12->pass[i].viewport);
cmd->lpVtbl->RSSetScissorRects(cmd, 1,
&d3d12->pass[i].scissorRect);
#ifdef D3D12_ROLLING_SCANLINE_SIMULATION
if ( (video_info->shader_subframes > 1)
&& (video_info->scan_subframes)
&& !black_frame_insertion
&& !nonblock_state
&& !runloop_is_slowmotion
&& !runloop_is_paused
&& (!(d3d12->flags & D3D12_ST_FLAG_MENU_ENABLE)))
{
D3D12_RECT scissor_rect;
scissor_rect.left = 0;
scissor_rect.top = (unsigned int)(((float)d3d12->pass[i].viewport.Height / (float)video_info->shader_subframes)
* (float)video_info->current_subframe);
scissor_rect.right = d3d12->pass[i].viewport.Width;
scissor_rect.bottom = (unsigned int)(((float)d3d12->pass[i].viewport.Height / (float)video_info->shader_subframes)
* (float)(video_info->current_subframe + 1));
cmd->lpVtbl->RSSetScissorRects(cmd, 1, &scissor_rect);
}
else
#endif // D3D12_ROLLING_SCANLINE_SIMULATION
{
cmd->lpVtbl->RSSetScissorRects(cmd, 1,
&d3d12->pass[i].scissorRect);
}
if (i == d3d12->shader_preset->passes - 1)
start_vertex_location = 0;
@ -3836,7 +3863,32 @@ static bool d3d12_gfx_frame(
}
cmd->lpVtbl->RSSetViewports(cmd, 1, &d3d12->frame.viewport);
cmd->lpVtbl->RSSetScissorRects(cmd, 1, &d3d12->frame.scissorRect);
#ifdef D3D12_ROLLING_SCANLINE_SIMULATION
if ( (video_info->shader_subframes > 1)
&& (video_info->scan_subframes)
&& !black_frame_insertion
&& !nonblock_state
&& !runloop_is_slowmotion
&& !runloop_is_paused
&& (!(d3d12->flags & D3D12_ST_FLAG_MENU_ENABLE)))
{
D3D12_RECT scissor_rect;
scissor_rect.left = 0;
scissor_rect.top = (unsigned int)(((float)video_height / (float)video_info->shader_subframes)
* (float)video_info->current_subframe);
scissor_rect.right = video_width ;
scissor_rect.bottom = (unsigned int)(((float)video_height / (float)video_info->shader_subframes)
* (float)(video_info->current_subframe + 1));
cmd->lpVtbl->RSSetScissorRects(cmd, 1, &scissor_rect);
}
else
#endif // D3D12_ROLLING_SCANLINE_SIMULATION
{
cmd->lpVtbl->RSSetScissorRects(cmd, 1, &d3d12->frame.scissorRect);
}
cmd->lpVtbl->DrawInstanced(cmd, 4, 1, 0, 0);
@ -4046,6 +4098,10 @@ static bool d3d12_gfx_frame(
d3d12->flags |= D3D12_ST_FLAG_FRAME_DUPE_LOCK;
for (k = 1; k < video_info->shader_subframes; k++)
{
#ifdef D3D12_ROLLING_SCANLINE_SIMULATION
video_info->current_subframe = k;
#endif // D3D12_ROLLING_SCANLINE_SIMULATION
if (d3d12->shader_preset)
for (m = 0; m < d3d12->shader_preset->passes; m++)
{

View file

@ -2604,6 +2604,25 @@ static bool gl3_frame(void *data, const void *frame,
gl->filter_chain, 1);
}
#ifdef GL3_ROLLING_SCANLINE_SIMULATION
if ( (video_info->shader_subframes > 1)
&& (video_info->scan_subframes)
&& !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_simulate_scanline(
gl->filter_chain, true);
}
else
{
gl3_filter_chain_set_simulate_scanline(
gl->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,
&gl->filter_chain_vp);

View file

@ -4232,6 +4232,28 @@ static bool vulkan_frame(void *data, const void *frame,
vulkan_filter_chain_set_current_shader_subframe(
(vulkan_filter_chain_t*)vk->filter_chain, 1);
}
#ifdef VULKAN_ROLLING_SCANLINE_SIMULATION
if ( (video_info->shader_subframes > 1)
&& (video_info->scan_subframes)
&& (backbuffer->image != VK_NULL_HANDLE)
&& !black_frame_insertion
&& !input_driver_nonblock_state
&& !runloop_is_slowmotion
&& !runloop_is_paused
&& (!(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);
}
else
{
vulkan_filter_chain_set_simulate_scanline(
(vulkan_filter_chain_t*)vk->filter_chain, false);
}
#endif // VULKAN_ROLLING_SCANLINE_SIMULATION
#ifdef HAVE_REWIND
vulkan_filter_chain_set_frame_direction(
(vulkan_filter_chain_t*)vk->filter_chain,

View file

@ -935,6 +935,13 @@ public:
current_subframe = cur_subframe;
}
#ifdef GL3_ROLLING_SCANLINE_SIMULATION
void set_simulate_scanline(bool simulate)
{
simulate_scanline = simulate;
}
#endif // GL3_ROLLING_SCANLINE_SIMULATION
void set_name(const char *name)
{
pass_name = name;
@ -1036,6 +1043,9 @@ private:
unsigned pass_number = 0;
uint32_t total_subframes = 1;
uint32_t current_subframe = 1;
#ifdef GL3_ROLLING_SCANLINE_SIMULATION
bool simulate_scanline = false;
#endif // GL3_ROLLING_SCANLINE_SIMULATION
size_t ubo_offset = 0;
std::string pass_name;
@ -1790,12 +1800,55 @@ void Pass::build_commands(
glClear(GL_COLOR_BUFFER_BIT);
}
#ifdef GL3_ROLLING_SCANLINE_SIMULATION
if (simulate_scanline)
{
glEnable(GL_SCISSOR_TEST);
}
#endif // GL3_ROLLING_SCANLINE_SIMULATION
if (final_pass)
{
glViewport(current_viewport.x, current_viewport.y,
current_viewport.width, current_viewport.height);
#ifdef GL3_ROLLING_SCANLINE_SIMULATION
if (simulate_scanline)
{
glScissor( current_viewport.x,
int32_t((float(current_viewport.height) / float(total_subframes))
* float(current_subframe - 1)),
current_viewport.width,
uint32_t(float(current_viewport.height) / float(total_subframes))
);
}
else
{
glScissor( current_viewport.x, current_viewport.y,
current_viewport.width, current_viewport.height);
}
#endif // GL3_ROLLING_SCANLINE_SIMULATION
}
else
{
glViewport(0, 0, size.width, size.height);
#ifdef GL3_ROLLING_SCANLINE_SIMULATION
if (simulate_scanline)
{
glScissor( 0,
int32_t((float(size.height) / float(total_subframes))
* float(current_subframe - 1)),
size.width,
uint32_t(float(size.height) / float(total_subframes))
);
}
else
{
glScissor(0, 0, size.width, size.height);
}
#endif // GL3_ROLLING_SCANLINE_SIMULATION
}
#if !defined(HAVE_OPENGLES)
if (framebuffer && framebuffer->get_format() == GL_SRGB8_ALPHA8)
glEnable(GL_FRAMEBUFFER_SRGB);
@ -1818,6 +1871,12 @@ void Pass::build_commands(
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
#ifdef GL3_ROLLING_SCANLINE_SIMULATION
if (simulate_scanline)
{
glDisable(GL_SCISSOR_TEST);
}
#endif // GL3_ROLLING_SCANLINE_SIMULATION
#if !defined(HAVE_OPENGLES)
glDisable(GL_FRAMEBUFFER_SRGB);
@ -1870,6 +1929,9 @@ public:
void set_rotation(uint32_t rot);
void set_shader_subframes(uint32_t tot_subframes);
void set_current_shader_subframe(uint32_t cur_subframe);
#ifdef GL3_ROLLING_SCANLINE_SIMULATION
void set_simulate_scanline(bool simulate);
#endif // GL3_ROLLING_SCANLINE_SIMULATION
void set_pass_name(unsigned pass, const char *name);
void add_static_texture(std::unique_ptr<gl3_shader::StaticTexture> texture);
@ -2373,6 +2435,15 @@ void gl3_filter_chain::set_current_shader_subframe(uint32_t cur_subframe)
passes[i]->set_current_shader_subframe(cur_subframe);
}
#ifdef GL3_ROLLING_SCANLINE_SIMULATION
void gl3_filter_chain::set_simulate_scanline(bool simulate_scanline)
{
unsigned i;
for (i = 0; i < passes.size(); i++)
passes[i]->set_simulate_scanline(simulate_scanline);
}
#endif // GL3_ROLLING_SCANLINE_SIMULATION
void gl3_filter_chain::set_pass_name(unsigned pass, const char *name)
{
passes[pass]->set_name(name);
@ -2807,6 +2878,15 @@ void gl3_filter_chain_set_current_shader_subframe(
chain->set_current_shader_subframe(cur_subframe);
}
#ifdef GL3_ROLLING_SCANLINE_SIMULATION
void gl3_filter_chain_set_simulate_scanline(
gl3_filter_chain_t *chain,
bool simulate_scanline)
{
chain->set_simulate_scanline(simulate_scanline);
}
#endif // GL3_ROLLING_SCANLINE_SIMULATION
void gl3_filter_chain_set_frame_count_period(
gl3_filter_chain_t *chain,
unsigned pass,

View file

@ -25,6 +25,8 @@
#include "glslang_util.h"
#define GL3_ROLLING_SCANLINE_SIMULATION
RETRO_BEGIN_DECLS
typedef struct gl3_filter_chain gl3_filter_chain_t;
@ -125,6 +127,12 @@ void gl3_filter_chain_set_current_shader_subframe(
gl3_filter_chain_t *chain,
uint32_t cur_subframe);
#ifdef GL3_ROLLING_SCANLINE_SIMULATION
void gl3_filter_chain_set_simulate_scanline(
gl3_filter_chain_t *chain,
bool simulate_scanline);
#endif // GL3_ROLLING_SCANLINE_SIMULATION
void gl3_filter_chain_set_pass_name(
gl3_filter_chain_t *chain,
unsigned pass,

View file

@ -512,6 +512,9 @@ class Pass
cache(cache),
num_sync_indices(num_sync_indices),
final_pass(final_pass)
#ifdef VULKAN_ROLLING_SCANLINE_SIMULATION
,simulate_scanline(false)
#endif // VULKAN_ROLLING_SCANLINE_SIMULATION
{}
~Pass();
@ -548,6 +551,9 @@ class Pass
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; }
#ifdef VULKAN_ROLLING_SCANLINE_SIMULATION
void set_simulate_scanline(bool simulate) { simulate_scanline = simulate; }
#endif // VULKAN_ROLLING_SCANLINE_SIMULATION
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; }
@ -581,6 +587,9 @@ class Pass
unsigned num_sync_indices;
unsigned sync_index;
bool final_pass;
#ifdef VULKAN_ROLLING_SCANLINE_SIMULATION
bool simulate_scanline;
#endif // VULKAN_ROLLING_SCANLINE_SIMULATION
Size2D get_output_size(const Size2D &original_size,
const Size2D &max_source) const;
@ -699,6 +708,9 @@ struct vulkan_filter_chain
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);
#ifdef VULKAN_ROLLING_SCANLINE_SIMULATION
void set_simulate_scanline(bool simulate_scanline);
#endif // VULKAN_ROLLING_SCANLINE_SIMULATION
void set_frame_direction(int32_t direction);
void set_rotation(uint32_t rot);
void set_pass_name(unsigned pass, const char *name);
@ -1707,6 +1719,15 @@ void vulkan_filter_chain::set_current_shader_subframe(uint32_t current_subframe)
passes[i]->set_current_shader_subframe(current_subframe);
}
#ifdef VULKAN_ROLLING_SCANLINE_SIMULATION
void vulkan_filter_chain::set_simulate_scanline(bool simulate_scanline)
{
unsigned i;
for (i = 0; i < passes.size(); i++)
passes[i]->set_simulate_scanline(simulate_scanline);
}
#endif // VULKAN_ROLLING_SCANLINE_SIMULATION
void vulkan_filter_chain::set_frame_direction(int32_t direction)
{
unsigned i;
@ -2758,18 +2779,39 @@ void Pass::build_commands(
if (final_pass)
{
const VkRect2D sci = {
{
int32_t(current_viewport.x),
int32_t(current_viewport.y)
},
{
uint32_t(current_viewport.width),
uint32_t(current_viewport.height)
},
};
vkCmdSetViewport(cmd, 0, 1, &current_viewport);
vkCmdSetScissor(cmd, 0, 1, &sci);
#ifdef VULKAN_ROLLING_SCANLINE_SIMULATION
if (simulate_scanline)
{
const VkRect2D sci = {
{
int32_t(current_viewport.x),
int32_t((current_viewport.height / float(total_subframes))
* float(current_subframe - 1))
},
{
uint32_t(current_viewport.width),
uint32_t(current_viewport.height / float(total_subframes))
},
};
vkCmdSetScissor(cmd, 0, 1, &sci);
}
else
#endif // VULKAN_ROLLING_SCANLINE_SIMULATION
{
const VkRect2D sci = {
{
int32_t(current_viewport.x),
int32_t(current_viewport.y)
},
{
uint32_t(current_viewport.width),
uint32_t(current_viewport.height)
},
};
vkCmdSetScissor(cmd, 0, 1, &sci);
}
}
else
{
@ -2779,16 +2821,37 @@ void Pass::build_commands(
float(current_framebuffer_size.height),
0.0f, 1.0f
};
const VkRect2D sci = {
{ 0, 0 },
{
current_framebuffer_size.width,
current_framebuffer_size.height
},
};
vkCmdSetViewport(cmd, 0, 1, &_vp);
vkCmdSetScissor(cmd, 0, 1, &sci);
#ifdef VULKAN_ROLLING_SCANLINE_SIMULATION
if (simulate_scanline)
{
const VkRect2D sci = {
{
0,
int32_t((float(current_framebuffer_size.height) / float(total_subframes))
* float(current_subframe - 1))
},
{
uint32_t(current_framebuffer_size.width),
uint32_t(float(current_framebuffer_size.height) / float(total_subframes))
},
};
vkCmdSetScissor(cmd, 0, 1, &sci);
}
else
#endif // VULKAN_ROLLING_SCANLINE_SIMULATION
{
const VkRect2D sci = {
{ 0, 0 },
{
current_framebuffer_size.width,
current_framebuffer_size.height
},
};
vkCmdSetScissor(cmd, 0, 1, &sci);
}
}
vkCmdDraw(cmd, 4, 1, 0, 0);
@ -3403,6 +3466,15 @@ void vulkan_filter_chain_set_current_shader_subframe(
chain->set_current_shader_subframe(cur_subframe);
}
#ifdef VULKAN_ROLLING_SCANLINE_SIMULATION
void vulkan_filter_chain_set_simulate_scanline(
vulkan_filter_chain_t *chain,
bool simulate_scanline)
{
chain->set_simulate_scanline(simulate_scanline);
}
#endif // VULKAN_ROLLING_SCANLINE_SIMULATION
void vulkan_filter_chain_set_frame_direction(
vulkan_filter_chain_t *chain,
int32_t direction)

View file

@ -27,6 +27,8 @@
#include "../include/vulkan/vulkan.h"
#define VULKAN_ROLLING_SCANLINE_SIMULATION
RETRO_BEGIN_DECLS
typedef struct vulkan_filter_chain vulkan_filter_chain_t;
@ -126,6 +128,11 @@ void vulkan_filter_chain_set_shader_subframes(vulkan_filter_chain_t *chain,
void vulkan_filter_chain_set_current_shader_subframe(vulkan_filter_chain_t *chain,
uint32_t cur_subframe);
#ifdef VULKAN_ROLLING_SCANLINE_SIMULATION
void vulkan_filter_chain_set_simulate_scanline(vulkan_filter_chain_t *chain,
bool simulate_scanline);
#endif // VULKAN_ROLLING_SCANLINE_SIMULATION
void vulkan_filter_chain_set_frame_direction(vulkan_filter_chain_t *chain,
int32_t direction);

View file

@ -2563,6 +2563,8 @@ void video_driver_build_info(video_frame_info_t *video_info)
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->current_subframe = 0;
video_info->scan_subframes = settings->bools.video_scan_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;
@ -3920,6 +3922,7 @@ void video_driver_frame(const void *data, unsigned width,
&& video_st->current_video
&& video_st->current_video->frame)
{
video_info.current_subframe = 0;
if (video_st->current_video->frame(
video_st->data, data, width, height,
video_st->frame_count, (unsigned)pitch,

View file

@ -417,6 +417,7 @@ typedef struct video_frame_info
unsigned black_frame_insertion;
unsigned bfi_dark_frames;
unsigned shader_subframes;
unsigned current_subframe;
unsigned fps_update_interval;
unsigned memory_update_interval;
unsigned msg_queue_delay;
@ -489,6 +490,7 @@ typedef struct video_frame_info
bool crt_switch_hires_menu;
bool hdr_enable;
bool overlay_behind_menu;
bool scan_subframes;
} video_frame_info_t;
typedef void (*update_window_title_cb)(void*);

View file

@ -4131,6 +4131,10 @@ MSG_HASH(
MENU_ENUM_LABEL_VIDEO_SHADER_SUBFRAMES,
"video_shader_subframes"
)
MSG_HASH(
MENU_ENUM_LABEL_VIDEO_SCAN_SUBFRAMES,
"video_scan_subframes"
)
MSG_HASH(
MENU_ENUM_LABEL_VIDEO_CROP_OVERSCAN,
"video_crop_overscan"

View file

@ -428,6 +428,9 @@ int msg_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len)
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_VIDEO_SCAN_SUBFRAMES:
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_HELP_VIDEO_SCAN_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

@ -2098,6 +2098,18 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_GPU_SCREENSHOT,
"GPU Screenshot"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_VIDEO_SCAN_SUBFRAMES,
"Rolling scanline simulation"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VIDEO_SCAN_SUBFRAMES,
"Simulates a basic rolling scanline over multiple sub-frames by dividing the screen up vertically and rendering each part of the screen according to how many sub frames there are"
)
MSG_HASH(
MENU_ENUM_LABEL_HELP_VIDEO_SCAN_SUBFRAMES,
"Simulates a basic rolling scanline over multiple sub-frames by dividing the screen up vertically and rendering each part of the screen according to how many sub frames there are from the top of the screen down"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_VIDEO_GPU_SCREENSHOT,
"Screenshots capture GPU shaded material if available."

View file

@ -501,6 +501,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_shader_delay, MENU_
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_video_scan_subframes, MENU_ENUM_SUBLABEL_VIDEO_SCAN_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)
@ -4828,6 +4829,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_VIDEO_SHADER_SUBFRAMES:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_shader_subframes);
break;
case MENU_ENUM_LABEL_VIDEO_SCAN_SUBFRAMES:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_scan_subframes);
break;
case MENU_ENUM_LABEL_VIDEO_FRAME_DELAY:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_video_frame_delay);
break;

View file

@ -9400,6 +9400,7 @@ unsigned menu_displaylist_build_list(
bool video_hard_sync = settings->bools.video_hard_sync;
bool video_wait_swap = settings->bools.video_waitable_swapchains;
unsigned bfi = settings->uints.video_black_frame_insertion;
unsigned shader_subframes = settings->uints.video_shader_subframes;
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
MENU_ENUM_LABEL_VIDEO_VSYNC,
@ -9418,6 +9419,14 @@ unsigned menu_displaylist_build_list(
MENU_ENUM_LABEL_VIDEO_SHADER_SUBFRAMES,
PARSE_ONLY_UINT, false) == 0)
count++;
if (shader_subframes > 1)
{
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
MENU_ENUM_LABEL_VIDEO_SCAN_SUBFRAMES,
PARSE_ONLY_BOOL, false) == 0)
count++;
}
}
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
MENU_ENUM_LABEL_VIDEO_BLACK_FRAME_INSERTION,

View file

@ -13551,6 +13551,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_BOOL(
list, list_info,
&settings->bools.video_scan_subframes,
MENU_ENUM_LABEL_VIDEO_SCAN_SUBFRAMES,
MENU_ENUM_LABEL_VALUE_VIDEO_SCAN_SUBFRAMES,
DEFAULT_SCAN_SUBFRAMES,
MENU_ENUM_LABEL_VALUE_OFF,
MENU_ENUM_LABEL_VALUE_ON,
&group_info,
&subgroup_info,
parent_group,
general_write_handler,
general_read_handler,
SD_FLAG_NONE);
(*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;
SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_CMD_APPLY_AUTO);
MENU_SETTINGS_LIST_CURRENT_ADD_CMD(list, list_info, CMD_EVENT_REINIT);
CONFIG_UINT(
list, list_info,
&settings->uints.video_max_swapchain_images,

View file

@ -1378,6 +1378,7 @@ enum msg_hash_enums
MENU_LBL_H(VIDEO_BLACK_FRAME_INSERTION),
MENU_LBL_H(VIDEO_BFI_DARK_FRAMES),
MENU_LBL_H(VIDEO_SHADER_SUBFRAMES),
MENU_LBL_H(VIDEO_SCAN_SUBFRAMES),
MENU_LBL_H(VIDEO_FRAME_DELAY),
MENU_LBL_H(VIDEO_FRAME_DELAY_AUTO),
MENU_ENUM_LABEL_VALUE_VIDEO_FRAME_DELAY_AUTOMATIC,

View file

@ -1375,6 +1375,7 @@ QWidget *VideoPage::widget()
vSyncGroup->add(MENU_ENUM_LABEL_VIDEO_SWAP_INTERVAL);
vSyncGroup->add(MENU_ENUM_LABEL_VIDEO_SHADER_SUBFRAMES);
vSyncGroup->add(MENU_ENUM_LABEL_VIDEO_SCAN_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);