diff --git a/bin/resources/GameIndex.yaml b/bin/resources/GameIndex.yaml index ca190a5a6b..ec84e2b12a 100644 --- a/bin/resources/GameIndex.yaml +++ b/bin/resources/GameIndex.yaml @@ -3448,6 +3448,7 @@ SCES-51635: halfPixelOffset: 2 # Fixes lighting/shadows. preloadFrameData: 1 # Fixes sun flickering. autoFlush: 1 # Fixes sun through objects. + bilinearUpscale: 1 # Smooths out sun glare textures like native. SCES-51648: name: "Everquest - Online Adventures" region: "PAL-E-I" @@ -46538,6 +46539,7 @@ SLUS-21127: halfPixelOffset: 2 # Fixes lighting/shadows. preloadFrameData: 1 # Fixes sun flickering. autoFlush: 1 # Fixes sun through objects. + bilinearUpscale: 1 # Smooths out sun glare textures like native. SLUS-21128: name: "Blitz - The League" region: "NTSC-U" diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp index 7d2ac7d488..9419c51f25 100644 --- a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp @@ -224,7 +224,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.alignSprite, "EmuCore/GS", "UserHacks_align_sprite_X", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.mergeSprite, "EmuCore/GS", "UserHacks_merge_pp_sprite", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.wildHack, "EmuCore/GS", "UserHacks_WildHack", false); - + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.bilinearHack, "EmuCore/GS", "UserHacks_BilinearHack", false); ////////////////////////////////////////////////////////////////////////// // Texture Replacements ////////////////////////////////////////////////////////////////////////// @@ -587,6 +587,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* dialog->registerWidgetHelp(m_ui.wildHack, tr("Wild Arms Hack"), tr("Unchecked"), tr("Lowers the GS precision to avoid gaps between pixels when upscaling. Fixes the text on Wild Arms games.")); + dialog->registerWidgetHelp(m_ui.bilinearHack, tr("Bilinear Upscale"), tr("Unchecked"), + tr("Can smooth out textures due to be bilinear filtered when upscaling. E.g. Brave sun glare.")); + dialog->registerWidgetHelp(m_ui.mergeSprite, tr("Merge Sprite"), tr("Unchecked"), tr("Replaces post-processing multiple paving sprites by a single fat sprite. It reduces various upscaling lines.")); } @@ -1075,6 +1078,7 @@ void GraphicsSettingsWidget::resetManualHardwareFixes() check_bool("EmuCore/GS", "UserHacks_align_sprite_X", false); check_bool("EmuCore/GS", "UserHacks_merge_pp_sprite", false); check_bool("EmuCore/GS", "UserHacks_WildHack", false); + check_bool("EmuCore/GS", "UserHacks_BilinearHack", false); } if (changed) diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.ui b/pcsx2-qt/Settings/GraphicsSettingsWidget.ui index 4ab9863c49..a26b95fc91 100644 --- a/pcsx2-qt/Settings/GraphicsSettingsWidget.ui +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.ui @@ -7,7 +7,7 @@ 0 0 720 - 466 + 476 @@ -58,7 +58,7 @@ - 0 + 4 true @@ -1222,6 +1222,13 @@ + + + + Bilinear Dirty Upscale + + + diff --git a/pcsx2/Config.h b/pcsx2/Config.h index 25981bf5dc..1d0ebb8445 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -677,6 +677,7 @@ struct Pcsx2Config UserHacks_DisableSafeFeatures : 1, UserHacks_MergePPSprite : 1, UserHacks_WildHack : 1, + UserHacks_BilinearHack : 1, UserHacks_TargetPartialInvalidation : 1, UserHacks_EstimateTextureRegion : 1, FXAA : 1, diff --git a/pcsx2/Docs/GameIndex.md b/pcsx2/Docs/GameIndex.md index 1946eaa7c2..c889675bae 100644 --- a/pcsx2/Docs/GameIndex.md +++ b/pcsx2/Docs/GameIndex.md @@ -169,6 +169,7 @@ The clamp modes are also numerically based. * alignSprite [`0` or `1`] {Off or On} Default: Off (`0`) * mergeSprite [`0` or `1`] {Off or On} Default: Off (`0`) * wildArmsHack [`0` or `1`] {Off or On} Default: Off (`0`) +* bilinearUpscale [`0` or `1`] {Off or On} Default: Off (`0`) * skipDrawStart [Value between `0` to `10000`] {0-10000} Default: Off (`0`) * skipDrawEnd [Value between `0` to `10000`] {0-10000} Default: Off (`0`) * halfPixelOffset [`0` or `1` or `2` or `3`] {Off, Normal Vertex, Special Texture or Special Texture Aggressive} Default: Off (`0`) diff --git a/pcsx2/Docs/gamedb-schema.json b/pcsx2/Docs/gamedb-schema.json index 763b02e19a..8e153bb9e5 100644 --- a/pcsx2/Docs/gamedb-schema.json +++ b/pcsx2/Docs/gamedb-schema.json @@ -171,6 +171,11 @@ "minimum": 0, "maximum": 1 }, + "bilinearUpscale": { + "type": "integer", + "minimum": 0, + "maximum": 1 + }, "estimateTextureRegion": { "type": "integer", "minimum": 0, diff --git a/pcsx2/Frontend/FullscreenUI.cpp b/pcsx2/Frontend/FullscreenUI.cpp index aacfbaffe1..f01bbd0e91 100644 --- a/pcsx2/Frontend/FullscreenUI.cpp +++ b/pcsx2/Frontend/FullscreenUI.cpp @@ -3234,6 +3234,9 @@ void FullscreenUI::DrawGraphicsSettingsPage() DrawToggleSetting(bsi, "Wild Arms Hack", "Lowers the GS precision to avoid gaps between pixels when upscaling. Fixes the text on Wild Arms games.", "EmuCore/GS", "UserHacks_WildHack", false, manual_hw_fixes); + DrawToggleSetting(bsi, "Bilinear Upscale", + "Can smooth out textures due to be bilinear filtered when upscaling. E.g. Brave sun glare.", "EmuCore/GS", + "UserHacks_BilinearHack", false, manual_hw_fixes); } } diff --git a/pcsx2/Frontend/ImGuiOverlays.cpp b/pcsx2/Frontend/ImGuiOverlays.cpp index 58763b379f..323a960ebb 100644 --- a/pcsx2/Frontend/ImGuiOverlays.cpp +++ b/pcsx2/Frontend/ImGuiOverlays.cpp @@ -415,6 +415,8 @@ void ImGuiManager::DrawSettingsOverlay() APPEND("TexRT={} ", static_cast(GSConfig.UserHacks_TextureInsideRt)); if (GSConfig.UserHacks_WildHack) APPEND("WA "); + if (GSConfig.UserHacks_BilinearHack) + APPEND("BLU "); if (GSConfig.UserHacks_MergePPSprite) APPEND("MS "); if (GSConfig.UserHacks_AlignSpriteX) diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp index 765e4aee4d..6aca6c542f 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp @@ -1426,14 +1426,14 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, con GL_INS("Preloading the RT DATA"); eerect = eerect.rintersect(newrect); dst->UpdateValidity(newrect); - AddDirtyRectTarget(dst, eerect, TEX0.PSM, TEX0.TBW, rgba/*, GSLocalMemory::m_psm[TEX0.PSM].trbpp >= 16*/); + AddDirtyRectTarget(dst, eerect, TEX0.PSM, TEX0.TBW, rgba, GSLocalMemory::m_psm[TEX0.PSM].trbpp >= 16); } } else { GL_INS("Preloading the RT DATA"); dst->UpdateValidity(newrect); - AddDirtyRectTarget(dst, newrect, TEX0.PSM, TEX0.TBW, rgba/*, GSLocalMemory::m_psm[TEX0.PSM].trbpp >= 16*/); + AddDirtyRectTarget(dst, newrect, TEX0.PSM, TEX0.TBW, rgba, GSLocalMemory::m_psm[TEX0.PSM].trbpp >= 16); } } dst->m_is_frame = is_frame; @@ -4313,6 +4313,7 @@ void GSTextureCache::Target::Update(bool reset_age) // Bilinear filtering this is probably not a good thing, at least in native, but upscaling Nearest can be gross and messy. // It's needed for depth, though.. filtering depth doesn't make much sense, but SMT3 needs it.. const bool upscaled = (m_scale != 1.0f); + const bool override_linear = upscaled && GSConfig.UserHacks_BilinearHack; const bool linear = (m_type == RenderTarget && upscaled); GSDevice::MultiStretchRect* drects = static_cast( @@ -4344,7 +4345,8 @@ void GSTextureCache::Target::Update(bool reset_age) drect.src = t; drect.src_rect = GSVector4(r - t_offset) / t_sizef; drect.dst_rect = GSVector4(r) * GSVector4(m_scale); - drect.linear = linear && m_dirty[i].req_linear; + drect.linear = linear && (m_dirty[i].req_linear || override_linear); + // Copy the new GS memory content into the destination texture. if (m_type == RenderTarget) { diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.h b/pcsx2/GS/Renderers/HW/GSTextureCache.h index 7eb71e47a8..3b98da9a0d 100644 --- a/pcsx2/GS/Renderers/HW/GSTextureCache.h +++ b/pcsx2/GS/Renderers/HW/GSTextureCache.h @@ -421,7 +421,7 @@ public: void Read(Source* t, const GSVector4i& r); void RemoveAll(); void ReadbackAll(); - void AddDirtyRectTarget(Target* target, GSVector4i rect, u32 psm, u32 bw, RGBAMask rgba, bool req_linear = true); + void AddDirtyRectTarget(Target* target, GSVector4i rect, u32 psm, u32 bw, RGBAMask rgba, bool req_linear = false); bool CanTranslate(u32 bp, u32 bw, u32 spsm, GSVector4i r, u32 dbp, u32 dpsm, u32 dbw); GSVector4i TranslateAlignedRectByPage(u32 sbp, u32 spsm, u32 sbw, GSVector4i src_r, u32 dbp, u32 dpsm, u32 bw, bool is_invalidation = false); void DirtyRectByPage(u32 sbp, u32 spsm, u32 sbw, Target* t, GSVector4i src_r, u32 dbp, u32 dpsm, u32 bw); diff --git a/pcsx2/GameDatabase.cpp b/pcsx2/GameDatabase.cpp index 1d561eb40b..7f22aeaf0d 100644 --- a/pcsx2/GameDatabase.cpp +++ b/pcsx2/GameDatabase.cpp @@ -352,6 +352,7 @@ static const char* s_gs_hw_fix_names[] = { "alignSprite", "mergeSprite", "wildArmsHack", + "bilinearUpscale", "estimateTextureRegion", "PCRTCOffsets", "PCRTCOverscan", @@ -588,6 +589,9 @@ bool GameDatabaseSchema::GameEntry::configMatchesHWFix(const Pcsx2Config::GSOpti case GSHWFixId::WildArmsHack: return (config.UpscaleMultiplier <= 1.0f || static_cast(config.UserHacks_WildHack) == value); + case GSHWFixId::BilinearUpscale: + return (config.UpscaleMultiplier <= 1.0f || static_cast(config.UserHacks_BilinearHack) == value); + case GSHWFixId::EstimateTextureRegion: return (static_cast(config.UserHacks_EstimateTextureRegion) == value); @@ -727,6 +731,10 @@ u32 GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions& config.UserHacks_WildHack = (value > 0); break; + case GSHWFixId::BilinearUpscale: + config.UserHacks_BilinearHack = (value > 0); + break; + case GSHWFixId::EstimateTextureRegion: config.UserHacks_EstimateTextureRegion = (value > 0); break; diff --git a/pcsx2/GameDatabase.h b/pcsx2/GameDatabase.h index 59874fe805..fb15cfc984 100644 --- a/pcsx2/GameDatabase.h +++ b/pcsx2/GameDatabase.h @@ -71,6 +71,7 @@ namespace GameDatabaseSchema AlignSprite, MergeSprite, WildArmsHack, + BilinearUpscale, EstimateTextureRegion, PCRTCOffsets, PCRTCOverscan, diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index 02c74cc688..56b081daf6 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -443,6 +443,7 @@ Pcsx2Config::GSOptions::GSOptions() UserHacks_DisableSafeFeatures = false; UserHacks_MergePPSprite = false; UserHacks_WildHack = false; + UserHacks_BilinearHack = false; DumpReplaceableTextures = false; DumpReplaceableMipmaps = false; @@ -656,6 +657,7 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap) GSSettingBoolEx(UserHacks_DisableSafeFeatures, "UserHacks_Disable_Safe_Features"); GSSettingBoolEx(UserHacks_MergePPSprite, "UserHacks_merge_pp_sprite"); GSSettingBoolEx(UserHacks_WildHack, "UserHacks_WildHack"); + GSSettingBoolEx(UserHacks_BilinearHack, "UserHacks_BilinearHack"); GSSettingIntEnumEx(UserHacks_TextureInsideRt, "UserHacks_TextureInsideRt"); GSSettingBoolEx(UserHacks_TargetPartialInvalidation, "UserHacks_TargetPartialInvalidation"); GSSettingBoolEx(UserHacks_EstimateTextureRegion, "UserHacks_EstimateTextureRegion"); @@ -773,6 +775,7 @@ void Pcsx2Config::GSOptions::MaskUserHacks() UserHacks_AlignSpriteX = false; UserHacks_MergePPSprite = false; UserHacks_WildHack = false; + UserHacks_BilinearHack = false; UserHacks_DisableSafeFeatures = false; UserHacks_HalfBottomOverride = -1; UserHacks_HalfPixelOffset = 0; @@ -803,6 +806,7 @@ void Pcsx2Config::GSOptions::MaskUpscalingHacks() UserHacks_AlignSpriteX = false; UserHacks_MergePPSprite = false; UserHacks_WildHack = false; + UserHacks_BilinearHack = false; UserHacks_HalfPixelOffset = 0; UserHacks_RoundSprite = 0; UserHacks_TCOffsetX = 0;