diff --git a/bin/resources-overlay/armsx2_overrides.yaml b/bin/resources-overlay/armsx2_overrides.yaml index e83a07c295..20759cd256 100644 --- a/bin/resources-overlay/armsx2_overrides.yaml +++ b/bin/resources-overlay/armsx2_overrides.yaml @@ -974,6 +974,12 @@ SLES-54385: gsHWFixes: roundSprite: 2 texturePreloading: 0 +# Guitar Hero II (PAL) — the note-highway readback is pure cost on a tiler; the +# game never samples the downloaded target back. Also covers GH2 Deluxe, which +# keeps the retail serial. +SLES-54442: + gsHWFixes: + hwDownloadMode: 2 # NoReadbacks. Large perf win, no visual regression. SLES-54490: gsHWFixes: halfPixelOffset: 2 @@ -1013,6 +1019,26 @@ SLES-54648: SLES-54822: gsHWFixes: roundSprite: 2 +# Guitar Hero III — NoReadbacks. Same rationale as the GH2 entries: the note-highway target is +# never sampled back, so the readback is pure cost on a tiler. gsHWFixes is a clear-then-replace +# map in this overlay, so every upstream key is restated here — dropping one would silently undo +# the crowd-texture / bloom / post-processing fixes. +SLES-54962: + gsHWFixes: + autoFlush: 1 # Fixes bloom intensity. + halfPixelOffset: 4 # Mostly aligns post-processing. + hwDownloadMode: 2 # NoReadbacks. Large perf win, no visual regression. + nativeScaling: 1 # Fixes post-processing smoothness and position. + textureInsideRT: 1 # Crowd textures. + getSkipCount: "GSC_GuitarHero" +SLES-54974: + gsHWFixes: + autoFlush: 1 # Fixes bloom intensity. + halfPixelOffset: 4 # Mostly aligns post-processing. + hwDownloadMode: 2 # NoReadbacks. Large perf win, no visual regression. + nativeScaling: 1 # Fixes post-processing smoothness and position. + textureInsideRT: 1 # Crowd textures. + getSkipCount: "GSC_GuitarHero" SLES-55135: gsHWFixes: halfPixelOffset: 4 @@ -1089,6 +1115,14 @@ SLKA-25334: gpuTargetCLUT: 1 halfPixelOffset: 5 nativeScaling: 2 +# Guitar Hero III (NTSC-K) — see the SLES-549xx note above. +SLKA-25363: + gsHWFixes: + autoFlush: 1 # Fixes bloom intensity. + halfPixelOffset: 4 # Mostly aligns post-processing. + hwDownloadMode: 2 # NoReadbacks. Large perf win, no visual regression. + nativeScaling: 1 # Fixes post-processing smoothness and position. + textureInsideRT: 1 # Crowd textures. SLKA-25372: gsHWFixes: autoFlush: 2 @@ -1102,6 +1136,14 @@ SLKA-25372: SLKA-25410: clampModes: vu1ClampMode: 1 +SLKA-25414: + gsHWFixes: + autoFlush: 1 # Fixes bloom intensity. + halfPixelOffset: 4 # Mostly aligns post-processing. + hwDownloadMode: 2 # NoReadbacks. Large perf win, no visual regression. + nativeScaling: 1 # Fixes post-processing smoothness and position. + textureInsideRT: 1 # Crowd textures. + getSkipCount: "GSC_GuitarHero" SLPM-55003: gsHWFixes: bilinearUpscale: 2 @@ -1632,6 +1674,11 @@ SLUS-21442: SLUS-21445: gsHWFixes: roundSprite: 2 +# Guitar Hero II (NTSC-U) — see the PAL entry above. This is also the serial the +# GH2 Deluxe mod ships under, so Deluxe inherits the fix. +SLUS-21447: + gsHWFixes: + hwDownloadMode: 2 # NoReadbacks. Large perf win, no visual regression. SLUS-21452: gsHWFixes: nativeScaling: 1 @@ -1652,6 +1699,14 @@ SLUS-21503: SLUS-21564: gsHWFixes: roundSprite: 2 +SLUS-21672: + gsHWFixes: + autoFlush: 1 # Fixes bloom intensity. + halfPixelOffset: 4 # Mostly aligns post-processing. + hwDownloadMode: 2 # NoReadbacks. Large perf win, no visual regression. + nativeScaling: 1 # Fixes post-processing smoothness and position. + textureInsideRT: 1 # Crowd textures. + getSkipCount: "GSC_GuitarHero" SLUS-21785: gsHWFixes: halfPixelOffset: 4 diff --git a/tests/ctest/core/gs/gs_interlace_policy_tests.cpp b/tests/ctest/core/gs/gs_interlace_policy_tests.cpp new file mode 100644 index 0000000000..b0182fc3be --- /dev/null +++ b/tests/ctest/core/gs/gs_interlace_policy_tests.cpp @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team +// SPDX-License-Identifier: GPL-3.0+ + +// Pins the two presentation/interlace policies extracted from GSRenderer so the decisions are +// checkable without a GS device. Ported from sashkinbro/EmuCoreX ("Fix GS interlace and Vulkan +// presentation policies"), which is also where the GT4 fade case below comes from. +// +// Both policies are constexpr and additionally static_assert their key cases at their definition, +// so a regression is a compile error there and a named failure here. + +#include "GS/Renderers/Common/GSInterlaceModePolicy.h" +#include "GS/Renderers/Common/GSPresentationPolicy.h" + +#include + +#include +#include + +TEST(GSInterlaceModePolicy, AutomaticFullFrameOutputRemainsPassThrough) +{ + const GSInterlaceModeSelection selection = + SelectGSInterlaceMode(0, true, false, false, false); + EXPECT_EQ(selection.field_offset, 0); + // -1, NOT clamped to FastMAD. This is the progressive case: clamping it here is what makes a + // deinterlace pass run over progressive output during a video-mode transition. + EXPECT_EQ(selection.shader_mode, -1); +} + +TEST(GSInterlaceModePolicy, AutomaticTemporalSourcesUseFastMAD) +{ + EXPECT_EQ(SelectGSInterlaceMode(0, true, true, false, false).shader_mode, 3); + EXPECT_EQ(SelectGSInterlaceMode(0, true, false, true, false).shader_mode, 3); + EXPECT_EQ(SelectGSInterlaceMode(0, true, false, false, true).shader_mode, 3); +} + +TEST(GSInterlaceModePolicy, ExplicitModesMapToExpectedShadersAndFields) +{ + EXPECT_EQ(SelectGSInterlaceMode(1, false, false, false, false).shader_mode, -1); + EXPECT_EQ(SelectGSInterlaceMode(2, false, false, false, false).shader_mode, 0); + EXPECT_EQ(SelectGSInterlaceMode(3, false, false, false, false).field_offset, 1); + EXPECT_EQ(SelectGSInterlaceMode(4, false, false, false, false).shader_mode, 1); + EXPECT_EQ(SelectGSInterlaceMode(6, false, false, false, false).shader_mode, 2); + EXPECT_EQ(SelectGSInterlaceMode(8, false, false, false, false).shader_mode, 3); +} + +TEST(GSPresentationPolicy, SkipsOnlyBlankFramesBeforeFirstOutput) +{ + EXPECT_TRUE(ShouldSkipAndroidBlankFrame(true, false, true, 1)); + EXPECT_FALSE(ShouldSkipAndroidBlankFrame(true, true, true, 1)); + EXPECT_FALSE(ShouldSkipAndroidBlankFrame(false, false, true, 0)); + EXPECT_FALSE(ShouldSkipAndroidBlankFrame(false, true, true, 0)); +} + +TEST(GSPresentationPolicy, PreservesExistingOpenGLBlankSuppression) +{ + EXPECT_TRUE(ShouldSkipAndroidBlankFrame(true, false, false, 1)); + EXPECT_TRUE(ShouldSkipAndroidBlankFrame(true, true, false, 1)); + EXPECT_FALSE(ShouldSkipAndroidBlankFrame(true, true, false, 2)); + EXPECT_FALSE(ShouldSkipAndroidBlankFrame(false, true, false, 0)); +} + +TEST(GSPresentationPolicy, KeepsAlternatingMidGameFadeFramesOnSubmissionPath) +{ + // GT4 result transitions can alternate between output and blank frames while remaining in + // SDTV 480p. Only the leading startup blank may bypass presentation. + constexpr std::array blank_frames = {true, false, true, false, true, false}; + bool has_current_output = false; + std::array skipped = {}; + + for (size_t i = 0; i < blank_frames.size(); i++) + { + skipped[i] = ShouldSkipAndroidBlankFrame( + blank_frames[i], has_current_output, true, blank_frames[i] ? 1 : 0); + if (!blank_frames[i]) + has_current_output = true; + } + + EXPECT_EQ(skipped, (std::array{true, false, false, false, false, false})); +}