From 4583c64ff74bb08d8c385e5c4f4ff7754cb0b0c5 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 25 Feb 2023 00:06:53 +1000 Subject: [PATCH] GS/HW: Add partial target invalidation option Eventually hopefully we can make this the default, but it breaks too much at the moment. Fixes missing/corrupted textures in True Crime: New York City. --- bin/resources/GameIndex.yaml | 42 +++-- pcsx2-qt/Settings/GraphicsSettingsWidget.cpp | 27 ++- pcsx2-qt/Settings/GraphicsSettingsWidget.h | 1 + pcsx2-qt/Settings/GraphicsSettingsWidget.ui | 183 ++++++++++--------- pcsx2/Config.h | 1 + pcsx2/Docs/gamedb-schema.json | 5 + pcsx2/Frontend/FullscreenUI.cpp | 4 + pcsx2/Frontend/ImGuiOverlays.cpp | 2 + pcsx2/GS/Renderers/HW/GSTextureCache.cpp | 15 +- pcsx2/GameDatabase.cpp | 8 + pcsx2/GameDatabase.h | 1 + pcsx2/Pcsx2Config.cpp | 2 + 12 files changed, 177 insertions(+), 114 deletions(-) diff --git a/bin/resources/GameIndex.yaml b/bin/resources/GameIndex.yaml index 6e3a4d4d36..c77334f0ad 100644 --- a/bin/resources/GameIndex.yaml +++ b/bin/resources/GameIndex.yaml @@ -17799,9 +17799,12 @@ SLES-53616: eeRoundMode: 0 # Fixes scene switching in intro. gsHWFixes: cpuSpriteRenderBW: 1 # Fixes textures. - preloadFrameData: 1 # Fixes static text screens. + partialTargetInvalidation: 1 # Needed due to procedural textures overlapping EE writes. + preloadFrameData: 1 # Fixes numberplates and ensures above targets are valid. roundSprite: 1 # Fixes lines in some post-effects. - cpuCLUTRender: 1 # Fixes light occlusion. + gpuTargetCLUT: 1 # Fixes light occlusion. + mipmap: 2 # Mipmap + trilinear, improves ground textures to match sw renderer. + trilinearFiltering: 1 SLES-53617: name: "True Crime - New York City" region: "PAL-G" @@ -17811,9 +17814,12 @@ SLES-53617: eeRoundMode: 0 # Fixes scene switching in intro. gsHWFixes: cpuSpriteRenderBW: 1 # Fixes textures. - preloadFrameData: 1 # Fixes static text screens. + partialTargetInvalidation: 1 # Needed due to procedural textures overlapping EE writes. + preloadFrameData: 1 # Fixes numberplates and ensures above targets are valid. roundSprite: 1 # Fixes lines in some post-effects. - cpuCLUTRender: 1 # Fixes light occlusion. + gpuTargetCLUT: 1 # Fixes light occlusion. + mipmap: 2 # Mipmap + trilinear, improves ground textures to match sw renderer. + trilinearFiltering: 1 SLES-53618: name: "True Crime - New York City" region: "PAL-S" @@ -17823,9 +17829,12 @@ SLES-53618: eeRoundMode: 0 # Fixes scene switching in intro. gsHWFixes: cpuSpriteRenderBW: 1 # Fixes textures. - preloadFrameData: 1 # Fixes static text screens. + partialTargetInvalidation: 1 # Needed due to procedural textures overlapping EE writes. + preloadFrameData: 1 # Fixes numberplates and ensures above targets are valid. roundSprite: 1 # Fixes lines in some post-effects. - cpuCLUTRender: 1 # Fixes light occlusion. + gpuTargetCLUT: 1 # Fixes light occlusion. + mipmap: 2 # Mipmap + trilinear, improves ground textures to match sw renderer. + trilinearFiltering: 1 SLES-53621: name: "Wallace & Gromit - The Curse of the Were-Rabbit" region: "PAL-M5" @@ -32922,9 +32931,12 @@ SLPM-66473: eeRoundMode: 0 # Fixes scene switching in intro. gsHWFixes: cpuSpriteRenderBW: 1 # Fixes textures. - preloadFrameData: 1 # Fixes static text screens. + partialTargetInvalidation: 1 # Needed due to procedural textures overlapping EE writes. + preloadFrameData: 1 # Fixes numberplates and ensures above targets are valid. roundSprite: 1 # Fixes lines in some post-effects. - cpuCLUTRender: 1 # Fixes light occlusion. + gpuTargetCLUT: 1 # Fixes light occlusion. + mipmap: 2 # Mipmap + trilinear, improves ground textures to match sw renderer. + trilinearFiltering: 1 SLPM-66474: name: "Odin Sphere" region: "NTSC-J" @@ -35287,9 +35299,12 @@ SLPM-74243: eeRoundMode: 0 # Fixes scene switching in intro. gsHWFixes: cpuSpriteRenderBW: 1 # Fixes textures. - preloadFrameData: 1 # Fixes static text screens. + partialTargetInvalidation: 1 # Needed due to procedural textures overlapping EE writes. + preloadFrameData: 1 # Fixes numberplates and ensures above targets are valid. roundSprite: 1 # Fixes lines in some post-effects. - cpuCLUTRender: 1 # Fixes light occlusion. + gpuTargetCLUT: 1 # Fixes light occlusion. + mipmap: 2 # Mipmap + trilinear, improves ground textures to match sw renderer. + trilinearFiltering: 1 SLPM-74244: name: "Phantasy Star Universe [PlayStation 2 The Best]" region: "NTSC-J" @@ -45988,9 +46003,12 @@ SLUS-21106: eeRoundMode: 0 # Fixes scene switching in intro. gsHWFixes: cpuSpriteRenderBW: 1 # Fixes textures. - preloadFrameData: 1 # Fixes static text screens. + partialTargetInvalidation: 1 # Needed due to procedural textures overlapping EE writes. + preloadFrameData: 1 # Fixes numberplates and ensures above targets are valid. roundSprite: 1 # Fixes lines in some post-effects. - cpuCLUTRender: 1 # Fixes light occlusion. + gpuTargetCLUT: 1 # Fixes light occlusion. + mipmap: 2 # Mipmap + trilinear, improves ground textures to match sw renderer. + trilinearFiltering: 1 SLUS-21107: name: "X-Men - The Official Game" region: "NTSC-U" diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp index db1ec41003..e34a347611 100644 --- a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp @@ -178,8 +178,11 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* &GraphicsSettingsWidget::onTrilinearFilteringChanged); connect(m_ui.gpuPaletteConversion, QOverload::of(&QCheckBox::stateChanged), this, &GraphicsSettingsWidget::onGpuPaletteConversionChanged); + connect(m_ui.textureInsideRt, QOverload::of(&QCheckBox::stateChanged), this, + &GraphicsSettingsWidget::onTextureInsideRtChanged); onTrilinearFilteringChanged(); onGpuPaletteConversionChanged(m_ui.gpuPaletteConversion->checkState()); + onTextureInsideRtChanged(m_ui.textureInsideRt->checkState()); ////////////////////////////////////////////////////////////////////////// // HW Renderer Fixes @@ -199,6 +202,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* sif, m_ui.disablePartialInvalidation, "EmuCore/GS", "UserHacks_DisablePartialInvalidation", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.textureInsideRt, "EmuCore/GS", "UserHacks_TextureInsideRt", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.readTCOnClose, "EmuCore/GS", "UserHacks_ReadTCOnClose", false); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.targetPartialInvalidation, "EmuCore/GS", "UserHacks_TargetPartialInvalidation", false); ////////////////////////////////////////////////////////////////////////// // HW Upscaling Fixes @@ -523,7 +527,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* "Disables accurate GS Memory Clearing to be done on the CPU, and let the GPU handle it, which can help Kingdom Hearts " "games.")); - dialog->registerWidgetHelp(m_ui.disablePartialInvalidation, tr("Disable Partial Invalidation"), tr("Unchecked"), + dialog->registerWidgetHelp(m_ui.disablePartialInvalidation, tr("Disable Partial Source Invalidation"), tr("Unchecked"), tr("By default, the texture cache handles partial invalidations. Unfortunately it is very costly to compute CPU wise. " "This hack replaces the partial invalidation with a complete deletion of the texture to reduce the CPU load. " "It helps snowblind engine games.")); @@ -536,12 +540,15 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* "Fixes black screen issues in games like Armored Core: Last Raven.")); dialog->registerWidgetHelp(m_ui.textureInsideRt, tr("Texture Inside RT"), tr("Unchecked"), - tr("Allows the texture cache to reuse as an input texture the inner portion of a previous framebuffer. " - "In some selected games this is enabled by default regardless of this setting.")); + tr("Allows the texture cache to reuse as an input texture the inner portion of a previous framebuffer.")); dialog->registerWidgetHelp(m_ui.readTCOnClose, tr("Read Targets When Closing"), tr("Unchecked"), tr("Flushes all targets in the texture cache back to local memory when shutting down. Can prevent lost visuals when saving " "state or switching renderers, but can also cause graphical corruption.")); + + dialog->registerWidgetHelp(m_ui.targetPartialInvalidation, tr("Target Partial Invalidation"), tr("Unchecked"), + tr("Allows partial invalidation of render targets, which can fix graphical errors in some games. Texture Inside Render Target " + "automatically enables this option.")); } // Upscaling Fixes tab @@ -844,9 +851,19 @@ void GraphicsSettingsWidget::onEnableAudioCaptureArgumentsChanged() void GraphicsSettingsWidget::onGpuPaletteConversionChanged(int state) { - const bool enabled = state == Qt::CheckState::PartiallyChecked ? Host::GetBaseBoolSettingValue("EmuCore/GS", "paltex", false) : state; + const bool disabled = + state == Qt::CheckState::PartiallyChecked ? Host::GetBaseBoolSettingValue("EmuCore/GS", "paltex", false) : (state != 0); - m_ui.anisotropicFiltering->setEnabled(!enabled); + m_ui.anisotropicFiltering->setDisabled(disabled); +} + +void GraphicsSettingsWidget::onTextureInsideRtChanged(int state) +{ + const bool disabled = state == Qt::CheckState::PartiallyChecked ? + Host::GetBaseBoolSettingValue("EmuCore/GS", "UserHacks_TextureInsideRt", false) : + (state != 0); + + m_ui.targetPartialInvalidation->setDisabled(disabled); } GSRendererType GraphicsSettingsWidget::getEffectiveRenderer() const diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.h b/pcsx2-qt/Settings/GraphicsSettingsWidget.h index c5af3454a6..d95a67bb24 100644 --- a/pcsx2-qt/Settings/GraphicsSettingsWidget.h +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.h @@ -41,6 +41,7 @@ private Q_SLOTS: void onAdapterChanged(int index); void onTrilinearFilteringChanged(); void onGpuPaletteConversionChanged(int state); + void onTextureInsideRtChanged(int state); void onFullscreenModeChanged(int index); void onShadeBoostChanged(); void onCaptureContainerChanged(); diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.ui b/pcsx2-qt/Settings/GraphicsSettingsWidget.ui index ead2e10fbf..8daf57b482 100644 --- a/pcsx2-qt/Settings/GraphicsSettingsWidget.ui +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.ui @@ -920,91 +920,13 @@ - - + + - Skipdraw Range: + Software CLUT Render: - - - - - - 10000 - - - - - - - 10000 - - - - - - - - - - - Frame Buffer Conversion - - - - - - - Auto Flush - - - - - - - Disable Depth Emulation - - - - - - - Preload Frame Data - - - - - - - Disable Partial Invalidation - - - - - - - Disable Safe Features - - - - - - - Texture Inside RT - - - - - - - Read Targets When Closing - - - - - @@ -1030,13 +952,6 @@ - - - - Software CLUT Render: - - - @@ -1063,6 +978,98 @@ + + + + Skipdraw Range: + + + + + + + + + 10000 + + + + + + + 10000 + + + + + + + + + + + Preload Frame Data + + + + + + + Texture Inside RT + + + + + + + Disable Depth Emulation + + + + + + + Disable Partial Source Invalidation + + + + + + + Disable Safe Features + + + + + + + Target Partial Invalidation + + + + + + + Frame Buffer Conversion + + + + + + + Auto Flush + + + + + + + Read Targets When Closing + + + + + diff --git a/pcsx2/Config.h b/pcsx2/Config.h index 9d7a3eb314..4ece102e9c 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -671,6 +671,7 @@ struct Pcsx2Config UserHacks_MergePPSprite : 1, UserHacks_WildHack : 1, UserHacks_TextureInsideRt : 1, + UserHacks_TargetPartialInvalidation : 1, FXAA : 1, ShadeBoost : 1, DumpGSData : 1, diff --git a/pcsx2/Docs/gamedb-schema.json b/pcsx2/Docs/gamedb-schema.json index 18c8a1357c..5e4e132fc6 100644 --- a/pcsx2/Docs/gamedb-schema.json +++ b/pcsx2/Docs/gamedb-schema.json @@ -151,6 +151,11 @@ "minimum": 0, "maximum": 1 }, + "partialTargetInvalidation": { + "type": "integer", + "minimum": 0, + "maximum": 1 + }, "textureInsideRT": { "type": "integer", "minimum": 0, diff --git a/pcsx2/Frontend/FullscreenUI.cpp b/pcsx2/Frontend/FullscreenUI.cpp index 881ea28be1..081b60701a 100644 --- a/pcsx2/Frontend/FullscreenUI.cpp +++ b/pcsx2/Frontend/FullscreenUI.cpp @@ -3196,6 +3196,10 @@ void FullscreenUI::DrawGraphicsSettingsPage() DrawToggleSetting(bsi, "Texture Inside Render Target", "Allows the texture cache to reuse as an input texture the inner portion of a previous framebuffer.", "EmuCore/GS", "UserHacks_TextureInsideRt", false, manual_hw_fixes); + DrawToggleSetting(bsi, "Target Partial Invalidation", + "Allows partial invalidation of render targets, which can fix graphical errors in some games.", "EmuCore/GS", + "UserHacks_TargetPartialInvalidation", false, + !GetEffectiveBoolSetting(bsi, "EmuCore/GS", "UserHacks_TextureInsideRt", false)); DrawToggleSetting(bsi, "Read Targets When Closing", "Flushes all targets in the texture cache back to local memory when shutting down.", "EmuCore/GS", "UserHacks_ReadTCOnClose", false, manual_hw_fixes); diff --git a/pcsx2/Frontend/ImGuiOverlays.cpp b/pcsx2/Frontend/ImGuiOverlays.cpp index 2c077bdd0e..9b4f1241ec 100644 --- a/pcsx2/Frontend/ImGuiOverlays.cpp +++ b/pcsx2/Frontend/ImGuiOverlays.cpp @@ -427,6 +427,8 @@ void ImGuiManager::DrawSettingsOverlay() APPEND("DDE "); if (GSConfig.UserHacks_DisablePartialInvalidation) APPEND("DPIV "); + if (GSConfig.UserHacks_TargetPartialInvalidation) + APPEND("TPV "); if (GSConfig.UserHacks_DisableSafeFeatures) APPEND("DSF "); if (GSConfig.WrapGSMem) diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp index 3a5bf063c7..7b26fb7cb4 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp @@ -1210,6 +1210,9 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r const u32 end_bp = off.bn(rect.z - 1, rect.w - 1); const u32 unwrapped_end_bp = end_bp + ((end_bp < bp) ? MAX_BLOCKS : 0); + // Ideally in the future we can turn this on unconditionally, but for now it breaks too much. + const bool check_inside_target = (GSConfig.UserHacks_TargetPartialInvalidation || GSConfig.UserHacks_TextureInsideRt); + for (int type = 0; type < 2; type++) { auto& list = m_dst[type]; @@ -1355,17 +1358,11 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r const u32 rowsize = bw * 8192; const u32 offset = static_cast((t->m_TEX0.TBP0 - bp) * 256); - // This grossness is needed to fix incorrect invalidations in True Crime: New York City. - // Because it's writing tiny texture blocks (which are later decompressed) over previous targets, - // we need to be ensure said targets are invalidated, otherwise the SW prim render path won't be - // triggered. This whole thing needs rewriting anyway, because it can't handle non-page-aligned - // writes, but for now we'll just use the unsafer logic when the TC hack is enabled. - const bool start_of_page = rowsize > 0 && (offset % rowsize == 0); - if (start_of_page || (rowsize > 0 && GSConfig.UserHacks_CPUSpriteRenderBW != 0)) + if (rowsize > 0 && offset % rowsize == 0) { int y = GSLocalMemory::m_psm[psm].pgs.y * offset / rowsize; - if (r.bottom > y && (start_of_page || r.top >= y)) + if (r.bottom > y) { GL_CACHE("TC: Dirty After Target(%s) %d (0x%x)", to_string(type), t->m_texture ? t->m_texture->GetID() : 0, @@ -1410,7 +1407,7 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r continue; } } - else if (GSConfig.UserHacks_TextureInsideRt && t->Overlaps(bp, bw, psm, rect) && GSUtil::HasCompatibleBits(psm, t->m_TEX0.PSM)) + else if (check_inside_target && t->Overlaps(bp, bw, psm, rect) && GSUtil::HasSharedBits(psm, t->m_TEX0.PSM)) { SurfaceOffsetKey sok; sok.elems[0].bp = bp; diff --git a/pcsx2/GameDatabase.cpp b/pcsx2/GameDatabase.cpp index 15388622f9..56e3254974 100644 --- a/pcsx2/GameDatabase.cpp +++ b/pcsx2/GameDatabase.cpp @@ -347,6 +347,7 @@ static const char* s_gs_hw_fix_names[] = { "wrapGSMem", "preloadFrameData", "disablePartialInvalidation", + "partialTargetInvalidation", "textureInsideRT", "alignSprite", "mergeSprite", @@ -564,6 +565,9 @@ bool GameDatabaseSchema::GameEntry::configMatchesHWFix(const Pcsx2Config::GSOpti case GSHWFixId::DisablePartialInvalidation: return (static_cast(config.UserHacks_DisablePartialInvalidation) == value); + case GSHWFixId::TargetPartialInvalidation: + return (static_cast(config.UserHacks_TargetPartialInvalidation) == value); + case GSHWFixId::TextureInsideRT: return (static_cast(config.UserHacks_TextureInsideRt) == value); @@ -678,6 +682,10 @@ u32 GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions& config.UserHacks_DisablePartialInvalidation = (value > 0); break; + case GSHWFixId::TargetPartialInvalidation: + config.UserHacks_TargetPartialInvalidation = (value > 0); + break; + case GSHWFixId::TextureInsideRT: config.UserHacks_TextureInsideRt = (value > 0); break; diff --git a/pcsx2/GameDatabase.h b/pcsx2/GameDatabase.h index 340a82054b..24c6228a43 100644 --- a/pcsx2/GameDatabase.h +++ b/pcsx2/GameDatabase.h @@ -67,6 +67,7 @@ namespace GameDatabaseSchema WrapGSMem, PreloadFrameData, DisablePartialInvalidation, + TargetPartialInvalidation, TextureInsideRT, AlignSprite, MergeSprite, diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index 62db4165cd..fa272acaf4 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -648,6 +648,7 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap) GSSettingBoolEx(UserHacks_MergePPSprite, "UserHacks_merge_pp_sprite"); GSSettingBoolEx(UserHacks_WildHack, "UserHacks_WildHack"); GSSettingBoolEx(UserHacks_TextureInsideRt, "UserHacks_TextureInsideRt"); + GSSettingBoolEx(UserHacks_TargetPartialInvalidation, "UserHacks_TargetPartialInvalidation"); GSSettingBoolEx(FXAA, "fxaa"); GSSettingBool(ShadeBoost); GSSettingBoolEx(DumpGSData, "dump"); @@ -774,6 +775,7 @@ void Pcsx2Config::GSOptions::MaskUserHacks() UserHacks_CPUFBConversion = false; UserHacks_ReadTCOnClose = false; UserHacks_TextureInsideRt = false; + UserHacks_TargetPartialInvalidation = false; UserHacks_TCOffsetX = 0; UserHacks_TCOffsetY = 0; UserHacks_CPUSpriteRenderBW = 0;