diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp index 3b4ae3c457..676fd300d9 100644 --- a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp @@ -245,7 +245,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.gsDumpCompression, "EmuCore/GS", "GSDumpCompression", static_cast(GSDumpCompressionMethod::LZMA)); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableFramebufferFetch, "EmuCore/GS", "DisableFramebufferFetch", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableDualSource, "EmuCore/GS", "DisableDualSourceBlend", false); - SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableHardwareReadbacks, "EmuCore/GS", "HWDisableReadbacks", false); + SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.gsDownloadMode, "EmuCore/GS", "HWDownloadMode", static_cast(GSHardwareDownloadMode::Enabled)); ////////////////////////////////////////////////////////////////////////// // SW Settings @@ -290,9 +290,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* connect(m_ui.swTextureFiltering, &QComboBox::currentIndexChanged, this, &GraphicsSettingsWidget::onSWTextureFilteringChange); updateRendererDependentOptions(); - // only allow disabling readbacks for per-game settings, it's too dangerous #ifndef PCSX2_DEVBUILD - m_ui.disableHardwareReadbacks->setEnabled(m_dialog->isPerGameSettings()); + // only allow disabling readbacks for per-game settings, it's too dangerous + m_ui.gsDownloadMode->setEnabled(m_dialog->isPerGameSettings()); // Remove texture offset and skipdraw range for global settings. if (!m_dialog->isPerGameSettings()) @@ -477,7 +477,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* "the GPU has more time to complete it (this is NOT frame skipping). Can smooth our frame time fluctuations when the CPU/GPU are near maximum " "utilization, but makes frame pacing more inconsistent and can increase input lag.")); - dialog->registerWidgetHelp(m_ui.disableHardwareReadbacks, tr("Disable Hardware Readbacks"), tr("Unchecked"), + dialog->registerWidgetHelp(m_ui.gsDownloadMode, tr("GS Download Mode"), tr("Accurate"), tr("Skips synchronizing with the GS thread and host GPU for GS downloads. " "Can result in a large speed boost on slower systems, at the cost of many broken graphical effects. " "If games are broken and you have this option enabled, please disable it first.")); diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.ui b/pcsx2-qt/Settings/GraphicsSettingsWidget.ui index a7f7a4e4d5..066d71ea2d 100644 --- a/pcsx2-qt/Settings/GraphicsSettingsWidget.ui +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.ui @@ -1429,8 +1429,15 @@ - + + + + + Skip Presenting Duplicate Frames + + + @@ -1438,6 +1445,13 @@ + + + + Disable Framebuffer Fetch + + + @@ -1452,37 +1466,16 @@ - - - - Disable Framebuffer Fetch - - - - - - - Skip Presenting Duplicate Frames - - - - - - - Disable Hardware Readbacks - - - - + GS Dump Compression: - + @@ -1501,6 +1494,37 @@ + + + + Hardware Download Mode: + + + + + + + + Accurate (Recommended) + + + + + Disable Readbacks (Synchronize GS Thread) + + + + + Unsynchronized (Non-Deterministic) + + + + + Disabled (Ignore Transfers) + + + + diff --git a/pcsx2/Config.h b/pcsx2/Config.h index f0da2a687a..c77e9be969 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -199,6 +199,14 @@ enum class GSDumpCompressionMethod : u8 Zstandard, }; +enum class GSHardwareDownloadMode : u8 +{ + Enabled, + NoReadbacks, + Unsynchronized, + Disabled +}; + // Template function for casting enumerations to their underlying type template typename std::underlying_type::type enum_cast(Enumeration E) @@ -488,7 +496,6 @@ struct Pcsx2Config OsdShowInputs : 1; bool - HWDisableReadbacks : 1, GPUPaletteConversion : 1, AutoFlushSW : 1, PreloadFrameWithGSData : 1, @@ -561,6 +568,7 @@ struct Pcsx2Config BiFiltering TextureFiltering{BiFiltering::PS2}; TexturePreloadingLevel TexturePreloading{TexturePreloadingLevel::Full}; GSDumpCompressionMethod GSDumpCompression{GSDumpCompressionMethod::LZMA}; + GSHardwareDownloadMode HWDownloadMode{GSHardwareDownloadMode::Enabled}; int Dithering{2}; int MaxAnisotropy{0}; int SWExtraThreads{2}; diff --git a/pcsx2/Frontend/FullscreenUI.cpp b/pcsx2/Frontend/FullscreenUI.cpp index 3ab3411938..92b106b1da 100644 --- a/pcsx2/Frontend/FullscreenUI.cpp +++ b/pcsx2/Frontend/FullscreenUI.cpp @@ -2579,6 +2579,8 @@ void FullscreenUI::DrawGraphicsSettingsPage() static constexpr const char* s_anisotropic_filtering_values[] = {"0", "2", "4", "8", "16"}; static constexpr const char* s_preloading_options[] = {"None", "Partial", "Full (Hash Cache)"}; static constexpr const char* s_generic_options[] = {"Automatic (Default)", "Force Disabled", "Force Enabled"}; + static constexpr const char* s_hw_download[] = {"Accurate (Recommended)", "Disable Readbacks (Synchronize GS Thread)", + "Unsynchronized (Non-Deterministic)", "Disabled (Ignore Transfers)"}; SettingsInterface* bsi = GetEditingSettingsInterface(); @@ -2654,6 +2656,8 @@ void FullscreenUI::DrawGraphicsSettingsPage() "Uploads full textures to the GPU on use, rather than only the utilized regions. Can improve performance in some games.", "EmuCore/GS", "texture_preloading", static_cast(TexturePreloadingLevel::Off), s_preloading_options, std::size(s_preloading_options)); + DrawIntListSetting(bsi, "Hardware Download Mode", "Changes synchronization behavior for GS downloads.", "EmuCore/GS", "HWDownloadMode", + static_cast(GSHardwareDownloadMode::Enabled), s_hw_download, std::size(s_hw_download)); DrawToggleSetting(bsi, "GPU Palette Conversion", "Applies palettes to textures on the GPU instead of the CPU. Can result in speed improvements in some games.", "EmuCore/GS", "paltex", false); @@ -2780,9 +2784,6 @@ void FullscreenUI::DrawGraphicsSettingsPage() DrawToggleSetting(bsi, "Skip Presenting Duplicate Frames", "Skips displaying frames that don't change in 25/30fps games. Can improve speed but increase input lag/make frame pacing worse.", "EmuCore/GS", "SkipDuplicateFrames", false); - DrawToggleSetting(bsi, "Disable Hardware Readbacks", - "Skips thread synchronization for GS downloads. Can improve speed, but break graphical effects.", "EmuCore/GS", - "HWDisableReadbacks", false); DrawIntListSetting(bsi, "Override Texture Barriers", "Forces texture barrier functionality to the specified value.", "EmuCore/GS", "OverrideTextureBarriers", -1, s_generic_options, std::size(s_generic_options), -1); DrawIntListSetting(bsi, "Override Geometry Shaders", "Forces geometry shader functionality to the specified value.", "EmuCore/GS", diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp index c78fbac0a4..d35d82b4bb 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp @@ -1023,16 +1023,16 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r r.z, r.w); - if (GSConfig.HWDisableReadbacks) - { - Console.Error("Skipping readback of %ux%u @ %u,%u", r.width(), r.height(), r.left, r.top); - return; - } - // No depth handling please. if (psm == PSM_PSMZ32 || psm == PSM_PSMZ24 || psm == PSM_PSMZ16 || psm == PSM_PSMZ16S) { GL_INS("ERROR: InvalidateLocalMem depth format isn't supported (%d,%d to %d,%d)", r.x, r.y, r.z, r.w); + if (GSConfig.HWDownloadMode != GSHardwareDownloadMode::Enabled) + { + DevCon.Error("Skipping depth readback of %ux%u @ %u,%u", r.width(), r.height(), r.left, r.top); + return; + } + if (!GSConfig.UserHacks_DisableDepthSupport) { auto& dss = m_dst[DepthStencil]; @@ -1082,7 +1082,12 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r if (t->m_32_bits_fmt && t->m_TEX0.PSM > PSM_PSMCT24) t->m_TEX0.PSM = PSM_PSMCT32; - if (GSConfig.UserHacks_DisablePartialInvalidation) + if (GSConfig.HWDownloadMode != GSHardwareDownloadMode::Enabled) + { + const GSVector4i rb_rc((!GSConfig.UserHacks_DisablePartialInvalidation && r.x == 0 && r.y == 0) ? t->m_valid : r.rintersect(t->m_valid)); + DevCon.Error("Skipping depth readback of %ux%u @ %u,%u", rb_rc.width(), rb_rc.height(), rb_rc.left, rb_rc.top); + } + else if (GSConfig.UserHacks_DisablePartialInvalidation) { Read(t, r.rintersect(t->m_valid)); } diff --git a/pcsx2/MTGS.cpp b/pcsx2/MTGS.cpp index ebb089010f..8d40ad0a9d 100644 --- a/pcsx2/MTGS.cpp +++ b/pcsx2/MTGS.cpp @@ -244,9 +244,13 @@ void SysMtgsThread::PostVsyncStart(bool registers_written) void SysMtgsThread::InitAndReadFIFO(u8* mem, u32 qwc) { - if (EmuConfig.GS.HWDisableReadbacks && GSConfig.UseHardwareRenderer()) + if (EmuConfig.GS.HWDownloadMode >= GSHardwareDownloadMode::Unsynchronized && GSConfig.UseHardwareRenderer()) { - GSReadLocalMemoryUnsync(mem, qwc, vif1.BITBLTBUF._u64, vif1.TRXPOS._u64, vif1.TRXREG._u64); + if (EmuConfig.GS.HWDownloadMode == GSHardwareDownloadMode::Unsynchronized) + GSReadLocalMemoryUnsync(mem, qwc, vif1.BITBLTBUF._u64, vif1.TRXPOS._u64, vif1.TRXREG._u64); + else + std::memset(mem, 0, qwc * 16); + return; } @@ -936,9 +940,8 @@ void SysMtgsThread::ApplySettings() // We need to synchronize the thread when changing any settings when the download mode // is unsynchronized, because otherwise we might potentially read in the middle of // the GS renderer being reopened. - if (EmuConfig.GS.HWDisableReadbacks) + if (EmuConfig.GS.HWDownloadMode == GSHardwareDownloadMode::Unsynchronized) WaitGS(false, false, false); - } void SysMtgsThread::ResizeDisplayWindow(int width, int height, float scale) @@ -985,7 +988,7 @@ void SysMtgsThread::SwitchRenderer(GSRendererType renderer, bool display_message }); // See note in ApplySettings() for reasoning here. - if (EmuConfig.GS.HWDisableReadbacks) + if (EmuConfig.GS.HWDownloadMode == GSHardwareDownloadMode::Unsynchronized) WaitGS(false, false, false); } diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index cee4aec585..32f15c1ffb 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -324,7 +324,7 @@ Pcsx2Config::GSOptions::GSOptions() OsdShowSettings = false; OsdShowInputs = false; - HWDisableReadbacks = false; + HWDownloadMode = GSHardwareDownloadMode::Enabled; GPUPaletteConversion = false; AutoFlushSW = true; PreloadFrameWithGSData = false; @@ -405,6 +405,7 @@ bool Pcsx2Config::GSOptions::OptionsAreEqual(const GSOptions& right) const OpEqu(TextureFiltering) && OpEqu(TexturePreloading) && OpEqu(GSDumpCompression) && + OpEqu(HWDownloadMode) && OpEqu(Dithering) && OpEqu(MaxAnisotropy) && OpEqu(SWExtraThreads) && @@ -545,7 +546,6 @@ void Pcsx2Config::GSOptions::ReloadIniSettings() GSSettingBool(OsdShowSettings); GSSettingBool(OsdShowInputs); - GSSettingBool(HWDisableReadbacks); GSSettingBoolEx(GPUPaletteConversion, "paltex"); GSSettingBoolEx(AutoFlushSW, "autoflush_sw"); GSSettingBoolEx(PreloadFrameWithGSData, "preload_frame_with_gs_data"); @@ -592,6 +592,7 @@ void Pcsx2Config::GSOptions::ReloadIniSettings() GSSettingIntEnumEx(TextureFiltering, "filter"); GSSettingIntEnumEx(TexturePreloading, "texture_preloading"); GSSettingIntEnumEx(GSDumpCompression, "GSDumpCompression"); + GSSettingIntEnumEx(HWDownloadMode, "HWDownloadMode"); GSSettingIntEx(Dithering, "dithering_ps2"); GSSettingIntEx(MaxAnisotropy, "MaxAnisotropy"); GSSettingIntEx(SWExtraThreads, "extrathreads");