mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-01-31 01:15:24 +01:00
Compare commits
25 Commits
gs_wrchack
...
v1.7.4064
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
750a74206c | ||
|
|
7e64dc2576 | ||
|
|
8a08e2fd97 | ||
|
|
d0a933cda8 | ||
|
|
d00845f56b | ||
|
|
3350e5ebb1 | ||
|
|
aeb4445cad | ||
|
|
73abae8cb9 | ||
|
|
7ecc7b76ba | ||
|
|
71d0bbbc25 | ||
|
|
26e691ba93 | ||
|
|
c7352d9e10 | ||
|
|
7b8f9a54ec | ||
|
|
28980af858 | ||
|
|
80dce398e0 | ||
|
|
06db8eec48 | ||
|
|
9c720efe46 | ||
|
|
cbf91a8d19 | ||
|
|
f99414708d | ||
|
|
9549a6b16a | ||
|
|
3206094545 | ||
|
|
5cfae80701 | ||
|
|
b4d140c6bb | ||
|
|
c65eb3c3ee | ||
|
|
eec0984dbe |
@@ -36,10 +36,10 @@ _Note: Recommended GPU is based on 3x Internal, ~1080p resolution requirements.
|
||||
|
||||
### Technical Notes
|
||||
|
||||
- You need the [Visual C++ 2019 x64 Redistributables](https://support.microsoft.com/en-us/help/2977003/) to run PCSX2.
|
||||
- You need the [Visual C++ 2019 x64 Redistributables](https://support.microsoft.com/en-us/help/2977003/) to run PCSX2 on Windows.
|
||||
- Windows XP and Direct3D9 support was dropped after stable release 1.4.0.
|
||||
- Windows 7, Windows 8.0, and Windows 8.1 support was dropped after stable release 1.6.0.
|
||||
- 32-bit and wxwidgets support was dropped after stable release 1.6.0, with the wxwidgets code being removed completely on 25th December 2022.
|
||||
- 32-bit and wxWidgets support was dropped after stable release 1.6.0, with the wxWidgets code being removed completely on 25th December 2022.
|
||||
- Make sure to update your operating system and drivers to ensure you have the best experience possible. Having a newer GPU is also recommended so you have the latest supported drivers.
|
||||
- Because of copyright issues, and the complexity of trying to work around it, you need a BIOS dump extracted from a legitimately-owned PS2 console to use the emulator. For more information about the BIOS and how to get it from your console, visit [this page](pcsx2/Docs/PCSX2_FAQ.md#question-13-where-do-i-get-a-ps2-bios).
|
||||
- PCSX2 uses two CPU cores for emulation by default. A third core can be used via the MTVU speed hack, which is compatible with most games. This can be a significant speedup on CPUs with 3+ cores, but it may be a slowdown on GS-limited games (or on CPUs with fewer than 2 cores). Software renderers will then additionally use however many rendering threads it is set to and will need higher core counts to run efficiently.
|
||||
|
||||
@@ -424,7 +424,6 @@ SCAJ-10008:
|
||||
name: "Taiko no Tatsujin - Atsumare! Matsuri da!! Yondaime"
|
||||
region: "NTSC-Unk"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SCAJ-10009:
|
||||
name: "Psikyo Shooting Collection Vol.1 - Strikers 1-2"
|
||||
@@ -439,31 +438,26 @@ SCAJ-10011:
|
||||
name: "Taiko no Tatsujin - Go! Go! Godaime"
|
||||
region: "NTSC-Unk"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SCAJ-10012:
|
||||
name: "Taiko Drum Master"
|
||||
region: "NTSC-Unk"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SCAJ-10013:
|
||||
name: "Taiko no Tatsujin - Tobikkiri! Anime Special"
|
||||
region: "NTSC-Unk"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SCAJ-10014:
|
||||
name: "Taiko no Tatsujin - Wai Wai Happy! Rokudaime"
|
||||
region: "NTSC-Unk"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SCAJ-10015:
|
||||
name: "Taiko no Tatsujin - Doka! to Oomori Nanadaime"
|
||||
region: "NTSC-Unk"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SCAJ-20001:
|
||||
name: "Ratchet & Clank"
|
||||
@@ -3246,8 +3240,6 @@ SCES-51159:
|
||||
SCES-51164:
|
||||
name: "Mark of Kri, The"
|
||||
region: "PAL-M5"
|
||||
gsHWFixes:
|
||||
deinterlace: 4 # Game requires bob de-interlacing when auto.
|
||||
SCES-51176:
|
||||
name: "Disney's Treasure Planet"
|
||||
region: "PAL-M4"
|
||||
@@ -7159,8 +7151,6 @@ SCUS-97140:
|
||||
name: "Mark of Kri, The"
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
deinterlace: 4 # Game requires bob de-interlacing when auto.
|
||||
patches:
|
||||
DBD09DD4:
|
||||
content: |-
|
||||
@@ -7390,8 +7380,6 @@ SCUS-97200:
|
||||
SCUS-97201:
|
||||
name: "Mark of Kri, The"
|
||||
region: "NTSC-U"
|
||||
gsHWFixes:
|
||||
deinterlace: 4 # Game requires bob de-interlacing when auto.
|
||||
SCUS-97203:
|
||||
name: "Wild ARMs 3"
|
||||
region: "NTSC-U"
|
||||
@@ -7473,8 +7461,6 @@ SCUS-97220:
|
||||
SCUS-97222:
|
||||
name: "Mark of Kri [Demo]"
|
||||
region: "NTSC-U"
|
||||
gsHWFixes:
|
||||
deinterlace: 4 # Game requires bob de-interlacing when auto.
|
||||
SCUS-97223:
|
||||
name: "NFL GameDay 2003 [Demo]"
|
||||
region: "NTSC-U"
|
||||
@@ -9180,7 +9166,7 @@ SLED-50117:
|
||||
name: "Metal Gear Solid 2 - Sons of Liberty [Demo] [Zone of the Enders Bonus Disc]"
|
||||
region: "PAL-E"
|
||||
gameFixes:
|
||||
- DMABusyHack # Fixes broken half-bottom artifacts.
|
||||
- InstantDMAHack # Fixes broken half-bottom artifacts.
|
||||
SLED-50286:
|
||||
name: "Red Faction [Demo]"
|
||||
region: "PAL-E"
|
||||
@@ -9392,6 +9378,9 @@ SLED-53512:
|
||||
SLED-53537:
|
||||
name: "187 - Ride or Die [Demo]"
|
||||
region: "PAL-E"
|
||||
gsHWFixes:
|
||||
autoFlush: 1 # Softens bloom on objects and cars.
|
||||
halfPixelOffset: 2 # Fixes misalgined bloom on objects and cars.
|
||||
SLED-53673:
|
||||
name: "007 - From Russia with Love [Demo]"
|
||||
region: "PAL-E"
|
||||
@@ -10365,17 +10354,17 @@ SLES-50383:
|
||||
region: "PAL-M3"
|
||||
compat: 5
|
||||
gameFixes:
|
||||
- DMABusyHack # Fixes broken half-bottom artifacts.
|
||||
- InstantDMAHack # Fixes broken half-bottom artifacts.
|
||||
SLES-50384:
|
||||
name: "Metal Gear Solid 2 - Sons of Liberty"
|
||||
region: "PAL-I"
|
||||
gameFixes:
|
||||
- DMABusyHack # Fixes broken half-bottom artifacts.
|
||||
- InstantDMAHack # Fixes broken half-bottom artifacts.
|
||||
SLES-50385:
|
||||
name: "Metal Gear Solid 2 - Sons of Liberty"
|
||||
region: "PAL-S"
|
||||
gameFixes:
|
||||
- DMABusyHack # Fixes broken half-bottom artifacts.
|
||||
- InstantDMAHack # Fixes broken half-bottom artifacts.
|
||||
SLES-50386:
|
||||
name: "Crash Bandicoot - The Wrath of Cortex"
|
||||
region: "PAL-M6"
|
||||
@@ -10800,6 +10789,9 @@ SLES-50632:
|
||||
SLES-50636:
|
||||
name: "Centre Court - Hard Hitter"
|
||||
region: "PAL-M3"
|
||||
gsHWFixes:
|
||||
mipmap: 2 # Improves ground texture rendering.
|
||||
trilinearFiltering: 1
|
||||
SLES-50637:
|
||||
name: "Pro Rally 2002"
|
||||
region: "PAL-M5"
|
||||
@@ -11749,6 +11741,9 @@ SLES-51056:
|
||||
SLES-51057:
|
||||
name: "Hard Hitter 2"
|
||||
region: "PAL-M3"
|
||||
gsHWFixes:
|
||||
mipmap: 2 # Improves ground texture rendering.
|
||||
trilinearFiltering: 1
|
||||
SLES-51058:
|
||||
name: "Maken Shao - Demon Sword"
|
||||
region: "PAL-E"
|
||||
@@ -14327,6 +14322,9 @@ SLES-52276:
|
||||
name: "187 - Ride or Die"
|
||||
region: "PAL-M5"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
autoFlush: 1 # Softens bloom on objects and cars.
|
||||
halfPixelOffset: 2 # Fixes misalgined bloom on objects and cars.
|
||||
SLES-52277:
|
||||
name: "Riding Spirits 2"
|
||||
region: "PAL-M5"
|
||||
@@ -14567,7 +14565,7 @@ SLES-52372:
|
||||
gsHWFixes:
|
||||
cpuSpriteRenderBW: 1 # Fixes textures.
|
||||
autoFlush: 1 # Fixes the position of the shadow and makes it not blocky.
|
||||
halfPixelOffset: 1 # Fixes shadows.
|
||||
halfPixelOffset: 2 # Fixes shadows.
|
||||
gpuPaletteConversion: 0 # Stops potential crashes from too many palette textures.
|
||||
SLES-52373:
|
||||
name: "Champions of Norrath"
|
||||
@@ -14730,7 +14728,7 @@ SLES-52447:
|
||||
gsHWFixes:
|
||||
cpuSpriteRenderBW: 1 # Fixes textures.
|
||||
autoFlush: 1 # Fixes the position of the shadow and makes it not blocky.
|
||||
halfPixelOffset: 1 # Fixes shadows.
|
||||
halfPixelOffset: 2 # Fixes shadows.
|
||||
gpuPaletteConversion: 0 # Stops potential crashes from too many palette textures.
|
||||
SLES-52448:
|
||||
name: "Knights of the Temple"
|
||||
@@ -14843,7 +14841,7 @@ SLES-52493:
|
||||
gsHWFixes:
|
||||
cpuSpriteRenderBW: 1 # Fixes textures.
|
||||
autoFlush: 1 # Fixes the position of the shadow and makes it not blocky.
|
||||
halfPixelOffset: 1 # Fixes shadows.
|
||||
halfPixelOffset: 2 # Fixes shadows.
|
||||
gpuPaletteConversion: 0 # Stops potential crashes from too many palette textures.
|
||||
SLES-52495:
|
||||
name: "Bujingai - Swordmaster"
|
||||
@@ -15519,7 +15517,7 @@ SLES-52745:
|
||||
region: "PAL-M4"
|
||||
SLES-52746:
|
||||
name: "Hugo - Cannon Cruise"
|
||||
region: "PAL-M4"
|
||||
region: "PAL-SC"
|
||||
SLES-52747:
|
||||
name: "Dukes of Hazzard, The - The Return of General Lee"
|
||||
region: "PAL-E"
|
||||
@@ -15705,7 +15703,7 @@ SLES-52816:
|
||||
cpuCLUTRender: 1 # Fixes duplicated post processing.
|
||||
SLES-52820:
|
||||
name: "Incredibles, The"
|
||||
region: "PAL-M4"
|
||||
region: "PAL-SC"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 1 # Fixes offset post processing.
|
||||
cpuCLUTRender: 1 # Fixes duplicated post processing.
|
||||
@@ -16119,7 +16117,6 @@ SLES-52988:
|
||||
gsHWFixes:
|
||||
autoFlush: 1 # Fixes bloom.
|
||||
roundSprite: 1 # Fixes misaligned bloom.
|
||||
deinterlace: 6 # Game requires blend tff de-interlacing when auto for 'fixing' subtitles flickering or half weaved.
|
||||
memcardFilters: # Reads Command Mission save for bonus boss.
|
||||
- "SLES-52988"
|
||||
- "SLES-52832"
|
||||
@@ -17776,7 +17773,6 @@ SLES-53621:
|
||||
region: "PAL-M5"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
deinterlace: 4 # Game requires bob de-interlacing when auto.
|
||||
halfPixelOffset: 1 # Fixes misalignment/bloom when upscaling.
|
||||
SLES-53623:
|
||||
name: "SpongeBob SquarePants - Battle for Bikini Bottom"
|
||||
@@ -18815,7 +18811,14 @@ SLES-54013:
|
||||
region: "PAL-E"
|
||||
SLES-54015:
|
||||
name: "Disney-Pixar's Cars"
|
||||
region: "PAL-M3"
|
||||
region: "PAL-SC"
|
||||
patches:
|
||||
B36778F5:
|
||||
content: |-
|
||||
author=refraction ported by jordanthetoast
|
||||
// COP2 Rearrangement. Fixes broken collisions.
|
||||
patch=1,EE,00203df8,word,484e9000
|
||||
patch=1,EE,00203dfc,word,4bdbd9ff
|
||||
SLES-54016:
|
||||
name: "AND 1 Streetball"
|
||||
region: "PAL-M5"
|
||||
@@ -19916,7 +19919,6 @@ SLES-54455:
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 1 # Fixes misaligned bloom effects.
|
||||
mergeSprite: 1 # Fixes blurriness but removes bloom + Recommended to use Shadeboost brightness 80.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff de-interlacing instead of auto for the whole game.
|
||||
SLES-54456:
|
||||
name: "Beverly Hills Cop"
|
||||
region: "PAL-M11"
|
||||
@@ -20082,14 +20084,12 @@ SLES-54516:
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 1 # Fixes misaligned bloom effects.
|
||||
mergeSprite: 1 # Fixes blurriness but removes bloom + Recommended to use Shadeboost brightness 80.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff de-interlacing instead of auto for the whole game.
|
||||
SLES-54517:
|
||||
name: "Thrillville"
|
||||
region: "PAL-M3"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 1 # Fixes misaligned bloom effects.
|
||||
mergeSprite: 1 # Fixes blurriness but removes bloom + Recommended to use Shadeboost brightness 80.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff de-interlacing instead of auto for the whole game.
|
||||
SLES-54518:
|
||||
name: "Clumsy Shumsy"
|
||||
region: "PAL-E"
|
||||
@@ -20840,13 +20840,11 @@ SLES-54806:
|
||||
region: "PAL-E"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes misaligned bloom effects, 1 is technically more correct compared to software but looks worse.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff de-interlacing instead of auto for the whole game.
|
||||
SLES-54807:
|
||||
name: "Thrillville - Off the Rails"
|
||||
region: "PAL-F"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes misaligned bloom effects, 1 is technically more correct compared to software but looks worse.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff de-interlacing instead of auto for the whole game.
|
||||
SLES-54809:
|
||||
name: "Charlotte's Web"
|
||||
region: "PAL-A"
|
||||
@@ -20864,6 +20862,8 @@ SLES-54812:
|
||||
SLES-54813:
|
||||
name: "NASCAR 08"
|
||||
region: "PAL-A-E"
|
||||
gsHWFixes:
|
||||
deinterlace: 8 # Game requires AdaptiveTFF de-interlacing when auto.
|
||||
SLES-54814:
|
||||
name: "Dead Eye Jim"
|
||||
region: "PAL-E"
|
||||
@@ -21080,7 +21080,6 @@ SLES-54887:
|
||||
region: "PAL-G"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes misaligned bloom effects, 1 is technically more correct compared to software but looks worse.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff de-interlacing instead of auto for the whole game.
|
||||
SLES-54888:
|
||||
name: "Pro Biker 2"
|
||||
region: "PAL-E"
|
||||
@@ -21442,13 +21441,11 @@ SLES-55010:
|
||||
region: "PAL-I"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes misaligned bloom effects, 1 is technically more correct compared to software but looks worse.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff de-interlacing instead of auto for the whole game.
|
||||
SLES-55011:
|
||||
name: "Thrillville - Fuera de Control"
|
||||
region: "PAL-S"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes misaligned bloom effects, 1 is technically more correct compared to software but looks worse.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff de-interlacing instead of auto for the whole game.
|
||||
SLES-55012:
|
||||
name: "Golden Compass, The"
|
||||
region: "PAL-M5"
|
||||
@@ -21898,6 +21895,8 @@ SLES-55199:
|
||||
name: "NASCAR 09"
|
||||
region: "PAL-M5"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
deinterlace: 8 # Game requires AdaptiveTFF de-interlacing when auto.
|
||||
SLES-55200:
|
||||
name: "Guitar Hero - Aerosmith"
|
||||
region: "PAL-M4"
|
||||
@@ -22231,37 +22230,27 @@ SLES-55349:
|
||||
region: "PAL-E"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes blurriness.
|
||||
skipDrawStart: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
skipDrawEnd: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
SLES-55350:
|
||||
name: "Need for Speed - Undercover"
|
||||
region: "PAL-F-G"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes blurriness.
|
||||
skipDrawStart: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
skipDrawEnd: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
SLES-55351:
|
||||
name: "Need for Speed - Undercover"
|
||||
region: "PAL-I-S"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes blurriness.
|
||||
skipDrawStart: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
skipDrawEnd: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
SLES-55352:
|
||||
name: "Need for Speed - Undercover"
|
||||
region: "PAL-M4"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes blurriness.
|
||||
skipDrawStart: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
skipDrawEnd: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
SLES-55353:
|
||||
name: "Need for Speed - Undercover"
|
||||
region: "PAL-M6"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes blurriness.
|
||||
skipDrawStart: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
skipDrawEnd: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
SLES-55354:
|
||||
name: "Shin Megami Tensei - Persona 3 FES"
|
||||
region: "PAL-E"
|
||||
@@ -22701,8 +22690,6 @@ SLES-55579:
|
||||
eeRoundMode: 1 # Fixes black textures in conjunction with VU0 clamping set to none.
|
||||
clampModes:
|
||||
vu0ClampMode: 0 # Fixes black textures and SPS.
|
||||
gsHWFixes:
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff de-interlacing instead of auto for the FMVs.
|
||||
SLES-55581:
|
||||
name: "FIFA 10"
|
||||
region: "PAL-M5"
|
||||
@@ -23238,6 +23225,9 @@ SLKA-15011:
|
||||
SLKA-15013:
|
||||
name: "Grand Slam 2003 Tennis" # Hard Hitter 2
|
||||
region: "NTSC-K"
|
||||
gsHWFixes:
|
||||
mipmap: 2 # Improves ground texture rendering.
|
||||
trilinearFiltering: 1
|
||||
SLKA-15015:
|
||||
name: "Ichigeki Sacchuu!! HoiHoi-San"
|
||||
region: "NTSC-K"
|
||||
@@ -23787,6 +23777,15 @@ SLKA-25182:
|
||||
SLKA-25183:
|
||||
name: "Street Fighter - Anniversary Collection"
|
||||
region: "NTSC-K"
|
||||
SLKA-25185:
|
||||
name: "Need for Speed - Carbon [Collector's Edition]"
|
||||
region: "NTSC-K"
|
||||
compat: 5
|
||||
clampModes:
|
||||
eeClampMode: 3 # Fixes game hang after opening intro.
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes blurriness.
|
||||
roundSprite: 2 # Fixes blurriness.
|
||||
SLKA-25186:
|
||||
name: "King of Fighters, The - Maximum Impact [Limited Edition]"
|
||||
region: "NTSC-K"
|
||||
@@ -24709,8 +24708,6 @@ SLPM-55127:
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes blurriness.
|
||||
skipDrawStart: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
skipDrawEnd: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
SLPM-55131:
|
||||
name: "World Soccer Winning Eleven 2009"
|
||||
region: "NTSC-J"
|
||||
@@ -24905,8 +24902,6 @@ SLPM-55244:
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes blurriness.
|
||||
skipDrawStart: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
skipDrawEnd: 1 # Removes even more blurriness and garbage texture like on top left.
|
||||
SLPM-55245:
|
||||
name: "Himawari - Pebble in the Sky"
|
||||
region: "NTSC-J"
|
||||
@@ -27874,7 +27869,7 @@ SLPM-65077:
|
||||
name: "Metal Gear Solid 2 - Sons of Liberty [Premium Package]"
|
||||
region: "NTSC-J"
|
||||
gameFixes:
|
||||
- DMABusyHack # Fixes broken half-bottom artifacts.
|
||||
- InstantDMAHack # Fixes broken half-bottom artifacts.
|
||||
memcardFilters:
|
||||
- "SLPM-65078"
|
||||
- "SLPM-65077"
|
||||
@@ -27882,7 +27877,7 @@ SLPM-65078:
|
||||
name: "Metal Gear Solid 2 - Sons of Liberty"
|
||||
region: "NTSC-J"
|
||||
gameFixes:
|
||||
- DMABusyHack # Fixes broken half-bottom artifacts.
|
||||
- InstantDMAHack # Fixes broken half-bottom artifacts.
|
||||
memcardFilters:
|
||||
- "SLPM-65078"
|
||||
- "SLPM-65077"
|
||||
@@ -28666,8 +28661,6 @@ SLPM-65309:
|
||||
SLPM-65310:
|
||||
name: "Mark of Kri, The"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
deinterlace: 4 # Game requires bob de-interlacing when auto.
|
||||
SLPM-65311:
|
||||
name: "Violet no Atelier - Gramnad no Renkinjutsushi"
|
||||
region: "NTSC-J"
|
||||
@@ -29783,6 +29776,14 @@ SLPM-65657:
|
||||
SLPM-65661:
|
||||
name: "Densha de Go! Professional 2 [Taito Best]"
|
||||
region: "NTSC-J"
|
||||
SLPM-65662:
|
||||
name: "Spider-Man 2"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
cpuSpriteRenderBW: 1 # Fixes textures.
|
||||
autoFlush: 1 # Fixes the position of the shadow and makes it not blocky.
|
||||
halfPixelOffset: 2 # Fixes shadows.
|
||||
gpuPaletteConversion: 0 # Stops potential crashes from too many palette textures.
|
||||
SLPM-65663:
|
||||
name: "Zwei!!"
|
||||
region: "NTSC-J"
|
||||
@@ -31436,6 +31437,14 @@ SLPM-66119:
|
||||
halfPixelOffset: 2 # Fixes blurriness.
|
||||
speedHacks:
|
||||
InstantVU1SpeedHack: 0 # Fixes missing letters in text such as E.
|
||||
SLPM-66121:
|
||||
name: "Spider-Man 2 [Taito Best]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
cpuSpriteRenderBW: 1 # Fixes textures.
|
||||
autoFlush: 1 # Fixes the position of the shadow and makes it not blocky.
|
||||
halfPixelOffset: 2 # Fixes shadows.
|
||||
gpuPaletteConversion: 0 # Stops potential crashes from too many palette textures.
|
||||
SLPM-66122:
|
||||
name: "Kingdom Hearts [Ultimate Hits]"
|
||||
region: "NTSC-J"
|
||||
@@ -32249,7 +32258,6 @@ SLPM-66327:
|
||||
name: "Wallace & Gromit - The Curse of the Were-Rabbit"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
deinterlace: 4 # Game requires bob de-interlacing when auto.
|
||||
halfPixelOffset: 1 # Fixes misalignment/bloom when upscaling.
|
||||
SLPM-66328:
|
||||
name: "Call of Duty 2 - Big Red One"
|
||||
@@ -34075,7 +34083,7 @@ SLPM-66792:
|
||||
name: "Metal Gear Solid 2 - Sons of Liberty [20th Anniversary Edition]"
|
||||
region: "NTSC-J"
|
||||
gameFixes:
|
||||
- DMABusyHack # Fixes broken half-bottom artifacts.
|
||||
- InstantDMAHack # Fixes broken half-bottom artifacts.
|
||||
SLPM-66794:
|
||||
name: "Metal Gear Solid 3 - Snake Eater [20th Anniversary Edition]"
|
||||
region: "NTSC-J"
|
||||
@@ -34841,7 +34849,7 @@ SLPM-67515:
|
||||
name: "Metal Gear Solid 2 - Sons of Liberty"
|
||||
region: "NTSC-K"
|
||||
gameFixes:
|
||||
- DMABusyHack # Fixes broken half-bottom artifacts
|
||||
- InstantDMAHack # Fixes broken half-bottom artifacts.
|
||||
SLPM-67518:
|
||||
name: "Onimusha 2 Samurai's Destiny"
|
||||
region: "NTSC-K"
|
||||
@@ -34955,6 +34963,8 @@ SLPM-68019:
|
||||
SLPM-68503:
|
||||
name: "Metal Gear Solid 2 - Sons of Liberty [Shareholder Edition]"
|
||||
region: "NTSC-J"
|
||||
gameFixes:
|
||||
- InstantDMAHack # Fixes broken half-bottom artifacts.
|
||||
SLPM-68504:
|
||||
name: "Tokimeki Memorial 3 - Special Sound Track"
|
||||
region: "NTSC-J"
|
||||
@@ -35642,6 +35652,9 @@ SLPS-20097:
|
||||
SLPS-20098:
|
||||
name: "Magical Sports - Hard Hitter"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
mipmap: 2 # Improves ground texture rendering.
|
||||
trilinearFiltering: 1
|
||||
SLPS-20099:
|
||||
name: "Dream Audition 3"
|
||||
region: "NTSC-J"
|
||||
@@ -35793,6 +35806,9 @@ SLPS-20171:
|
||||
SLPS-20173:
|
||||
name: "Magical Sports - Hard Hitter 2"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
mipmap: 2 # Improves ground texture rendering.
|
||||
trilinearFiltering: 1
|
||||
SLPS-20174:
|
||||
name: "Typing Renai Hakusho - Boys Be... [with Keyboard]"
|
||||
region: "NTSC-J"
|
||||
@@ -36278,13 +36294,11 @@ SLPS-20382:
|
||||
name: "Taiko no Tatsujin - Atsumare! Matsuri da!! Yondaime [with Tatacon]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SLPS-20383:
|
||||
name: "Taiko no Tatsujin - Atsumare! Matsuri da!! Yondaime"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SLPS-20384:
|
||||
name: "Hayarigami - Keishichou Kaii Jiken File"
|
||||
@@ -36326,13 +36340,11 @@ SLPS-20399:
|
||||
name: "Taiko no Tatsujin - Go! Go! Godaime [with Tatacon]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SLPS-20400:
|
||||
name: "Taiko no Tatsujin - Go! Go! Godaime"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SLPS-20401:
|
||||
name: "Tecmo Hit Parade"
|
||||
@@ -36373,14 +36385,12 @@ SLPS-20413:
|
||||
name: "Taiko no Tatsujin - Taiko Drum Master [with Tatacon]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SLPS-20414:
|
||||
name: "Taiko no Tatsujin - Taiko Drum Master"
|
||||
region: "NTSC-J"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SLPS-20416:
|
||||
name: "Inyou Taisenki - Byakko Enbu [with EyeToy]"
|
||||
@@ -36409,13 +36419,11 @@ SLPS-20424:
|
||||
name: "Taiko no Tatsujin - Tobikkiri! Anime Special [with Tatacon]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SLPS-20425:
|
||||
name: "Taiko no Tatsujin - Tobikkiri! Anime Special"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SLPS-20426:
|
||||
name: "Dreamworks Madagascar"
|
||||
@@ -36481,13 +36489,11 @@ SLPS-20450:
|
||||
name: "Taiko no Tatsujin - Wai Wai Happy! Rokudaime [with Tatacon]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SLPS-20451:
|
||||
name: "Taiko no Tatsujin - Wai Wai Happy! Rokudaime"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SLPS-20452:
|
||||
name: "Simple 2000 Series Ultimate Vol. 30 - Kourin! Zokusha Goddo!"
|
||||
@@ -36608,13 +36614,11 @@ SLPS-20485:
|
||||
name: "Taiko no Tatsujin - Doka! to Oomori Nanadaime [with Tatacon]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SLPS-20486:
|
||||
name: "Taiko no Tatsujin - Doka! to Oomori Nanadaime"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SLPS-20487:
|
||||
name: "Pachi-Slot King! Kagaku Ninja-Tai Gatchaman"
|
||||
@@ -40200,7 +40204,6 @@ SLPS-73107:
|
||||
name: "Taiko no Tatsujin - Go! Go! Godaime [PlayStation 2 The Best]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SLPS-73108:
|
||||
name: "Phantom Brave [PlayStation 2 The Best]"
|
||||
@@ -41239,7 +41242,7 @@ SLUS-20144:
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gameFixes:
|
||||
- DMABusyHack # Fixes broken half-bottom artifacts.
|
||||
- InstantDMAHack # Fixes broken half-bottom artifacts.
|
||||
SLUS-20145:
|
||||
name: "Ring of Red"
|
||||
region: "NTSC-U"
|
||||
@@ -42755,8 +42758,10 @@ SLUS-20474:
|
||||
SLUS-20475:
|
||||
name: "Dual Hearts"
|
||||
region: "NTSC-U"
|
||||
gameFixes:
|
||||
- SoftwareRendererFMVHack # Fixes flashing FMV.
|
||||
gsHWFixes:
|
||||
deinterlace: 6 # Game requires blend tff de-interlacing when auto.
|
||||
deinterlace: 9 # Game looks better with Adaptive BFF.
|
||||
compat: 5
|
||||
SLUS-20476:
|
||||
name: "NBA 2K3 - Sega Sports"
|
||||
@@ -43196,6 +43201,9 @@ SLUS-20567:
|
||||
SLUS-20568:
|
||||
name: "Hard Hitter Tennis"
|
||||
region: "NTSC-U"
|
||||
gsHWFixes:
|
||||
mipmap: 2 # Improves ground texture rendering.
|
||||
trilinearFiltering: 1
|
||||
SLUS-20569:
|
||||
name: "All-Star Baseball 2004"
|
||||
region: "NTSC-U"
|
||||
@@ -44212,7 +44220,7 @@ SLUS-20776:
|
||||
gsHWFixes:
|
||||
cpuSpriteRenderBW: 1 # Fixes textures.
|
||||
autoFlush: 1 # Fixes the position of the shadow and makes it not blocky.
|
||||
halfPixelOffset: 1 # Fixes shadows.
|
||||
halfPixelOffset: 2 # Fixes shadows.
|
||||
gpuPaletteConversion: 0 # Stops potential crashes from too many palette textures.
|
||||
SLUS-20777:
|
||||
name: "Obscure"
|
||||
@@ -44305,7 +44313,6 @@ SLUS-20800:
|
||||
name: "Taiko Drum Master"
|
||||
region: "NTSC-U"
|
||||
gsHWFixes:
|
||||
deinterlace: 5 # Game requires bob bff de-interlacing when auto.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
SLUS-20801:
|
||||
name: "Midway Arcade Treasures"
|
||||
@@ -45042,7 +45049,6 @@ SLUS-20948:
|
||||
gsHWFixes:
|
||||
wildArmsHack: 1 # Fixes blurriness.
|
||||
roundSprite: 2 # Reduces misalignment bloom effects.
|
||||
# deinterlace: 6 # Game requires blend tff de-interlacing when auto for 'fixing' shimmer on character models and more flickering or half weaved, though the game suffers from the field order.
|
||||
SLUS-20949:
|
||||
name: "Street Fighter Anniversary Collection"
|
||||
region: "NTSC-U"
|
||||
@@ -45938,6 +45944,9 @@ SLUS-21116:
|
||||
name: "187 - Ride or Die"
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
autoFlush: 1 # Softens bloom on objects and cars.
|
||||
halfPixelOffset: 2 # Fixes misalgined bloom on objects and cars.
|
||||
SLUS-21117:
|
||||
name: "World Soccer - Winning Eleven 8 - International"
|
||||
region: "NTSC-U"
|
||||
@@ -47017,7 +47026,6 @@ SLUS-21312:
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
deinterlace: 4 # Game requires bob de-interlacing when auto.
|
||||
halfPixelOffset: 1 # Fixes misalignment/bloom when upscaling.
|
||||
SLUS-21313:
|
||||
name: "Friends - The One with all the Trivia"
|
||||
@@ -47640,7 +47648,6 @@ SLUS-21413:
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 1 # Fixes misaligned bloom effects.
|
||||
mergeSprite: 1 # Fixes blurriness but removes bloom + Recommended to use Shadeboost brightness 80.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff de-interlacing instead of auto for the whole game.
|
||||
SLUS-21414:
|
||||
name: "Delta Force - Black Hawk Down - Team Sabre"
|
||||
region: "NTSC-U"
|
||||
@@ -48525,7 +48532,6 @@ SLUS-21611:
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes misaligned bloom effects, 1 is technically more correct compared to software but looks worse.
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff de-interlacing instead of auto for the whole game.
|
||||
SLUS-21612:
|
||||
name: "Legend of the Dragon"
|
||||
region: "NTSC-U"
|
||||
@@ -48683,6 +48689,8 @@ SLUS-21639:
|
||||
name: "NASCAR '08"
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
deinterlace: 8 # Game requires AdaptiveTFF de-interlacing when auto.
|
||||
SLUS-21640:
|
||||
name: "Rugby '08"
|
||||
region: "NTSC-U"
|
||||
@@ -49172,6 +49180,8 @@ SLUS-21744:
|
||||
name: "NASCAR '09"
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
deinterlace: 8 # Game requires AdaptiveTFF de-interlacing when auto.
|
||||
patches:
|
||||
1B6C22B9:
|
||||
content: |-
|
||||
@@ -49872,8 +49882,6 @@ SLUS-21902:
|
||||
eeRoundMode: 1 # Fixes black textures in conjunction with VU0 clamping set to none.
|
||||
clampModes:
|
||||
vu0ClampMode: 0 # Fixes black textures and SPS.
|
||||
gsHWFixes:
|
||||
deinterlace: 8 # Game requires adaptive (or blend) tff de-interlacing instead of auto for the FMVs.
|
||||
SLUS-21904:
|
||||
name: "Teenage Mutant Ninja Turtles - Smash-Up"
|
||||
region: "NTSC-U"
|
||||
@@ -50279,7 +50287,7 @@ SLUS-29003:
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gameFixes:
|
||||
- DMABusyHack # Fixes broken half-bottom artifacts.
|
||||
- InstantDMAHack # Fixes broken half-bottom artifacts.
|
||||
SLUS-29004:
|
||||
name: "Unison & Dead or Alive 2 Hardcore [Demo]"
|
||||
region: "NTSC-U"
|
||||
|
||||
@@ -445,7 +445,7 @@ ID3D12GraphicsCommandList4* Context::GetInitCommandList()
|
||||
return res.command_lists[0].get();
|
||||
}
|
||||
|
||||
void Context::ExecuteCommandList(WaitType wait_for_completion)
|
||||
bool Context::ExecuteCommandList(WaitType wait_for_completion)
|
||||
{
|
||||
CommandListResources& res = m_command_lists[m_current_command_list];
|
||||
HRESULT hr;
|
||||
@@ -463,12 +463,21 @@ void Context::ExecuteCommandList(WaitType wait_for_completion)
|
||||
if (res.init_command_list_used)
|
||||
{
|
||||
hr = res.command_lists[0]->Close();
|
||||
pxAssertRel(SUCCEEDED(hr), "Close init command list");
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("Closing init command list failed with HRESULT %08X", hr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Close and queue command list.
|
||||
hr = res.command_lists[1]->Close();
|
||||
pxAssertRel(SUCCEEDED(hr), "Close command list");
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("Closing main command list failed with HRESULT %08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (res.init_command_list_used)
|
||||
{
|
||||
const std::array<ID3D12CommandList*, 2> execute_lists{res.command_lists[0].get(), res.command_lists[1].get()};
|
||||
@@ -487,6 +496,8 @@ void Context::ExecuteCommandList(WaitType wait_for_completion)
|
||||
MoveToNextCommandList();
|
||||
if (wait_for_completion != WaitType::None)
|
||||
WaitForFence(res.ready_fence_value, wait_for_completion == WaitType::Spin);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Context::InvalidateSamplerGroups()
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace D3D12
|
||||
};
|
||||
|
||||
/// Executes the current command list.
|
||||
void ExecuteCommandList(WaitType wait_for_completion);
|
||||
bool ExecuteCommandList(WaitType wait_for_completion);
|
||||
|
||||
/// Waits for a specific fence.
|
||||
void WaitForFence(u64 fence, bool spin);
|
||||
|
||||
@@ -119,7 +119,7 @@ namespace GL
|
||||
if (!context)
|
||||
return nullptr;
|
||||
|
||||
Console.WriteLn("Created a %s context", context->IsGLES() ? "OpenGL ES" : "OpenGL");
|
||||
Console.WriteLn("Created an %s context", context->IsGLES() ? "OpenGL ES" : "OpenGL");
|
||||
|
||||
// NOTE: Not thread-safe. But this is okay, since we're not going to be creating more than one context at a time.
|
||||
static Context* context_being_created;
|
||||
|
||||
@@ -1122,9 +1122,13 @@ namespace Vulkan
|
||||
void Context::WaitForCommandBufferCompletion(u32 index)
|
||||
{
|
||||
// Wait for this command buffer to be completed.
|
||||
VkResult res = vkWaitForFences(m_device, 1, &m_frame_resources[index].fence, VK_TRUE, UINT64_MAX);
|
||||
const VkResult res = vkWaitForFences(m_device, 1, &m_frame_resources[index].fence, VK_TRUE, UINT64_MAX);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkWaitForFences failed: ");
|
||||
m_last_submit_failed.store(true, std::memory_order_release);
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean up any resources for command buffers between the last known completed buffer and this
|
||||
// now-completed command buffer. If we use >2 buffers, this may be more than one buffer.
|
||||
@@ -1266,11 +1270,12 @@ namespace Vulkan
|
||||
submit_info.pSignalSemaphores = &m_spin_resources[index].semaphore;
|
||||
}
|
||||
|
||||
VkResult res = vkQueueSubmit(m_graphics_queue, 1, &submit_info, resources.fence);
|
||||
const VkResult res = vkQueueSubmit(m_graphics_queue, 1, &submit_info, resources.fence);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkQueueSubmit failed: ");
|
||||
pxFailRel("Failed to submit command buffer.");
|
||||
m_last_submit_failed.store(true, std::memory_order_release);
|
||||
return;
|
||||
}
|
||||
|
||||
if (spin_cycles != 0)
|
||||
@@ -1286,14 +1291,14 @@ namespace Vulkan
|
||||
|
||||
present_swap_chain->ReleaseCurrentImage();
|
||||
|
||||
VkResult res = vkQueuePresentKHR(m_present_queue, &present_info);
|
||||
const VkResult res = vkQueuePresentKHR(m_present_queue, &present_info);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
// VK_ERROR_OUT_OF_DATE_KHR is not fatal, just means we need to recreate our swap chain.
|
||||
if (res != VK_ERROR_OUT_OF_DATE_KHR && res != VK_SUBOPTIMAL_KHR)
|
||||
LOG_VULKAN_ERROR(res, "vkQueuePresentKHR failed: ");
|
||||
|
||||
m_last_present_failed.store(true);
|
||||
m_last_present_failed.store(true, std::memory_order_release);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1460,6 +1465,9 @@ namespace Vulkan
|
||||
|
||||
void Context::ExecuteCommandBuffer(WaitType wait_for_completion)
|
||||
{
|
||||
if (m_last_submit_failed.load(std::memory_order_acquire))
|
||||
return;
|
||||
|
||||
// If we're waiting for completion, don't bother waking the worker thread.
|
||||
const u32 current_frame = m_current_frame;
|
||||
SubmitCommandBuffer();
|
||||
@@ -1481,9 +1489,12 @@ namespace Vulkan
|
||||
|
||||
bool Context::CheckLastPresentFail()
|
||||
{
|
||||
bool res = m_last_present_failed;
|
||||
m_last_present_failed = false;
|
||||
return res;
|
||||
return m_last_present_failed.exchange(false, std::memory_order_acq_rel);
|
||||
}
|
||||
|
||||
bool Context::CheckLastSubmitFail()
|
||||
{
|
||||
return m_last_submit_failed.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
void Context::DeferBufferDestruction(VkBuffer object)
|
||||
@@ -1596,7 +1607,7 @@ namespace Vulkan
|
||||
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
|
||||
DebugMessengerCallback, nullptr};
|
||||
|
||||
VkResult res =
|
||||
const VkResult res =
|
||||
vkCreateDebugUtilsMessengerEXT(m_instance, &messenger_info, nullptr, &m_debug_messenger_callback);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
@@ -1688,7 +1699,7 @@ namespace Vulkan
|
||||
subpass_dependency_ptr};
|
||||
|
||||
VkRenderPass pass;
|
||||
VkResult res = vkCreateRenderPass(m_device, &pass_info, nullptr, &pass);
|
||||
const VkResult res = vkCreateRenderPass(m_device, &pass_info, nullptr, &pass);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateRenderPass failed: ");
|
||||
@@ -1894,9 +1905,14 @@ void main()
|
||||
SpinResources& resources = m_spin_resources[index];
|
||||
if (!resources.in_progress)
|
||||
return;
|
||||
VkResult res = vkWaitForFences(m_device, 1, &resources.fence, VK_TRUE, UINT64_MAX);
|
||||
|
||||
const VkResult res = vkWaitForFences(m_device, 1, &resources.fence, VK_TRUE, UINT64_MAX);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkWaitForFences failed: ");
|
||||
m_last_submit_failed.store(true, std::memory_order_release);
|
||||
return;
|
||||
}
|
||||
SpinCommandCompleted(index);
|
||||
}
|
||||
|
||||
@@ -1906,7 +1922,7 @@ void main()
|
||||
resources.in_progress = false;
|
||||
const u32 timestamp_base = (index + NUM_COMMAND_BUFFERS) * 2;
|
||||
std::array<u64, 2> timestamps;
|
||||
VkResult res = vkGetQueryPoolResults(m_device, m_timestamp_query_pool, timestamp_base, static_cast<u32>(timestamps.size()),
|
||||
const VkResult res = vkGetQueryPoolResults(m_device, m_timestamp_query_pool, timestamp_base, static_cast<u32>(timestamps.size()),
|
||||
sizeof(timestamps), timestamps.data(), sizeof(u64), VK_QUERY_RESULT_64_BIT);
|
||||
if (res == VK_SUCCESS)
|
||||
{
|
||||
@@ -2014,7 +2030,7 @@ void main()
|
||||
constexpr u64 MAX_MAX_DEVIATION = 100000; // 100µs
|
||||
for (int i = 0; i < 4; i++) // 4 tries to get under MAX_MAX_DEVIATION
|
||||
{
|
||||
VkResult res = vkGetCalibratedTimestampsEXT(m_device, std::size(infos), infos, timestamps, &maxDeviation);
|
||||
const VkResult res = vkGetCalibratedTimestampsEXT(m_device, std::size(infos), infos, timestamps, &maxDeviation);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetCalibratedTimestampsEXT failed: ");
|
||||
|
||||
@@ -209,6 +209,7 @@ namespace Vulkan
|
||||
|
||||
// Was the last present submitted to the queue a failure? If so, we must recreate our swapchain.
|
||||
bool CheckLastPresentFail();
|
||||
bool CheckLastSubmitFail();
|
||||
|
||||
// Schedule a vulkan resource for destruction later on. This will occur when the command buffer
|
||||
// is next re-used, and the GPU has finished working with the specified resource.
|
||||
@@ -373,6 +374,7 @@ namespace Vulkan
|
||||
|
||||
StreamBuffer m_texture_upload_buffer;
|
||||
|
||||
std::atomic_bool m_last_submit_failed{false};
|
||||
std::atomic_bool m_last_present_failed{false};
|
||||
std::atomic_bool m_present_done{true};
|
||||
std::mutex m_present_mutex;
|
||||
|
||||
@@ -280,7 +280,7 @@ void Host::ReleaseHostDisplay(bool clear_state)
|
||||
g_host_display.reset();
|
||||
}
|
||||
|
||||
bool Host::BeginPresentFrame(bool frame_skip)
|
||||
HostDisplay::PresentResult Host::BeginPresentFrame(bool frame_skip)
|
||||
{
|
||||
if (s_loop_number == 0)
|
||||
{
|
||||
@@ -291,12 +291,15 @@ bool Host::BeginPresentFrame(bool frame_skip)
|
||||
std::string dump_path(fmt::format("{}_frame{}.png", s_output_prefix, s_dump_frame_number));
|
||||
GSQueueSnapshot(dump_path);
|
||||
}
|
||||
if (g_host_display->BeginPresent(frame_skip))
|
||||
return true;
|
||||
|
||||
// don't render imgui
|
||||
ImGuiManager::NewFrame();
|
||||
return false;
|
||||
const HostDisplay::PresentResult result = g_host_display->BeginPresent(frame_skip);
|
||||
if (result != HostDisplay::PresentResult::OK)
|
||||
{
|
||||
// don't render imgui
|
||||
ImGuiManager::SkipFrame();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Host::EndPresentFrame()
|
||||
|
||||
@@ -58,6 +58,7 @@ target_sources(pcsx2-qt PRIVATE
|
||||
Settings/ControllerGlobalSettingsWidget.ui
|
||||
Settings/ControllerMacroEditWidget.ui
|
||||
Settings/ControllerMacroWidget.ui
|
||||
Settings/ControllerMouseSettingsDialog.ui
|
||||
Settings/ControllerSettingsDialog.cpp
|
||||
Settings/ControllerSettingsDialog.h
|
||||
Settings/ControllerSettingsDialog.ui
|
||||
|
||||
@@ -967,17 +967,17 @@ void Host::ReleaseHostDisplay(bool clear_state)
|
||||
g_emu_thread->releaseHostDisplay(clear_state);
|
||||
}
|
||||
|
||||
bool Host::BeginPresentFrame(bool frame_skip)
|
||||
HostDisplay::PresentResult Host::BeginPresentFrame(bool frame_skip)
|
||||
{
|
||||
if (!g_host_display->BeginPresent(frame_skip))
|
||||
const HostDisplay::PresentResult result = g_host_display->BeginPresent(frame_skip);
|
||||
if (result != HostDisplay::PresentResult::OK)
|
||||
{
|
||||
// if we're skipping a frame, we need to reset imgui's state, since
|
||||
// we won't be calling EndPresentFrame().
|
||||
ImGuiManager::NewFrame();
|
||||
return false;
|
||||
ImGuiManager::SkipFrame();
|
||||
}
|
||||
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Host::EndPresentFrame()
|
||||
|
||||
@@ -45,10 +45,10 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent,
|
||||
#endif
|
||||
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableMouseMapping, "UI", "EnableMouseMapping", false);
|
||||
connect(m_ui.mouseSettings, &QToolButton::clicked, this, &ControllerGlobalSettingsWidget::mouseSettingsClicked);
|
||||
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.multitapPort1, "Pad", "MultitapPort1", false);
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.multitapPort2, "Pad", "MultitapPort2", false);
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerXScale, "Pad", "PointerXScale", 8.0f);
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerYScale, "Pad", "PointerYScale", 8.0f);
|
||||
|
||||
#ifdef _WIN32
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableXInputSource, "InputSources", "XInput", false);
|
||||
@@ -81,13 +81,6 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent,
|
||||
for (QCheckBox* cb : {m_ui.multitapPort1, m_ui.multitapPort2})
|
||||
connect(cb, &QCheckBox::stateChanged, this, [this]() { emit bindingSetupChanged(); });
|
||||
|
||||
connect(m_ui.pointerXScale, &QSlider::valueChanged, this,
|
||||
[this](int value) { m_ui.pointerXScaleLabel->setText(QStringLiteral("%1").arg(value)); });
|
||||
connect(m_ui.pointerYScale, &QSlider::valueChanged, this,
|
||||
[this](int value) { m_ui.pointerYScaleLabel->setText(QStringLiteral("%1").arg(value)); });
|
||||
m_ui.pointerXScaleLabel->setText(QStringLiteral("%1").arg(m_ui.pointerXScale->value()));
|
||||
m_ui.pointerYScaleLabel->setText(QStringLiteral("%1").arg(m_ui.pointerYScale->value()));
|
||||
|
||||
updateSDLOptionsEnabled();
|
||||
}
|
||||
|
||||
@@ -128,6 +121,12 @@ void ControllerGlobalSettingsWidget::ledSettingsClicked()
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
void ControllerGlobalSettingsWidget::mouseSettingsClicked()
|
||||
{
|
||||
ControllerMouseSettingsDialog dialog(this, m_dialog);
|
||||
dialog.exec();
|
||||
}
|
||||
|
||||
ControllerLEDSettingsDialog::ControllerLEDSettingsDialog(QWidget* parent, ControllerSettingsDialog* dialog)
|
||||
: QDialog(parent)
|
||||
, m_dialog(dialog)
|
||||
@@ -156,3 +155,35 @@ void ControllerLEDSettingsDialog::linkButton(ColorPickerButton* button, u32 play
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
ControllerMouseSettingsDialog::ControllerMouseSettingsDialog(QWidget* parent, ControllerSettingsDialog* dialog)
|
||||
: QDialog(parent)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
SettingsInterface* sif = dialog->getProfileSettingsInterface();
|
||||
|
||||
m_ui.icon->setPixmap(QIcon::fromTheme(QStringLiteral("mouse-line")).pixmap(32, 32));
|
||||
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerXSpeedSlider, "Pad", "PointerXSpeed", 40.0f);
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerYSpeedSlider, "Pad", "PointerYSpeed", 40.0f);
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerXDeadZoneSlider, "Pad", "PointerXDeadZone", 20.0f);
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerYDeadZoneSlider, "Pad", "PointerYDeadZone", 20.0f);
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerInertiaSlider, "Pad", "PointerInertia", 10.0f);
|
||||
|
||||
connect(m_ui.pointerXSpeedSlider, &QSlider::valueChanged, this, [this](int value) { m_ui.pointerXSpeedVal->setText(QStringLiteral("%1").arg(value)); });
|
||||
connect(m_ui.pointerYSpeedSlider, &QSlider::valueChanged, this, [this](int value) { m_ui.pointerYSpeedVal->setText(QStringLiteral("%1").arg(value)); });
|
||||
connect(m_ui.pointerXDeadZoneSlider, &QSlider::valueChanged, this, [this](int value) { m_ui.pointerXDeadZoneVal->setText(QStringLiteral("%1").arg(value)); });
|
||||
connect(m_ui.pointerYDeadZoneSlider, &QSlider::valueChanged, this, [this](int value) { m_ui.pointerYDeadZoneVal->setText(QStringLiteral("%1").arg(value)); });
|
||||
connect(m_ui.pointerInertiaSlider, &QSlider::valueChanged, this, [this](int value) { m_ui.pointerInertiaVal->setText(QStringLiteral("%1").arg(value)); });
|
||||
|
||||
m_ui.pointerXSpeedVal->setText(QStringLiteral("%1").arg(m_ui.pointerXSpeedSlider->value()));
|
||||
m_ui.pointerYSpeedVal->setText(QStringLiteral("%1").arg(m_ui.pointerYSpeedSlider->value()));
|
||||
m_ui.pointerXDeadZoneVal->setText(QStringLiteral("%1").arg(m_ui.pointerXDeadZoneSlider->value()));
|
||||
m_ui.pointerYDeadZoneVal->setText(QStringLiteral("%1").arg(m_ui.pointerYDeadZoneSlider->value()));
|
||||
m_ui.pointerInertiaVal->setText(QStringLiteral("%1").arg(m_ui.pointerInertiaSlider->value()));
|
||||
|
||||
connect(m_ui.buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this, &QDialog::accept);
|
||||
}
|
||||
|
||||
ControllerMouseSettingsDialog::~ControllerMouseSettingsDialog() = default;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "ui_ControllerGlobalSettingsWidget.h"
|
||||
#include "ui_ControllerLEDSettingsDialog.h"
|
||||
#include "ui_ControllerMouseSettingsDialog.h"
|
||||
|
||||
class ControllerSettingsDialog;
|
||||
|
||||
@@ -44,6 +45,7 @@ Q_SIGNALS:
|
||||
private Q_SLOTS:
|
||||
void updateSDLOptionsEnabled();
|
||||
void ledSettingsClicked();
|
||||
void mouseSettingsClicked();
|
||||
|
||||
private:
|
||||
Ui::ControllerGlobalSettingsWidget m_ui;
|
||||
@@ -64,3 +66,15 @@ private:
|
||||
Ui::ControllerLEDSettingsDialog m_ui;
|
||||
ControllerSettingsDialog* m_dialog;
|
||||
};
|
||||
|
||||
class ControllerMouseSettingsDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ControllerMouseSettingsDialog(QWidget* parent, ControllerSettingsDialog* dialog);
|
||||
~ControllerMouseSettingsDialog();
|
||||
|
||||
private:
|
||||
Ui::ControllerMouseSettingsDialog m_ui;
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>902</width>
|
||||
<height>677</height>
|
||||
<height>583</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -26,6 +26,32 @@
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="5" column="0">
|
||||
<widget class="QGroupBox" name="profileSettings">
|
||||
<property name="title">
|
||||
<string>Profile Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>When this option is enabled, hotkeys can be set in this input profile, and will be used instead of the global hotkeys. By default, hotkeys are always shared between all profiles.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="useProfileHotkeyBindings">
|
||||
<property name="text">
|
||||
<string>Use Per-Profile Hotkeys</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="sdlGroup">
|
||||
<property name="title">
|
||||
@@ -65,8 +91,7 @@
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="lightbulb-line">
|
||||
<normaloff>.</normaloff>.
|
||||
</iconset>
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -75,6 +100,19 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>45</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QGroupBox" name="xinputGroup">
|
||||
<property name="title">
|
||||
@@ -101,71 +139,6 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QGroupBox" name="dinputGroup">
|
||||
<property name="title">
|
||||
<string>DInput Source</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_6">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>The DInput source provides support for legacy controllers which do not support XInput. Accessing these controllers via SDL instead is recommended, but DirectInput can be used if they are not compatible with SDL.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="enableDInputSource">
|
||||
<property name="text">
|
||||
<string>Enable DInput Input Source</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>45</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QGroupBox" name="profileSettings">
|
||||
<property name="title">
|
||||
<string>Profile Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>When this option is enabled, hotkeys can be set in this input profile, and will be used instead of the global hotkeys. By default, hotkeys are always shared between all profiles.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="useProfileHotkeyBindings">
|
||||
<property name="text">
|
||||
<string>Use Per-Profile Hotkeys</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QGroupBox" name="multitapGroup">
|
||||
<property name="title">
|
||||
@@ -199,146 +172,6 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QGroupBox" name="mouseGroup">
|
||||
<property name="title">
|
||||
<string>Mouse/Pointer Source</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Using raw input improves precision when you bind controller sticks to the mouse pointer. Also enables multiple mice to be used.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Horizontal Sensitivity:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QSlider" name="pointerXScale">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>30</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="pointerXScaleLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>10</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Vertical Sensitivity:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QSlider" name="pointerYScale">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>30</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="pointerYScaleLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>10</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="enableMouseMapping">
|
||||
<property name="text">
|
||||
<string>Enable Mouse Mapping</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" rowspan="7">
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="title">
|
||||
@@ -364,6 +197,72 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QGroupBox" name="dinputGroup">
|
||||
<property name="title">
|
||||
<string>DInput Source</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_6">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>The DInput source provides support for legacy controllers which do not support XInput. Accessing these controllers via SDL instead is recommended, but DirectInput can be used if they are not compatible with SDL.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="enableDInputSource">
|
||||
<property name="text">
|
||||
<string>Enable DInput Input Source</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QGroupBox" name="mouseGroup">
|
||||
<property name="title">
|
||||
<string>Mouse/Pointer Source</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>PCSX2 allows you to use your mouse to simulate analog stick movement.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="1,0">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="enableMouseMapping">
|
||||
<property name="text">
|
||||
<string>Enable Mouse Mapping</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="mouseSettings">
|
||||
<property name="toolTip">
|
||||
<string>Controller LED Settings</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Settings...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
||||
417
pcsx2-qt/Settings/ControllerMouseSettingsDialog.ui
Normal file
417
pcsx2-qt/Settings/ControllerMouseSettingsDialog.ui
Normal file
@@ -0,0 +1,417 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ControllerMouseSettingsDialog</class>
|
||||
<widget class="QDialog" name="ControllerMouseSettingsDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>654</width>
|
||||
<height>169</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Mouse Mapping Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="5" column="1">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<layout class="QHBoxLayout" name="pointerYSpeed">
|
||||
<item>
|
||||
<widget class="QLabel" name="pointerYSpeedLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Y Speed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="pointerYSpeedSlider">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="pointerYSpeedVal">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>10</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QHBoxLayout" name="pointerXSpeed">
|
||||
<item>
|
||||
<widget class="QLabel" name="pointerXSpeedLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>X Speed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="pointerXSpeedSlider">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="pointerXSpeedVal">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>10</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="bottomMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="icon">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><span style=" font-weight:700;">Mouse Mapping Settings</span><br/>These settings fine-tune the behavior when mapping a mouse to the emulated controller.</p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<layout class="QHBoxLayout" name="pointerInertia">
|
||||
<item>
|
||||
<widget class="QLabel" name="pointerInertiaLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Inertia</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="pointerInertiaSlider">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="pointerInertiaVal">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>10</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QHBoxLayout" name="pointerXDeadZone">
|
||||
<item>
|
||||
<widget class="QLabel" name="pointerXDeadZoneLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>X Dead Zone</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="pointerXDeadZoneSlider">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="pointerXDeadZoneVal">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>10</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QHBoxLayout" name="pointerYDeadZone">
|
||||
<item>
|
||||
<widget class="QLabel" name="pointerYDeadZoneLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Y Dead Zone</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="pointerYDeadZoneSlider">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="pointerYDeadZoneVal">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>10</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../resources/resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -423,6 +423,9 @@
|
||||
<QtUi Include="Settings\ControllerLEDSettingsDialog.ui">
|
||||
<FileType>Document</FileType>
|
||||
</QtUi>
|
||||
<QtUi Include="Settings\ControllerMouseSettingsDialog.ui">
|
||||
<FileType>Document</FileType>
|
||||
</QtUi>
|
||||
<None Include="Settings\USBBindingWidget_DrivingForce.ui" />
|
||||
<None Include="Settings\USBBindingWidget_GTForce.ui" />
|
||||
<QtUi Include="Settings\USBDeviceWidget.ui">
|
||||
@@ -439,4 +442,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="$(SolutionDir)common\vsprops\QtCompile.targets" />
|
||||
<ImportGroup Label="ExtensionTargets" />
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -583,6 +583,9 @@
|
||||
<QtUi Include="Settings\ControllerLEDSettingsDialog.ui">
|
||||
<Filter>Settings</Filter>
|
||||
</QtUi>
|
||||
<QtUi Include="Settings\ControllerMouseSettingsDialog.ui">
|
||||
<Filter>Settings</Filter>
|
||||
</QtUi>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Settings\FolderSettingsWidget.ui">
|
||||
|
||||
1
pcsx2-qt/resources/icons/black/svg/mouse-line.svg
Normal file
1
pcsx2-qt/resources/icons/black/svg/mouse-line.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.141 4c-1.582 0-2.387.169-3.128.565a3.453 3.453 0 0 0-1.448 1.448C6.169 6.753 6 7.559 6 9.14v5.718c0 1.582.169 2.387.565 3.128.337.63.818 1.111 1.448 1.448.74.396 1.546.565 3.128.565h1.718c1.582 0 2.387-.169 3.128-.565a3.453 3.453 0 0 0 1.448-1.448c.396-.74.565-1.546.565-3.128V9.14c0-1.582-.169-2.387-.565-3.128a3.453 3.453 0 0 0-1.448-1.448C15.247 4.169 14.441 4 12.86 4H11.14zm0-2h1.718c2.014 0 3.094.278 4.072.801a5.452 5.452 0 0 1 2.268 2.268c.523.978.801 2.058.801 4.072v5.718c0 2.014-.278 3.094-.801 4.072a5.452 5.452 0 0 1-2.268 2.268c-.978.523-2.058.801-4.072.801H11.14c-2.014 0-3.094-.278-4.072-.801a5.452 5.452 0 0 1-2.268-2.268C4.278 17.953 4 16.873 4 14.859V9.14c0-2.014.278-3.094.801-4.072A5.452 5.452 0 0 1 7.07 2.801C8.047 2.278 9.127 2 11.141 2zM11 6h2v5h-2V6z"/></svg>
|
||||
|
After Width: | Height: | Size: 918 B |
1
pcsx2-qt/resources/icons/white/svg/mouse-line.svg
Normal file
1
pcsx2-qt/resources/icons/white/svg/mouse-line.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.141 4c-1.582 0-2.387.169-3.128.565a3.453 3.453 0 0 0-1.448 1.448C6.169 6.753 6 7.559 6 9.14v5.718c0 1.582.169 2.387.565 3.128.337.63.818 1.111 1.448 1.448.74.396 1.546.565 3.128.565h1.718c1.582 0 2.387-.169 3.128-.565a3.453 3.453 0 0 0 1.448-1.448c.396-.74.565-1.546.565-3.128V9.14c0-1.582-.169-2.387-.565-3.128a3.453 3.453 0 0 0-1.448-1.448C15.247 4.169 14.441 4 12.86 4H11.14zm0-2h1.718c2.014 0 3.094.278 4.072.801a5.452 5.452 0 0 1 2.268 2.268c.523.978.801 2.058.801 4.072v5.718c0 2.014-.278 3.094-.801 4.072a5.452 5.452 0 0 1-2.268 2.268c-.978.523-2.058.801-4.072.801H11.14c-2.014 0-3.094-.278-4.072-.801a5.452 5.452 0 0 1-2.268-2.268C4.278 17.953 4 16.873 4 14.859V9.14c0-2.014.278-3.094.801-4.072A5.452 5.452 0 0 1 7.07 2.801C8.047 2.278 9.127 2 11.141 2zM11 6h2v5h-2V6z" fill="#ffffff"/></svg>
|
||||
|
After Width: | Height: | Size: 933 B |
@@ -40,6 +40,7 @@
|
||||
<file>icons/black/svg/lightbulb-line.svg</file>
|
||||
<file>icons/black/svg/list-check.svg</file>
|
||||
<file>icons/black/svg/login-box-line.svg</file>
|
||||
<file>icons/black/svg/mouse-line.svg</file>
|
||||
<file>icons/black/svg/pause-line.svg</file>
|
||||
<file>icons/black/svg/play-line.svg</file>
|
||||
<file>icons/black/svg/price-tag-3-line.svg</file>
|
||||
@@ -100,6 +101,7 @@
|
||||
<file>icons/white/svg/lightbulb-line.svg</file>
|
||||
<file>icons/white/svg/list-check.svg</file>
|
||||
<file>icons/white/svg/login-box-line.svg</file>
|
||||
<file>icons/white/svg/mouse-line.svg</file>
|
||||
<file>icons/white/svg/pause-line.svg</file>
|
||||
<file>icons/white/svg/play-line.svg</file>
|
||||
<file>icons/white/svg/price-tag-3-line.svg</file>
|
||||
|
||||
@@ -641,13 +641,10 @@ bool D3D11HostDisplay::UpdateImGuiFontTexture()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D11HostDisplay::BeginPresent(bool frame_skip)
|
||||
HostDisplay::PresentResult D3D11HostDisplay::BeginPresent(bool frame_skip)
|
||||
{
|
||||
if (frame_skip || !m_swap_chain)
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
return false;
|
||||
}
|
||||
return PresentResult::FrameSkipped;
|
||||
|
||||
// When using vsync, the time here seems to include the time for the buffer to become available.
|
||||
// This blows our our GPU usage number considerably, so read the timestamp before the final blit
|
||||
@@ -664,7 +661,7 @@ bool D3D11HostDisplay::BeginPresent(bool frame_skip)
|
||||
const CD3D11_RECT scissor(0, 0, m_window_info.surface_width, m_window_info.surface_height);
|
||||
m_context->RSSetViewports(1, &vp);
|
||||
m_context->RSSetScissorRects(1, &scissor);
|
||||
return true;
|
||||
return PresentResult::OK;
|
||||
}
|
||||
|
||||
void D3D11HostDisplay::EndPresent()
|
||||
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
bool BeginPresent(bool frame_skip) override;
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
|
||||
@@ -554,13 +554,13 @@ bool D3D12HostDisplay::UpdateImGuiFontTexture()
|
||||
return ImGui_ImplDX12_CreateFontsTexture();
|
||||
}
|
||||
|
||||
bool D3D12HostDisplay::BeginPresent(bool frame_skip)
|
||||
HostDisplay::PresentResult D3D12HostDisplay::BeginPresent(bool frame_skip)
|
||||
{
|
||||
if (m_device_lost)
|
||||
return HostDisplay::PresentResult::DeviceLost;
|
||||
|
||||
if (frame_skip || !m_swap_chain)
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
return false;
|
||||
}
|
||||
return PresentResult::FrameSkipped;
|
||||
|
||||
static constexpr std::array<float, 4> clear_color = {};
|
||||
D3D12::Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
|
||||
@@ -574,7 +574,7 @@ bool D3D12HostDisplay::BeginPresent(bool frame_skip)
|
||||
const D3D12_RECT scissor{0, 0, static_cast<LONG>(m_window_info.surface_width), static_cast<LONG>(m_window_info.surface_height)};
|
||||
cmdlist->RSSetViewports(1, &vp);
|
||||
cmdlist->RSSetScissorRects(1, &scissor);
|
||||
return true;
|
||||
return PresentResult::OK;
|
||||
}
|
||||
|
||||
void D3D12HostDisplay::EndPresent()
|
||||
@@ -586,7 +586,11 @@ void D3D12HostDisplay::EndPresent()
|
||||
m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast<u32>(m_swap_chain_buffers.size()));
|
||||
|
||||
swap_chain_buf.TransitionToState(g_d3d12_context->GetCommandList(), D3D12_RESOURCE_STATE_PRESENT);
|
||||
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::None);
|
||||
if (!g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::None))
|
||||
{
|
||||
m_device_lost = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const bool vsync = static_cast<UINT>(m_vsync_mode != VsyncMode::Off);
|
||||
if (!vsync && m_using_allow_tearing)
|
||||
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
bool BeginPresent(bool frame_skip) override;
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
@@ -97,4 +97,5 @@ protected:
|
||||
|
||||
bool m_allow_tearing_supported = false;
|
||||
bool m_using_allow_tearing = false;
|
||||
bool m_device_lost = false;
|
||||
};
|
||||
|
||||
@@ -229,6 +229,12 @@ void ImGuiManager::NewFrame()
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiManager::SkipFrame()
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
NewFrame();
|
||||
}
|
||||
|
||||
void ImGuiManager::SetStyle()
|
||||
{
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
@@ -40,6 +40,9 @@ namespace ImGuiManager
|
||||
/// Call at the beginning of the frame to set up ImGui state.
|
||||
void NewFrame();
|
||||
|
||||
/// Call when skipping rendering a frame, to update internal state.
|
||||
void SkipFrame();
|
||||
|
||||
/// Renders any on-screen display elements.
|
||||
void RenderOSD();
|
||||
|
||||
|
||||
@@ -160,7 +160,11 @@ struct PointerAxisState
|
||||
static std::array<std::array<float, static_cast<u8>(InputPointerAxis::Count)>, InputManager::MAX_POINTER_DEVICES> s_host_pointer_positions;
|
||||
static std::array<std::array<PointerAxisState, static_cast<u8>(InputPointerAxis::Count)>, InputManager::MAX_POINTER_DEVICES>
|
||||
s_pointer_state;
|
||||
static std::array<float, static_cast<u8>(InputPointerAxis::Count)> s_pointer_axis_scale;
|
||||
static std::array<float, 2> s_pointer_axis_speed;
|
||||
static std::array<float, 2> s_pointer_axis_dead_zone;
|
||||
static std::array<float, 2> s_pointer_axis_range;
|
||||
static std::array<float, 2> s_pointer_pos = {0.0f, 0.0f};
|
||||
static float s_pointer_inertia = 0.0f;
|
||||
|
||||
using PointerMoveCallback = std::function<void(InputBindingKey key, float value)>;
|
||||
using KeyboardEventCallback = std::function<void(InputBindingKey key, float value)>;
|
||||
@@ -950,15 +954,30 @@ void InputManager::GenerateRelativeMouseEvents()
|
||||
{
|
||||
for (u32 axis = 0; axis < static_cast<u32>(static_cast<u8>(InputPointerAxis::Count)); axis++)
|
||||
{
|
||||
const InputBindingKey key(MakePointerAxisKey(device, static_cast<InputPointerAxis>(axis)));
|
||||
|
||||
PointerAxisState& state = s_pointer_state[device][axis];
|
||||
const float delta = static_cast<float>(state.delta.exchange(0, std::memory_order_acquire)) / 65536.0f;
|
||||
const float unclamped_value = delta * s_pointer_axis_scale[axis];
|
||||
float value = 0.0f;
|
||||
|
||||
const InputBindingKey key(MakePointerAxisKey(device, static_cast<InputPointerAxis>(axis)));
|
||||
if (axis >= static_cast<u32>(InputPointerAxis::WheelX) && ImGuiManager::ProcessPointerAxisEvent(key, unclamped_value))
|
||||
continue;
|
||||
if (axis < 2)
|
||||
{
|
||||
s_pointer_pos[axis] += delta * s_pointer_axis_speed[axis];
|
||||
value = std::clamp(s_pointer_pos[axis], -1.0f, 1.0f);
|
||||
s_pointer_pos[axis] -= value;
|
||||
s_pointer_pos[axis] *= s_pointer_inertia;
|
||||
|
||||
value *= s_pointer_axis_range[axis];
|
||||
if (value > 0.0f)
|
||||
value += s_pointer_axis_dead_zone[axis];
|
||||
else if (value < 0.0f)
|
||||
value -= s_pointer_axis_dead_zone[axis];
|
||||
}
|
||||
else
|
||||
{
|
||||
value = std::clamp(delta, -1.0f, 1.0f);
|
||||
}
|
||||
|
||||
const float value = std::clamp(unclamped_value, -1.0f, 1.0f);
|
||||
if (value != state.last_value)
|
||||
{
|
||||
state.last_value = value;
|
||||
@@ -1203,14 +1222,18 @@ void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& bind
|
||||
for (u32 pad = 0; pad < PAD::NUM_CONTROLLER_PORTS; pad++)
|
||||
AddPadBindings(binding_si, pad, PAD::GetDefaultPadType(pad));
|
||||
|
||||
for (u32 axis = 0; axis < static_cast<u32>(InputPointerAxis::Count); axis++)
|
||||
constexpr float ui_ctrl_range = 100.0f;
|
||||
constexpr float pointer_sensitivity = 0.05f;
|
||||
for (u32 axis = 0; axis <= static_cast<u32>(InputPointerAxis::Y); axis++)
|
||||
{
|
||||
// From lilypad: 1 mouse pixel = 1/8th way down.
|
||||
const float default_scale = (axis <= static_cast<u32>(InputPointerAxis::Y)) ? 8.0f : 1.0f;
|
||||
s_pointer_axis_scale[axis] =
|
||||
1.0f /
|
||||
std::max(si.GetFloatValue("Pad", fmt::format("Pointer{}Scale", s_pointer_axis_names[axis]).c_str(), default_scale), 1.0f);
|
||||
s_pointer_axis_speed[axis] = si.GetFloatValue("Pad", fmt::format("Pointer{}Speed", s_pointer_axis_names[axis]).c_str(), 40.0f) /
|
||||
ui_ctrl_range * pointer_sensitivity;
|
||||
s_pointer_axis_dead_zone[axis] = std::min(
|
||||
si.GetFloatValue("Pad", fmt::format("Pointer{}DeadZone", s_pointer_axis_names[axis]).c_str(), 20.0f) / ui_ctrl_range, 1.0f);
|
||||
s_pointer_axis_range[axis] = 1.0f - s_pointer_axis_dead_zone[axis];
|
||||
}
|
||||
s_pointer_inertia = si.GetFloatValue("Pad", "PointerInertia", 10.0f) / ui_ctrl_range;
|
||||
s_pointer_pos = {};
|
||||
|
||||
for (u32 port = 0; port < USB::NUM_PORTS; port++)
|
||||
AddUSBBindings(binding_si, port);
|
||||
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic = false) override;
|
||||
void UpdateTexture(id<MTLTexture> texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride);
|
||||
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride) override;
|
||||
bool BeginPresent(bool frame_skip) override;
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
|
||||
@@ -265,7 +265,7 @@ void MetalHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y,
|
||||
|
||||
static bool s_capture_next = false;
|
||||
|
||||
bool MetalHostDisplay::BeginPresent(bool frame_skip)
|
||||
HostDisplay::PresentResult MetalHostDisplay::BeginPresent(bool frame_skip)
|
||||
{ @autoreleasepool {
|
||||
GSDeviceMTL* dev = static_cast<GSDeviceMTL*>(g_gs_device.get());
|
||||
if (dev && m_capture_start_frame && dev->FrameNo() == m_capture_start_frame)
|
||||
@@ -273,7 +273,7 @@ bool MetalHostDisplay::BeginPresent(bool frame_skip)
|
||||
if (frame_skip || m_window_info.type == WindowInfo::Type::Surfaceless || !g_gs_device)
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
return false;
|
||||
return PresentResult::FrameSkipped;
|
||||
}
|
||||
id<MTLCommandBuffer> buf = dev->GetRenderCmdBuf();
|
||||
m_current_drawable = MRCRetain([m_layer nextDrawable]);
|
||||
@@ -284,13 +284,13 @@ bool MetalHostDisplay::BeginPresent(bool frame_skip)
|
||||
[buf popDebugGroup];
|
||||
dev->FlushEncoders();
|
||||
ImGui::EndFrame();
|
||||
return false;
|
||||
return PresentResult::FrameSkipped;
|
||||
}
|
||||
[m_pass_desc colorAttachments][0].texture = [m_current_drawable texture];
|
||||
id<MTLRenderCommandEncoder> enc = [buf renderCommandEncoderWithDescriptor:m_pass_desc];
|
||||
[enc setLabel:@"Present"];
|
||||
dev->m_current_render.encoder = MRCRetain(enc);
|
||||
return true;
|
||||
return PresentResult::OK;
|
||||
}}
|
||||
|
||||
void MetalHostDisplay::EndPresent()
|
||||
|
||||
@@ -335,13 +335,10 @@ bool OpenGLHostDisplay::UpdateImGuiFontTexture()
|
||||
return ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||
}
|
||||
|
||||
bool OpenGLHostDisplay::BeginPresent(bool frame_skip)
|
||||
HostDisplay::PresentResult OpenGLHostDisplay::BeginPresent(bool frame_skip)
|
||||
{
|
||||
if (frame_skip || m_window_info.type == WindowInfo::Type::Surfaceless)
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
return false;
|
||||
}
|
||||
return PresentResult::FrameSkipped;
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
@@ -350,7 +347,7 @@ bool OpenGLHostDisplay::BeginPresent(bool frame_skip)
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glViewport(0, 0, m_window_info.surface_width, m_window_info.surface_height);
|
||||
|
||||
return true;
|
||||
return PresentResult::OK;
|
||||
}
|
||||
|
||||
void OpenGLHostDisplay::EndPresent()
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
bool BeginPresent(bool frame_skip) override;
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
|
||||
@@ -339,17 +339,18 @@ bool VulkanHostDisplay::DoneCurrent()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanHostDisplay::BeginPresent(bool frame_skip)
|
||||
HostDisplay::PresentResult VulkanHostDisplay::BeginPresent(bool frame_skip)
|
||||
{
|
||||
if (frame_skip || !m_swap_chain)
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
return false;
|
||||
}
|
||||
return PresentResult::FrameSkipped;
|
||||
|
||||
// Previous frame needs to be presented before we can acquire the swap chain.
|
||||
g_vulkan_context->WaitForPresentComplete();
|
||||
|
||||
// Check if the device was lost.
|
||||
if (g_vulkan_context->CheckLastSubmitFail())
|
||||
return PresentResult::DeviceLost;
|
||||
|
||||
VkResult res = m_swap_chain->AcquireNextImage();
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
@@ -367,7 +368,7 @@ bool VulkanHostDisplay::BeginPresent(bool frame_skip)
|
||||
{
|
||||
Console.Error("Failed to recreate surface after loss");
|
||||
g_vulkan_context->ExecuteCommandBuffer(Vulkan::Context::WaitType::None);
|
||||
return false;
|
||||
return PresentResult::FrameSkipped;
|
||||
}
|
||||
|
||||
res = m_swap_chain->AcquireNextImage();
|
||||
@@ -380,7 +381,7 @@ bool VulkanHostDisplay::BeginPresent(bool frame_skip)
|
||||
// Still submit the command buffer, otherwise we'll end up with several frames waiting.
|
||||
LOG_VULKAN_ERROR(res, "vkAcquireNextImageKHR() failed: ");
|
||||
g_vulkan_context->ExecuteCommandBuffer(Vulkan::Context::WaitType::None);
|
||||
return false;
|
||||
return PresentResult::FrameSkipped;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,7 +402,7 @@ bool VulkanHostDisplay::BeginPresent(bool frame_skip)
|
||||
const VkRect2D scissor{{0, 0}, {static_cast<u32>(swap_chain_texture.GetWidth()), static_cast<u32>(swap_chain_texture.GetHeight())}};
|
||||
vkCmdSetViewport(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &vp);
|
||||
vkCmdSetScissor(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &scissor);
|
||||
return true;
|
||||
return PresentResult::OK;
|
||||
}
|
||||
|
||||
void VulkanHostDisplay::EndPresent()
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
|
||||
void SetVSync(VsyncMode mode) override;
|
||||
|
||||
bool BeginPresent(bool frame_skip) override;
|
||||
PresentResult BeginPresent(bool frame_skip) override;
|
||||
void EndPresent() override;
|
||||
|
||||
bool SetGPUTimingEnabled(bool enabled) override;
|
||||
|
||||
@@ -209,15 +209,15 @@ static bool DoGSOpen(GSRendererType renderer, u8* basemem)
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
if (!g_gs_device->Create())
|
||||
{
|
||||
if (!g_gs_device->Create())
|
||||
{
|
||||
g_gs_device->Destroy();
|
||||
g_gs_device.reset();
|
||||
return false;
|
||||
}
|
||||
g_gs_device->Destroy();
|
||||
g_gs_device.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!g_gs_renderer)
|
||||
{
|
||||
if (renderer == GSRendererType::Null)
|
||||
{
|
||||
g_gs_renderer = std::make_unique<GSRendererNull>();
|
||||
@@ -230,55 +230,68 @@ static bool DoGSOpen(GSRendererType renderer, u8* basemem)
|
||||
{
|
||||
g_gs_renderer = std::unique_ptr<GSRenderer>(MULTI_ISA_SELECT(makeGSRendererSW)(GSConfig.SWExtraThreads));
|
||||
}
|
||||
|
||||
g_gs_renderer->SetRegsMem(basemem);
|
||||
g_gs_renderer->ResetPCRTC();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
else
|
||||
{
|
||||
Host::ReportFormattedErrorAsync("GS", "GS error: Exception caught in GSopen: %s", ex.what());
|
||||
g_gs_renderer.reset();
|
||||
g_gs_device->Destroy();
|
||||
g_gs_device.reset();
|
||||
return false;
|
||||
Console.Warning("(DoGSOpen) Using existing renderer.");
|
||||
}
|
||||
|
||||
GSConfig.OsdShowGPU = EmuConfig.GS.OsdShowGPU && g_host_display->SetGPUTimingEnabled(true);
|
||||
|
||||
g_gs_renderer->SetRegsMem(basemem);
|
||||
g_perfmon.Reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSreopen(bool recreate_display, const Pcsx2Config::GSOptions& old_config)
|
||||
bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::GSOptions& old_config)
|
||||
{
|
||||
Console.WriteLn("Reopening GS with %s display", recreate_display ? "new" : "existing");
|
||||
|
||||
g_gs_renderer->Flush(GSState::GSFlushReason::GSREOPEN);
|
||||
if (recreate_renderer)
|
||||
g_gs_renderer->Flush(GSState::GSFlushReason::GSREOPEN);
|
||||
|
||||
freezeData fd = {};
|
||||
if (g_gs_renderer->Freeze(&fd, true) != 0)
|
||||
std::unique_ptr<u8[]> fd_data;
|
||||
if (recreate_renderer)
|
||||
{
|
||||
Console.Error("(GSreopen) Failed to get GS freeze size");
|
||||
return false;
|
||||
}
|
||||
if (g_gs_renderer->Freeze(&fd, true) != 0)
|
||||
{
|
||||
Console.Error("(GSreopen) Failed to get GS freeze size");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<u8[]> fd_data = std::make_unique<u8[]>(fd.size);
|
||||
fd.data = fd_data.get();
|
||||
if (g_gs_renderer->Freeze(&fd, false) != 0)
|
||||
fd_data = std::make_unique<u8[]>(fd.size);
|
||||
fd.data = fd_data.get();
|
||||
if (g_gs_renderer->Freeze(&fd, false) != 0)
|
||||
{
|
||||
Console.Error("(GSreopen) Failed to freeze GS");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error("(GSreopen) Failed to freeze GS");
|
||||
return false;
|
||||
// Make sure nothing is left over.
|
||||
g_gs_renderer->PurgePool();
|
||||
g_gs_renderer->PurgeTextureCache();
|
||||
}
|
||||
|
||||
if (recreate_display)
|
||||
{
|
||||
g_gs_device->ResetAPIState();
|
||||
if (Host::BeginPresentFrame(true))
|
||||
if (Host::BeginPresentFrame(false) == HostDisplay::PresentResult::OK)
|
||||
Host::EndPresentFrame();
|
||||
}
|
||||
|
||||
u8* basemem = g_gs_renderer->GetRegsMem();
|
||||
const u32 gamecrc = g_gs_renderer->GetGameCRC();
|
||||
g_gs_renderer->Destroy();
|
||||
g_gs_renderer.reset();
|
||||
if (recreate_renderer)
|
||||
{
|
||||
g_gs_renderer->Destroy();
|
||||
g_gs_renderer.reset();
|
||||
}
|
||||
|
||||
g_gs_device->Destroy();
|
||||
g_gs_device.reset();
|
||||
|
||||
@@ -327,13 +340,17 @@ bool GSreopen(bool recreate_display, const Pcsx2Config::GSOptions& old_config)
|
||||
}
|
||||
}
|
||||
|
||||
if (g_gs_renderer->Defrost(&fd) != 0)
|
||||
if (recreate_renderer)
|
||||
{
|
||||
Console.Error("(GSreopen) Failed to defrost");
|
||||
return false;
|
||||
if (g_gs_renderer->Defrost(&fd) != 0)
|
||||
{
|
||||
Console.Error("(GSreopen) Failed to defrost");
|
||||
return false;
|
||||
}
|
||||
|
||||
g_gs_renderer->SetGameCRC(gamecrc);
|
||||
}
|
||||
|
||||
g_gs_renderer->SetGameCRC(gamecrc);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -698,7 +715,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
|
||||
GSConfig.DisableShaderCache != old_config.DisableShaderCache ||
|
||||
GSConfig.DisableThreadedPresentation != old_config.DisableThreadedPresentation
|
||||
);
|
||||
if (!GSreopen(do_full_restart, old_config))
|
||||
if (!GSreopen(do_full_restart, true, old_config))
|
||||
pxFailRel("Failed to do full GS reopen");
|
||||
return;
|
||||
}
|
||||
@@ -709,7 +726,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
|
||||
GSConfig.SWExtraThreads != old_config.SWExtraThreads ||
|
||||
GSConfig.SWExtraThreadsHeight != old_config.SWExtraThreadsHeight)
|
||||
{
|
||||
if (!GSreopen(false, old_config))
|
||||
if (!GSreopen(false, true, old_config))
|
||||
pxFailRel("Failed to do quick GS reopen");
|
||||
|
||||
return;
|
||||
@@ -787,7 +804,7 @@ void GSSwitchRenderer(GSRendererType new_renderer)
|
||||
const bool recreate_display = (!is_software_switch && existing_api != GetAPIForRenderer(new_renderer));
|
||||
const Pcsx2Config::GSOptions old_config(GSConfig);
|
||||
GSConfig.Renderer = new_renderer;
|
||||
if (!GSreopen(recreate_display, old_config))
|
||||
if (!GSreopen(recreate_display, true, old_config))
|
||||
pxFailRel("Failed to reopen GS for renderer switch.");
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ s16 GSLookupBeforeDrawFunctionId(const std::string_view& name);
|
||||
int GSinit();
|
||||
void GSshutdown();
|
||||
bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* basemem);
|
||||
bool GSreopen(bool recreate_display, const Pcsx2Config::GSOptions& old_config);
|
||||
bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::GSOptions& old_config);
|
||||
void GSreset(bool hardware_reset);
|
||||
void GSclose();
|
||||
void GSgifSoftReset(u32 mask);
|
||||
|
||||
@@ -24,15 +24,6 @@ const CRC::Game CRC::m_games[] =
|
||||
{
|
||||
// Note: IDs 0x7ACF7E03, 0x7D4EA48F, 0x37C53760 - shouldn't be added as it's from the multiloaders when packing games.
|
||||
{0x00000000, NoTitle /* NoRegion */},
|
||||
{0x9AAC5309, FFX2 /* EU */},
|
||||
{0x9AAC530C, FFX2 /* FR */},
|
||||
{0x9AAC530A, FFX2 /* ES */},
|
||||
{0x9AAC530D, FFX2 /* DE */},
|
||||
{0x9AAC530B, FFX2 /* IT */},
|
||||
{0x48FE0C71, FFX2 /* US */},
|
||||
{0x8A6D7F14, FFX2 /* JP */},
|
||||
{0xE1FD9A2D, FFX2 /* JP */}, // int.
|
||||
{0x11624CD6, FFX2 /* KO */},
|
||||
{0x08C1ED4D, HauntingGround /* EU */},
|
||||
{0x2CD5794C, HauntingGround /* EU */},
|
||||
{0x867BB945, HauntingGround /* JP */},
|
||||
|
||||
@@ -23,7 +23,6 @@ public:
|
||||
enum Title : u32
|
||||
{
|
||||
NoTitle,
|
||||
FFX2,
|
||||
GetawayGames,
|
||||
HauntingGround,
|
||||
ICO,
|
||||
|
||||
@@ -481,7 +481,7 @@ public:
|
||||
static psm_t m_psm[64];
|
||||
static readImage m_readImageX;
|
||||
|
||||
static const int m_vmsize = 1024 * 1024 * 4;
|
||||
static constexpr int m_vmsize = 1024 * 1024 * 4;
|
||||
|
||||
u8* m_vm8;
|
||||
|
||||
|
||||
@@ -37,14 +37,61 @@ static __fi bool IsFirstProvokingVertex()
|
||||
return (GSConfig.Renderer != GSRendererType::SW && !g_gs_device->Features().provoking_vertex_last);
|
||||
}
|
||||
|
||||
constexpr int GSState::GetSaveStateSize()
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
size += sizeof(STATE_VERSION);
|
||||
size += sizeof(m_env.PRIM);
|
||||
size += sizeof(m_env.PRMODECONT);
|
||||
size += sizeof(m_env.TEXCLUT);
|
||||
size += sizeof(m_env.SCANMSK);
|
||||
size += sizeof(m_env.TEXA);
|
||||
size += sizeof(m_env.FOGCOL);
|
||||
size += sizeof(m_env.DIMX);
|
||||
size += sizeof(m_env.DTHE);
|
||||
size += sizeof(m_env.COLCLAMP);
|
||||
size += sizeof(m_env.PABE);
|
||||
size += sizeof(m_env.BITBLTBUF);
|
||||
size += sizeof(m_env.TRXDIR);
|
||||
size += sizeof(m_env.TRXPOS);
|
||||
size += sizeof(m_env.TRXREG);
|
||||
size += sizeof(m_env.TRXREG); // obsolete
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
size += sizeof(m_env.CTXT[i].XYOFFSET);
|
||||
size += sizeof(m_env.CTXT[i].TEX0);
|
||||
size += sizeof(m_env.CTXT[i].TEX1);
|
||||
size += sizeof(m_env.CTXT[i].CLAMP);
|
||||
size += sizeof(m_env.CTXT[i].MIPTBP1);
|
||||
size += sizeof(m_env.CTXT[i].MIPTBP2);
|
||||
size += sizeof(m_env.CTXT[i].SCISSOR);
|
||||
size += sizeof(m_env.CTXT[i].ALPHA);
|
||||
size += sizeof(m_env.CTXT[i].TEST);
|
||||
size += sizeof(m_env.CTXT[i].FBA);
|
||||
size += sizeof(m_env.CTXT[i].FRAME);
|
||||
size += sizeof(m_env.CTXT[i].ZBUF);
|
||||
}
|
||||
|
||||
size += sizeof(m_v.RGBAQ);
|
||||
size += sizeof(m_v.ST);
|
||||
size += sizeof(m_v.UV);
|
||||
size += sizeof(m_v.FOG);
|
||||
size += sizeof(m_v.XYZ);
|
||||
size += sizeof(GIFReg); // obsolete
|
||||
|
||||
size += sizeof(m_tr.x);
|
||||
size += sizeof(m_tr.y);
|
||||
size += GSLocalMemory::m_vmsize;
|
||||
size += (sizeof(GIFPath::tag) + sizeof(GIFPath::reg)) * 4 /* std::size(GSState::m_path) */; // std::size won't work without an instance.
|
||||
size += sizeof(m_q);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
GSState::GSState()
|
||||
: m_version(STATE_VERSION)
|
||||
, m_q(1.0f)
|
||||
, m_scanmask_used(0)
|
||||
, tex_flushed(true)
|
||||
, m_vt(this, IsFirstProvokingVertex())
|
||||
, m_regs(NULL)
|
||||
, m_crc(0)
|
||||
: m_vt(this, IsFirstProvokingVertex())
|
||||
{
|
||||
// m_nativeres seems to be a hack. Unfortunately it impacts draw call number which make debug painful in the replayer.
|
||||
// Let's keep it disabled to ease debug.
|
||||
@@ -63,54 +110,6 @@ GSState::GSState()
|
||||
|
||||
m_draw_transfers.clear();
|
||||
|
||||
m_sssize = 0;
|
||||
|
||||
m_sssize += sizeof(m_version);
|
||||
m_sssize += sizeof(m_env.PRIM);
|
||||
m_sssize += sizeof(m_env.PRMODECONT);
|
||||
m_sssize += sizeof(m_env.TEXCLUT);
|
||||
m_sssize += sizeof(m_env.SCANMSK);
|
||||
m_sssize += sizeof(m_env.TEXA);
|
||||
m_sssize += sizeof(m_env.FOGCOL);
|
||||
m_sssize += sizeof(m_env.DIMX);
|
||||
m_sssize += sizeof(m_env.DTHE);
|
||||
m_sssize += sizeof(m_env.COLCLAMP);
|
||||
m_sssize += sizeof(m_env.PABE);
|
||||
m_sssize += sizeof(m_env.BITBLTBUF);
|
||||
m_sssize += sizeof(m_env.TRXDIR);
|
||||
m_sssize += sizeof(m_env.TRXPOS);
|
||||
m_sssize += sizeof(m_env.TRXREG);
|
||||
m_sssize += sizeof(m_env.TRXREG); // obsolete
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
m_sssize += sizeof(m_env.CTXT[i].XYOFFSET);
|
||||
m_sssize += sizeof(m_env.CTXT[i].TEX0);
|
||||
m_sssize += sizeof(m_env.CTXT[i].TEX1);
|
||||
m_sssize += sizeof(m_env.CTXT[i].CLAMP);
|
||||
m_sssize += sizeof(m_env.CTXT[i].MIPTBP1);
|
||||
m_sssize += sizeof(m_env.CTXT[i].MIPTBP2);
|
||||
m_sssize += sizeof(m_env.CTXT[i].SCISSOR);
|
||||
m_sssize += sizeof(m_env.CTXT[i].ALPHA);
|
||||
m_sssize += sizeof(m_env.CTXT[i].TEST);
|
||||
m_sssize += sizeof(m_env.CTXT[i].FBA);
|
||||
m_sssize += sizeof(m_env.CTXT[i].FRAME);
|
||||
m_sssize += sizeof(m_env.CTXT[i].ZBUF);
|
||||
}
|
||||
|
||||
m_sssize += sizeof(m_v.RGBAQ);
|
||||
m_sssize += sizeof(m_v.ST);
|
||||
m_sssize += sizeof(m_v.UV);
|
||||
m_sssize += sizeof(m_v.FOG);
|
||||
m_sssize += sizeof(m_v.XYZ);
|
||||
m_sssize += sizeof(GIFReg); // obsolete
|
||||
|
||||
m_sssize += sizeof(m_tr.x);
|
||||
m_sssize += sizeof(m_tr.y);
|
||||
m_sssize += m_mem.m_vmsize;
|
||||
m_sssize += (sizeof(m_path[0].tag) + sizeof(m_path[0].reg)) * std::size(m_path);
|
||||
m_sssize += sizeof(m_q);
|
||||
|
||||
PRIM = &m_env.PRIM;
|
||||
//CSR->rREV = 0x20;
|
||||
m_env.PRMODECONT.AC = 1;
|
||||
@@ -285,6 +284,14 @@ void GSState::ResetHandlers()
|
||||
m_fpGIFRegHandlers[GIF_A_D_REG_LABEL] = &GSState::GIFRegHandlerNull;
|
||||
}
|
||||
|
||||
void GSState::ResetPCRTC()
|
||||
{
|
||||
PCRTCDisplays.SetVideoMode(GetVideoMode());
|
||||
PCRTCDisplays.EnableDisplays(m_regs->PMODE, m_regs->SMODE2, isReallyInterlaced());
|
||||
PCRTCDisplays.SetRects(0, m_regs->DISP[0].DISPLAY, m_regs->DISP[0].DISPFB);
|
||||
PCRTCDisplays.SetRects(1, m_regs->DISP[1].DISPLAY, m_regs->DISP[1].DISPFB);
|
||||
}
|
||||
|
||||
void GSState::UpdateSettings(const Pcsx2Config::GSOptions& old_config)
|
||||
{
|
||||
m_mipmap = GSConfig.Mipmap;
|
||||
@@ -340,264 +347,6 @@ GSVideoMode GSState::GetVideoMode()
|
||||
__assume(0); // unreachable
|
||||
}
|
||||
|
||||
bool GSState::IsAnalogue()
|
||||
{
|
||||
return GetVideoMode() == GSVideoMode::NTSC || GetVideoMode() == GSVideoMode::PAL || GetVideoMode() == GSVideoMode::HDTV_1080I;
|
||||
}
|
||||
|
||||
GSVector4i GSState::GetFrameMagnifiedRect(int i)
|
||||
{
|
||||
GSVector4i rectangle = { 0, 0, 0, 0 };
|
||||
|
||||
if (!IsEnabled(i))
|
||||
return rectangle;
|
||||
|
||||
const int videomode = static_cast<int>(GetVideoMode()) - 1;
|
||||
const auto& DISP = m_regs->DISP[i].DISPLAY;
|
||||
const bool ignore_offset = !GSConfig.PCRTCOffsets;
|
||||
|
||||
const u32 DW = DISP.DW + 1;
|
||||
const u32 DH = DISP.DH + 1;
|
||||
;
|
||||
// The number of sub pixels to draw are given in DH and DW, the MAGH/V relates to the size of the original square in the FB
|
||||
// but the size it's drawn to uses the default size of the display mode (for PAL/NTSC this is a MAGH of 3)
|
||||
// so for example a game will have a DW of 2559 and a MAGH of 4 to make it 512 (from the FB), but because it's drawing 2560 subpixels
|
||||
// it will cover the entire 640 wide of the screen (2560 / (3+1)).
|
||||
int width;
|
||||
int height;
|
||||
if (ignore_offset)
|
||||
{
|
||||
width = (DW / (DISP.MAGH + 1));
|
||||
height = (DH / (DISP.MAGV + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
width = (DW / (VideoModeDividers[videomode].x + 1));
|
||||
height = (DH / (VideoModeDividers[videomode].y + 1));
|
||||
}
|
||||
|
||||
int res_multi = 1;
|
||||
|
||||
if (isinterlaced() && m_regs->SMODE2.FFMD && height > 1)
|
||||
res_multi = 2;
|
||||
|
||||
// Set up the display rectangle based on the values obtained from DISPLAY registers
|
||||
rectangle.right = width;
|
||||
rectangle.bottom = height / res_multi;
|
||||
|
||||
return rectangle;
|
||||
}
|
||||
|
||||
int GSState::GetDisplayHMagnification()
|
||||
{
|
||||
// Pick one of the DISPLAY's and hope that they are both the same. Favour DISPLAY[1]
|
||||
for (int i = 1; i >= 0; i--)
|
||||
{
|
||||
if (IsEnabled(i))
|
||||
return m_regs->DISP[i].DISPLAY.MAGH + 1;
|
||||
}
|
||||
|
||||
// If neither DISPLAY is enabled, fallback to resolution offset (should never happen)
|
||||
const int videomode = static_cast<int>(GetVideoMode()) - 1;
|
||||
return VideoModeDividers[videomode].x + 1;
|
||||
}
|
||||
|
||||
GSVector4i GSState::GetDisplayRect(int i)
|
||||
{
|
||||
GSVector4i rectangle = { 0, 0, 0, 0 };
|
||||
|
||||
if (i == -1)
|
||||
{
|
||||
return GetDisplayRect(0).runion(GetDisplayRect(1));
|
||||
}
|
||||
|
||||
if (!IsEnabled(i))
|
||||
return rectangle;
|
||||
|
||||
const auto& DISP = m_regs->DISP[i].DISPLAY;
|
||||
|
||||
const u32 DW = DISP.DW + 1;
|
||||
const u32 DH = DISP.DH + 1;
|
||||
const u32 MAGH = DISP.MAGH + 1;
|
||||
const u32 MAGV = DISP.MAGV + 1;
|
||||
|
||||
const GSVector2i magnification(MAGH, MAGV);
|
||||
|
||||
const int width = DW / magnification.x;
|
||||
const int height = DH / magnification.y;
|
||||
|
||||
const GSVector2i offsets = GetResolutionOffset(i);
|
||||
|
||||
// Set up the display rectangle based on the values obtained from DISPLAY registers
|
||||
rectangle.left = offsets.x;
|
||||
rectangle.top = offsets.y;
|
||||
|
||||
rectangle.right = rectangle.left + width;
|
||||
rectangle.bottom = rectangle.top + height;
|
||||
|
||||
return rectangle;
|
||||
}
|
||||
|
||||
GSVector2i GSState::GetResolutionOffset(int i)
|
||||
{
|
||||
GSVector2i offset = { 0, 0 };
|
||||
|
||||
if (!IsEnabled(i))
|
||||
return offset;
|
||||
|
||||
const int videomode = static_cast<int>(GetVideoMode()) - 1;
|
||||
const auto& DISP = m_regs->DISP[i].DISPLAY;
|
||||
|
||||
const auto& SMODE2 = m_regs->SMODE2;
|
||||
const int res_multi = (SMODE2.INT + 1);
|
||||
const GSVector4i offsets = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode] : VideoModeOffsetsOverscan[videomode];
|
||||
|
||||
offset.x = (static_cast<int>(DISP.DX) - offsets.z) / (VideoModeDividers[videomode].x + 1);
|
||||
offset.y = (static_cast<int>(DISP.DY) - (offsets.w * ((IsAnalogue() && res_multi) ? res_multi : 1))) / (VideoModeDividers[videomode].y + 1);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
GSVector2i GSState::GetResolution()
|
||||
{
|
||||
const int videomode = static_cast<int>(GetVideoMode()) - 1;
|
||||
const bool ignore_offset = !GSConfig.PCRTCOffsets;
|
||||
|
||||
const GSVector4i offsets = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode] : VideoModeOffsetsOverscan[videomode];
|
||||
|
||||
GSVector2i resolution(offsets.x, offsets.y);
|
||||
|
||||
// The resolution of the framebuffer is double when in FRAME mode and interlaced.
|
||||
// Also we need a special check because no-interlace patches like to render in the original height, but in non-interlaced mode
|
||||
// which means it would normally go off the bottom of the screen. Advantages of emulation, i guess... Limited to Ignore Offsets + Deinterlacing = Off.
|
||||
if ((isinterlaced() && !m_regs->SMODE2.FFMD) || (GSConfig.InterlaceMode == GSInterlaceMode::Off && !GSConfig.PCRTCOffsets && !isinterlaced()))
|
||||
resolution.y *= 2;
|
||||
|
||||
if (ignore_offset)
|
||||
{
|
||||
// Ideally we'd just cut the width at the resolution, but of course we have to hack the hack...
|
||||
// Some games (Mortal Kombat Armageddon) render the image at 834 pixels then shrink it to 624 pixels
|
||||
// which does fit, but when we ignore offsets we go on framebuffer size and some other games
|
||||
// such as Johnny Mosleys Mad Trix and Transformers render too much but design it to go off the screen.
|
||||
int magnified_width = (VideoModeDividers[videomode].z + 1) / GetDisplayHMagnification();
|
||||
|
||||
// When viewing overscan allow up to the overscan size
|
||||
if (GSConfig.PCRTCOverscan)
|
||||
magnified_width = std::max(magnified_width, offsets.x);
|
||||
|
||||
GSVector4i total_rect = GetDisplayRect(0).runion(GetDisplayRect(1));
|
||||
total_rect.z = total_rect.z - total_rect.x;
|
||||
total_rect.w = total_rect.w - total_rect.y;
|
||||
total_rect.z = std::min(total_rect.z, magnified_width);
|
||||
total_rect.w = std::min(total_rect.w, resolution.y);
|
||||
resolution.x = total_rect.z;
|
||||
resolution.y = total_rect.w;
|
||||
}
|
||||
|
||||
return resolution;
|
||||
}
|
||||
|
||||
GSVector4i GSState::GetFrameRect(int i, bool ignore_off)
|
||||
{
|
||||
// If no specific context is requested then pass the merged rectangle as return value
|
||||
if (i == -1)
|
||||
return GetFrameRect(0, ignore_off).runion(GetFrameRect(1, ignore_off));
|
||||
|
||||
GSVector4i rectangle = { 0, 0, 0, 0 };
|
||||
|
||||
if (!IsEnabled(i))
|
||||
return rectangle;
|
||||
|
||||
const auto& DISP = m_regs->DISP[i].DISPLAY;
|
||||
|
||||
const u32 DW = DISP.DW + 1;
|
||||
const u32 DH = DISP.DH + 1;
|
||||
const GSVector2i magnification(DISP.MAGH+1, DISP.MAGV + 1);
|
||||
|
||||
const u32 DBX = m_regs->DISP[i].DISPFB.DBX;
|
||||
int DBY = m_regs->DISP[i].DISPFB.DBY;
|
||||
|
||||
|
||||
const int w = DW / magnification.x;
|
||||
const int h = DH / magnification.y;
|
||||
|
||||
// If the combined height overflows 2048, it's likely adding a bit of extra data before the picture for offsetting the interlace
|
||||
// only game known to do this is NASCAR '08
|
||||
if (!ignore_off && (DBY + h) >= 2048)
|
||||
DBY = DBY - 2048;
|
||||
|
||||
rectangle.left = (ignore_off) ? 0 : DBX;
|
||||
rectangle.top = (ignore_off) ? 0 : DBY;
|
||||
|
||||
rectangle.right = rectangle.left + w;
|
||||
rectangle.bottom = rectangle.top + h;
|
||||
|
||||
if (isinterlaced() && m_regs->SMODE2.FFMD && h > 1)
|
||||
{
|
||||
rectangle.bottom += 1;
|
||||
rectangle.bottom >>= 1;
|
||||
}
|
||||
|
||||
return rectangle;
|
||||
}
|
||||
|
||||
int GSState::GetFramebufferHeight()
|
||||
{
|
||||
// Framebuffer height is 11 bits max
|
||||
constexpr int height_limit = (1 << 11);
|
||||
|
||||
const GSVector4i disp1_rect = GetFrameRect(0, true);
|
||||
const GSVector4i disp2_rect = GetFrameRect(1, true);
|
||||
const GSVector4i combined = disp1_rect.runion(disp2_rect);
|
||||
|
||||
// DBY isn't an offset to the frame memory but rather an offset to read output circuit inside
|
||||
// the frame memory, hence the top offset should also be calculated for the total height of the
|
||||
// frame memory. Also we need to wrap the value only when we're dealing with values with range of the
|
||||
// frame memory (offset + read output circuit height, IOW bottom of merged_output)
|
||||
const int max_height = std::max(disp1_rect.height(), disp2_rect.height());
|
||||
const int frame_memory_height = std::max(max_height, combined.bottom % height_limit);
|
||||
|
||||
if (frame_memory_height > 1024)
|
||||
GL_PERF("Massive framebuffer height detected! (height:%d)", frame_memory_height);
|
||||
|
||||
return frame_memory_height;
|
||||
}
|
||||
|
||||
int GSState::GetFramebufferBitDepth()
|
||||
{
|
||||
if (IsEnabled(0))
|
||||
return GSLocalMemory::m_psm[m_regs->DISP[0].DISPFB.PSM].bpp;
|
||||
else if (IsEnabled(1))
|
||||
return GSLocalMemory::m_psm[m_regs->DISP[1].DISPFB.PSM].bpp;
|
||||
|
||||
return 32;
|
||||
}
|
||||
|
||||
int GSState::GetFramebufferWidth()
|
||||
{
|
||||
const GSVector4i disp1_rect = GetFrameRect(0, true);
|
||||
const GSVector4i disp2_rect = GetFrameRect(1, true);
|
||||
|
||||
const int max_width = std::max(disp1_rect.width(), disp2_rect.width());
|
||||
|
||||
return max_width;
|
||||
}
|
||||
|
||||
bool GSState::IsEnabled(int i)
|
||||
{
|
||||
ASSERT(i >= 0 && i < 2);
|
||||
|
||||
const auto& DISP = m_regs->DISP[i].DISPLAY;
|
||||
|
||||
const bool disp1_enabled = m_regs->PMODE.EN1;
|
||||
const bool disp2_enabled = m_regs->PMODE.EN2;
|
||||
|
||||
if ((i == 0 && disp1_enabled) || (i == 1 && disp2_enabled))
|
||||
return DISP.DW && DISP.DH;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
float GSState::GetTvRefreshRate()
|
||||
{
|
||||
const GSVideoMode videomode = GetVideoMode();
|
||||
@@ -2580,18 +2329,19 @@ int GSState::Freeze(freezeData* fd, bool sizeonly)
|
||||
{
|
||||
if (sizeonly)
|
||||
{
|
||||
fd->size = m_sssize;
|
||||
fd->size = GetSaveStateSize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!fd->data || fd->size < m_sssize)
|
||||
if (!fd->data || fd->size < GetSaveStateSize())
|
||||
return -1;
|
||||
|
||||
Flush(GSFlushReason::SAVESTATE);
|
||||
|
||||
u8* data = fd->data;
|
||||
const u32 version = STATE_VERSION;
|
||||
|
||||
WriteState(data, &m_version);
|
||||
WriteState(data, &version);
|
||||
WriteState(data, &m_env.PRIM);
|
||||
WriteState(data, &m_env.PRMODECONT);
|
||||
WriteState(data, &m_env.TEXCLUT);
|
||||
@@ -2659,7 +2409,7 @@ int GSState::Defrost(const freezeData* fd)
|
||||
if (!fd || !fd->data || fd->size == 0)
|
||||
return -1;
|
||||
|
||||
if (fd->size < m_sssize)
|
||||
if (fd->size < GetSaveStateSize())
|
||||
return -1;
|
||||
|
||||
u8* data = fd->data;
|
||||
@@ -2668,7 +2418,7 @@ int GSState::Defrost(const freezeData* fd)
|
||||
|
||||
ReadState(&version, data);
|
||||
|
||||
if (version > m_version)
|
||||
if (version > STATE_VERSION)
|
||||
{
|
||||
Console.Error("GS: Savestate version is incompatible. Load aborted.");
|
||||
return -1;
|
||||
@@ -2775,6 +2525,8 @@ int GSState::Defrost(const freezeData* fd)
|
||||
|
||||
g_perfmon.SetFrame(5000);
|
||||
|
||||
ResetPCRTC();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4098,9 +3850,6 @@ GIFRegTEX0 GSState::GetTex0Layer(u32 lod)
|
||||
|
||||
GSState::GSTransferBuffer::GSTransferBuffer()
|
||||
{
|
||||
x = y = 0;
|
||||
start = end = total = 0;
|
||||
|
||||
constexpr size_t alloc_size = 1024 * 1024 * 4;
|
||||
buff = reinterpret_cast<u8*>(_aligned_malloc(alloc_size, 32));
|
||||
}
|
||||
|
||||
@@ -35,13 +35,15 @@ public:
|
||||
GSState();
|
||||
virtual ~GSState();
|
||||
|
||||
static constexpr int GetSaveStateSize();
|
||||
|
||||
private:
|
||||
// RESTRICT prevents multiple loads of the same part of the register when accessing its bitfields (the compiler is happy to know that memory writes in-between will not go there)
|
||||
|
||||
typedef void (GSState::*GIFPackedRegHandler)(const GIFPackedReg* RESTRICT r);
|
||||
|
||||
GIFPackedRegHandler m_fpGIFPackedRegHandlers[16];
|
||||
GIFPackedRegHandler m_fpGIFPackedRegHandlerXYZ[8][4];
|
||||
GIFPackedRegHandler m_fpGIFPackedRegHandlers[16] = {};
|
||||
GIFPackedRegHandler m_fpGIFPackedRegHandlerXYZ[8][4] = {};
|
||||
|
||||
void CheckFlushes();
|
||||
|
||||
@@ -58,14 +60,14 @@ private:
|
||||
|
||||
typedef void (GSState::*GIFRegHandler)(const GIFReg* RESTRICT r);
|
||||
|
||||
GIFRegHandler m_fpGIFRegHandlers[256];
|
||||
GIFRegHandler m_fpGIFRegHandlerXYZ[8][4];
|
||||
GIFRegHandler m_fpGIFRegHandlers[256] = {};
|
||||
GIFRegHandler m_fpGIFRegHandlerXYZ[8][4] = {};
|
||||
|
||||
typedef void (GSState::*GIFPackedRegHandlerC)(const GIFPackedReg* RESTRICT r, u32 size);
|
||||
|
||||
GIFPackedRegHandlerC m_fpGIFPackedRegHandlersC[2];
|
||||
GIFPackedRegHandlerC m_fpGIFPackedRegHandlerSTQRGBAXYZF2[8];
|
||||
GIFPackedRegHandlerC m_fpGIFPackedRegHandlerSTQRGBAXYZ2[8];
|
||||
GIFPackedRegHandlerC m_fpGIFPackedRegHandlersC[2] = {};
|
||||
GIFPackedRegHandlerC m_fpGIFPackedRegHandlerSTQRGBAXYZF2[8] = {};
|
||||
GIFPackedRegHandlerC m_fpGIFPackedRegHandlerSTQRGBAXYZ2[8] = {};
|
||||
|
||||
template<u32 prim, bool auto_flush, bool index_swap> void GIFPackedRegHandlerSTQRGBAXYZF2(const GIFPackedReg* RESTRICT r, u32 size);
|
||||
template<u32 prim, bool auto_flush, bool index_swap> void GIFPackedRegHandlerSTQRGBAXYZ2(const GIFPackedReg* RESTRICT r, u32 size);
|
||||
@@ -117,15 +119,12 @@ private:
|
||||
template<bool auto_flush, bool index_swap>
|
||||
void SetPrimHandlers();
|
||||
|
||||
u32 m_version;
|
||||
int m_sssize;
|
||||
|
||||
struct GSTransferBuffer
|
||||
{
|
||||
int x, y;
|
||||
int start, end, total;
|
||||
u8* buff;
|
||||
GIFRegBITBLTBUF m_blit;
|
||||
int x = 0, y = 0;
|
||||
int start = 0, end = 0, total = 0;
|
||||
u8* buff = nullptr;
|
||||
GIFRegBITBLTBUF m_blit = {};
|
||||
|
||||
GSTransferBuffer();
|
||||
virtual ~GSTransferBuffer();
|
||||
@@ -139,14 +138,14 @@ private:
|
||||
void CalcAlphaMinMax();
|
||||
|
||||
protected:
|
||||
GSVertex m_v;
|
||||
float m_q;
|
||||
GSVector4i m_scissor;
|
||||
GSVector4i m_ofxy;
|
||||
GSVertex m_v = {};
|
||||
float m_q = 1.0f;
|
||||
GSVector4i m_scissor = {};
|
||||
GSVector4i m_ofxy = {};
|
||||
|
||||
u8 m_scanmask_used;
|
||||
bool tex_flushed;
|
||||
bool m_isPackedUV_HackFlag;
|
||||
u8 m_scanmask_used = 0;
|
||||
bool tex_flushed = true;
|
||||
bool m_isPackedUV_HackFlag = false;
|
||||
|
||||
struct
|
||||
{
|
||||
@@ -154,13 +153,13 @@ protected:
|
||||
u32 head, tail, next, maxcount; // head: first vertex, tail: last vertex + 1, next: last indexed + 1
|
||||
u32 xy_tail;
|
||||
u64 xy[4];
|
||||
} m_vertex;
|
||||
} m_vertex = {};
|
||||
|
||||
struct
|
||||
{
|
||||
u32* buff;
|
||||
u32 tail;
|
||||
} m_index;
|
||||
} m_index = {};
|
||||
|
||||
void UpdateContext();
|
||||
void UpdateScissor();
|
||||
@@ -213,24 +212,24 @@ public:
|
||||
int draw;
|
||||
};
|
||||
|
||||
GIFPath m_path[4];
|
||||
GIFRegPRIM* PRIM;
|
||||
GSPrivRegSet* m_regs;
|
||||
GIFPath m_path[4] = {};
|
||||
GIFRegPRIM* PRIM = nullptr;
|
||||
GSPrivRegSet* m_regs = nullptr;
|
||||
GSLocalMemory m_mem;
|
||||
GSDrawingEnvironment m_env;
|
||||
GSDrawingEnvironment m_backup_env;
|
||||
GSDrawingEnvironment m_prev_env;
|
||||
GSVector4i temp_draw_rect;
|
||||
GSDrawingContext* m_context;
|
||||
u32 m_crc;
|
||||
CRC::Game m_game;
|
||||
GSDrawingEnvironment m_env = {};
|
||||
GSDrawingEnvironment m_backup_env = {};
|
||||
GSDrawingEnvironment m_prev_env = {};
|
||||
GSVector4i temp_draw_rect = {};
|
||||
GSDrawingContext* m_context = nullptr;
|
||||
u32 m_crc = 0;
|
||||
CRC::Game m_game = {};
|
||||
std::unique_ptr<GSDumpBase> m_dump;
|
||||
bool m_nativeres;
|
||||
bool m_mipmap;
|
||||
u32 m_dirty_gs_regs;
|
||||
int m_backed_up_ctx;
|
||||
bool m_nativeres = false;
|
||||
bool m_mipmap = false;
|
||||
u8 m_force_preload = 0;
|
||||
u32 m_dirty_gs_regs = 0;
|
||||
int m_backed_up_ctx = 0;
|
||||
std::vector<GSUploadQueue> m_draw_transfers;
|
||||
bool m_force_preload;
|
||||
|
||||
static int s_n;
|
||||
|
||||
@@ -278,7 +277,7 @@ public:
|
||||
GSREOPEN = 1 << 13,
|
||||
};
|
||||
|
||||
GSFlushReason m_state_flush_reason;
|
||||
GSFlushReason m_state_flush_reason = UNKNOWN;
|
||||
|
||||
enum PRIM_OVERLAP
|
||||
{
|
||||
@@ -287,62 +286,511 @@ public:
|
||||
PRIM_OVERLAP_NO
|
||||
};
|
||||
|
||||
PRIM_OVERLAP m_prim_overlap;
|
||||
PRIM_OVERLAP m_prim_overlap = PRIM_OVERLAP_UNKNOW;
|
||||
std::vector<size_t> m_drawlist;
|
||||
|
||||
// The horizontal offset values (under z) for PAL and NTSC have been tweaked
|
||||
// they should be apparently 632 and 652 respectively, but that causes a thick black line on the left
|
||||
// these values leave a small black line on the right in a bunch of games, but it's not so bad.
|
||||
// The only conclusion I can come to is there is horizontal overscan expected so there would normally
|
||||
// be black borders either side anyway, or both sides slightly covered.
|
||||
const GSVector4i VideoModeOffsets[6] = {
|
||||
GSVector4i(640, 224, 642, 25),
|
||||
GSVector4i(640, 256, 676, 36),
|
||||
GSVector4i(640, 480, 276, 34),
|
||||
GSVector4i(720, 480, 232, 35),
|
||||
GSVector4i(1280, 720, 302, 24),
|
||||
GSVector4i(1920, 540, 238, 40)
|
||||
};
|
||||
struct GSPCRTCRegs
|
||||
{
|
||||
// The horizontal offset values (under z) for PAL and NTSC have been tweaked
|
||||
// they should be apparently 632 and 652 respectively, but that causes a thick black line on the left
|
||||
// these values leave a small black line on the right in a bunch of games, but it's not so bad.
|
||||
// The only conclusion I can come to is there is horizontal overscan expected so there would normally
|
||||
// be black borders either side anyway, or both sides slightly covered.
|
||||
static inline constexpr GSVector4i VideoModeOffsets[6] = {
|
||||
GSVector4i::cxpr(640, 224, 642, 25),
|
||||
GSVector4i::cxpr(640, 256, 676, 36),
|
||||
GSVector4i::cxpr(640, 480, 276, 34),
|
||||
GSVector4i::cxpr(720, 480, 232, 35),
|
||||
GSVector4i::cxpr(1280, 720, 302, 24),
|
||||
GSVector4i::cxpr(1920, 540, 238, 40)
|
||||
};
|
||||
|
||||
const GSVector4i VideoModeOffsetsOverscan[6] = {
|
||||
GSVector4i(711, 243, 498, 12),
|
||||
GSVector4i(702, 288, 532, 18),
|
||||
GSVector4i(640, 480, 276, 34),
|
||||
GSVector4i(720, 480, 232, 35),
|
||||
GSVector4i(1280, 720, 302, 24),
|
||||
GSVector4i(1920, 540, 238, 40)
|
||||
};
|
||||
static inline constexpr GSVector4i VideoModeOffsetsOverscan[6] = {
|
||||
GSVector4i::cxpr(711, 243, 498, 12),
|
||||
GSVector4i::cxpr(702, 288, 532, 18),
|
||||
GSVector4i::cxpr(640, 480, 276, 34),
|
||||
GSVector4i::cxpr(720, 480, 232, 35),
|
||||
GSVector4i::cxpr(1280, 720, 302, 24),
|
||||
GSVector4i::cxpr(1920, 540, 238, 40)
|
||||
};
|
||||
|
||||
const GSVector4i VideoModeDividers[6] = {
|
||||
GSVector4i(3, 0, 2559, 239),
|
||||
GSVector4i(3, 0, 2559, 287),
|
||||
GSVector4i(1, 0, 1279, 479),
|
||||
GSVector4i(1, 0, 1439, 479),
|
||||
GSVector4i(0, 0, 1279, 719),
|
||||
GSVector4i(0, 0, 1919, 1079)
|
||||
};
|
||||
static inline constexpr GSVector4i VideoModeDividers[6] = {
|
||||
GSVector4i::cxpr(3, 0, 2559, 239),
|
||||
GSVector4i::cxpr(3, 0, 2559, 287),
|
||||
GSVector4i::cxpr(1, 0, 1279, 479),
|
||||
GSVector4i::cxpr(1, 0, 1439, 479),
|
||||
GSVector4i::cxpr(0, 0, 1279, 719),
|
||||
GSVector4i::cxpr(0, 0, 1919, 1079)
|
||||
};
|
||||
|
||||
struct PCRTCDisplay
|
||||
{
|
||||
bool enabled;
|
||||
int FBP;
|
||||
int FBW;
|
||||
int PSM;
|
||||
GSRegDISPFB prevFramebufferReg;
|
||||
GSVector2i prevDisplayOffset;
|
||||
GSVector2i displayOffset;
|
||||
GSVector4i displayRect;
|
||||
GSVector2i magnification;
|
||||
GSVector2i prevFramebufferOffsets;
|
||||
GSVector2i framebufferOffsets;
|
||||
GSVector4i framebufferRect;
|
||||
|
||||
int Block()
|
||||
{
|
||||
return FBP << 5;
|
||||
}
|
||||
};
|
||||
|
||||
int videomode = 0;
|
||||
int interlaced = 0;
|
||||
int FFMD = 0;
|
||||
bool PCRTCSameSrc = false;
|
||||
bool toggling_field = false;
|
||||
PCRTCDisplay PCRTCDisplays[2] = {};
|
||||
|
||||
bool IsAnalogue()
|
||||
{
|
||||
GSVideoMode video = static_cast<GSVideoMode>(videomode + 1);
|
||||
return video == GSVideoMode::NTSC || video == GSVideoMode::PAL || video == GSVideoMode::HDTV_1080I;
|
||||
}
|
||||
|
||||
// Calculates which display is closest to matching zero offsets in either direction.
|
||||
GSVector2i NearestToZeroOffset()
|
||||
{
|
||||
GSVector2i returnValue = { 1, 1 };
|
||||
|
||||
if (!PCRTCDisplays[0].enabled && !PCRTCDisplays[1].enabled)
|
||||
return returnValue;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (!PCRTCDisplays[i].enabled)
|
||||
{
|
||||
returnValue.x = 1 - i;
|
||||
returnValue.y = 1 - i;
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (abs(PCRTCDisplays[0].displayOffset.x - VideoModeOffsets[videomode].z) <
|
||||
abs(PCRTCDisplays[1].displayOffset.x - VideoModeOffsets[videomode].z))
|
||||
returnValue.x = 0;
|
||||
|
||||
// When interlaced, the vertical base offset is doubled
|
||||
int verticalOffset = VideoModeOffsets[videomode].w * (1 << interlaced);
|
||||
|
||||
if (abs(PCRTCDisplays[0].displayOffset.y - verticalOffset) <
|
||||
abs(PCRTCDisplays[1].displayOffset.y - verticalOffset))
|
||||
returnValue.y = 0;
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void SetVideoMode(GSVideoMode videoModeIn)
|
||||
{
|
||||
videomode = static_cast<int>(videoModeIn) - 1;
|
||||
}
|
||||
|
||||
// Enable each of the displays.
|
||||
void EnableDisplays(GSRegPMODE pmode, GSRegSMODE2 smode2, bool smodetoggle)
|
||||
{
|
||||
PCRTCDisplays[0].enabled = pmode.EN1;
|
||||
PCRTCDisplays[1].enabled = pmode.EN2;
|
||||
|
||||
interlaced = smode2.INT && IsAnalogue();
|
||||
FFMD = smode2.FFMD;
|
||||
toggling_field = smodetoggle && IsAnalogue();
|
||||
}
|
||||
|
||||
void CheckSameSource()
|
||||
{
|
||||
if (PCRTCDisplays[0].enabled != PCRTCDisplays[1].enabled || (PCRTCDisplays[0].enabled | PCRTCDisplays[1].enabled) == false)
|
||||
{
|
||||
PCRTCSameSrc = false;
|
||||
return;
|
||||
}
|
||||
|
||||
PCRTCSameSrc = PCRTCDisplays[0].FBP == PCRTCDisplays[1].FBP &&
|
||||
PCRTCDisplays[0].FBW == PCRTCDisplays[1].FBW &&
|
||||
GSUtil::HasCompatibleBits(PCRTCDisplays[0].PSM, PCRTCDisplays[1].PSM);
|
||||
}
|
||||
|
||||
bool FrameWrap()
|
||||
{
|
||||
GSVector4i combined_rect = GSVector4i(PCRTCDisplays[0].framebufferRect.runion(PCRTCDisplays[1].framebufferRect));
|
||||
return combined_rect.w >= 2048 || combined_rect.z >= 2048;
|
||||
}
|
||||
|
||||
// If the start point of both frames match, we can do a single read
|
||||
bool FrameRectMatch()
|
||||
{
|
||||
return PCRTCSameSrc;
|
||||
}
|
||||
|
||||
GSVector2i GetResolution()
|
||||
{
|
||||
GSVector2i resolution;
|
||||
|
||||
const GSVector4i offsets = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode] : VideoModeOffsetsOverscan[videomode];
|
||||
const bool is_full_height = interlaced || (toggling_field && GSConfig.InterlaceMode != GSInterlaceMode::Off) || GSConfig.InterlaceMode == GSInterlaceMode::Off;
|
||||
|
||||
if (!GSConfig.PCRTCOffsets)
|
||||
{
|
||||
if (PCRTCDisplays[0].enabled && PCRTCDisplays[1].enabled)
|
||||
{
|
||||
GSVector4i combined_size = PCRTCDisplays[0].displayRect.runion(PCRTCDisplays[1].displayRect);
|
||||
resolution = { combined_size.width(), combined_size.height() };
|
||||
}
|
||||
else if (PCRTCDisplays[0].enabled)
|
||||
{
|
||||
resolution = { PCRTCDisplays[0].displayRect.width(), PCRTCDisplays[0].displayRect.height() };
|
||||
}
|
||||
else
|
||||
{
|
||||
resolution = { PCRTCDisplays[1].displayRect.width(), PCRTCDisplays[1].displayRect.height() };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const int shift = is_full_height ? 1 : 0;
|
||||
resolution = { offsets.x, offsets.y << shift };
|
||||
}
|
||||
|
||||
resolution.x = std::min(resolution.x, offsets.x);
|
||||
resolution.y = std::min(resolution.y, is_full_height ? offsets.y << 1 : offsets.y);
|
||||
|
||||
return resolution;
|
||||
}
|
||||
|
||||
GSVector4i GetFramebufferRect(int display)
|
||||
{
|
||||
if (display == -1)
|
||||
{
|
||||
return GSVector4i(PCRTCDisplays[0].framebufferRect.runion(PCRTCDisplays[1].framebufferRect));
|
||||
}
|
||||
else
|
||||
{
|
||||
return PCRTCDisplays[display].framebufferRect;
|
||||
}
|
||||
}
|
||||
|
||||
int GetFramebufferBitDepth()
|
||||
{
|
||||
if (PCRTCDisplays[0].enabled)
|
||||
return GSLocalMemory::m_psm[PCRTCDisplays[0].PSM].bpp;
|
||||
else if (PCRTCDisplays[1].enabled)
|
||||
return GSLocalMemory::m_psm[PCRTCDisplays[1].PSM].bpp;
|
||||
|
||||
return 32;
|
||||
}
|
||||
|
||||
GSVector2i GetFramebufferSize(int display)
|
||||
{
|
||||
if (display == -1)
|
||||
{
|
||||
GSVector4i combined_rect = PCRTCDisplays[0].framebufferRect.runion(PCRTCDisplays[1].framebufferRect);
|
||||
|
||||
if (combined_rect.z >= 2048)
|
||||
{
|
||||
int high_x = (PCRTCDisplays[0].framebufferRect.x > PCRTCDisplays[1].framebufferRect.x) ? PCRTCDisplays[0].framebufferRect.x : PCRTCDisplays[1].framebufferRect.x;
|
||||
combined_rect.z -= GSConfig.UseHardwareRenderer() ? 2048 : high_x;
|
||||
combined_rect.x = 0;
|
||||
}
|
||||
|
||||
if (combined_rect.w >= 2048)
|
||||
{
|
||||
int high_y = (PCRTCDisplays[0].framebufferRect.y > PCRTCDisplays[1].framebufferRect.y) ? PCRTCDisplays[0].framebufferRect.y : PCRTCDisplays[1].framebufferRect.y;
|
||||
combined_rect.w -= GSConfig.UseHardwareRenderer() ? 2048 : high_y;
|
||||
combined_rect.y = 0;
|
||||
}
|
||||
return GSVector2i(combined_rect.z, combined_rect.w);
|
||||
}
|
||||
else
|
||||
{
|
||||
GSVector4i out_rect = PCRTCDisplays[display].framebufferRect;
|
||||
|
||||
if (out_rect.z >= 2048)
|
||||
{
|
||||
out_rect.z -= GSConfig.UseHardwareRenderer() ? 2048 : out_rect.x;
|
||||
out_rect.x = 0;
|
||||
}
|
||||
|
||||
if (out_rect.w >= 2048)
|
||||
{
|
||||
out_rect.w -= GSConfig.UseHardwareRenderer() ? 2048 : out_rect.y;
|
||||
out_rect.y = 0;
|
||||
}
|
||||
return GSVector2i(out_rect.z, out_rect.w);
|
||||
}
|
||||
}
|
||||
|
||||
// Sets up the rectangles for both the framebuffer read and the displays for the merge circuit.
|
||||
void SetRects(int display, GSRegDISPLAY displayReg, GSRegDISPFB framebufferReg)
|
||||
{
|
||||
// Save framebuffer information first, while we're here.
|
||||
PCRTCDisplays[display].FBP = framebufferReg.FBP;
|
||||
PCRTCDisplays[display].FBW = framebufferReg.FBW;
|
||||
PCRTCDisplays[display].PSM = framebufferReg.PSM;
|
||||
PCRTCDisplays[display].prevFramebufferReg = framebufferReg;
|
||||
// Probably not really enabled but will cause a mess.
|
||||
// Q-Ball Billiards enables both circuits but doesn't set one of them up.
|
||||
if (PCRTCDisplays[display].FBW == 0 && displayReg.DW == 0 && displayReg.DH == 0 && displayReg.MAGH == 0)
|
||||
{
|
||||
PCRTCDisplays[display].enabled = false;
|
||||
return;
|
||||
}
|
||||
PCRTCDisplays[display].magnification = GSVector2i(displayReg.MAGH + 1, displayReg.MAGV + 1);
|
||||
const u32 DW = displayReg.DW + 1;
|
||||
const u32 DH = displayReg.DH + 1;
|
||||
|
||||
const int renderWidth = DW / PCRTCDisplays[display].magnification.x;
|
||||
const int renderHeight = DH / PCRTCDisplays[display].magnification.y;
|
||||
|
||||
u32 finalDisplayWidth = renderWidth;
|
||||
u32 finalDisplayHeight = renderHeight;
|
||||
// When using screen offsets the screen gets squashed/resized in to the actual screen size.
|
||||
if (GSConfig.PCRTCOffsets)
|
||||
{
|
||||
finalDisplayWidth = DW / (VideoModeDividers[videomode].x + 1);
|
||||
finalDisplayHeight = DH / (VideoModeDividers[videomode].y + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
finalDisplayWidth = std::min(finalDisplayWidth ,DW / (VideoModeDividers[videomode].x + 1));
|
||||
finalDisplayHeight = std::min(finalDisplayHeight, DH / (VideoModeDividers[videomode].y + 1));
|
||||
}
|
||||
|
||||
// Framebuffer size and offsets.
|
||||
PCRTCDisplays[display].prevFramebufferOffsets = PCRTCDisplays[display].framebufferOffsets;
|
||||
PCRTCDisplays[display].framebufferRect.x = 0;
|
||||
PCRTCDisplays[display].framebufferRect.y = 0;
|
||||
PCRTCDisplays[display].framebufferRect.z = renderWidth;
|
||||
|
||||
if(FFMD && interlaced) // Round up the height as if it's an odd value, this will cause havok with the merge circuit.
|
||||
PCRTCDisplays[display].framebufferRect.w = (renderHeight + 1) >> (FFMD * interlaced); // Half height read if FFMD + INT enabled.
|
||||
else
|
||||
PCRTCDisplays[display].framebufferRect.w = renderHeight;
|
||||
PCRTCDisplays[display].framebufferOffsets.x = framebufferReg.DBX;
|
||||
PCRTCDisplays[display].framebufferOffsets.y = framebufferReg.DBY;
|
||||
|
||||
const bool is_interlaced_resolution = interlaced || (toggling_field && GSConfig.InterlaceMode != GSInterlaceMode::Off);
|
||||
|
||||
// If the interlace flag isn't set, but it's still interlacing, the height is likely reported wrong.
|
||||
// Q-Ball Billiards.
|
||||
if (is_interlaced_resolution && !interlaced)
|
||||
finalDisplayHeight *= 2;
|
||||
|
||||
// Display size and offsets.
|
||||
PCRTCDisplays[display].displayRect.x = 0;
|
||||
PCRTCDisplays[display].displayRect.y = 0;
|
||||
PCRTCDisplays[display].displayRect.z = finalDisplayWidth;
|
||||
PCRTCDisplays[display].displayRect.w = finalDisplayHeight;
|
||||
PCRTCDisplays[display].prevDisplayOffset = PCRTCDisplays[display].displayOffset;
|
||||
PCRTCDisplays[display].displayOffset.x = displayReg.DX;
|
||||
PCRTCDisplays[display].displayOffset.y = displayReg.DY;
|
||||
}
|
||||
|
||||
// Calculate framebuffer read offsets, should be considered if only one circuit is enabled, or difference is more than 1 line.
|
||||
// Only considered if "Anti-blur" is enabled.
|
||||
void CalculateFramebufferOffset()
|
||||
{
|
||||
if (GSConfig.PCRTCAntiBlur && PCRTCDisplays[0].enabled && PCRTCSameSrc)
|
||||
{
|
||||
if (abs(PCRTCDisplays[1].framebufferOffsets.y - PCRTCDisplays[0].framebufferOffsets.y) == 1
|
||||
&& PCRTCDisplays[0].displayOffset.y == PCRTCDisplays[1].displayOffset.y)
|
||||
{
|
||||
if (PCRTCDisplays[1].framebufferOffsets.y < PCRTCDisplays[0].framebufferOffsets.y)
|
||||
PCRTCDisplays[0].framebufferOffsets.y = PCRTCDisplays[1].framebufferOffsets.y;
|
||||
else
|
||||
PCRTCDisplays[1].framebufferOffsets.y = PCRTCDisplays[0].framebufferOffsets.y;
|
||||
}
|
||||
}
|
||||
PCRTCDisplays[0].framebufferRect.x += PCRTCDisplays[0].framebufferOffsets.x;
|
||||
PCRTCDisplays[0].framebufferRect.z += PCRTCDisplays[0].framebufferOffsets.x;
|
||||
PCRTCDisplays[0].framebufferRect.y += PCRTCDisplays[0].framebufferOffsets.y;
|
||||
PCRTCDisplays[0].framebufferRect.w += PCRTCDisplays[0].framebufferOffsets.y;
|
||||
|
||||
PCRTCDisplays[1].framebufferRect.x += PCRTCDisplays[1].framebufferOffsets.x;
|
||||
PCRTCDisplays[1].framebufferRect.z += PCRTCDisplays[1].framebufferOffsets.x;
|
||||
PCRTCDisplays[1].framebufferRect.y += PCRTCDisplays[1].framebufferOffsets.y;
|
||||
PCRTCDisplays[1].framebufferRect.w += PCRTCDisplays[1].framebufferOffsets.y;
|
||||
}
|
||||
|
||||
// Used in software mode to align the buffer when reading. Offset is accounted for (block aligned) by GetOutput.
|
||||
void RemoveFramebufferOffset(int display)
|
||||
{
|
||||
if (display >= 0)
|
||||
{
|
||||
// Hardware needs nothing but handling for wrapped framebuffers.
|
||||
if (GSConfig.UseHardwareRenderer())
|
||||
{
|
||||
if (PCRTCDisplays[display].framebufferRect.z >= 2048)
|
||||
{
|
||||
PCRTCDisplays[display].framebufferRect.x = 0;
|
||||
PCRTCDisplays[display].framebufferRect.z -= 2048;
|
||||
}
|
||||
if (PCRTCDisplays[display].framebufferRect.w >= 2048)
|
||||
{
|
||||
PCRTCDisplays[display].framebufferRect.y = 0;
|
||||
PCRTCDisplays[display].framebufferRect.w -= 2048;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[PCRTCDisplays[display].PSM];
|
||||
|
||||
GSVector4i r = PCRTCDisplays[display].framebufferRect;
|
||||
r = r.ralign<Align_Outside>(psm.bs);
|
||||
|
||||
PCRTCDisplays[display].framebufferRect.z -= r.x;
|
||||
PCRTCDisplays[display].framebufferRect.w -= r.y;
|
||||
PCRTCDisplays[display].framebufferRect.x -= r.x;
|
||||
PCRTCDisplays[display].framebufferRect.y -= r.y;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This code is to read the framebuffer nicely block aligned in software, then leave the remaining offset in to the block.
|
||||
// In hardware mode this doesn't happen, it reads the whole framebuffer, so we need to keep the offset.
|
||||
if (!GSConfig.UseHardwareRenderer())
|
||||
{
|
||||
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[PCRTCDisplays[1].PSM];
|
||||
|
||||
GSVector4i r = PCRTCDisplays[0].framebufferRect.runion(PCRTCDisplays[1].framebufferRect);
|
||||
r = r.ralign<Align_Outside>(psm.bs);
|
||||
|
||||
PCRTCDisplays[0].framebufferRect.x -= r.x;
|
||||
PCRTCDisplays[0].framebufferRect.y -= r.y;
|
||||
PCRTCDisplays[0].framebufferRect.z -= r.x;
|
||||
PCRTCDisplays[0].framebufferRect.w -= r.y;
|
||||
PCRTCDisplays[1].framebufferRect.x -= r.x;
|
||||
PCRTCDisplays[1].framebufferRect.y -= r.y;
|
||||
PCRTCDisplays[1].framebufferRect.z -= r.x;
|
||||
PCRTCDisplays[1].framebufferRect.w -= r.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the two displays are offset from each other, move them to the correct offsets.
|
||||
// If using screen offsets, calculate the positions here.
|
||||
void CalculateDisplayOffset(bool scanmask)
|
||||
{
|
||||
// Offsets are generally ignored, the "hacky" way of doing the displays, but direct to framebuffers.
|
||||
if (!GSConfig.PCRTCOffsets)
|
||||
{
|
||||
const GSVector4i offsets = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode] : VideoModeOffsetsOverscan[videomode];
|
||||
int int_off[2] = { 0, 0 };
|
||||
GSVector2i zeroDisplay = NearestToZeroOffset();
|
||||
GSVector2i baseOffset = PCRTCDisplays[zeroDisplay.y].displayOffset;
|
||||
|
||||
int blurOffset = abs(PCRTCDisplays[1].displayOffset.y - PCRTCDisplays[0].displayOffset.y);
|
||||
if (GSConfig.PCRTCAntiBlur && !scanmask && blurOffset < 4)
|
||||
{
|
||||
if (PCRTCDisplays[1].displayOffset.y > PCRTCDisplays[0].displayOffset.y)
|
||||
PCRTCDisplays[1].displayOffset.y -= blurOffset;
|
||||
else
|
||||
PCRTCDisplays[0].displayOffset.y -= blurOffset;
|
||||
}
|
||||
|
||||
// If there's a single pixel offset, account for it else it can throw interlacing out.
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (!PCRTCDisplays[i].enabled)
|
||||
continue;
|
||||
|
||||
// Should this be MAGV/H in the DISPLAY register rather than the "default" magnification?
|
||||
const int offset = (PCRTCDisplays[i].displayOffset.y - (offsets.w * (interlaced + 1))) / (VideoModeDividers[videomode].y + 1);
|
||||
|
||||
if (offset > 4)
|
||||
continue;
|
||||
|
||||
int_off[i] = offset & 1;
|
||||
if (offset < 0)
|
||||
int_off[i] = -int_off[i];
|
||||
|
||||
PCRTCDisplays[i].displayRect.y += int_off[i];
|
||||
PCRTCDisplays[i].displayRect.w += int_off[i];
|
||||
}
|
||||
|
||||
// Handle difference in offset between the two displays, used in games like DmC and Time Crisis 2 (for split screen).
|
||||
// Offset is not screen based, but relative to each other.
|
||||
if (PCRTCDisplays[0].enabled && PCRTCDisplays[1].enabled)
|
||||
{
|
||||
GSVector2i offset;
|
||||
|
||||
offset.x = (PCRTCDisplays[1 - zeroDisplay.x].displayOffset.x - PCRTCDisplays[zeroDisplay.x].displayOffset.x) / (VideoModeDividers[videomode].x + 1);
|
||||
offset.y = (PCRTCDisplays[1 - zeroDisplay.y].displayOffset.y - PCRTCDisplays[zeroDisplay.y].displayOffset.y) / (VideoModeDividers[videomode].y + 1);
|
||||
|
||||
if (offset.x >= 4 || !GSConfig.PCRTCAntiBlur)
|
||||
{
|
||||
PCRTCDisplays[1 - zeroDisplay.x].displayRect.x += offset.x;
|
||||
PCRTCDisplays[1 - zeroDisplay.x].displayRect.z += offset.x;
|
||||
}
|
||||
if (offset.y >= 4 || !GSConfig.PCRTCAntiBlur)
|
||||
{
|
||||
PCRTCDisplays[1 - zeroDisplay.y].displayRect.y += offset.y - int_off[1 - zeroDisplay.y];
|
||||
PCRTCDisplays[1 - zeroDisplay.y].displayRect.w += offset.y - int_off[1 - zeroDisplay.y];
|
||||
}
|
||||
|
||||
baseOffset = PCRTCDisplays[zeroDisplay.y].displayOffset;
|
||||
}
|
||||
|
||||
// Handle any large vertical offset from the zero position on the screen.
|
||||
// Example: Hokuto no Ken, does a rougly -14 offset to bring the screen up.
|
||||
// Ignore the lowest bit, we've already accounted for this
|
||||
int vOffset = ((static_cast<int>(baseOffset.y) - (offsets.w * (interlaced + 1))) / (VideoModeDividers[videomode].y + 1));
|
||||
|
||||
if(vOffset <= 4 && vOffset != 0)
|
||||
{
|
||||
PCRTCDisplays[0].displayRect.y += vOffset - int_off[0];
|
||||
PCRTCDisplays[0].displayRect.w += vOffset - int_off[0];
|
||||
PCRTCDisplays[1].displayRect.y += vOffset - int_off[1];
|
||||
PCRTCDisplays[1].displayRect.w += vOffset - int_off[1];
|
||||
}
|
||||
}
|
||||
else // We're using screen offsets, so just calculate the entire offset.
|
||||
{
|
||||
const GSVector4i offsets = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode] : VideoModeOffsetsOverscan[videomode];
|
||||
GSVector2i offset = { 0, 0 };
|
||||
|
||||
int blurOffset = abs(PCRTCDisplays[1].displayOffset.y - PCRTCDisplays[0].displayOffset.y);
|
||||
if (GSConfig.PCRTCAntiBlur && !scanmask && blurOffset < 4)
|
||||
{
|
||||
if (PCRTCDisplays[1].displayOffset.y > PCRTCDisplays[0].displayOffset.y)
|
||||
PCRTCDisplays[1].displayOffset.y -= blurOffset;
|
||||
else
|
||||
PCRTCDisplays[0].displayOffset.y -= blurOffset;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
// Should this be MAGV/H in the DISPLAY register rather than the "default" magnification?
|
||||
offset.x = (static_cast<int>(PCRTCDisplays[i].displayOffset.x) - offsets.z) / (VideoModeDividers[videomode].x + 1);
|
||||
offset.y = (static_cast<int>(PCRTCDisplays[i].displayOffset.y) - (offsets.w * (interlaced + 1))) / (VideoModeDividers[videomode].y + 1);
|
||||
|
||||
PCRTCDisplays[i].displayRect.x += offset.x;
|
||||
PCRTCDisplays[i].displayRect.z += offset.x;
|
||||
PCRTCDisplays[i].displayRect.y += offset.y;
|
||||
PCRTCDisplays[i].displayRect.w += offset.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
} PCRTCDisplays;
|
||||
|
||||
public:
|
||||
/// Returns the appropriate directory for draw dumping.
|
||||
static std::string GetDrawDumpPath(const char* format, ...);
|
||||
|
||||
void ResetHandlers();
|
||||
void ResetPCRTC();
|
||||
|
||||
int GetFramebufferHeight();
|
||||
int GetFramebufferWidth();
|
||||
int GetFramebufferBitDepth();
|
||||
int GetDisplayHMagnification();
|
||||
GSVector4i GetDisplayRect(int i = -1);
|
||||
GSVector4i GetFrameMagnifiedRect(int i = -1);
|
||||
GSVector2i GetResolutionOffset(int i = -1);
|
||||
GSVector2i GetResolution();
|
||||
GSVector4i GetFrameRect(int i = -1, bool ignore_off = false);
|
||||
GSVideoMode GetVideoMode();
|
||||
|
||||
bool IsEnabled(int i);
|
||||
bool isinterlaced();
|
||||
bool isReallyInterlaced();
|
||||
bool IsAnalogue();
|
||||
|
||||
float GetTvRefreshRate();
|
||||
|
||||
|
||||
@@ -356,6 +356,7 @@ void GSDevice::Interlace(const GSVector2i& ds, int field, int mode, float yoffse
|
||||
break;
|
||||
default:
|
||||
m_current = m_merge;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@ std::unique_ptr<GSRenderer> g_gs_renderer;
|
||||
// we might be switching while the other thread reads it.
|
||||
static GSVector4 s_last_draw_rect;
|
||||
|
||||
// Last time we reset the renderer due to a GPU crash, if any.
|
||||
static Common::Timer::Value s_last_gpu_reset_time;
|
||||
|
||||
GSRenderer::GSRenderer()
|
||||
: m_shader_time_start(Common::Timer::GetCurrentValue())
|
||||
@@ -75,264 +77,109 @@ void GSRenderer::Destroy()
|
||||
|
||||
bool GSRenderer::Merge(int field)
|
||||
{
|
||||
bool en[2];
|
||||
|
||||
GSVector4i fr[2];
|
||||
GSVector4i dr[2];
|
||||
GSVector2i display_offsets[2];
|
||||
|
||||
GSVector2i display_baseline = {INT_MAX, INT_MAX};
|
||||
GSVector2i frame_baseline = {INT_MAX, INT_MAX};
|
||||
GSVector2i display_combined = {0, 0};
|
||||
bool feedback_merge = m_regs->EXTWRITE.WRITE == 1;
|
||||
bool display_offset = false;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
en[i] = IsEnabled(i) || (m_regs->EXTBUF.FBIN == i && feedback_merge);
|
||||
|
||||
if (en[i])
|
||||
{
|
||||
fr[i] = GetFrameRect(i);
|
||||
dr[i] = GetDisplayRect(i);
|
||||
display_offsets[i] = GetResolutionOffset(i);
|
||||
|
||||
display_combined.x = std::max(((dr[i].right) - dr[i].left) + display_offsets[i].x, display_combined.x);
|
||||
display_combined.y = std::max((dr[i].bottom - dr[i].top) + display_offsets[i].y, display_combined.y);
|
||||
|
||||
display_baseline.x = std::min(display_offsets[i].x, display_baseline.x);
|
||||
display_baseline.y = std::min(display_offsets[i].y, display_baseline.y);
|
||||
frame_baseline.x = std::min(std::max(fr[i].left, 0), frame_baseline.x);
|
||||
frame_baseline.y = std::min(std::max(fr[i].top, 0), frame_baseline.y);
|
||||
|
||||
display_offset |= std::abs(display_baseline.y - display_offsets[i].y) == 1;
|
||||
/*DevCon.Warning("Read offset was X %d(left %d) Y %d(top %d)", display_baseline.x, dr[i].left, display_baseline.y, dr[i].top);
|
||||
DevCon.Warning("[%d]: %d %d %d %d, %d %d %d %d\n", i, fr[i].x,fr[i].y,fr[i].z,fr[i].w , dr[i].x,dr[i].y,dr[i].z,dr[i].w);
|
||||
DevCon.Warning("Offset X %d Offset Y %d", display_offsets[i].x, display_offsets[i].y);*/
|
||||
}
|
||||
}
|
||||
|
||||
if (!en[0] && !en[1])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
GL_PUSH("Renderer Merge %d (0: enabled %d 0x%x, 1: enabled %d 0x%x)", s_n, en[0], m_regs->DISP[0].DISPFB.Block(), en[1], m_regs->DISP[1].DISPFB.Block());
|
||||
|
||||
// try to avoid fullscreen blur, could be nice on tv but on a monitor it's like double vision, hurts my eyes (persona 4, guitar hero)
|
||||
//
|
||||
// NOTE: probably the technique explained in graphtip.pdf (Antialiasing by Supersampling / 4. Reading Odd/Even Scan Lines Separately with the PCRTC then Blending)
|
||||
|
||||
const bool samesrc =
|
||||
en[0] && en[1] &&
|
||||
m_regs->DISP[0].DISPFB.FBP == m_regs->DISP[1].DISPFB.FBP &&
|
||||
m_regs->DISP[0].DISPFB.FBW == m_regs->DISP[1].DISPFB.FBW &&
|
||||
GSUtil::HasCompatibleBits(m_regs->DISP[0].DISPFB.PSM, m_regs->DISP[1].DISPFB.PSM);
|
||||
bool single_fetch = false;
|
||||
|
||||
GSVector2i fs(0, 0);
|
||||
GSVector2i ds(0, 0);
|
||||
GSTexture* tex[3] = { NULL, NULL, NULL };
|
||||
int y_offset[3] = { 0, 0, 0 };
|
||||
bool feedback_merge = m_regs->EXTWRITE.WRITE == 1;
|
||||
|
||||
GSTexture* tex[3] = {NULL, NULL, NULL};
|
||||
int y_offset[3] = {0, 0, 0};
|
||||
PCRTCDisplays.SetVideoMode(GetVideoMode());
|
||||
PCRTCDisplays.EnableDisplays(m_regs->PMODE, m_regs->SMODE2, isReallyInterlaced());
|
||||
|
||||
s_n++;
|
||||
if (!PCRTCDisplays.PCRTCDisplays[0].enabled && !PCRTCDisplays.PCRTCDisplays[1].enabled)
|
||||
return false;
|
||||
|
||||
// Need to do this here, if the user has Anti-Blur enabled, these offsets can get wiped out/changed.
|
||||
const bool game_deinterlacing = (m_regs->DISP[0].DISPFB.DBY != PCRTCDisplays.PCRTCDisplays[0].prevFramebufferReg.DBY) !=
|
||||
(m_regs->DISP[1].DISPFB.DBY != PCRTCDisplays.PCRTCDisplays[1].prevFramebufferReg.DBY);
|
||||
|
||||
PCRTCDisplays.SetRects(0, m_regs->DISP[0].DISPLAY, m_regs->DISP[0].DISPFB);
|
||||
PCRTCDisplays.SetRects(1, m_regs->DISP[1].DISPLAY, m_regs->DISP[1].DISPFB);
|
||||
PCRTCDisplays.CalculateDisplayOffset(m_scanmask_used);
|
||||
PCRTCDisplays.CalculateFramebufferOffset();
|
||||
PCRTCDisplays.CheckSameSource();
|
||||
|
||||
// Only need to check the right/bottom on software renderer, hardware always gets the full texture then cuts a bit out later.
|
||||
if (samesrc && !feedback_merge && (GSConfig.UseHardwareRenderer() || (fr[0].right == fr[1].right && fr[0].bottom == fr[1].bottom)))
|
||||
if (PCRTCDisplays.FrameRectMatch() && !PCRTCDisplays.FrameWrap() && !feedback_merge)
|
||||
{
|
||||
tex[0] = GetOutput(0, y_offset[0]);
|
||||
tex[0] = GetOutput(-1, y_offset[0]);
|
||||
tex[1] = tex[0]; // saves one texture fetch
|
||||
y_offset[1] = y_offset[0];
|
||||
single_fetch = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (en[0])
|
||||
if (PCRTCDisplays.PCRTCDisplays[0].enabled)
|
||||
tex[0] = GetOutput(0, y_offset[0]);
|
||||
if (en[1])
|
||||
if (PCRTCDisplays.PCRTCDisplays[1].enabled)
|
||||
tex[1] = GetOutput(1, y_offset[1]);
|
||||
if (feedback_merge)
|
||||
tex[2] = GetFeedbackOutput();
|
||||
}
|
||||
|
||||
if (!tex[0] && !tex[1])
|
||||
return false;
|
||||
|
||||
s_n++;
|
||||
|
||||
GSVector4 src_out_rect[2];
|
||||
GSVector4 src_gs_read[2];
|
||||
GSVector4 dst[3];
|
||||
|
||||
const bool slbg = m_regs->PMODE.SLBG;
|
||||
|
||||
GSVector2i resolution(GetResolution());
|
||||
bool scanmask_frame = m_scanmask_used && !display_offset;
|
||||
const bool ignore_offset = !GSConfig.PCRTCOffsets;
|
||||
const bool is_bob = GSConfig.InterlaceMode == GSInterlaceMode::BobTFF || GSConfig.InterlaceMode == GSInterlaceMode::BobBFF;
|
||||
|
||||
// Use offset for bob deinterlacing always, extra offset added later for FFMD mode.
|
||||
float offset = is_bob ? (tex[1] ? tex[1]->GetScale().y : tex[0]->GetScale().y) : 0.0f;
|
||||
|
||||
const bool scanmask_frame = m_scanmask_used && abs(PCRTCDisplays.PCRTCDisplays[0].displayRect.y - PCRTCDisplays.PCRTCDisplays[1].displayRect.y) != 1;
|
||||
int field2 = 0;
|
||||
int mode = 3;
|
||||
int mode = 3; // If the game is manually deinterlacing then we need to bob (if we want to get away with no deinterlacing).
|
||||
bool is_bob = GSConfig.InterlaceMode == GSInterlaceMode::BobTFF || GSConfig.InterlaceMode == GSInterlaceMode::BobBFF;
|
||||
|
||||
// FFMD (half frames) requires blend deinterlacing, so automatically use that. Same when SCANMSK is used but not blended in the merge circuit (Alpine Racer 3)
|
||||
// FFMD (half frames) requires blend deinterlacing, so automatically use that. Same when SCANMSK is used but not blended in the merge circuit (Alpine Racer 3).
|
||||
if (GSConfig.InterlaceMode != GSInterlaceMode::Automatic || (!m_regs->SMODE2.FFMD && !scanmask_frame))
|
||||
{
|
||||
field2 = ((static_cast<int>(GSConfig.InterlaceMode) - 2) & 1);
|
||||
mode = ((static_cast<int>(GSConfig.InterlaceMode) - 2) >> 1);
|
||||
// If the game is offsetting each frame itself and we're using full height buffers, we can offset this with Bob.
|
||||
if (game_deinterlacing && !scanmask_frame && GSConfig.InterlaceMode == GSInterlaceMode::Automatic)
|
||||
{
|
||||
mode = 1; // Bob.
|
||||
is_bob = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
field2 = ((static_cast<int>(GSConfig.InterlaceMode) - 2) & 1);
|
||||
mode = ((static_cast<int>(GSConfig.InterlaceMode) - 2) >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (!en[i] || !tex[i])
|
||||
const GSPCRTCRegs::PCRTCDisplay& curCircuit = PCRTCDisplays.PCRTCDisplays[i];
|
||||
|
||||
if (!curCircuit.enabled || !tex[i])
|
||||
continue;
|
||||
|
||||
GSVector4i r = GetFrameMagnifiedRect(i);
|
||||
GSVector4 scale = GSVector4(tex[i]->GetScale()).xyxy();
|
||||
|
||||
|
||||
GSVector2i off(ignore_offset ? 0 : display_offsets[i]);
|
||||
GSVector2i display_diff(display_offsets[i].x - display_baseline.x, display_offsets[i].y - display_baseline.y);
|
||||
GSVector2i frame_diff(fr[i].left - frame_baseline.x, fr[i].top - frame_baseline.y);
|
||||
|
||||
if (!GSConfig.UseHardwareRenderer())
|
||||
{
|
||||
// Clear any frame offsets, offset is already done in the software GetOutput.
|
||||
fr[i].right -= fr[i].left;
|
||||
fr[i].left = 0;
|
||||
fr[i].bottom -= fr[i].top;
|
||||
fr[i].top = 0;
|
||||
|
||||
// Put any frame offset difference back if we aren't anti-blurring on a single fetch (not offset).
|
||||
if (!GSConfig.PCRTCAntiBlur && single_fetch)
|
||||
{
|
||||
fr[i].right += frame_diff.x;
|
||||
fr[i].left += frame_diff.x;
|
||||
fr[i].bottom += frame_diff.y;
|
||||
fr[i].top += frame_diff.y;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If using scanmsk we have to keep the single line offset, regardless of upscale
|
||||
// so we handle this separately after the rect calculations.
|
||||
float interlace_offset = 0.0f;
|
||||
|
||||
if ((!GSConfig.PCRTCAntiBlur || m_scanmask_used) && display_offset)
|
||||
{
|
||||
interlace_offset = static_cast<float>(display_diff.y & 1);
|
||||
|
||||
// When the displays are offset by 1 we need to adjust for upscale to handle it (reduces bounce in MGS2 when upscaling)
|
||||
interlace_offset += (tex[i]->GetScale().y - 1.0f) / 2;
|
||||
|
||||
if (interlace_offset >= 1.0f)
|
||||
{
|
||||
if (!ignore_offset)
|
||||
off.y -= 1;
|
||||
|
||||
display_diff.y -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Start of Anti-Blur code.
|
||||
if (!ignore_offset)
|
||||
{
|
||||
if (GSConfig.PCRTCAntiBlur)
|
||||
{
|
||||
if (samesrc)
|
||||
{
|
||||
// Offset by DISPLAY setting
|
||||
if (display_diff.x < 4)
|
||||
off.x -= display_diff.x;
|
||||
if (display_diff.y < 4)
|
||||
off.y -= display_diff.y;
|
||||
|
||||
// Only functional in HW mode, software clips/positions the framebuffer on read.
|
||||
if (GSConfig.UseHardwareRenderer())
|
||||
{
|
||||
// Offset by DISPFB setting
|
||||
if (abs(frame_diff.x) < 4)
|
||||
off.x += frame_diff.x;
|
||||
if (abs(frame_diff.y) < 4)
|
||||
off.y += frame_diff.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!slbg || !feedback_merge)
|
||||
{
|
||||
// If the offsets between the two displays are quite large, it's probably intended for an effect.
|
||||
if (display_diff.x >= 4 || !GSConfig.PCRTCAntiBlur)
|
||||
off.x = display_diff.x;
|
||||
|
||||
if (display_diff.y >= 4 || !GSConfig.PCRTCAntiBlur)
|
||||
off.y = display_diff.y;
|
||||
|
||||
// Need to check if only circuit 2 is enabled. Stuntman toggles circuit 1 on and off every other frame.
|
||||
if (samesrc || m_regs->PMODE.EN == 2)
|
||||
{
|
||||
// Adjusting the screen offset when using a negative offset.
|
||||
const int videomode = static_cast<int>(GetVideoMode()) - 1;
|
||||
const GSVector4i offsets = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode] : VideoModeOffsetsOverscan[videomode];
|
||||
GSVector2i base_resolution(offsets.x, offsets.y);
|
||||
|
||||
if (isinterlaced() && !m_regs->SMODE2.FFMD)
|
||||
base_resolution.y *= 2;
|
||||
|
||||
// Offset by DISPLAY setting
|
||||
if (display_diff.x < 0)
|
||||
{
|
||||
off.x = 0;
|
||||
if (base_resolution.x > resolution.x)
|
||||
resolution.x -= display_diff.x;
|
||||
}
|
||||
if (display_diff.y < 0)
|
||||
{
|
||||
off.y = 0;
|
||||
if (base_resolution.y > resolution.y)
|
||||
resolution.y -= display_diff.y;
|
||||
}
|
||||
|
||||
// Don't do X, we only care about height, this would need to be tailored for games using X (Black does -5).
|
||||
// Mainly for Hokuto no Ken which does -14 Y offset.
|
||||
if (display_baseline.y < -4)
|
||||
off.y += display_baseline.y;
|
||||
|
||||
// Anti-Blur stuff
|
||||
// Only functional in HW mode, software clips/positions the framebuffer on read.
|
||||
if (GSConfig.PCRTCAntiBlur && GSConfig.UseHardwareRenderer())
|
||||
{
|
||||
// Offset by DISPFB setting
|
||||
if (abs(frame_diff.x) < 4)
|
||||
off.x += frame_diff.x;
|
||||
if (abs(frame_diff.y) < 4)
|
||||
off.y += frame_diff.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// End of Anti-Blur code.
|
||||
|
||||
if (isinterlaced() && m_regs->SMODE2.FFMD)
|
||||
off.y >>= 1;
|
||||
|
||||
// dst is the final destination rect with offset on the screen.
|
||||
dst[i] = scale * (GSVector4(off).xyxy() + GSVector4(r.rsize()));
|
||||
dst[i] = scale * GSVector4(curCircuit.displayRect);
|
||||
|
||||
// src_gs_read is the size which we're really reading from GS memory.
|
||||
src_gs_read[i] = ((GSVector4(fr[i]) + GSVector4(0, y_offset[i], 0, y_offset[i])) * scale) / GSVector4(tex[i]->GetSize()).xyxy();
|
||||
|
||||
// src_out_rect is the resized rect for output. (Not really used)
|
||||
src_out_rect[i] = (GSVector4(r) * scale) / GSVector4(tex[i]->GetSize()).xyxy();
|
||||
|
||||
if (m_regs->SMODE2.FFMD && !is_bob && !GSConfig.DisableInterlaceOffset && GSConfig.InterlaceMode != GSInterlaceMode::Off)
|
||||
src_gs_read[i] = ((GSVector4(curCircuit.framebufferRect) + GSVector4(0, y_offset[i], 0, y_offset[i])) * scale) / GSVector4(tex[i]->GetSize()).xyxy();
|
||||
|
||||
float interlace_offset = 0.0f;
|
||||
if (isReallyInterlaced() && m_regs->SMODE2.FFMD && !is_bob && !GSConfig.DisableInterlaceOffset && GSConfig.InterlaceMode != GSInterlaceMode::Off)
|
||||
{
|
||||
// We do half because FFMD is a half sized framebuffer, then we offset by 1 in the shader for the actual interlace
|
||||
if(GetUpscaleMultiplier() > 1.0f)
|
||||
interlace_offset += ((((tex[1] ? tex[1]->GetScale().y : tex[0]->GetScale().y) + 0.5f) * 0.5f) - 1.0f) * static_cast<float>(field ^ field2);
|
||||
offset = 1.0f;
|
||||
interlace_offset = (scale.y) * static_cast<float>(field ^ field2);
|
||||
}
|
||||
// Restore manually offset "interlace" lines
|
||||
// Scanmask frame offsets. It's gross, I'm sorry but it sucks.
|
||||
if (m_scanmask_used)
|
||||
{
|
||||
int displayIntOffset = PCRTCDisplays.PCRTCDisplays[i].displayRect.y - PCRTCDisplays.PCRTCDisplays[1 - i].displayRect.y;
|
||||
|
||||
if (displayIntOffset > 0)
|
||||
{
|
||||
displayIntOffset &= 1;
|
||||
dst[i].y -= displayIntOffset * scale.y;
|
||||
dst[i].w -= displayIntOffset * scale.y;
|
||||
interlace_offset += displayIntOffset;
|
||||
}
|
||||
}
|
||||
|
||||
dst[i] += GSVector4(0.0f, interlace_offset, 0.0f, interlace_offset);
|
||||
}
|
||||
|
||||
@@ -349,41 +196,18 @@ bool GSRenderer::Merge(int field)
|
||||
dst[2] = GSVector4(scale * GSVector4(feedback_rect.rsize()));
|
||||
}
|
||||
|
||||
// Set the resolution to the height of the displays (kind of a saturate height)
|
||||
if (ignore_offset && !feedback_merge)
|
||||
{
|
||||
GSVector2i max_resolution = GetResolution();
|
||||
resolution.x = display_combined.x - display_baseline.x;
|
||||
resolution.y = display_combined.y - display_baseline.y;
|
||||
|
||||
if (isinterlaced() && m_regs->SMODE2.FFMD)
|
||||
{
|
||||
resolution.y >>= 1;
|
||||
}
|
||||
|
||||
resolution.x = std::min(max_resolution.x, resolution.x);
|
||||
resolution.y = std::min(max_resolution.y, resolution.y);
|
||||
}
|
||||
|
||||
GSVector2i resolution = PCRTCDisplays.GetResolution();
|
||||
fs = GSVector2i(static_cast<int>(static_cast<float>(resolution.x) * GetUpscaleMultiplier()),
|
||||
static_cast<int>(static_cast<float>(resolution.y) * GetUpscaleMultiplier()));
|
||||
ds = fs;
|
||||
|
||||
// When interlace(FRAME) mode, the rect is half height, so it needs to be stretched.
|
||||
const bool is_interlaced_resolution = m_regs->SMODE2.INT || (isReallyInterlaced() && IsAnalogue() && GSConfig.InterlaceMode != GSInterlaceMode::Off);
|
||||
m_real_size = GSVector2i(fs.x, fs.y);
|
||||
|
||||
if (is_interlaced_resolution && m_regs->SMODE2.FFMD)
|
||||
ds.y *= 2;
|
||||
|
||||
m_real_size = GSVector2i(fs.x, is_interlaced_resolution ? ds.y : fs.y);
|
||||
|
||||
if (!tex[0] && !tex[1])
|
||||
return false;
|
||||
|
||||
if ((tex[0] == tex[1]) && (src_out_rect[0] == src_out_rect[1]).alltrue() && (dst[0] == dst[1]).alltrue() && !feedback_merge && !slbg)
|
||||
if ((tex[0] == tex[1]) && (src_out_rect[0] == src_out_rect[1]).alltrue() &&
|
||||
(PCRTCDisplays.PCRTCDisplays[0].displayRect == PCRTCDisplays.PCRTCDisplays[1].displayRect).alltrue() &&
|
||||
(PCRTCDisplays.PCRTCDisplays[0].framebufferRect == PCRTCDisplays.PCRTCDisplays[1].framebufferRect).alltrue() &&
|
||||
!feedback_merge && !m_regs->PMODE.SLBG)
|
||||
{
|
||||
// the two outputs are identical, skip drawing one of them (the one that is alpha blended)
|
||||
|
||||
tex[0] = NULL;
|
||||
}
|
||||
|
||||
@@ -392,7 +216,11 @@ bool GSRenderer::Merge(int field)
|
||||
g_gs_device->Merge(tex, src_gs_read, dst, fs, m_regs->PMODE, m_regs->EXTBUF, c);
|
||||
|
||||
if (isReallyInterlaced() && GSConfig.InterlaceMode != GSInterlaceMode::Off)
|
||||
g_gs_device->Interlace(ds, field ^ field2, mode, offset);
|
||||
{
|
||||
const float offset = is_bob ? (tex[1] ? tex[1]->GetScale().y : tex[0]->GetScale().y) : 0.0f;
|
||||
|
||||
g_gs_device->Interlace(fs, field ^ field2, mode, offset);
|
||||
}
|
||||
|
||||
if (GSConfig.ShadeBoost)
|
||||
g_gs_device->ShadeBoost();
|
||||
@@ -602,6 +430,40 @@ void GSJoinSnapshotThreads()
|
||||
}
|
||||
}
|
||||
|
||||
bool GSRenderer::BeginPresentFrame(bool frame_skip)
|
||||
{
|
||||
const HostDisplay::PresentResult result = Host::BeginPresentFrame(frame_skip);
|
||||
if (result == HostDisplay::PresentResult::OK)
|
||||
return true;
|
||||
else if (result == HostDisplay::PresentResult::FrameSkipped)
|
||||
return false;
|
||||
|
||||
// If we're constantly crashing on something in particular, we don't want to end up in an
|
||||
// endless reset loop.. that'd probably end up leaking memory and/or crashing us for other
|
||||
// reasons. So just abort in such case.
|
||||
const Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
|
||||
if (s_last_gpu_reset_time != 0 &&
|
||||
Common::Timer::ConvertValueToSeconds(current_time - s_last_gpu_reset_time) < 15.0f)
|
||||
{
|
||||
pxFailRel("Host GPU lost too many times, device is probably completely wedged.");
|
||||
}
|
||||
s_last_gpu_reset_time = current_time;
|
||||
|
||||
// Device lost, something went really bad.
|
||||
// Let's just toss out everything, and try to hobble on.
|
||||
if (!GSreopen(true, false, GSConfig))
|
||||
{
|
||||
pxFailRel("Failed to recreate GS device after loss.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// First frame after reopening is definitely going to be trash, so skip it.
|
||||
Host::AddIconOSDMessage("GSDeviceLost", ICON_FA_EXCLAMATION_TRIANGLE,
|
||||
"Host GPU device encountered an error and was recovered. This may have broken rendering.",
|
||||
Host::OSD_CRITICAL_ERROR_DURATION);
|
||||
return false;
|
||||
}
|
||||
|
||||
void GSRenderer::VSync(u32 field, bool registers_written)
|
||||
{
|
||||
Flush(GSFlushReason::VSYNC);
|
||||
@@ -647,7 +509,7 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
||||
if (skip_frame)
|
||||
{
|
||||
g_gs_device->ResetAPIState();
|
||||
if (Host::BeginPresentFrame(true))
|
||||
if (BeginPresentFrame(true))
|
||||
Host::EndPresentFrame();
|
||||
g_gs_device->RestoreAPIState();
|
||||
PerformanceMetrics::Update(registers_written, fb_sprite_frame, true);
|
||||
@@ -694,7 +556,7 @@ void GSRenderer::VSync(u32 field, bool registers_written)
|
||||
}
|
||||
|
||||
g_gs_device->ResetAPIState();
|
||||
if (Host::BeginPresentFrame(false))
|
||||
if (BeginPresentFrame(false))
|
||||
{
|
||||
if (current && !blank_frame)
|
||||
{
|
||||
@@ -910,7 +772,7 @@ void GSRenderer::StopGSDump()
|
||||
void GSRenderer::PresentCurrentFrame()
|
||||
{
|
||||
g_gs_device->ResetAPIState();
|
||||
if (Host::BeginPresentFrame(false))
|
||||
if (BeginPresentFrame(false))
|
||||
{
|
||||
GSTexture* current = g_gs_device->GetCurrent();
|
||||
if (current)
|
||||
|
||||
@@ -23,6 +23,7 @@ class GSRenderer : public GSState
|
||||
{
|
||||
private:
|
||||
bool Merge(int field);
|
||||
bool BeginPresentFrame(bool frame_skip);
|
||||
|
||||
u64 m_shader_time_start = 0;
|
||||
|
||||
|
||||
@@ -19,10 +19,8 @@
|
||||
#include "GS/GSState.h"
|
||||
|
||||
GSVertexTrace::GSVertexTrace(const GSState* state, bool provoking_vertex_first)
|
||||
: m_accurate_stq(false), m_state(state), m_primclass(GS_INVALID_CLASS)
|
||||
: m_state(state)
|
||||
{
|
||||
memset(&m_alpha, 0, sizeof(m_alpha));
|
||||
|
||||
MULTI_ISA_SELECT(GSVertexTracePopulateFunctions)(*this, provoking_vertex_first);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
int min, max;
|
||||
bool valid;
|
||||
};
|
||||
bool m_accurate_stq;
|
||||
bool m_accurate_stq = false;
|
||||
|
||||
protected:
|
||||
const GSState* m_state;
|
||||
@@ -54,25 +54,25 @@ protected:
|
||||
FindMinMaxPtr m_fmm[2][2][2][2][4];
|
||||
|
||||
public:
|
||||
GS_PRIM_CLASS m_primclass;
|
||||
GS_PRIM_CLASS m_primclass = GS_INVALID_CLASS;
|
||||
|
||||
Vertex m_min;
|
||||
Vertex m_max;
|
||||
VertexAlpha m_alpha; // source alpha range after tfx, GSRenderer::GetAlphaMinMax() updates it
|
||||
Vertex m_min = {};
|
||||
Vertex m_max = {};
|
||||
VertexAlpha m_alpha = {}; // source alpha range after tfx, GSRenderer::GetAlphaMinMax() updates it
|
||||
|
||||
union
|
||||
{
|
||||
u32 value;
|
||||
struct { u32 r:4, g:4, b:4, a:4, x:1, y:1, z:1, f:1, s:1, t:1, q:1, _pad:1; };
|
||||
struct { u32 rgba:16, xyzf:4, stq:4; };
|
||||
} m_eq;
|
||||
} m_eq = {};
|
||||
|
||||
union
|
||||
{
|
||||
struct { u32 mmag:1, mmin:1, linear:1, opt_linear:1; };
|
||||
} m_filter;
|
||||
} m_filter = {};
|
||||
|
||||
GSVector2 m_lod; // x = min, y = max
|
||||
GSVector2 m_lod = {}; // x = min, y = max
|
||||
|
||||
public:
|
||||
GSVertexTrace(const GSState* state, bool provoking_vertex_first);
|
||||
|
||||
@@ -821,14 +821,14 @@ void GSDevice11::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
{
|
||||
// 2nd output is enabled and selected. Copy it to destination so we can blend it with 1st output
|
||||
// Note: value outside of dRect must contains the background color (c)
|
||||
StretchRect(sTex[1], sRect[1], dTex, PMODE.SLBG ? dRect[2] : dRect[1], ShaderConvert::COPY);
|
||||
StretchRect(sTex[1], sRect[1], dTex, PMODE.SLBG ? dRect[2] : dRect[1], ShaderConvert::COPY, false);
|
||||
}
|
||||
|
||||
// Save 2nd output
|
||||
if (feedback_write_2)
|
||||
{
|
||||
StretchRect(dTex, full_r, sTex[2], dRect[2], m_convert.ps[static_cast<int>(ShaderConvert::YUV)].get(),
|
||||
m_merge.cb.get(), nullptr, true);
|
||||
m_merge.cb.get(), nullptr, false);
|
||||
}
|
||||
|
||||
// Restore background color to process the normal merge
|
||||
@@ -838,13 +838,13 @@ void GSDevice11::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
if (sTex[0])
|
||||
{
|
||||
// 1st output is enabled. It must be blended
|
||||
StretchRect(sTex[0], sRect[0], dTex, dRect[0], m_merge.ps[PMODE.MMOD].get(), m_merge.cb.get(), m_merge.bs.get(), true);
|
||||
StretchRect(sTex[0], sRect[0], dTex, dRect[0], m_merge.ps[PMODE.MMOD].get(), m_merge.cb.get(), m_merge.bs.get(), false);
|
||||
}
|
||||
|
||||
if (feedback_write_1)
|
||||
{
|
||||
StretchRect(sTex[0], full_r, sTex[2], dRect[2], m_convert.ps[static_cast<int>(ShaderConvert::YUV)].get(),
|
||||
m_merge.cb.get(), nullptr, true);
|
||||
m_merge.cb.get(), nullptr, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -605,7 +605,7 @@ void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
{
|
||||
static_cast<GSTexture12*>(sTex[1])->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
|
||||
OMSetRenderTargets(dTex, nullptr, darea);
|
||||
SetUtilityTexture(sTex[1], m_linear_sampler_cpu);
|
||||
SetUtilityTexture(sTex[1], m_point_sampler_cpu);
|
||||
BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
|
||||
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
|
||||
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS, c);
|
||||
@@ -625,7 +625,7 @@ void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
EndRenderPass();
|
||||
OMSetRenderTargets(sTex[2], nullptr, fbarea);
|
||||
if (dcleared)
|
||||
SetUtilityTexture(dTex, m_linear_sampler_cpu);
|
||||
SetUtilityTexture(dTex, m_point_sampler_cpu);
|
||||
|
||||
// sTex[2] can be sTex[0], in which case it might be cleared (e.g. Xenosaga).
|
||||
BeginRenderPassForStretchRect(static_cast<GSTexture12*>(sTex[2]), fbarea, GSVector4i(dRect[2]));
|
||||
@@ -663,7 +663,7 @@ void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
{
|
||||
// 1st output is enabled. It must be blended
|
||||
SetUtilityRootSignature();
|
||||
SetUtilityTexture(sTex[0], m_linear_sampler_cpu);
|
||||
SetUtilityTexture(sTex[0], m_point_sampler_cpu);
|
||||
SetPipeline(m_merge[PMODE.MMOD].get());
|
||||
DrawStretchRect(sRect[0], dRect[0], dTex->GetSize());
|
||||
}
|
||||
@@ -673,7 +673,7 @@ void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
EndRenderPass();
|
||||
SetUtilityRootSignature();
|
||||
SetPipeline(m_convert[static_cast<int>(ShaderConvert::YUV)].get());
|
||||
SetUtilityTexture(dTex, m_linear_sampler_cpu);
|
||||
SetUtilityTexture(dTex, m_point_sampler_cpu);
|
||||
OMSetRenderTargets(sTex[2], nullptr, fbarea);
|
||||
BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE);
|
||||
DrawStretchRect(full_r, dRect[2], dsize);
|
||||
|
||||
@@ -24,14 +24,6 @@
|
||||
GSRendererHW::GSRendererHW()
|
||||
: GSRenderer()
|
||||
, m_tc(new GSTextureCache())
|
||||
, m_src(nullptr)
|
||||
, m_reset(false)
|
||||
, m_tex_is_fb(false)
|
||||
, m_channel_shuffle(false)
|
||||
, m_userhacks_tcoffset(false)
|
||||
, m_userhacks_tcoffset_x(0)
|
||||
, m_userhacks_tcoffset_y(0)
|
||||
, m_lod(GSVector2i(0, 0))
|
||||
{
|
||||
MULTI_ISA_SELECT(GSRendererHWPopulateFunctions)(*this);
|
||||
m_mipmap = (GSConfig.HWMipmap >= HWMipmapLevel::Basic);
|
||||
@@ -44,49 +36,12 @@ GSRendererHW::GSRendererHW()
|
||||
|
||||
memset(&m_conf, 0, sizeof(m_conf));
|
||||
|
||||
m_prim_overlap = PRIM_OVERLAP_UNKNOW;
|
||||
ResetStates();
|
||||
}
|
||||
|
||||
GSVector2i GSRendererHW::GetOutputSize(int real_h)
|
||||
{
|
||||
GSVector2i crtc_size(GetResolution());
|
||||
|
||||
// Correct framebuffer size to get output size when offsets not considered (uses framebuffer height)
|
||||
if (!GSConfig.PCRTCOffsets)
|
||||
{
|
||||
const int videomode = static_cast<int>(GetVideoMode()) - 1;
|
||||
const int display_width = (VideoModeDividers[videomode].z + 1) / GetDisplayHMagnification();
|
||||
const GSVector4i offsets = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode] : VideoModeOffsetsOverscan[videomode];
|
||||
int display_height = offsets.y;
|
||||
|
||||
if (isinterlaced() && !m_regs->SMODE2.FFMD)
|
||||
display_height *= 2;
|
||||
|
||||
if (crtc_size.x < display_width || crtc_size.y < display_height)
|
||||
{
|
||||
GSVector2i display_baseline = { 4096, 4096 };
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (IsEnabled(i))
|
||||
{
|
||||
const GSVector4i dr = GetDisplayRect(i);
|
||||
|
||||
const GSVector2i display_diff(dr.left - display_baseline.x, dr.top - display_baseline.y);
|
||||
|
||||
if (display_diff.x != 0 && abs(display_diff.x) < 4 && crtc_size.x < display_width)
|
||||
crtc_size.x -= display_diff.x;
|
||||
|
||||
if (display_diff.y != 0 && abs(display_diff.y) < 4 && crtc_size.y < display_height)
|
||||
crtc_size.y -= display_diff.y;
|
||||
|
||||
display_baseline.x = std::min(dr.left, display_baseline.x);
|
||||
display_baseline.y = std::min(dr.top, display_baseline.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GSVector2i crtc_size(PCRTCDisplays.GetResolution());
|
||||
|
||||
// Include negative display offsets in the height here.
|
||||
crtc_size.y = std::max(crtc_size.y, real_h);
|
||||
@@ -183,10 +138,11 @@ float GSRendererHW::GetUpscaleMultiplier()
|
||||
|
||||
void GSRendererHW::Reset(bool hardware_reset)
|
||||
{
|
||||
// TODO: GSreset can come from the main thread too => crash
|
||||
// m_tc->RemoveAll();
|
||||
// Force targets to preload for 2 frames (for 30fps games).
|
||||
static constexpr u8 TARGET_PRELOAD_FRAMES = 2;
|
||||
|
||||
m_reset = true;
|
||||
m_tc->RemoveAll();
|
||||
m_force_preload = TARGET_PRELOAD_FRAMES;
|
||||
|
||||
GSRenderer::Reset(hardware_reset);
|
||||
}
|
||||
@@ -200,21 +156,18 @@ void GSRendererHW::UpdateSettings(const Pcsx2Config::GSOptions& old_config)
|
||||
|
||||
void GSRendererHW::VSync(u32 field, bool registers_written)
|
||||
{
|
||||
if (m_reset)
|
||||
if (m_force_preload > 0)
|
||||
{
|
||||
m_tc->RemoveAll();
|
||||
m_reset = false;
|
||||
m_force_preload = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_force_preload = false;
|
||||
std::vector<GSState::GSUploadQueue>::iterator iter;
|
||||
for (iter = m_draw_transfers.begin(); iter != m_draw_transfers.end(); ) {
|
||||
if ((s_n - iter->draw) > 5)
|
||||
iter = m_draw_transfers.erase(iter);
|
||||
else
|
||||
iter++;
|
||||
m_force_preload--;
|
||||
if (m_force_preload == 0)
|
||||
{
|
||||
for (auto iter = m_draw_transfers.begin(); iter != m_draw_transfers.end();)
|
||||
{
|
||||
if ((s_n - iter->draw) > 5)
|
||||
iter = m_draw_transfers.erase(iter);
|
||||
else
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,40 +194,33 @@ void GSRendererHW::VSync(u32 field, bool registers_written)
|
||||
|
||||
GSTexture* GSRendererHW::GetOutput(int i, int& y_offset)
|
||||
{
|
||||
const GSRegDISPFB& DISPFB = m_regs->DISP[i].DISPFB;
|
||||
int index = i >= 0 ? i : 1;
|
||||
|
||||
GIFRegTEX0 TEX0 = {};
|
||||
GSPCRTCRegs::PCRTCDisplay& curFramebuffer = PCRTCDisplays.PCRTCDisplays[index];
|
||||
GSVector2i framebufferSize = PCRTCDisplays.GetFramebufferSize(i);
|
||||
const int fb_width = framebufferSize.x;
|
||||
const int fb_height = framebufferSize.y;
|
||||
|
||||
TEX0.TBP0 = DISPFB.Block();
|
||||
TEX0.TBW = DISPFB.FBW;
|
||||
TEX0.PSM = DISPFB.PSM;
|
||||
|
||||
const int videomode = static_cast<int>(GetVideoMode()) - 1;
|
||||
const GSVector4i offsets = VideoModeOffsets[videomode];
|
||||
|
||||
const int fb_width = std::min<int>(std::min<int>(GetFramebufferWidth(), DISPFB.FBW * 64) + static_cast<int>(DISPFB.DBX), 2048);
|
||||
const int display_height = offsets.y * ((isinterlaced() && !m_regs->SMODE2.FFMD) ? 2 : 1);
|
||||
const int display_offset = GetResolutionOffset(i).y;
|
||||
int fb_height = (std::min<int>(GetFramebufferHeight(), display_height) + static_cast<int>(DISPFB.DBY)) % 2048;
|
||||
// If there is a negative vertical offset on the picture, we need to read more.
|
||||
if (display_offset < 0)
|
||||
{
|
||||
fb_height += -display_offset;
|
||||
}
|
||||
// TRACE(_T("[%d] GetOutput %d %05x (%d)\n"), static_cast<int>(g_perfmon.GetFrame()), i, static_cast<int>(TEX0.TBP0), static_cast<int>(TEX0.PSM));
|
||||
PCRTCDisplays.RemoveFramebufferOffset(i);
|
||||
// TRACE(_T("[%d] GetOutput %d %05x (%d)\n"), (int)m_perfmon.GetFrame(), i, (int)TEX0.TBP0, (int)TEX0.PSM);
|
||||
|
||||
GSTexture* t = nullptr;
|
||||
|
||||
GIFRegTEX0 TEX0 = {};
|
||||
TEX0.TBP0 = curFramebuffer.Block();
|
||||
TEX0.TBW = curFramebuffer.FBW;
|
||||
TEX0.PSM = curFramebuffer.PSM;
|
||||
|
||||
if (GSTextureCache::Target* rt = m_tc->LookupDisplayTarget(TEX0, GetOutputSize(fb_height) * GSConfig.UpscaleMultiplier, fb_width, fb_height))
|
||||
{
|
||||
t = rt->m_texture;
|
||||
|
||||
const int delta = TEX0.TBP0 - rt->m_TEX0.TBP0;
|
||||
if (delta > 0 && DISPFB.FBW != 0)
|
||||
if (delta > 0 && curFramebuffer.FBW != 0)
|
||||
{
|
||||
const int pages = delta >> 5u;
|
||||
int y_pages = pages / DISPFB.FBW;
|
||||
y_offset = y_pages * GSLocalMemory::m_psm[DISPFB.PSM].pgs.y;
|
||||
int y_pages = pages / curFramebuffer.FBW;
|
||||
y_offset = y_pages * GSLocalMemory::m_psm[curFramebuffer.PSM].pgs.y;
|
||||
GL_CACHE("Frame y offset %d pixels, unit %d", y_offset, i);
|
||||
}
|
||||
|
||||
@@ -797,8 +743,8 @@ GSVector2i GSRendererHW::GetTargetSize(GSVector2i* unscaled_size)
|
||||
const int page_y = GSLocalMemory::m_psm[m_context->FRAME.PSM].pgs.y - 1;
|
||||
|
||||
// Round up the page as channel shuffles are generally done in pages at a time
|
||||
width = (std::max(static_cast<u32>(GetResolution().x), width) + page_x) & ~page_x;
|
||||
min_height = (std::max(static_cast<u32>(GetResolution().y), min_height) + page_y) & ~page_y;
|
||||
width = (std::max(static_cast<u32>(PCRTCDisplays.GetResolution().x), width) + page_x) & ~page_x;
|
||||
min_height = (std::max(static_cast<u32>(PCRTCDisplays.GetResolution().y), min_height) + page_y) & ~page_y;
|
||||
}
|
||||
|
||||
// Align to even lines, reduces the chance of tiny resizes.
|
||||
@@ -1371,7 +1317,7 @@ void GSRendererHW::Draw()
|
||||
}
|
||||
|
||||
// SW CLUT Render enable.
|
||||
bool preload = GSConfig.PreloadFrameWithGSData | m_force_preload;
|
||||
bool preload = GSConfig.PreloadFrameWithGSData | (m_force_preload > 0);
|
||||
if (GSConfig.UserHacks_CPUCLUTRender > 0 || GSConfig.UserHacks_GPUTargetCLUTMode != GSGPUTargetCLUTMode::Disabled)
|
||||
{
|
||||
const CLUTDrawTestResult result = (GSConfig.UserHacks_CPUCLUTRender == 2) ? PossibleCLUTDrawAggressive() : PossibleCLUTDraw();
|
||||
@@ -1443,13 +1389,14 @@ void GSRendererHW::Draw()
|
||||
const bool clear_height_valid = (m_r.w >= 1024);
|
||||
if (clear_height_valid && context->FRAME.FBW == 1)
|
||||
{
|
||||
u32 width = ceil(static_cast<float>(m_r.w) / GetFramebufferHeight()) * 64;
|
||||
GSVector2i fb_size = PCRTCDisplays.GetFramebufferSize(-1);
|
||||
u32 width = ceil(static_cast<float>(m_r.w) / fb_size.y) * 64;
|
||||
// Framebuffer is likely to be read as 16bit later, so we will need to double the width if the write is 32bit.
|
||||
const bool double_width = GSLocalMemory::m_psm[context->FRAME.PSM].bpp == 32 && GetFramebufferBitDepth() == 16;
|
||||
const bool double_width = GSLocalMemory::m_psm[context->FRAME.PSM].bpp == 32 && PCRTCDisplays.GetFramebufferBitDepth() == 16;
|
||||
m_r.x = 0;
|
||||
m_r.y = 0;
|
||||
m_r.w = GetFramebufferHeight();
|
||||
m_r.z = std::max((width * (double_width ? 2 : 1)), static_cast<u32>(GetFramebufferWidth()));
|
||||
m_r.w = fb_size.y;
|
||||
m_r.z = std::max((width * (double_width ? 2 : 1)), static_cast<u32>(fb_size.x));
|
||||
context->FRAME.FBW = (m_r.z + 63) / 64;
|
||||
m_context->scissor.in.z = context->FRAME.FBW * 64;
|
||||
|
||||
@@ -1486,8 +1433,9 @@ void GSRendererHW::Draw()
|
||||
}
|
||||
}
|
||||
TextureMinMaxResult tmm;
|
||||
const bool process_texture = PRIM->TME && !(PRIM->ABE && m_context->ALPHA.IsBlack() && !m_context->TEX0.TCC);
|
||||
// Disable texture mapping if the blend is black and using alpha from vertex.
|
||||
if (PRIM->TME && !(PRIM->ABE && m_context->ALPHA.IsBlack() && !m_context->TEX0.TCC))
|
||||
if (process_texture)
|
||||
{
|
||||
GIFRegCLAMP MIP_CLAMP = context->CLAMP;
|
||||
GSVector2i hash_lod_range(0, 0);
|
||||
@@ -1607,7 +1555,7 @@ void GSRendererHW::Draw()
|
||||
if (!no_ds)
|
||||
ds = m_tc->LookupTarget(TEX0, t_size, GSTextureCache::DepthStencil, context->DepthWrite(), 0, false, 0, 0, preload);
|
||||
|
||||
if (PRIM->TME)
|
||||
if (process_texture)
|
||||
{
|
||||
GIFRegCLAMP MIP_CLAMP = context->CLAMP;
|
||||
|
||||
@@ -4025,8 +3973,8 @@ GSRendererHW::CLUTDrawTestResult GSRendererHW::PossibleCLUTDraw()
|
||||
(PRIM->TME && ((m_regs->DISP[0].DISPFB.Block() == m_context->TEX0.TBP0) || (m_regs->DISP[1].DISPFB.Block() == m_context->TEX0.TBP0)) && !(m_mem.m_clut.IsInvalid() & 2)))
|
||||
return CLUTDrawTestResult::NotCLUTDraw;
|
||||
|
||||
// Ignore recursive/shuffle effects, but possible it will recursively draw, but make sure it's staying in page width
|
||||
if (PRIM->TME && m_context->TEX0.TBP0 == m_context->FRAME.Block() && (m_context->FRAME.FBW != 1 && m_context->TEX0.TBW == m_context->FRAME.FBW))
|
||||
// Ignore large render targets, make sure it's staying in page width.
|
||||
if (PRIM->TME && (m_context->FRAME.FBW != 1 && m_context->TEX0.TBW == m_context->FRAME.FBW))
|
||||
return CLUTDrawTestResult::NotCLUTDraw;
|
||||
|
||||
// Hopefully no games draw a CLUT with a CLUT, that would be evil, most likely a channel shuffle.
|
||||
|
||||
@@ -97,8 +97,8 @@ private:
|
||||
void SetTCOffset();
|
||||
|
||||
GSTextureCache* m_tc;
|
||||
GSVector4i m_r;
|
||||
GSTextureCache::Source* m_src;
|
||||
GSVector4i m_r = {};
|
||||
GSTextureCache::Source* m_src = nullptr;
|
||||
|
||||
// CRC Hacks
|
||||
bool IsBadFrame();
|
||||
@@ -107,16 +107,15 @@ private:
|
||||
int m_skip = 0;
|
||||
int m_skip_offset = 0;
|
||||
|
||||
bool m_reset;
|
||||
bool m_tex_is_fb;
|
||||
bool m_channel_shuffle;
|
||||
bool m_userhacks_tcoffset;
|
||||
float m_userhacks_tcoffset_x;
|
||||
float m_userhacks_tcoffset_y;
|
||||
bool m_tex_is_fb = false;
|
||||
bool m_channel_shuffle = false;
|
||||
bool m_userhacks_tcoffset = false;
|
||||
float m_userhacks_tcoffset_x = 0.0f;
|
||||
float m_userhacks_tcoffset_y = 0.0f;
|
||||
|
||||
GSVector2i m_lod; // Min & Max level of detail
|
||||
GSVector2i m_lod = {}; // Min & Max level of detail
|
||||
|
||||
GSHWDrawConfig m_conf;
|
||||
GSHWDrawConfig m_conf = {};
|
||||
|
||||
// software sprite renderer state
|
||||
std::vector<GSVertexSW> m_sw_vertex_buffer;
|
||||
|
||||
@@ -43,24 +43,9 @@ GSTextureCache::~GSTextureCache()
|
||||
|
||||
RemoveAll();
|
||||
|
||||
m_surface_offset_cache.clear();
|
||||
|
||||
_aligned_free(s_unswizzle_buffer);
|
||||
}
|
||||
|
||||
void GSTextureCache::RemovePartial()
|
||||
{
|
||||
//m_src.RemoveAll();
|
||||
|
||||
for (int type = 0; type < 2; type++)
|
||||
{
|
||||
for (auto t : m_dst[type])
|
||||
delete t;
|
||||
|
||||
m_dst[type].clear();
|
||||
}
|
||||
}
|
||||
|
||||
void GSTextureCache::RemoveAll()
|
||||
{
|
||||
m_src.RemoveAll();
|
||||
@@ -85,6 +70,8 @@ void GSTextureCache::RemoveAll()
|
||||
|
||||
m_source_memory_usage = 0;
|
||||
m_target_memory_usage = 0;
|
||||
|
||||
m_surface_offset_cache.clear();
|
||||
}
|
||||
|
||||
void GSTextureCache::AddDirtyRectTarget(Target* target, GSVector4i rect, u32 psm, u32 bw)
|
||||
@@ -788,16 +775,17 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, con
|
||||
// From a performance point of view, it might cost a little on big upscaling
|
||||
// but normally few RT are miss so it must remain reasonable.
|
||||
const bool supported_fmt = !GSConfig.UserHacks_DisableDepthSupport || psm_s.depth == 0;
|
||||
const bool forced_preload = static_cast<GSRendererHW*>(g_gs_renderer.get())->m_force_preload;
|
||||
const bool forced_preload = GSRendererHW::GetInstance()->m_force_preload > 0;
|
||||
if ((is_frame || preload || forced_preload) && TEX0.TBW > 0 && supported_fmt)
|
||||
{
|
||||
if (!is_frame && !forced_preload)
|
||||
{
|
||||
std::vector<GSState::GSUploadQueue>::iterator iter;
|
||||
for (iter = static_cast<GSRendererHW*>(g_gs_renderer.get())->m_draw_transfers.begin(); iter != static_cast<GSRendererHW*>(g_gs_renderer.get())->m_draw_transfers.end(); ) {
|
||||
for (iter = GSRendererHW::GetInstance()->m_draw_transfers.begin(); iter != GSRendererHW::GetInstance()->m_draw_transfers.end(); )
|
||||
{
|
||||
if (iter->blit.DBP == TEX0.TBP0 && GSUtil::HasSharedBits(iter->blit.DPSM, TEX0.PSM))
|
||||
{
|
||||
iter = static_cast<GSRendererHW*>(g_gs_renderer.get())->m_draw_transfers.erase(iter);
|
||||
GSRendererHW::GetInstance()->m_draw_transfers.erase(iter);
|
||||
GL_INS("Preloading the RT DATA");
|
||||
const GSVector4i newrect = GSVector4i(0, 0, real_w, real_h);
|
||||
AddDirtyRectTarget(dst, newrect, TEX0.PSM, TEX0.TBW);
|
||||
@@ -1579,6 +1567,24 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
|
||||
// Look for an exact match on the targets.
|
||||
GSTextureCache::Target* src = GetExactTarget(SBP, SBW, SPSM);
|
||||
GSTextureCache::Target* dst = GetExactTarget(DBP, DBW, DPSM);
|
||||
|
||||
// Beware of the case where a game might create a larger texture by moving a bunch of chunks around.
|
||||
// We use dx/dy == 0 and the TBW check as a safeguard to make sure these go through to local memory.
|
||||
// Good test case for this is the Xenosaga I cutscene transitions, or Gradius V.
|
||||
if (src && !dst && dx == 0 && dy == 0 && ((static_cast<u32>(w) + 63) / 64) == DBW)
|
||||
{
|
||||
GIFRegTEX0 new_TEX0 = {};
|
||||
new_TEX0.TBP0 = DBP;
|
||||
new_TEX0.TBW = DBW;
|
||||
new_TEX0.PSM = DPSM;
|
||||
|
||||
const int real_height = GetTargetHeight(DBP, DBW, DPSM, h);
|
||||
const GSVector2 scale(src->m_texture->GetScale());
|
||||
dst = LookupTarget(new_TEX0, GSVector2i(static_cast<int>(w * scale.x), static_cast<int>(real_height * scale.y)), src->m_type, true);
|
||||
if (dst)
|
||||
dst->m_texture->SetScale(scale);
|
||||
}
|
||||
|
||||
if (!src || !dst || src->m_texture->GetScale() != dst->m_texture->GetScale())
|
||||
return false;
|
||||
|
||||
@@ -3070,21 +3076,6 @@ void GSTextureCache::Target::Update(bool reset_age)
|
||||
m_dirty.ClearDirty();
|
||||
return;
|
||||
}
|
||||
else if (m_type == DepthStencil && g_gs_renderer->m_game.title == CRC::FFX2)
|
||||
{
|
||||
GL_INS("ERROR: bad invalidation detected, depth buffer will be cleared");
|
||||
// FFX2 menu. Invalidation of the depth is wrongly done and only the first
|
||||
// page is invalidated. Technically a CRC hack will be better but I don't expect
|
||||
// any games to only upload a single page of data for the depth.
|
||||
//
|
||||
// FFX2 menu got another bug. I'm not sure the top-left is properly written or not. It
|
||||
// could be a gs transfer bug too due to unaligned-page transfer.
|
||||
//
|
||||
// So the quick and dirty solution is just to clean the depth buffer.
|
||||
g_gs_device->ClearDepth(m_texture);
|
||||
m_dirty.ClearDirty();
|
||||
return;
|
||||
}
|
||||
|
||||
GIFRegTEXA TEXA = {};
|
||||
|
||||
@@ -3116,7 +3107,7 @@ void GSTextureCache::Target::Update(bool reset_age)
|
||||
}
|
||||
else
|
||||
{
|
||||
int pitch = ((r.width()+3) & ~3) * 4;
|
||||
const int pitch = Common::AlignUpPow2(r.width() * sizeof(u32), 32);
|
||||
g_gs_renderer->m_mem.ReadTexture(off, r, s_unswizzle_buffer, pitch, TEXA);
|
||||
|
||||
t->Update(r, s_unswizzle_buffer, pitch);
|
||||
|
||||
@@ -395,7 +395,6 @@ public:
|
||||
void Read(Target* t, const GSVector4i& r);
|
||||
void Read(Source* t, const GSVector4i& r);
|
||||
void RemoveAll();
|
||||
void RemovePartial();
|
||||
void AddDirtyRectTarget(Target* target, GSVector4i rect, u32 psm, u32 bw);
|
||||
|
||||
GSTexture* LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, const GSVector2i& size);
|
||||
|
||||
@@ -563,12 +563,12 @@ void GSDeviceMTL::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
{
|
||||
// 2nd output is enabled and selected. Copy it to destination so we can blend it with 1st output
|
||||
// Note: value outside of dRect must contains the background color (c)
|
||||
StretchRect(sTex[1], sRect[1], dTex, dRect[1], ShaderConvert::COPY);
|
||||
StretchRect(sTex[1], sRect[1], dTex, dRect[1], ShaderConvert::COPY, false);
|
||||
}
|
||||
|
||||
// Save 2nd output
|
||||
if (feedback_write_2) // FIXME I'm not sure dRect[1] is always correct
|
||||
DoStretchRect(dTex, full_r, sTex[2], dRect[1], m_convert_pipeline[static_cast<int>(ShaderConvert::YUV)], true, LoadAction::DontCareIfFull, &cb_yuv, sizeof(cb_yuv));
|
||||
DoStretchRect(dTex, full_r, sTex[2], dRect[1], m_convert_pipeline[static_cast<int>(ShaderConvert::YUV)], false, LoadAction::DontCareIfFull, &cb_yuv, sizeof(cb_yuv));
|
||||
|
||||
if (feedback_write_2_but_blend_bg)
|
||||
ClearRenderTarget(dTex, c);
|
||||
@@ -582,17 +582,17 @@ void GSDeviceMTL::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
if (PMODE.MMOD == 1)
|
||||
{
|
||||
// Blend with a constant alpha
|
||||
DoStretchRect(sTex[0], sRect[0], dTex, dRect[0], pipeline, true, LoadAction::Load, &cb_c, sizeof(cb_c));
|
||||
DoStretchRect(sTex[0], sRect[0], dTex, dRect[0], pipeline, false, LoadAction::Load, &cb_c, sizeof(cb_c));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Blend with 2 * input alpha
|
||||
DoStretchRect(sTex[0], sRect[0], dTex, dRect[0], pipeline, true, LoadAction::Load, nullptr, 0);
|
||||
DoStretchRect(sTex[0], sRect[0], dTex, dRect[0], pipeline, false, LoadAction::Load, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (feedback_write_1) // FIXME I'm not sure dRect[0] is always correct
|
||||
StretchRect(dTex, full_r, sTex[2], dRect[0], ShaderConvert::YUV);
|
||||
StretchRect(dTex, full_r, sTex[2], dRect[0], ShaderConvert::YUV, false);
|
||||
}}
|
||||
|
||||
void GSDeviceMTL::DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset, int bufIdx)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "GLLoader.h"
|
||||
#include "GS/GS.h"
|
||||
#include "Host.h"
|
||||
#include "HostSettings.h"
|
||||
|
||||
namespace ReplaceGL
|
||||
{
|
||||
@@ -112,8 +113,8 @@ namespace GLLoader
|
||||
bool vendor_id_amd = false;
|
||||
bool vendor_id_nvidia = false;
|
||||
bool vendor_id_intel = false;
|
||||
bool mesa_driver = false;
|
||||
bool buggy_pbo = false;
|
||||
bool disable_download_pbo = false;
|
||||
|
||||
bool is_gles = false;
|
||||
bool has_dual_source_blend = false;
|
||||
@@ -131,14 +132,8 @@ namespace GLLoader
|
||||
vendor_id_amd = true;
|
||||
else if (strstr(vendor, "NVIDIA Corporation"))
|
||||
vendor_id_nvidia = true;
|
||||
|
||||
#ifdef _WIN32
|
||||
else if (strstr(vendor, "Intel"))
|
||||
vendor_id_intel = true;
|
||||
#else
|
||||
// On linux assumes the free driver if it isn't nvidia or amd pro driver
|
||||
mesa_driver = !vendor_id_nvidia && !vendor_id_amd;
|
||||
#endif
|
||||
|
||||
if (GSConfig.OverrideGeometryShaders != -1)
|
||||
{
|
||||
@@ -225,10 +220,16 @@ namespace GLLoader
|
||||
|
||||
// Don't use PBOs when we don't have ARB_buffer_storage, orphaning buffers probably ends up worse than just
|
||||
// using the normal texture update routines and letting the driver take care of it.
|
||||
GLLoader::buggy_pbo = !GLAD_GL_VERSION_4_4 && !GLAD_GL_ARB_buffer_storage && !GLAD_GL_EXT_buffer_storage;
|
||||
if (GLLoader::buggy_pbo)
|
||||
buggy_pbo = !GLAD_GL_VERSION_4_4 && !GLAD_GL_ARB_buffer_storage && !GLAD_GL_EXT_buffer_storage;
|
||||
if (buggy_pbo)
|
||||
Console.Warning("Not using PBOs for texture uploads because buffer_storage is unavailable.");
|
||||
|
||||
// Give the user the option to disable PBO usage for downloads.
|
||||
// Most drivers seem to be faster with PBO.
|
||||
disable_download_pbo = Host::GetBoolSettingValue("EmuCore/GS", "DisableGLDownloadPBO", false);
|
||||
if (disable_download_pbo)
|
||||
Console.Warning("Not using PBOs for texture downloads, this may reduce performance.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,8 @@ namespace GLLoader
|
||||
extern bool vendor_id_amd;
|
||||
extern bool vendor_id_nvidia;
|
||||
extern bool vendor_id_intel;
|
||||
extern bool mesa_driver;
|
||||
extern bool buggy_pbo;
|
||||
extern bool in_replayer;
|
||||
extern bool disable_download_pbo;
|
||||
|
||||
// GL
|
||||
extern bool is_gles;
|
||||
|
||||
@@ -1327,7 +1327,7 @@ void GSDeviceOGL::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
{
|
||||
// 2nd output is enabled and selected. Copy it to destination so we can blend it with 1st output
|
||||
// Note: value outside of dRect must contains the background color (c)
|
||||
StretchRect(sTex[1], sRect[1], dTex, PMODE.SLBG ? dRect[2] : dRect[1], ShaderConvert::COPY);
|
||||
StretchRect(sTex[1], sRect[1], dTex, PMODE.SLBG ? dRect[2] : dRect[1], ShaderConvert::COPY, false);
|
||||
}
|
||||
|
||||
// Upload constant to select YUV algo
|
||||
@@ -1340,7 +1340,7 @@ void GSDeviceOGL::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
|
||||
// Save 2nd output
|
||||
if (feedback_write_2)
|
||||
StretchRect(dTex, full_r, sTex[2], dRect[2], ShaderConvert::YUV);
|
||||
StretchRect(dTex, full_r, sTex[2], dRect[2], ShaderConvert::YUV, false);
|
||||
|
||||
// Restore background color to process the normal merge
|
||||
if (feedback_write_2_but_blend_bg)
|
||||
@@ -1357,17 +1357,17 @@ void GSDeviceOGL::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
// Blend with a constant alpha
|
||||
m_merge_obj.ps[1].Bind();
|
||||
m_merge_obj.ps[1].Uniform4fv(0, c.v);
|
||||
StretchRect(sTex[0], sRect[0], dTex, dRect[0], m_merge_obj.ps[1], true, OMColorMaskSelector());
|
||||
StretchRect(sTex[0], sRect[0], dTex, dRect[0], m_merge_obj.ps[1], true, OMColorMaskSelector(), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Blend with 2 * input alpha
|
||||
StretchRect(sTex[0], sRect[0], dTex, dRect[0], m_merge_obj.ps[0], true, OMColorMaskSelector());
|
||||
StretchRect(sTex[0], sRect[0], dTex, dRect[0], m_merge_obj.ps[0], true, OMColorMaskSelector(), false);
|
||||
}
|
||||
}
|
||||
|
||||
if (feedback_write_1)
|
||||
StretchRect(dTex, full_r, sTex[2], dRect[2], ShaderConvert::YUV);
|
||||
StretchRect(dTex, full_r, sTex[2], dRect[2], ShaderConvert::YUV, false);
|
||||
}
|
||||
|
||||
void GSDeviceOGL::DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset, int bufIdx)
|
||||
|
||||
@@ -453,9 +453,10 @@ GSDownloadTextureOGL::~GSDownloadTextureOGL()
|
||||
|
||||
std::unique_ptr<GSDownloadTextureOGL> GSDownloadTextureOGL::Create(u32 width, u32 height, GSTexture::Format format)
|
||||
{
|
||||
const u32 buffer_size = GetBufferSize(width, height, format, GSTexture::GetCompressedBytesPerBlock(format));
|
||||
const u32 buffer_size = GetBufferSize(width, height, format, TEXTURE_UPLOAD_PITCH_ALIGNMENT);
|
||||
|
||||
const bool use_buffer_storage = (GLAD_GL_VERSION_4_4 || GLAD_GL_ARB_buffer_storage || GLAD_GL_EXT_buffer_storage);
|
||||
const bool use_buffer_storage = (GLAD_GL_VERSION_4_4 || GLAD_GL_ARB_buffer_storage || GLAD_GL_EXT_buffer_storage) &&
|
||||
!GLLoader::disable_download_pbo;
|
||||
if (use_buffer_storage)
|
||||
{
|
||||
GLuint buffer_id;
|
||||
|
||||
@@ -82,22 +82,6 @@ void GSRendererSW::VSync(u32 field, bool registers_written)
|
||||
{
|
||||
Sync(0); // IncAge might delete a cached texture in use
|
||||
|
||||
if (0) if (LOG)
|
||||
{
|
||||
fprintf(s_fp, "%llu\n", g_perfmon.GetFrame());
|
||||
|
||||
GSVector4i dr = GetDisplayRect();
|
||||
GSVector4i fr = GetFrameRect();
|
||||
|
||||
fprintf(s_fp, "dr %d %d %d %d, fr %d %d %d %d\n",
|
||||
dr.x, dr.y, dr.z, dr.w,
|
||||
fr.x, fr.y, fr.z, fr.w);
|
||||
|
||||
m_regs->Dump(s_fp);
|
||||
|
||||
fflush(s_fp);
|
||||
}
|
||||
|
||||
/*
|
||||
int draw[8], sum = 0;
|
||||
|
||||
@@ -127,27 +111,20 @@ GSTexture* GSRendererSW::GetOutput(int i, int& y_offset)
|
||||
{
|
||||
Sync(1);
|
||||
|
||||
const GSRegDISPFB& DISPFB = m_regs->DISP[i].DISPFB;
|
||||
int index = i >= 0 ? i : 1;
|
||||
GSPCRTCRegs::PCRTCDisplay& curFramebuffer = PCRTCDisplays.PCRTCDisplays[index];
|
||||
GSVector2i framebufferSize = PCRTCDisplays.GetFramebufferSize(i);
|
||||
GSVector4i framebufferRect = PCRTCDisplays.GetFramebufferRect(i);
|
||||
int w = curFramebuffer.FBW * 64;
|
||||
int h = framebufferSize.y;
|
||||
|
||||
int w = DISPFB.FBW * 64;
|
||||
|
||||
const int videomode = static_cast<int>(GetVideoMode()) - 1;
|
||||
const int display_offset = GetResolutionOffset(i).y;
|
||||
const GSVector4i offsets = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode] : VideoModeOffsetsOverscan[videomode];
|
||||
const int display_height = offsets.y * ((isinterlaced() && !m_regs->SMODE2.FFMD) ? 2 : 1);
|
||||
int h = std::min(GetFramebufferHeight(), display_height);
|
||||
|
||||
// If there is a negative vertical offset on the picture, we need to read more.
|
||||
if (display_offset < 0)
|
||||
{
|
||||
h += -display_offset;
|
||||
}
|
||||
|
||||
if (g_gs_device->ResizeTarget(&m_texture[i], w, h))
|
||||
if (g_gs_device->ResizeTarget(&m_texture[index], w, h))
|
||||
{
|
||||
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[curFramebuffer.PSM];
|
||||
constexpr int pitch = 1024 * 4;
|
||||
const int off_x = DISPFB.DBX & 0x7ff;
|
||||
const int off_y = DISPFB.DBY & 0x7ff;
|
||||
// Should really be framebufferOffsets rather than framebufferRect but this might be compensated with anti-blur in some games.
|
||||
const int off_x = (framebufferRect.x & 0x7ff) & ~(psm.bs.x-1);
|
||||
const int off_y = (framebufferRect.y & 0x7ff) & ~(psm.bs.y-1);
|
||||
const GSVector4i out_r(0, 0, w, h);
|
||||
GSVector4i r(off_x, off_y, w + off_x, h + off_y);
|
||||
GSVector4i rh(off_x, off_y, w + off_x, (h + off_y) & 0x7FF);
|
||||
@@ -155,6 +132,7 @@ GSTexture* GSRendererSW::GetOutput(int i, int& y_offset)
|
||||
bool h_wrap = false;
|
||||
bool w_wrap = false;
|
||||
|
||||
PCRTCDisplays.RemoveFramebufferOffset(i);
|
||||
// Need to read it in 2 parts, since you can't do a split rect.
|
||||
if (r.bottom >= 2048)
|
||||
{
|
||||
@@ -172,50 +150,51 @@ GSTexture* GSRendererSW::GetOutput(int i, int& y_offset)
|
||||
w_wrap = true;
|
||||
}
|
||||
|
||||
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[DISPFB.PSM];
|
||||
|
||||
// Display doesn't use texa, and instead uses the equivalent of this
|
||||
GIFRegTEXA texa = {};
|
||||
texa.AEM = 0;
|
||||
texa.TA0 = (DISPFB.PSM == PSM_PSMCT24 || DISPFB.PSM == PSM_PSGPU24) ? 0x80 : 0;
|
||||
texa.TA0 = (curFramebuffer.PSM == PSM_PSMCT24 || curFramebuffer.PSM == PSM_PSGPU24) ? 0x80 : 0;
|
||||
texa.TA1 = 0x80;
|
||||
|
||||
// Top left rect
|
||||
psm.rtx(m_mem, m_mem.GetOffset(DISPFB.Block(), DISPFB.FBW, DISPFB.PSM), r.ralign<Align_Outside>(psm.bs), m_output, pitch, texa);
|
||||
psm.rtx(m_mem, m_mem.GetOffset(curFramebuffer.Block(), curFramebuffer.FBW, curFramebuffer.PSM), r.ralign<Align_Outside>(psm.bs), m_output, pitch, texa);
|
||||
|
||||
// Top left rect
|
||||
psm.rtx(m_mem, m_mem.GetOffset(curFramebuffer.Block(), curFramebuffer.FBW, curFramebuffer.PSM), r.ralign<Align_Outside>(psm.bs), m_output, pitch, texa);
|
||||
|
||||
int top = (h_wrap) ? ((r.bottom - r.top) * pitch) : 0;
|
||||
int left = (w_wrap) ? (r.right - r.left) * (GSLocalMemory::m_psm[DISPFB.PSM].bpp / 8) : 0;
|
||||
int left = (w_wrap) ? (r.right - r.left) * (GSLocalMemory::m_psm[curFramebuffer.PSM].bpp / 8) : 0;
|
||||
|
||||
// The following only happen if the DBX/DBY wrap around at 2048.
|
||||
|
||||
// Top right rect
|
||||
if (w_wrap)
|
||||
psm.rtx(m_mem, m_mem.GetOffset(DISPFB.Block(), DISPFB.FBW, DISPFB.PSM), rw.ralign<Align_Outside>(psm.bs), &m_output[left], pitch, texa);
|
||||
psm.rtx(m_mem, m_mem.GetOffset(curFramebuffer.Block(), curFramebuffer.FBW, curFramebuffer.PSM), rw.ralign<Align_Outside>(psm.bs), &m_output[left], pitch, texa);
|
||||
|
||||
// Bottom left rect
|
||||
if (h_wrap)
|
||||
psm.rtx(m_mem, m_mem.GetOffset(DISPFB.Block(), DISPFB.FBW, DISPFB.PSM), rh.ralign<Align_Outside>(psm.bs), &m_output[top], pitch, texa);
|
||||
psm.rtx(m_mem, m_mem.GetOffset(curFramebuffer.Block(), curFramebuffer.FBW, curFramebuffer.PSM), rh.ralign<Align_Outside>(psm.bs), &m_output[top], pitch, texa);
|
||||
|
||||
// Bottom right rect
|
||||
if (h_wrap && w_wrap)
|
||||
{
|
||||
// Needs also rw with the start/end height of rh, fills in the bottom right rect which will be missing if both overflow.
|
||||
const GSVector4i rwh(rw.left, rh.top, rw.right, rh.bottom);
|
||||
psm.rtx(m_mem, m_mem.GetOffset(DISPFB.Block(), DISPFB.FBW, DISPFB.PSM), rwh.ralign<Align_Outside>(psm.bs), &m_output[top + left], pitch, texa);
|
||||
psm.rtx(m_mem, m_mem.GetOffset(curFramebuffer.Block(), curFramebuffer.FBW, curFramebuffer.PSM), rwh.ralign<Align_Outside>(psm.bs), &m_output[top + left], pitch, texa);
|
||||
}
|
||||
|
||||
m_texture[i]->Update(out_r, m_output, pitch);
|
||||
m_texture[index]->Update(out_r, m_output, pitch);
|
||||
|
||||
if (GSConfig.DumpGSData)
|
||||
{
|
||||
if (GSConfig.SaveFrame && s_n >= GSConfig.SaveN)
|
||||
{
|
||||
m_texture[i]->Save(GetDrawDumpPath("%05d_f%lld_fr%d_%05x_%s.bmp", s_n, g_perfmon.GetFrame(), i, (int)DISPFB.Block(), psm_str(DISPFB.PSM)));
|
||||
m_texture[i]->Save(GetDrawDumpPath("%05d_f%lld_fr%d_%05x_%s.bmp", s_n, g_perfmon.GetFrame(), i, (int)curFramebuffer.Block(), psm_str(curFramebuffer.PSM)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_texture[i];
|
||||
return m_texture[index];
|
||||
}
|
||||
|
||||
GSTexture* GSRendererSW::GetFeedbackOutput()
|
||||
@@ -490,18 +469,18 @@ void GSRendererSW::Draw()
|
||||
{
|
||||
// Dump the RT in 32 bits format. It helps to debug texture shuffle effect
|
||||
s = GetDrawDumpPath("%05d_f%lld_rt0_%05x_32bits.bmp", s_n, frame, m_context->FRAME.Block());
|
||||
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, 0, GetFrameRect().width(), 512);
|
||||
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, 0, PCRTCDisplays.GetFramebufferRect(-1).width(), 512);
|
||||
}
|
||||
|
||||
s = GetDrawDumpPath("%05d_f%lld_rt0_%05x_%s.bmp", s_n, frame, m_context->FRAME.Block(), psm_str(m_context->FRAME.PSM));
|
||||
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, GetFrameRect().width(), 512);
|
||||
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, PCRTCDisplays.GetFramebufferRect(-1).width(), 512);
|
||||
}
|
||||
|
||||
if (GSConfig.SaveDepth && s_n >= GSConfig.SaveN)
|
||||
{
|
||||
s = GetDrawDumpPath("%05d_f%lld_rz0_%05x_%s.bmp", s_n, frame, m_context->ZBUF.Block(), psm_str(m_context->ZBUF.PSM));
|
||||
|
||||
m_mem.SaveBMP(s, m_context->ZBUF.Block(), m_context->FRAME.FBW, m_context->ZBUF.PSM, GetFrameRect().width(), 512);
|
||||
m_mem.SaveBMP(s, m_context->ZBUF.Block(), m_context->FRAME.FBW, m_context->ZBUF.PSM, PCRTCDisplays.GetFramebufferRect(-1).width(), 512);
|
||||
}
|
||||
|
||||
Queue(data);
|
||||
@@ -514,18 +493,18 @@ void GSRendererSW::Draw()
|
||||
{
|
||||
// Dump the RT in 32 bits format. It helps to debug texture shuffle effect
|
||||
s = GetDrawDumpPath("%05d_f%lld_rt1_%05x_32bits.bmp", s_n, frame, m_context->FRAME.Block());
|
||||
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, 0, GetFrameRect().width(), 512);
|
||||
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, 0, PCRTCDisplays.GetFramebufferRect(-1).width(), 512);
|
||||
}
|
||||
|
||||
s = GetDrawDumpPath("%05d_f%lld_rt1_%05x_%s.bmp", s_n, frame, m_context->FRAME.Block(), psm_str(m_context->FRAME.PSM));
|
||||
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, GetFrameRect().width(), 512);
|
||||
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, PCRTCDisplays.GetFramebufferRect(-1).width(), 512);
|
||||
}
|
||||
|
||||
if (GSConfig.SaveDepth && s_n >= GSConfig.SaveN)
|
||||
{
|
||||
s = GetDrawDumpPath("%05d_f%lld_rz1_%05x_%s.bmp", s_n, frame, m_context->ZBUF.Block(), psm_str(m_context->ZBUF.PSM));
|
||||
|
||||
m_mem.SaveBMP(s, m_context->ZBUF.Block(), m_context->FRAME.FBW, m_context->ZBUF.PSM, GetFrameRect().width(), 512);
|
||||
m_mem.SaveBMP(s, m_context->ZBUF.Block(), m_context->FRAME.FBW, m_context->ZBUF.PSM, PCRTCDisplays.GetFramebufferRect(-1).width(), 512);
|
||||
}
|
||||
|
||||
if (GSConfig.SaveL > 0 && (s_n - GSConfig.SaveN) > GSConfig.SaveL)
|
||||
@@ -613,14 +592,14 @@ void GSRendererSW::Sync(int reason)
|
||||
{
|
||||
s = GetDrawDumpPath("%05d_f%lld_rt1_%05x_%s.bmp", s_n, g_perfmon.GetFrame(), m_context->FRAME.Block(), psm_str(m_context->FRAME.PSM));
|
||||
|
||||
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, GetFrameRect().width(), 512);
|
||||
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, PCRTCDisplays.GetFramebufferRect(-1).width(), 512);
|
||||
}
|
||||
|
||||
if (GSConfig.SaveDepth)
|
||||
{
|
||||
s = GetDrawDumpPath("%05d_f%lld_zb1_%05x_%s.bmp", s_n, g_perfmon.GetFrame(), m_context->ZBUF.Block(), psm_str(m_context->ZBUF.PSM));
|
||||
|
||||
m_mem.SaveBMP(s, m_context->ZBUF.Block(), m_context->FRAME.FBW, m_context->ZBUF.PSM, GetFrameRect().width(), 512);
|
||||
m_mem.SaveBMP(s, m_context->ZBUF.Block(), m_context->FRAME.FBW, m_context->ZBUF.PSM, PCRTCDisplays.GetFramebufferRect(-1).width(), 512);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -736,7 +736,7 @@ void GSDeviceVK::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
{
|
||||
static_cast<GSTextureVK*>(sTex[1])->TransitionToLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
OMSetRenderTargets(dTex, nullptr, darea, false);
|
||||
SetUtilityTexture(sTex[1], m_linear_sampler);
|
||||
SetUtilityTexture(sTex[1], m_point_sampler);
|
||||
BeginClearRenderPass(m_utility_color_render_pass_clear, darea, c);
|
||||
SetPipeline(m_convert[static_cast<int>(ShaderConvert::COPY)]);
|
||||
DrawStretchRect(sRect[1], PMODE.SLBG ? dRect[2] : dRect[1], dsize);
|
||||
@@ -753,7 +753,7 @@ void GSDeviceVK::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
EndRenderPass();
|
||||
OMSetRenderTargets(sTex[2], nullptr, fbarea, false);
|
||||
if (dcleared)
|
||||
SetUtilityTexture(dTex, m_linear_sampler);
|
||||
SetUtilityTexture(dTex, m_point_sampler);
|
||||
// sTex[2] can be sTex[0], in which case it might be cleared (e.g. Xenosaga).
|
||||
BeginRenderPassForStretchRect(static_cast<GSTextureVK*>(sTex[2]), fbarea, GSVector4i(dRect[2]));
|
||||
if (dcleared)
|
||||
@@ -787,7 +787,7 @@ void GSDeviceVK::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
if (sTex[0] && sTex[0]->GetState() == GSTexture::State::Dirty)
|
||||
{
|
||||
// 1st output is enabled. It must be blended
|
||||
SetUtilityTexture(sTex[0], m_linear_sampler);
|
||||
SetUtilityTexture(sTex[0], m_point_sampler);
|
||||
SetPipeline(m_merge[PMODE.MMOD]);
|
||||
SetUtilityPushConstants(&c, sizeof(c));
|
||||
DrawStretchRect(sRect[0], dRect[0], dTex->GetSize());
|
||||
@@ -797,7 +797,7 @@ void GSDeviceVK::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
{
|
||||
EndRenderPass();
|
||||
SetPipeline(m_convert[static_cast<int>(ShaderConvert::YUV)]);
|
||||
SetUtilityTexture(dTex, m_linear_sampler);
|
||||
SetUtilityTexture(dTex, m_point_sampler);
|
||||
SetUtilityPushConstants(yuv_constants, sizeof(yuv_constants));
|
||||
OMSetRenderTargets(sTex[2], nullptr, fbarea, false);
|
||||
BeginRenderPass(m_utility_color_render_pass_load, fbarea);
|
||||
|
||||
@@ -60,6 +60,13 @@ public:
|
||||
RightOrBottom
|
||||
};
|
||||
|
||||
enum class PresentResult
|
||||
{
|
||||
OK,
|
||||
FrameSkipped,
|
||||
DeviceLost
|
||||
};
|
||||
|
||||
struct AdapterAndModeList
|
||||
{
|
||||
std::vector<std::string> adapter_names;
|
||||
@@ -137,7 +144,7 @@ public:
|
||||
|
||||
/// Returns false if the window was completely occluded. If frame_skip is set, the frame won't be
|
||||
/// displayed, but the GPU command queue will still be flushed.
|
||||
virtual bool BeginPresent(bool frame_skip) = 0;
|
||||
virtual PresentResult BeginPresent(bool frame_skip) = 0;
|
||||
|
||||
/// Presents the frame to the display, and renders OSD elements.
|
||||
virtual void EndPresent() = 0;
|
||||
@@ -184,7 +191,7 @@ namespace Host
|
||||
|
||||
/// Returns false if the window was completely occluded. If frame_skip is set, the frame won't be
|
||||
/// displayed, but the GPU command queue will still be flushed.
|
||||
bool BeginPresentFrame(bool frame_skip);
|
||||
HostDisplay::PresentResult BeginPresentFrame(bool frame_skip);
|
||||
|
||||
/// Presents the frame to the display, and renders OSD elements.
|
||||
void EndPresentFrame();
|
||||
|
||||
@@ -267,8 +267,11 @@ void PAD::SetDefaultControllerConfig(SettingsInterface& si)
|
||||
#endif
|
||||
si.SetBoolValue("Pad", "MultitapPort1", false);
|
||||
si.SetBoolValue("Pad", "MultitapPort2", false);
|
||||
si.SetFloatValue("Pad", "PointerXScale", 8.0f);
|
||||
si.SetFloatValue("Pad", "PointerYScale", 8.0f);
|
||||
si.SetFloatValue("Pad", "PointerXSpeed", 40.0f);
|
||||
si.SetFloatValue("Pad", "PointerYSpeed", 40.0f);
|
||||
si.SetFloatValue("Pad", "PointerXDeadZone", 20.0f);
|
||||
si.SetFloatValue("Pad", "PointerYDeadZone", 20.0f);
|
||||
si.SetFloatValue("Pad", "PointerInertia", 10.0f);
|
||||
|
||||
// PCSX2 Controller Settings - Default pad types and parameters.
|
||||
for (u32 i = 0; i < NUM_CONTROLLER_PORTS; i++)
|
||||
@@ -530,8 +533,11 @@ void PAD::CopyConfiguration(SettingsInterface* dest_si, const SettingsInterface&
|
||||
dest_si->CopyBoolValue(src_si, "Pad", "MultitapPort2");
|
||||
dest_si->CopyBoolValue(src_si, "Pad", "MultitapPort1");
|
||||
dest_si->CopyBoolValue(src_si, "Pad", "MultitapPort2");
|
||||
dest_si->CopyFloatValue(src_si, "Pad", "PointerXScale");
|
||||
dest_si->CopyFloatValue(src_si, "Pad", "PointerYScale");
|
||||
dest_si->CopyFloatValue(src_si, "Pad", "PointerXSpeed");
|
||||
dest_si->CopyFloatValue(src_si, "Pad", "PointerYSpeed");
|
||||
dest_si->CopyFloatValue(src_si, "Pad", "PointerXDeadZone");
|
||||
dest_si->CopyFloatValue(src_si, "Pad", "PointerYDeadZone");
|
||||
dest_si->CopyFloatValue(src_si, "Pad", "PointerInertia");
|
||||
for (u32 i = 0; i < static_cast<u32>(InputSourceType::Count); i++)
|
||||
{
|
||||
dest_si->CopyBoolValue(src_si, "InputSources",
|
||||
|
||||
@@ -114,9 +114,9 @@ void Host::ReleaseHostDisplay(bool clear_state)
|
||||
{
|
||||
}
|
||||
|
||||
bool Host::BeginPresentFrame(bool frame_skip)
|
||||
HostDisplay::PresentResult Host::BeginPresentFrame(bool frame_skip)
|
||||
{
|
||||
return false;
|
||||
return HostDisplay::PresentResult::FrameSkipped;
|
||||
}
|
||||
|
||||
void Host::EndPresentFrame()
|
||||
|
||||
Reference in New Issue
Block a user