Skip to content

Commit

Permalink
Add an option to present/skip presenting duplicate frames
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
stenzek committed Jan 14, 2020
1 parent efc1ee8 commit 11ba623
Show file tree
Hide file tree
Showing 11 changed files with 60 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,7 @@ private void addHackSettings(ArrayList<SettingsItem> sl)
Setting gpuTextureDecoding = gfxSection.getSetting(SettingsFile.KEY_GPU_TEXTURE_DECODING);
Setting xfbToTexture = hacksSection.getSetting(SettingsFile.KEY_XFB_TEXTURE);
Setting immediateXfb = hacksSection.getSetting(SettingsFile.KEY_IMMEDIATE_XFB);
Setting skipDuplicateXfbs = hacksSection.getSetting(SettingsFile.KEY_SKIP_DUPLICATE_XFBS);
Setting fastDepth = gfxSection.getSetting(SettingsFile.KEY_FAST_DEPTH);

sl.add(new HeaderSetting(null, null, R.string.embedded_frame_buffer, 0));
Expand Down Expand Up @@ -613,6 +614,9 @@ private void addHackSettings(ArrayList<SettingsItem> sl)
R.string.xfb_copy_method, R.string.xfb_copy_method_description, true, xfbToTexture));
sl.add(new CheckBoxSetting(SettingsFile.KEY_IMMEDIATE_XFB, Settings.SECTION_GFX_HACKS,
R.string.immediate_xfb, R.string.immediate_xfb_description, false, immediateXfb));
sl.add(new CheckBoxSetting(SettingsFile.KEY_SKIP_DUPLICATE_XFBS, Settings.SECTION_GFX_HACKS,
R.string.skip_duplicate_xfbs, R.string.skip_duplicate_xfbs_description, true,
skipDuplicateXfbs));

sl.add(new HeaderSetting(null, null, R.string.other, 0));
sl.add(new CheckBoxSetting(SettingsFile.KEY_FAST_DEPTH, Settings.SECTION_GFX_SETTINGS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ public final class SettingsFile
public static final String KEY_GPU_TEXTURE_DECODING = "EnableGPUTextureDecoding";
public static final String KEY_XFB_TEXTURE = "XFBToTextureEnable";
public static final String KEY_IMMEDIATE_XFB = "ImmediateXFBEnable";
public static final String KEY_SKIP_DUPLICATE_XFBS = "SkipDuplicateXFBs";
public static final String KEY_FAST_DEPTH = "FastDepthCalc";
public static final String KEY_ASPECT_RATIO = "AspectRatio";
public static final String KEY_SHADER_COMPILATION_MODE = "ShaderCompilationMode";
Expand Down
2 changes: 2 additions & 0 deletions Source/Android/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@
<string name="xfb_copy_method_description">Stores XFB Copies exclusively on the GPU, bypassing system memory. Causes graphical defects in a small number of games that need to readback from memory. If unsure, leave this checked.</string>
<string name="immediate_xfb">Immediately Present XFB</string>
<string name="immediate_xfb_description">Displays the XFB copies as soon as they are created, without waiting for scanout. Causes graphical defects in some games but reduces latency. If unsure, leave this unchecked.</string>
<string name="skip_duplicate_xfbs">Immediately Present XFB</string>
<string name="skip_duplicate_xfbs_description">Skips presentation of duplicate frames. This may improve performance on low-end devices, while making frame pacing less consistent. If unsure, leave this checked.</string>
<string name="disable_destination_alpha">Disable Destination Alpha</string>
<string name="disable_destination_alpha_description">Disables emulation of a hardware feature called destination alpha, which is used in many games for various effects.</string>
<string name="fast_depth_calculation">Fast Depth Calculation</string>
Expand Down
2 changes: 2 additions & 0 deletions Source/Core/Core/Config/GraphicsSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
1 change: 1 addition & 0 deletions Source/Core/Core/Config/GraphicsSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
20 changes: 20 additions & 0 deletions Source/Core/DolphinQt/Config/Graphics/HacksWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
Expand Down Expand Up @@ -117,6 +120,7 @@ void HacksWidget::CreateWidgets()
setLayout(main_layout);

UpdateDeferEFBCopiesEnabled();
UpdateSkipPresentingDuplicateFramesEnabled();
}

void HacksWidget::OnBackendChanged(const QString& backend_name)
Expand All @@ -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()
Expand Down Expand Up @@ -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 "
Expand Down Expand Up @@ -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);
Expand All @@ -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());
}
2 changes: 2 additions & 0 deletions Source/Core/DolphinQt/Config/Graphics/HacksWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class HacksWidget final : public GraphicsWidget
// External Framebuffer
QCheckBox* m_store_xfb_copies;
QCheckBox* m_immediate_xfb;
QCheckBox* m_skip_duplicate_xfbs;

// Other
QCheckBox* m_fast_depth_calculation;
Expand All @@ -50,4 +51,5 @@ class HacksWidget final : public GraphicsWidget
void AddDescriptions();

void UpdateDeferEFBCopiesEnabled();
void UpdateSkipPresentingDuplicateFramesEnabled();
};
40 changes: 24 additions & 16 deletions Source/Core/VideoCommon/RenderBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
Expand All @@ -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
{
Expand Down
1 change: 1 addition & 0 deletions Source/Core/VideoCommon/VideoConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions Source/Core/VideoCommon/VideoConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ struct VideoConfig final
bool bDisableCopyToVRAM;
bool bDeferEFBCopies;
bool bImmediateXFB;
bool bSkipPresentingDuplicateXFBs;
bool bCopyEFBScaled;
int iSafeTextureCache_ColorSamples;
float fAspectRatioHackW, fAspectRatioHackH;
Expand Down

0 comments on commit 11ba623

Please sign in to comment.