From 7d7b6ec1e20e122eb2602fedecda5567206d38ff Mon Sep 17 00:00:00 2001 From: refractionpcsx2 Date: Thu, 30 Jun 2022 21:15:52 +0100 Subject: [PATCH] GS: Add Pre-Round Sprite hack This attempts to preproduce hardware behaviour, but falls down in a bunch of cases, hence the hack. --- pcsx2-qt/Settings/GraphicsSettingsWidget.cpp | 1 + pcsx2-qt/Settings/GraphicsSettingsWidget.ui | 56 ++++++++++++-------- pcsx2/Config.h | 1 + pcsx2/GS/GS.cpp | 1 + pcsx2/GS/GSState.cpp | 54 ++++++++++++++++++- pcsx2/GS/Window/GSSetting.cpp | 4 ++ pcsx2/GS/Window/GSSetting.h | 1 + pcsx2/GS/Window/GSwxDialog.cpp | 1 + pcsx2/GameDatabase.cpp | 5 ++ pcsx2/GameDatabase.h | 1 + pcsx2/Pcsx2Config.cpp | 2 + 11 files changed, 102 insertions(+), 25 deletions(-) diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp index c5094f3f07d3ad..63af7cc4c487f8 100644 --- a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp @@ -113,6 +113,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.PCRTCOverscan, "EmuCore/GS", "pcrtc_overscan", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.PCRTCAntiBlur, "EmuCore/GS", "pcrtc_antiblur", true); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.DisableInterlaceOffset, "EmuCore/GS", "disable_interlace_offset", false); + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.PreRoundSprites, "EmuCore/GS", "preround_sprites", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.internalResolutionScreenshots, "EmuCore/GS", "InternalResolutionScreenshots", false); SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.zoom, "EmuCore/GS", "Zoom", 100.0f); SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.stretchY, "EmuCore/GS", "StretchY", 100.0f); diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.ui b/pcsx2-qt/Settings/GraphicsSettingsWidget.ui index 6d70ab70c36b99..4f66030b652ae3 100644 --- a/pcsx2-qt/Settings/GraphicsSettingsWidget.ui +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.ui @@ -6,7 +6,7 @@ 0 0 - 657 + 652 890 @@ -278,6 +278,13 @@ + + + + Integer Upscaling + + + @@ -285,24 +292,33 @@ - - + + + + Disables interlacing offset which may reduce blurring in some situations. + - Internal Resolution Screenshots + Disable Interlace Offset - - + + - Integer Upscaling + Internal Resolution Screenshots - - + + + + Enables internal Anti-Blur hacks. Less accurate to PS2 rendering but will make a lot of games look less blurry. + - Show Overscan + Anti-Blur + + + Ctrl+S @@ -313,26 +329,20 @@ - - - - Disables interlacing offset which may reduce blurring in some situations. - + + - Disable Interlace Offset + Show Overscan - - + + - Enables internal Anti-Blur hacks. Less accurate to PS2 rendering but will make a lot of games look less blurry. + Attempts to pre-round sprite texel coordinates to resolve rounding issues. Helpful for games such as Beyond Good and Evil, and Manhunt - Anti-Blur - - - Ctrl+S + Pre-Round Sprites diff --git a/pcsx2/Config.h b/pcsx2/Config.h index 060a10de26c369..0301420a5e97f0 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -468,6 +468,7 @@ struct Pcsx2Config Mipmap : 1, AA1 : 1, PointListPalette : 1, + PreRoundSprites : 1, ManualUserHacks : 1, UserHacks_AlignSpriteX : 1, UserHacks_AutoFlush : 1, diff --git a/pcsx2/GS/GS.cpp b/pcsx2/GS/GS.cpp index 1d009a0cd1b4c7..ae92593f756b64 100644 --- a/pcsx2/GS/GS.cpp +++ b/pcsx2/GS/GS.cpp @@ -1488,6 +1488,7 @@ void GSApp::Init() m_default_configuration["paltex"] = "0"; m_default_configuration["png_compression_level"] = std::to_string(Z_BEST_SPEED); m_default_configuration["PointListPalette"] = "0"; + m_default_configuration["preround_sprites"] = "0"; m_default_configuration["PrecacheTextureReplacements"] = "0"; m_default_configuration["preload_frame_with_gs_data"] = "0"; m_default_configuration["Renderer"] = std::to_string(static_cast(GSRendererType::Auto)); diff --git a/pcsx2/GS/GSState.cpp b/pcsx2/GS/GSState.cpp index 7411f396902b5e..5cb7ccd384c4f0 100644 --- a/pcsx2/GS/GSState.cpp +++ b/pcsx2/GS/GSState.cpp @@ -1830,6 +1830,56 @@ void GSState::FlushPrim() ASSERT((int)unused < GSUtil::GetVertexCount(PRIM->PRIM)); } + // Texel coordinate rounding + // Helps Beyond Good & Evil (water) and Manhunt (lights shining through objects). + // Dark Cloud 2 also benefits in some ways but is broken in others. + // Can help with some alignment issues when upscaling too, and is for both Software and Hardware renderers. + // This is *NOT* 100% safe, some games hate it (Gran Turismo 4's post processing for example). + // I'm sure some of this is wrong, so as of now it serves as a hack fix. + if (m_env.PRIM.TME && !m_context->TEX1.MMAG && !(m_context->TEX1.MMIN & 1) && !m_context->TEX1.MXL && GSConfig.PreRoundSprites) + { + if (m_env.PRIM.FST) // UV's + { + // UV's on sprites only alter the final UV, I believe the problem is we're drawing too much (drawing stops earlier than we do) + // But this fixes Beyond Good adn Evil water and Dark Cloud 2's UI + if (GSUtil::GetPrimClass(PRIM->PRIM) == GS_PRIM_CLASS::GS_SPRITE_CLASS) + { + for (int i = 0; i < m_vertex.tail; i++) + { + if (!(i & 1)) + continue; + GSVertex* v = &m_vertex.buff[i]; + + if ((v->U & 0xF) < 0x8 && v->U >= 0x10) + { + v->U = (v->U - 0x1); + v->U = (v->U & ~0xF); + } + if ((v->V & 0xF) < 0x8 && v->V >= 0x10) + { + v->V = (v->V - 0x1); + v->V = (v->V & ~0xF); + } + } + } + } + else + { + // ST's have the lowest 8 bits rounding down (Manhunt lighting) + for (int i = 0; i < m_vertex.tail; i++) + { + // Don't worry, I hate me too. If there's a modern way to do bit manipulation on floats, I'd love to hear it! + GSVertex* v = &m_vertex.buff[i]; + u32 S = *(u32*)&v->ST.S; + S &= ~0xff; + v->ST.S = *(float*)&S; + u32 T = *(u32*)&v->ST.T; + T &= ~0xff; + v->ST.T = *(float*)&T; + } + } + } + // If the PSM format of Z is invalid, but it is masked (no write) and ZTST is set to ALWAYS pass (no test, just allow) // we can ignore the Z format, since it won't be used in the draw (Star Ocean 3 transitions) const bool ignoreZ = m_context->ZBUF.ZMSK && m_context->TEST.ZTST == 1; @@ -1869,7 +1919,6 @@ void GSState::FlushPrim() if (unused > 0) { memcpy(m_vertex.buff, buff, sizeof(GSVertex) * unused); - m_vertex.tail = unused; m_vertex.next = next > head ? next - head : 0; } @@ -3030,10 +3079,11 @@ __forceinline void GSState::VertexKick(u32 skip) GSVector4i v1(m_v.m[1]); GSVector4i* RESTRICT tailptr = (GSVector4i*)&m_vertex.buff[tail]; - + tailptr[0] = v0; tailptr[1] = v1; + const GSVector4i xy = v1.xxxx().u16to32().sub32(m_ofxy); GSVector4i::storel(&m_vertex.xy[xy_tail & 3], xy.blend16<0xf0>(xy.sra32(4)).ps32()); diff --git a/pcsx2/GS/Window/GSSetting.cpp b/pcsx2/GS/Window/GSSetting.cpp index 0bd299961129f7..e22bd052b7d9f1 100644 --- a/pcsx2/GS/Window/GSSetting.cpp +++ b/pcsx2/GS/Window/GSSetting.cpp @@ -104,6 +104,10 @@ const char* dialog_message(int ID, bool* updateText) return cvtString("Enable: Removes the offset for interlacing when upscaling.\n" "Can reduce blurring in some games, where the opposite is true most of the time.\n" "Used for ICO to reduce blur."); + case IDC_PREROUND_SPRITES: + return cvtString("Enable: Attempts to pre-round sprite texel coordinates.\n" + "Can improve effects in some games, such as the water in Beyond Good and Evil\n" + "and the light bleed in Manhunt"); case IDC_ACCURATE_DATE: return cvtString("Implement a more accurate algorithm to compute GS destination alpha testing.\n" "It improves shadow and transparency rendering.\n\n" diff --git a/pcsx2/GS/Window/GSSetting.h b/pcsx2/GS/Window/GSSetting.h index 048a60c4eeeab6..21f413e7904f92 100644 --- a/pcsx2/GS/Window/GSSetting.h +++ b/pcsx2/GS/Window/GSSetting.h @@ -46,6 +46,7 @@ enum IDC_PCRTC_OVERSCAN, IDC_PCRTC_ANTIBLUR, IDC_DISABLE_INTERLACE_OFFSETS, + IDC_PREROUND_SPRITES, // Hardware Renderer IDC_PRELOAD_TEXTURES, IDC_ACCURATE_DATE, diff --git a/pcsx2/GS/Window/GSwxDialog.cpp b/pcsx2/GS/Window/GSwxDialog.cpp index 87a6ae2a99b31d..c508162f20c657 100644 --- a/pcsx2/GS/Window/GSwxDialog.cpp +++ b/pcsx2/GS/Window/GSwxDialog.cpp @@ -325,6 +325,7 @@ RendererTab::RendererTab(wxWindow* parent) m_ui.addCheckBox(pcrtc_checks_box, "Show Overscan", "pcrtc_overscan", IDC_PCRTC_OVERSCAN); m_ui.addCheckBox(pcrtc_checks_box, "Disable Interlace Offset", "disable_interlace_offset", IDC_DISABLE_INTERLACE_OFFSETS); m_ui.addCheckBox(pcrtc_checks_box, "Anti-Blur", "pcrtc_antiblur", IDC_PCRTC_ANTIBLUR); + m_ui.addCheckBox(pcrtc_checks_box, "Pre-Round Sprites", "preround_sprites", IDC_PREROUND_SPRITES); general_box->Add(pcrtc_checks_box, wxSizerFlags().Center()); tab_box->Add(hardware_box.outer, wxSizerFlags().Expand()); diff --git a/pcsx2/GameDatabase.cpp b/pcsx2/GameDatabase.cpp index d3aa6903027aed..f36fae899ea555 100644 --- a/pcsx2/GameDatabase.cpp +++ b/pcsx2/GameDatabase.cpp @@ -281,6 +281,7 @@ static const char* s_gs_hw_fix_names[] = { "mergeSprite", "wildArmsHack", "pointListPalette", + "preRoundSprites", "mipmap", "trilinearFiltering", "skipDrawStart", @@ -319,6 +320,7 @@ bool GameDatabaseSchema::isUserHackHWFix(GSHWFixId id) case GSHWFixId::TexturePreloading: case GSHWFixId::ConservativeFramebuffer: case GSHWFixId::PointListPalette: + case GSHWFixId::PreRoundSprites: return false; #ifdef PCSX2_CORE @@ -504,6 +506,9 @@ u32 GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions& case GSHWFixId::PointListPalette: config.PointListPalette = (value > 0); break; + case GSHWFixId::PreRoundSprites: + config.PreRoundSprites = (value > 0); + break; case GSHWFixId::Mipmap: { diff --git a/pcsx2/GameDatabase.h b/pcsx2/GameDatabase.h index d24ecafefb41d3..a47165aa252954 100644 --- a/pcsx2/GameDatabase.h +++ b/pcsx2/GameDatabase.h @@ -71,6 +71,7 @@ namespace GameDatabaseSchema MergeSprite, WildArmsHack, PointListPalette, + PreRoundSprites, // integer settings Mipmap, diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index c3641b147908aa..e0a93134153c8c 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -332,6 +332,7 @@ Pcsx2Config::GSOptions::GSOptions() Mipmap = true; AA1 = true; PointListPalette = false; + PreRoundSprites = false; ManualUserHacks = false; UserHacks_AlignSpriteX = false; @@ -533,6 +534,7 @@ void Pcsx2Config::GSOptions::ReloadIniSettings() GSSettingBool(OsdShowResolution); GSSettingBool(OsdShowGSStats); GSSettingBool(OsdShowIndicators); + GSSettingBoolEx(PreRoundSprites, "preround_sprites"); GSSettingBool(HWDisableReadbacks); GSSettingBoolEx(AccurateDATE, "accurate_date");