mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-24 14:49:42 -06:00
Add an option to present/skip presenting duplicate frames
Currently, we do not display every second frame in 25fps/30fps games which run to vsync. This improves performance as there's less rendering for the GPU to perform, but when combined with vsync, could cause frame pacing issues. This commit adds an option to force every frame generated by the console to be displayed to the host, which may improve pacing for these games.
This commit is contained in:
@ -154,6 +154,8 @@ const ConfigInfo<bool> GFX_HACK_DISABLE_COPY_TO_VRAM{{System::GFX, "Hacks", "Dis
|
||||
false};
|
||||
const ConfigInfo<bool> GFX_HACK_DEFER_EFB_COPIES{{System::GFX, "Hacks", "DeferEFBCopies"}, true};
|
||||
const ConfigInfo<bool> GFX_HACK_IMMEDIATE_XFB{{System::GFX, "Hacks", "ImmediateXFBEnable"}, false};
|
||||
const ConfigInfo<bool> GFX_HACK_SKIP_DUPLICATE_XFBS{{System::GFX, "Hacks", "SkipDuplicateXFBs"},
|
||||
true};
|
||||
const ConfigInfo<bool> GFX_HACK_COPY_EFB_SCALED{{System::GFX, "Hacks", "EFBScaledCopy"}, true};
|
||||
const ConfigInfo<bool> GFX_HACK_EFB_EMULATE_FORMAT_CHANGES{
|
||||
{System::GFX, "Hacks", "EFBEmulateFormatChanges"}, false};
|
||||
|
@ -111,6 +111,7 @@ extern const ConfigInfo<bool> GFX_HACK_SKIP_XFB_COPY_TO_RAM;
|
||||
extern const ConfigInfo<bool> GFX_HACK_DISABLE_COPY_TO_VRAM;
|
||||
extern const ConfigInfo<bool> GFX_HACK_DEFER_EFB_COPIES;
|
||||
extern const ConfigInfo<bool> GFX_HACK_IMMEDIATE_XFB;
|
||||
extern const ConfigInfo<bool> GFX_HACK_SKIP_DUPLICATE_XFBS;
|
||||
extern const ConfigInfo<bool> GFX_HACK_COPY_EFB_SCALED;
|
||||
extern const ConfigInfo<bool> GFX_HACK_EFB_EMULATE_FORMAT_CHANGES;
|
||||
extern const ConfigInfo<bool> GFX_HACK_VERTEX_ROUDING;
|
||||
|
@ -28,7 +28,7 @@ bool IsSettingSaveable(const Config::ConfigLocation& config_location)
|
||||
return true;
|
||||
}
|
||||
|
||||
static constexpr std::array<const Config::ConfigLocation*, 92> s_setting_saveable = {
|
||||
static constexpr std::array<const Config::ConfigLocation*, 93> s_setting_saveable = {
|
||||
// Main.Core
|
||||
|
||||
&Config::MAIN_DEFAULT_ISO.location,
|
||||
@ -136,6 +136,7 @@ bool IsSettingSaveable(const Config::ConfigLocation& config_location)
|
||||
&Config::GFX_HACK_DISABLE_COPY_TO_VRAM.location,
|
||||
&Config::GFX_HACK_DEFER_EFB_COPIES.location,
|
||||
&Config::GFX_HACK_IMMEDIATE_XFB.location,
|
||||
&Config::GFX_HACK_SKIP_DUPLICATE_XFBS.location,
|
||||
&Config::GFX_HACK_COPY_EFB_SCALED.location,
|
||||
&Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES.location,
|
||||
&Config::GFX_HACK_VERTEX_ROUDING.location,
|
||||
|
@ -86,9 +86,12 @@ void HacksWidget::CreateWidgets()
|
||||
m_store_xfb_copies = new GraphicsBool(tr("Store XFB Copies to Texture Only"),
|
||||
Config::GFX_HACK_SKIP_XFB_COPY_TO_RAM);
|
||||
m_immediate_xfb = new GraphicsBool(tr("Immediately Present XFB"), Config::GFX_HACK_IMMEDIATE_XFB);
|
||||
m_skip_duplicate_xfbs = new GraphicsBool(tr("Skip Presenting Duplicate Frames"),
|
||||
Config::GFX_HACK_SKIP_DUPLICATE_XFBS);
|
||||
|
||||
xfb_layout->addWidget(m_store_xfb_copies);
|
||||
xfb_layout->addWidget(m_immediate_xfb);
|
||||
xfb_layout->addWidget(m_skip_duplicate_xfbs);
|
||||
|
||||
// Other
|
||||
auto* other_box = new QGroupBox(tr("Other"));
|
||||
@ -117,6 +120,7 @@ void HacksWidget::CreateWidgets()
|
||||
setLayout(main_layout);
|
||||
|
||||
UpdateDeferEFBCopiesEnabled();
|
||||
UpdateSkipPresentingDuplicateFramesEnabled();
|
||||
}
|
||||
|
||||
void HacksWidget::OnBackendChanged(const QString& backend_name)
|
||||
@ -140,6 +144,8 @@ void HacksWidget::ConnectWidgets()
|
||||
[this](int) { UpdateDeferEFBCopiesEnabled(); });
|
||||
connect(m_store_xfb_copies, &QCheckBox::stateChanged,
|
||||
[this](int) { UpdateDeferEFBCopiesEnabled(); });
|
||||
connect(m_immediate_xfb, &QCheckBox::stateChanged,
|
||||
[this](int) { UpdateSkipPresentingDuplicateFramesEnabled(); });
|
||||
}
|
||||
|
||||
void HacksWidget::LoadSettings()
|
||||
@ -235,6 +241,12 @@ void HacksWidget::AddDescriptions()
|
||||
"expect all XFB copies to be displayed. However, turning this setting on reduces "
|
||||
"latency.\n\nIf unsure, leave this unchecked.");
|
||||
|
||||
static const char TR_SKIP_DUPLICATE_XFBS_DESCRIPTION[] = QT_TR_NOOP(
|
||||
"Skips presentation of duplicate frames (XFB copies) in 25fps/30fps games. This may improve "
|
||||
"performance on low-end devices, while making frame pacing less consistent.\n\nDisable this "
|
||||
"option as well as enabling V-Sync for optimal frame pacing.\n\nIf unsure, leave this "
|
||||
"checked.");
|
||||
|
||||
static const char TR_GPU_DECODING_DESCRIPTION[] =
|
||||
QT_TR_NOOP("Enables texture decoding using the GPU instead of the CPU.\n\nThis may result in "
|
||||
"performance gains in some scenarios, or on systems where the CPU is the "
|
||||
@ -263,6 +275,7 @@ void HacksWidget::AddDescriptions()
|
||||
AddDescription(m_accuracy, TR_ACCUARCY_DESCRIPTION);
|
||||
AddDescription(m_store_xfb_copies, TR_STORE_XFB_TO_TEXTURE_DESCRIPTION);
|
||||
AddDescription(m_immediate_xfb, TR_IMMEDIATE_XFB_DESCRIPTION);
|
||||
AddDescription(m_skip_duplicate_xfbs, TR_SKIP_DUPLICATE_XFBS_DESCRIPTION);
|
||||
AddDescription(m_gpu_texture_decoding, TR_GPU_DECODING_DESCRIPTION);
|
||||
AddDescription(m_fast_depth_calculation, TR_FAST_DEPTH_CALC_DESCRIPTION);
|
||||
AddDescription(m_disable_bounding_box, TR_DISABLE_BOUNDINGBOX_DESCRIPTION);
|
||||
@ -277,3 +290,10 @@ void HacksWidget::UpdateDeferEFBCopiesEnabled()
|
||||
const bool can_defer = m_store_efb_copies->isChecked() && m_store_xfb_copies->isChecked();
|
||||
m_defer_efb_copies->setEnabled(!can_defer);
|
||||
}
|
||||
|
||||
void HacksWidget::UpdateSkipPresentingDuplicateFramesEnabled()
|
||||
{
|
||||
// If Immediate XFB is on, there's no point to skipping duplicate XFB copies as immediate presents
|
||||
// when the XFB is created, therefore all XFB copies will be unique.
|
||||
m_skip_duplicate_xfbs->setEnabled(!m_immediate_xfb->isChecked());
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ private:
|
||||
// External Framebuffer
|
||||
QCheckBox* m_store_xfb_copies;
|
||||
QCheckBox* m_immediate_xfb;
|
||||
QCheckBox* m_skip_duplicate_xfbs;
|
||||
|
||||
// Other
|
||||
QCheckBox* m_fast_depth_calculation;
|
||||
@ -50,4 +51,5 @@ private:
|
||||
void AddDescriptions();
|
||||
|
||||
void UpdateDeferEFBCopiesEnabled();
|
||||
void UpdateSkipPresentingDuplicateFramesEnabled();
|
||||
};
|
||||
|
@ -1190,8 +1190,10 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6
|
||||
MathUtil::Rectangle<int> xfb_rect;
|
||||
const auto* xfb_entry =
|
||||
g_texture_cache->GetXFBTexture(xfb_addr, fb_width, fb_height, fb_stride, &xfb_rect);
|
||||
if (xfb_entry && xfb_entry->id != m_last_xfb_id)
|
||||
if (xfb_entry &&
|
||||
(!g_ActiveConfig.bSkipPresentingDuplicateXFBs || xfb_entry->id != m_last_xfb_id))
|
||||
{
|
||||
const bool is_duplicate_frame = xfb_entry->id == m_last_xfb_id;
|
||||
m_last_xfb_id = xfb_entry->id;
|
||||
|
||||
// Since we use the common pipelines here and draw vertices if a batch is currently being
|
||||
@ -1235,20 +1237,24 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6
|
||||
SetWindowSize(xfb_rect.GetWidth(), xfb_rect.GetHeight());
|
||||
}
|
||||
|
||||
m_fps_counter.Update();
|
||||
if (!is_duplicate_frame)
|
||||
{
|
||||
m_fps_counter.Update();
|
||||
|
||||
DolphinAnalytics::PerformanceSample perf_sample;
|
||||
perf_sample.speed_ratio = SystemTimers::GetEstimatedEmulationPerformance();
|
||||
perf_sample.num_prims = g_stats.this_frame.num_prims + g_stats.this_frame.num_dl_prims;
|
||||
perf_sample.num_draw_calls = g_stats.this_frame.num_draw_calls;
|
||||
DolphinAnalytics::Instance().ReportPerformanceInfo(std::move(perf_sample));
|
||||
DolphinAnalytics::PerformanceSample perf_sample;
|
||||
perf_sample.speed_ratio = SystemTimers::GetEstimatedEmulationPerformance();
|
||||
perf_sample.num_prims = g_stats.this_frame.num_prims + g_stats.this_frame.num_dl_prims;
|
||||
perf_sample.num_draw_calls = g_stats.this_frame.num_draw_calls;
|
||||
DolphinAnalytics::Instance().ReportPerformanceInfo(std::move(perf_sample));
|
||||
|
||||
if (IsFrameDumping())
|
||||
DumpCurrentFrame(xfb_entry->texture.get(), xfb_rect, ticks);
|
||||
if (IsFrameDumping())
|
||||
DumpCurrentFrame(xfb_entry->texture.get(), xfb_rect, ticks);
|
||||
|
||||
// Begin new frame
|
||||
m_frame_count++;
|
||||
g_stats.ResetFrame();
|
||||
}
|
||||
|
||||
// Begin new frame
|
||||
m_frame_count++;
|
||||
g_stats.ResetFrame();
|
||||
g_shader_cache->RetrieveAsyncShaders();
|
||||
g_vertex_manager->OnEndFrame();
|
||||
BeginImGuiFrame();
|
||||
@ -1263,16 +1269,18 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6
|
||||
// rate and not waiting for vblank. Otherwise, we'd end up with a huge list of pending copies.
|
||||
g_texture_cache->FlushEFBCopies();
|
||||
|
||||
// Remove stale EFB/XFB copies.
|
||||
g_texture_cache->Cleanup(m_frame_count);
|
||||
if (!is_duplicate_frame)
|
||||
{
|
||||
// Remove stale EFB/XFB copies.
|
||||
g_texture_cache->Cleanup(m_frame_count);
|
||||
Core::Callback_VideoCopiedToXFB(true);
|
||||
}
|
||||
|
||||
// Handle any config changes, this gets propogated to the backend.
|
||||
CheckForConfigChanges();
|
||||
g_Config.iSaveTargetId = 0;
|
||||
|
||||
EndUtilityDrawing();
|
||||
|
||||
Core::Callback_VideoCopiedToXFB(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -152,6 +152,7 @@ void VideoConfig::Refresh()
|
||||
bDisableCopyToVRAM = Config::Get(Config::GFX_HACK_DISABLE_COPY_TO_VRAM);
|
||||
bDeferEFBCopies = Config::Get(Config::GFX_HACK_DEFER_EFB_COPIES);
|
||||
bImmediateXFB = Config::Get(Config::GFX_HACK_IMMEDIATE_XFB);
|
||||
bSkipPresentingDuplicateXFBs = Config::Get(Config::GFX_HACK_SKIP_DUPLICATE_XFBS);
|
||||
bCopyEFBScaled = Config::Get(Config::GFX_HACK_COPY_EFB_SCALED);
|
||||
bEFBEmulateFormatChanges = Config::Get(Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES);
|
||||
bVertexRounding = Config::Get(Config::GFX_HACK_VERTEX_ROUDING);
|
||||
|
@ -124,6 +124,7 @@ struct VideoConfig final
|
||||
bool bDisableCopyToVRAM;
|
||||
bool bDeferEFBCopies;
|
||||
bool bImmediateXFB;
|
||||
bool bSkipPresentingDuplicateXFBs;
|
||||
bool bCopyEFBScaled;
|
||||
int iSafeTextureCache_ColorSamples;
|
||||
float fAspectRatioHackW, fAspectRatioHackH;
|
||||
|
Reference in New Issue
Block a user