Compare commits

...
202 Commits
Author SHA1 Message Date
RedDevilusandrefractionpcsx2 39fb64cdcd GameDB: Dark Cloud 2 + Dark Chronicle
I had a dark joke on the fix, but let's not jester about it. The clown now has more colors than just 1.
2023-02-21 14:10:14 +00:00
JordanTheToasterandrefractionpcsx2 d531a7f1af GameDB: Remove CPU CLUT from Transformers The Game
Something changed and now this just causes rainbow vomit in game and in menus instead of fixing the issue it was originally put in for.
2023-02-21 09:36:49 +00:00
PCSX2 Botandlightningterror bb7ff414ae PAD: Update to latest controller database. 2023-02-21 02:14:48 +01:00
JordanTheToasterandrefractionpcsx2 215f112521 GameDB: Port patch to PAL Transformers ROTF
Ports a patch over to the PAL version to fix the pause menu hud and other parts.
2023-02-19 15:50:05 +00:00
Florin9doiandlightningterror 644766d965 USB: Fix Sega Seamic buttons 2023-02-19 13:15:53 +01:00
JordanTheToasterandrefractionpcsx2 1e1a555d3b GameDB: Various fixes
Fixes for Transformers Revenge of the Fallen misaligned bloom and soft shadows and Legend of Kay broken shadow rectangles and misaligned bloom.
2023-02-19 03:53:20 +00:00
refractionpcsx2 c64ae2684d GS-PCRTC: Handle extended height when there's a negative offset 2023-02-19 00:57:52 +00:00
refractionpcsx2 50ed04436d GS-HW: Preserve width of frame textures 2023-02-19 00:57:38 +00:00
Stenzekandrefractionpcsx2 2fb9beca52 GS/HW: Use block instead of page pointer for height lookup
Some callers were using blocks, others were using pages before.

Enables target expansion in Stuntman, which slightly improves rendering
without CPU sprite. The full "fix" needs page handling in P8 conversion.
2023-02-19 00:57:24 +00:00
refractionpcsx2 cd4c1e920e GS: Flush targets back to GS memory when swapping renderers 2023-02-19 00:57:01 +00:00
refractionpcsx2 e846ac367a GS-HW: Fix downloads in offset BP and different widths 2023-02-19 00:03:49 +00:00
Mrlinkwiiandrefractionpcsx2 ef31c733ee Gamedb: update Armored Core - Nexus 2023-02-18 15:53:30 +00:00
Stenzekandrefractionpcsx2 724aa657f3 Qt: Fix window geometry not saving on exit 2023-02-18 14:50:09 +00:00
kenshen112andrefractionpcsx2 0284c35f4c GameDB: Add Fatal Frame 1 / Project Zero final boss game bug fix patch 2023-02-18 05:04:20 +00:00
TheTechnician27andGitHub 6ce33de287 UI: Update and Add mouseover dialog (#8119) 2023-02-18 01:58:32 +00:00
refractionpcsx2 6ccfa011d4 Pad/Counters: Rearrange Pad/Core updates on VSync
Apparently this gets around some weird input lag issue.
2023-02-18 01:13:39 +00:00
refractionpcsx2 c9078af45e GS: Bump shader cache version 2023-02-17 19:40:16 +00:00
refractionpcsx2 6745428d0c GS-SW: Fix SW texture dumping 2023-02-17 19:06:35 +00:00
refractionpcsx2 925e874ada GS-HW: Fix real 16bit value shuffles 2023-02-17 19:06:35 +00:00
lightningterror 03f0f2f803 GameDB: Add missing db entry for Silent Scope 3.
Korean version.
2023-02-17 16:35:46 +01:00
lightningterror 857360d6b2 GameDB: Add full mipmap with ps2 trilinear to Matrix, The - Path of Neo.
Improves ground textures to match sw renderer.
2023-02-17 16:35:46 +01:00
Stenzekandrefractionpcsx2 2dd76c3f12 GS/HW: Force region match on fixed TEX0
Fixes lightmapping in Spinter Cell - Double Agent.
2023-02-17 13:20:04 +00:00
Stenzekandrefractionpcsx2 666de3a874 GS/HW: Allow hardware sampling when clamp_max is larger
Fixes some mipmapping issues in GT4.
2023-02-17 13:20:04 +00:00
Stenzekandrefractionpcsx2 e0e9b64db6 GS/HW: Fix region textures being used when redundant 2023-02-17 13:20:04 +00:00
JordanTheToasterandrefractionpcsx2 2f521348c6 GameDB: Add VU Sync hack to Salt Lake 2002
Fixes the awful flickering and missing textures that happens all over the game.
2023-02-17 13:12:33 +00:00
Stenzekandrefractionpcsx2 8ac2949a1f GS/HW: Fix incorrect bitfield width in height cache
This lead to duplication and possibly larger-than-intended heights.
2023-02-16 16:44:40 +00:00
Stenzekandrefractionpcsx2 d0d5d991ce GS/HW: Fix repeat sampler being used for region clamp/repeat
The coordinates are clamped in the shader, but normalized UV of 1 and
bilinear may repeat.
2023-02-16 16:44:40 +00:00
Mrlinkwiiandrefractionpcsx2 1fc2d7de3c GameDB : remove skipdraw from Need for Speed - Undercover 2023-02-16 15:00:27 +00:00
refractionpcsx2 2598e8d9b9 GS-PCRTC: Fix Anti-blur in screen offset.
No this doesn't mean it won't look blurry, that's a downside of screen offsets in some games, but it might look *less* blurry.
2023-02-16 14:47:09 +00:00
refractionpcsx2 01f65e98e6 GS-PCRTC: Fix up some PCRTC anti-blur behaviour + code cleanup 2023-02-16 14:47:09 +00:00
Ganael LaplancheandGitHub c5330cf166 Common: FreeBSD fixes. (#8163)
* Fix SIGSEGV handler on FreeBSD

* Fix config dir location on FreeBSD
2023-02-16 09:30:18 +01:00
JordanTheToasterandrefractionpcsx2 b78796d0c1 GameDB: Add HPO Special to Arthur and the minimoys
Fixes misaligned bloom that looks gross.
2023-02-16 00:32:17 +00:00
RedDevilusandrefractionpcsx2 da1e9db2c0 GameDB: Ruff Trigger
Rename Europe serial and fix offset and blooming.
2023-02-15 19:18:11 +00:00
RedDevilusandrefractionpcsx2 6beb6aa05b GameDB: Bakugan Battle Brawlers
Se Deinterlace to Adaptive TFF. I thought this was fixed but needs to be checked later.
2023-02-15 19:15:49 +00:00
MrlinkwiiandGitHub 6ea7777a3a GS-HW: remove Fighting Beauty Wulong & Spartan Total Warrior CRC (#8158) 2023-02-15 14:56:53 +00:00
Stenzekandrefractionpcsx2 f97191e241 GS/HW: Split up consecutive channel shuffles 2023-02-15 11:04:05 +00:00
Stenzekandrefractionpcsx2 51420dade4 VMManager: Clear host root on booting ISO 2023-02-15 10:04:39 +00:00
Tokman5andrefractionpcsx2 cc55c01197 GameDB: Add EETimingHack to Crazy Taxi 2023-02-15 10:04:04 +00:00
JordanTheToasterandrefractionpcsx2 86ce464ee3 GameDB: Fix V-Rally 3 graphical corruption
Fixes random graphical corruption that can happen by adding EE Timing hack.
2023-02-15 00:04:44 +00:00
refractionpcsx2 0fa52a75ad GS-HW: Optimise readback performance for some cases 2023-02-14 22:27:35 +00:00
Mrlinkwiiandrefractionpcsx2 c91e7dc3b0 GameDB: remove not needed hw fixes for Metal Slug 6 2023-02-14 22:27:17 +00:00
Mrlinkwiiandrefractionpcsx2 88034b176c GS-HW:purge MetalSlug6 CRC 2023-02-14 22:27:17 +00:00
JordanTheToasterandrefractionpcsx2 efeaff488c GameDB: Add HPO Special to 007 EON
Lines bad must fix lines.
2023-02-14 17:49:53 +00:00
BerylskidandGitHub 5df30f5bdd OSD: Change the lower limit of OSD Scale from 100 to 50 (#8135) 2023-02-14 14:03:24 +00:00
C.W. BettsandGitHub cf179c42b8 cmake: Quiet macOS building warning, we can build on macOS just fine. (#8136) 2023-02-14 11:32:39 +01:00
BerylskidandGitHub a615f8bf17 Qt: Change "Save State On Shutdown" to Unchecked (#8147) 2023-02-14 10:30:34 +00:00
refractionpcsx2 b38964e814 GS: Partial revert of #8101 pending investigation.
Sometimes great ideas don't always go smoothly to plan, this is one of those, but it will be back when we work out what's up with Soul Calibur 2 and DBZ BT3 :)
2023-02-14 10:23:09 +00:00
JordanTheToasterandrefractionpcsx2 013c9eec58 GameDB: Various fixes
Some fixes I missed in the last PR because I am a doughnut and can't remember what I needed to do.
2023-02-14 00:20:52 +00:00
refractionpcsx2 ddbd6eddf7 GameDB: Add Autoflush for Astro Boy and CPU Sprite for Alias 2023-02-14 00:18:08 +00:00
refractionpcsx2 982fd42683 GS: Preload whenever it matches an upload. GUI option forces preloads. 2023-02-14 00:18:08 +00:00
RedDevilusandrefractionpcsx2 90e28e7957 GameDB: Fix Japanese KH2 - Final Mix
Change Normal Vertex halfpixel with roundsprite
2023-02-13 23:59:41 +00:00
JordanTheToasterandrefractionpcsx2 f4201f3947 GameDB: Remove fix from Soulcalibur 3
No longer needed as the hash cache no longer does an explosion.
2023-02-13 23:27:27 +00:00
PCSX2 Botandlightningterror 7844b40243 PAD: Update to latest controller database. 2023-02-13 17:37:59 +01:00
JordanTheToasterandrefractionpcsx2 d0839a3d55 GameDB: Fixes for graphics in Mana Khemia 2
Fixes missing graphical effects in battles by disabling mvu speedhack.
2023-02-13 16:07:31 +00:00
JordanTheToasterandrefractionpcsx2 876fd9ba9e GameDB: Vexx underwater rendering fixes
Adds Tex in RT to fix underwater rendering being totally broken.
2023-02-13 09:37:31 +00:00
lightningterror e12717c108 CDVD: Fix FreeBSD compile. 2023-02-13 03:02:59 +01:00
refractionpcsx2 af0b17bb7a Counters: Present at VSync End 2023-02-12 21:13:42 +00:00
Stenzekandrefractionpcsx2 6f595b7d87 GS/HW: Allow previous frame in depth source lookup
Fixes pulling random junk from local memory in Black after adjusting
vsync timing.
2023-02-12 21:13:42 +00:00
JordanTheToasterandrefractionpcsx2 59cbdc79f5 GameDB: Fixes for V-Rally 3 and Berserk
Fixes sun occlusion in V-Rally 3 and fixes missing subtitles in FMVs in Berserk.
2023-02-12 19:28:13 +00:00
refractionpcsx2 50ff3649b1 GS: Fix divide by zero error 2023-02-12 19:14:54 +00:00
refractionpcsx2 9ca9db8770 GS: Fix undefined behaviour bug in PCRTC 2023-02-12 15:03:24 +00:00
Stenzekandrefractionpcsx2 fa70f0e764 GS/HW: Don't age texture cache on idle frames 2023-02-12 08:34:39 +00:00
Stenzekandrefractionpcsx2 3eb629f133 InputManager: Restore passing wheel events to ImGui 2023-02-12 07:25:56 +00:00
Stenzekandrefractionpcsx2 c9aba6bbe1 GS/TextureCache: Allow tex-in-rt for 16/24/32-bit targets
Fixes swirl battle transition in Valkyrie Profile 2.
Fixes top-left screen rendering in Lego Racers 2.
2023-02-12 07:07:52 +00:00
Stenzekandrefractionpcsx2 0a26adae76 GS/TextureCache: Dirty with target PSM/TBW, not the EE write 2023-02-12 07:07:52 +00:00
Stenzekandrefractionpcsx2 f0798f6510 GS/HW: Combine dirty rectangles for target updates
Fewer texture uploads makes Intel GPUs happy.
2023-02-12 07:07:52 +00:00
refractionpcsx2 245b03e208 GS: Limit the height of framebuffer reads 2023-02-12 07:07:26 +00:00
refractionpcsx2 4e31e5fdc2 GS: Use linear interpolation for Screen Offsets 2023-02-12 04:34:07 +00:00
Stenzekandrefractionpcsx2 750a74206c InputManager: Warning fix/default interia value 2023-02-12 04:32:47 +00:00
Stenzekandrefractionpcsx2 7e64dc2576 GS/HW: Don't defer TC reset until next vsync 2023-02-12 04:32:47 +00:00
Stenzekandrefractionpcsx2 8a08e2fd97 GS: Make sure everything in GSState is initialized 2023-02-12 04:32:47 +00:00
Stenzekandrefractionpcsx2 d0a933cda8 GS/TextureCache: Clear surface offset cache on reset
Also get rid of RemovePartial(), it's never called.
2023-02-12 04:32:47 +00:00
lightningterrorandrefractionpcsx2 d00845f56b GameDB: Add missing Need for Speed - Carbon entry. 2023-02-12 00:46:01 +00:00
lightningterrorandrefractionpcsx2 3350e5ebb1 GameDB: Replace DMABusyHack with InstantDMAHack for MGS2 Sons of Liberty.
DMA timing problem.
Fixes broken half-bottom artifacts.
2023-02-12 00:46:01 +00:00
SideProjectsLabandGitHub aeb4445cad Qt/Input: Improved how mouse movements are mapped to analog (#7910) 2023-02-11 23:58:58 +00:00
refractionpcsx2 73abae8cb9 GameDB: Remove forced deinterlacing modes which are no longer required 2023-02-11 20:08:36 +00:00
refractionpcsx2 7ecc7b76ba GS-PCRTC: Improve automatic de-interlacing to avoid it more often. 2023-02-11 20:08:36 +00:00
refractionpcsx2 71d0bbbc25 GS: Rework of PCRTC code. 2023-02-11 20:08:36 +00:00
JordanTheToasterandrefractionpcsx2 26e691ba93 GameDB: Update HPO on Spider Man 2
Changes HPO to HPO Special to fix rainbow garbage on the top and left side of the screen when moving.
2023-02-11 17:43:05 +00:00
Stenzekandrefractionpcsx2 c7352d9e10 GS: Attempt to recreate device if GPU crashes 2023-02-11 15:33:55 +00:00
Stenzekandrefractionpcsx2 7b8f9a54ec GS/HW: Purge FFX-2 depth clear CRC hack 2023-02-11 15:26:04 +00:00
JordanTheToasterandrefractionpcsx2 28980af858 GameDB: Port COP2 patch for Disneys Cars
Ports patch to fix broken collisions to PAL Disney's Cars
2023-02-11 15:24:23 +00:00
Stenzekandrefractionpcsx2 80dce398e0 GS/HW: Carefully allow move to create new targets
Xenosaga I does a move from BP 1C00 to E00, then from E00 to 2A00 a few
frames later for its cutscene transitions. 2A00 then gets used as a
texture and blended on top of the later frames. Because there's no
target at 2A00, the move falls back to the CPU, and E00 contains junk
which gets moved and eventually preloaded instead.

Gradius V uses moves for a screen move/wobble-like effect, by moving
chunks of the framebuffer out, then using those as a texture. It's not
broken at the moment, but it does readback (slow), and break upscaling.
2023-02-11 07:16:19 +00:00
TheTechnician27andrefractionpcsx2 06db8eec48 Context.cpp: fix minor typo 2023-02-11 06:46:44 +00:00
Stenzekandrefractionpcsx2 9c720efe46 GS/OGL: Fix possible crash downloading odd texture sizes 2023-02-11 06:43:20 +00:00
refractionpcsx2 cbf91a8d19 GS-HW: Tighten CLUT detection slightly. 2023-02-11 02:07:01 +00:00
TheTechnician27andGitHub f99414708d Readme: Two minor changes to the README (#8105) 2023-02-11 00:00:25 +00:00
refractionpcsx2 9549a6b16a GS: Fix TME processing when Alpha->IsBlack & !TEX0->TCC 2023-02-10 23:48:43 +00:00
lightningterror 3206094545 GameDB: Add full mipmap and trilinear ps2 to Hard Hitter games.
Improves ground texture rendering.
2023-02-10 22:24:33 +01:00
Stenzekandrefractionpcsx2 5cfae80701 GL/OpenGL: Add a hidden [EmuCore/GS] DisableGLDownloadPBO option
.. to disable the use of PBOs when reading back.
2023-02-10 14:38:21 +00:00
JordanTheToasterandrefractionpcsx2 b4d140c6bb GameDB: Fixes for 187 Ride or Die
Add autoflush to soften bloom and HPO Special Texture to fix misaligned bloom.
2023-02-10 14:15:51 +00:00
Stenzekandrefractionpcsx2 c65eb3c3ee GS/HW: Fix crash with AVX2 due to unaligned pitch 2023-02-10 14:15:33 +00:00
Mrlinkwiiandlightningterror eec0984dbe Gamedb: remove skipdraw from Need for Speed - Undercover 2023-02-10 14:47:52 +01:00
Stenzekandrefractionpcsx2 9b1163f959 GS/HW: Use sampler for clamp/repeat when it's redundant
But only for local memory sources (not targets), it causes issues with
upscaling otherwise.
2023-02-10 11:20:37 +00:00
Stenzekandrefractionpcsx2 5b5edc506d Misc: Warning fixes 2023-02-10 11:20:37 +00:00
refractionpcsx2 b30b4375e7 GS: Detect 16bit source copy in to 32bit target shuffle 2023-02-10 11:19:41 +00:00
Stenzekandrefractionpcsx2 1b0c03d892 GS/HW: Fix fixed/invalid TEX0 case in TimeSplitters 2 2023-02-10 03:43:10 +00:00
ZiemasandGitHub bca49184e7 Debugger: Fix missing return path. (#8094) 2023-02-09 19:48:54 +00:00
Stenzekandlightningterror 26d6c33163 GSRunner: Clear bindings and disable game settings 2023-02-09 17:29:47 +01:00
Ziemasandrefractionpcsx2 9420615317 Implement thread listing for IOP
Abstracts away threads behind a common interface for both EE and IOP
2023-02-09 15:49:56 +00:00
Stenzekandrefractionpcsx2 7198c6b8c6 GS: Use u32 instead of size_t for index storage
These are never going to be greater than 2^32-1, and they're truncated
in a bunch of places anyway.
2023-02-09 14:54:42 +00:00
Stenzekandrefractionpcsx2 9513864851 GS: Make prim a template parameter for auto flush
It's force inlined into VertexKick(), which also has prim as a template
parameter.
2023-02-09 14:54:42 +00:00
Stenzekandrefractionpcsx2 f9b8aa1862 GS: Handle index swap case for auto flush
DirectX and OpenGL no longer have different draw splitting.
2023-02-09 14:54:42 +00:00
refractionpcsx2 34371c070c Mcd-Folder: Prevent crash trying to delete non-existent node. 2023-02-09 14:06:21 +00:00
Stenzekandrefractionpcsx2 d6099dd263 GS/Vulkan: Use Bresenham line rasterization when supported 2023-02-09 13:42:05 +00:00
Stenzekandrefractionpcsx2 a1bc39141e PAD: Fix trigger deadzone/scale setting 2023-02-09 12:30:20 +00:00
Connor McLaughlinandrefractionpcsx2 13ed41d077 GS/DX11: Disable multisampling in rasterizer state 2023-02-09 11:13:42 +00:00
Stenzekandrefractionpcsx2 8a0a8f718f GameDB: Remove preloading disable for a bunch of games
These are fine now.
2023-02-09 10:51:06 +00:00
Stenzekandrefractionpcsx2 7d08a54ad9 GS/HW: Optimize TC source size based on CLAMP 2023-02-09 10:51:06 +00:00
Stenzekandrefractionpcsx2 b9b47e3ec7 GS/OGL: Fix upload row length for texture Update() 2023-02-09 10:51:06 +00:00
refractionpcsx2 16989f2122 GS-HW: On overwrite of large incompatible format texture, erase old. 2023-02-09 10:50:40 +00:00
Berylskidandrefractionpcsx2 892d3a370d GameDB: Fix texture flickers of Armored Core Nexus 2023-02-09 09:37:17 +00:00
Stenzekandlightningterror 6af7ca9867 PAD: Fix invert in deadzone computation 2023-02-09 10:03:55 +01:00
Stenzekandlightningterror c3c354f794 PAD: Add trigger deadzone/sensitivity settings 2023-02-09 10:03:55 +01:00
Stenzekandlightningterror 43572a1560 Qt: Mark widget binder functions as inline
Should also fix the unused warning without using [[maybe_unused]];
2023-02-09 10:03:55 +01:00
Stenzekandlightningterror 79daed63ee Qt: Handle multiplier in controller settings 2023-02-09 10:03:55 +01:00
Stenzekandlightningterror 21d3ad86d4 Qt: Make controller settings page scrollable 2023-02-09 10:03:55 +01:00
Stenzekandlightningterror 31ebe842e8 Qt: Add per-bind sensitivity/deadzone controls (shift-click) 2023-02-09 10:03:55 +01:00
eyfixandGitHub 88487de72f USB: Add values to GunCon2 config. (#8078) 2023-02-09 09:55:23 +01:00
Harukaandlightningterror 616da8c99d CDVD: simplify code 2023-02-09 09:52:00 +01:00
Harukaandlightningterror ec8712cceb CDVD: use DevCon logging instead of stdout / stderr 2023-02-09 09:52:00 +01:00
Harukaandlightningterror 3d6923b2a1 CDVD: implement SetSpindleSpeed on macOS 2023-02-09 09:52:00 +01:00
Harukaandlightningterror f04337becf CDVD: fix macOS CD reading 2023-02-09 09:52:00 +01:00
Harukaandlightningterror 9a5dd4c19d CDVD: macOS physical disc support 2023-02-09 09:52:00 +01:00
lightningterror 643e0b1039 GameDB: Add Autoflush hw fix to MGS3 Subsistence.
Fixes lens flare.
2023-02-09 07:06:21 +01:00
Stenzekandlightningterror 330061a6e7 GS/OGL: Fix offseted downloads not being offset
Fixes background screen effect in Gradius V.
2023-02-09 07:04:50 +01:00
Stenzekandlightningterror 0a292715cf GS/OGL: Align texture uploads to 64 bytes
Fixes potential crash in some games with odd-sized targets
and preloading (e.g. Densha De Go).
2023-02-09 07:04:50 +01:00
Berylskidandlightningterror 52f034a513 GameDB: Add missing quatation marks in memcardFilters for Armored Core Last Raven 2023-02-08 17:32:49 +01:00
Stenzekandlightningterror 25e05388ba Qt: Open fullscreen window on same display as main 2023-02-08 17:29:59 +01:00
JordanTheToasterandlightningterror 7ad9a1af03 GameDB: Add Paltex to Gacharoku
Stops the hash cache from being silly and exploding.
2023-02-08 17:29:22 +01:00
refractionpcsx2 9346c69343 GS-HW: Iterate dirty rects in reverse to join existing ones. 2023-02-08 00:05:44 +00:00
refractionpcsx2 4a3f0ccf96 GS-HW: Process dirty rects separately, improved Tex in RT compatibility 2023-02-08 00:05:44 +00:00
refractionpcsx2 52a1396e29 GSDumpRunner: Fix new options, add missing Preload Frame 2023-02-07 23:40:01 +00:00
JordanTheToasterandrefractionpcsx2 f556dd2584 GameDB: Add missing The Godfather fixes
Adds missing fixes for other entry's of The Godfarther.
2023-02-07 23:26:04 +00:00
refractionpcsx2 aea5c09825 GSDumpRunner: Ability to enable manual hw hacks for testing 2023-02-07 22:31:36 +00:00
refractionpcsx2 8c3c9a1219 GSDumpRunner: Tweaks to reduce console messages and redundant frame dumps 2023-02-07 22:31:36 +00:00
eyfixandGitHub a4f1f383a7 USB/LightGun/GunCon2: Fix wrong code of GunCon2. (#8066)
240 is used for the default screen height elsewhere in the file.
BID_SHOOT_OFFSCREEN is already handled at line 295.
2023-02-07 06:21:48 +01:00
JordanTheToasterandlightningterror ee73c5c1b5 GameDB: Various small fixes
Fixes for Transformers The Game to fix bloom and mipmapping issues and add missing db entrys.
2023-02-07 06:19:59 +01:00
refractionpcsx2 c2904a4633 GS-HW: Don't enable merge sprite in native resolution 2023-02-06 23:59:06 +00:00
JordanTheToasterandlightningterror 6faa2249f9 GameDB: Add Disable InstantVU to MGS 3 Subsistence
Disables InstantVU1 to fix missing letters in text such as the letter E also adds more fixes and missing fixes for Forever Kingdom / Evergrace 2.
2023-02-07 00:24:47 +01:00
lightningterror 119c3acfe7 GS-tc: Cleanup hw texturecache.
Initializations, constants, casts.
2023-02-07 00:22:46 +01:00
lightningterror 138a2683f2 USB: Fix Variable is assigned a value that is never used warning.
Codacy.
2023-02-07 00:22:46 +01:00
PCSX2 Botandlightningterror 463637fa10 PAD: Update to latest controller database. 2023-02-07 00:19:55 +01:00
refractionpcsx2 d5aab926bf GameDB: Add Game Fix for Scandal 2023-02-05 17:40:02 +00:00
KamFretoZandGitHub 37ba04b770 GameDB: Fixes for "Toy Story 3" & "WALL-E" (#8045) 2023-02-05 14:53:09 +00:00
refractionpcsx2 3d0b7dee71 GameDB: Update K-1 Grand Prix fixes to use Instant DMA instead.
This seems to be more reliable in correcting the issue, and for some reason the old way is no longer working properly.
2023-02-05 14:50:36 +00:00
JordanTheToasterandrefractionpcsx2 a287c2caac GameDB: Add Align Sprite to Football Kingdom
Fixes vertical lines.
2023-02-05 14:42:58 +00:00
Stenzekandrefractionpcsx2 1b673d9dd0 GS/HW: Simplify m_vertex/m_index
Switches DX11 to a larger streaming buffer, adds missing texture copy
counter for colclip.
2023-02-04 12:30:24 +00:00
Stenzekandrefractionpcsx2 d10621c201 GS/DX11: Use annotations for debug messages 2023-02-04 12:30:24 +00:00
Ziemasandrefractionpcsx2 e1d6dfc324 Add command line boot and debug option 2023-02-04 12:30:07 +00:00
Stenzekandrefractionpcsx2 130ea2a7ca PAD: Fix pressure getting set to 1 after modifier 2023-02-04 12:29:53 +00:00
Stenzekandrefractionpcsx2 c8d53253d2 Qt: Defer application quit on window close
Fixes Mac builds crashing when the Metal renderer tries to clear the
layer on a non-existant window.
2023-02-04 12:29:44 +00:00
Stenzekandrefractionpcsx2 5c67438925 VMManager: Only exit CPU execution if we were running
Fixes crash if you were paused when you shut down the VM with the EE
interpreter enabled.
2023-02-04 12:29:44 +00:00
lightningterror 1012dba8d7 GameDB: Add basic mipmap to Narc and Tom & Jerry War of the Whiskers.
Fixes textures.
2023-02-03 23:51:05 +01:00
Mrlinkwiiandrefractionpcsx2 e8d43f53d9 GameDB: fixes for 'Tom & Jerry - War of the Whiskers' & 'Brave - The Search for Spirit Dancer'' 2023-02-03 18:19:42 +00:00
RedDevilusandrefractionpcsx2 a0e8ce4b13 Qt: Fix compatibility string
Playable rating was missing which is the most common rating for PCSX2. There are some other issues like the images look bad for star rating and some other stuff. Also resize the compatibility table width a bit.
2023-02-03 16:09:46 +00:00
refractionpcsx2 0e35b3edcb GS: Extend things to handle the alpha blended clear in NARC/Brave 2023-02-03 15:17:10 +00:00
refractionpcsx2 25bb5851ec GS-HW: Fix special CLUT case that uses preloading. 2023-02-03 15:17:10 +00:00
refractionpcsx2 80b0bc0869 GS-HW: Adjust clear detection for clears with FBMSK 2023-02-03 15:17:10 +00:00
refractionpcsx2 c05743b7b9 GS-HW: Improve FBW=1 clear for Singstar games 2023-02-03 15:17:10 +00:00
Stenzekandrefractionpcsx2 7c2a1f0f37 GS: Vectorize CLUT rect update 2023-02-03 14:36:37 +00:00
Stenzekandrefractionpcsx2 0b8b9e75d1 GS: Don't do CLUT auto flush test on invalid PRIM
Fixes OOB array access in Xenosaga I's cutscenes.
2023-02-03 14:36:37 +00:00
refractionpcsx2 d65c343e91 GameDB: Add preload to Siren 2 2023-02-03 06:54:56 +00:00
refractionpcsx2 cd8e7cc947 GS-HW: Queue preloads from Local->Local moves on CPU also 2023-02-03 06:54:56 +00:00
refractionpcsx2 0df5cf2e91 GS-CLUT: Handle invalidation on wrapping writes 2023-02-03 01:55:25 +00:00
refractionpcsx2 412275e40d GS: Convert ee write list to vector, fix some bugs. 2023-02-03 00:09:54 +00:00
refractionpcsx2 4c7ad66bd7 GS-HW: Check for CLUT upload before invalidating or running on GPU.
Clean up some preload code and improve the detection.
2023-02-03 00:09:54 +00:00
refractionpcsx2 8ac21357c3 GS-HW: Force preload 1 frame after reset 2023-02-03 00:09:54 +00:00
refractionpcsx2 2fd88b901b GS-TC: Don't clear on reset, only invalidate frames.
Fixes bug with counting writes before flushing.
Track what textures are used as frames.

It's possible that further to this the PCRTC disables on CSR reset, but we need to test this first, but that would fix the logo at the bottom when booting through the bios in Software mode.
2023-02-03 00:09:54 +00:00
refractionpcsx2 5697759d9e GS-HW: Try to make Preload Frame Data slightly less gross.
These changes make sure there's a matching EE transfer to texture allocation in that frame, if not, then don't allocate anything. This should reduce bad data being loaded in to the Rt.

Preload replaced for Simple 2000 Series Vol. 114 - The Onna Okappichi Torimonochou, preload worked by chance (but no more), disable depth works.
2023-02-03 00:09:54 +00:00
refractionpcsx2 ee0042c768 GameDB: Remove no longer needed Preload Frame settings 2023-02-03 00:09:54 +00:00
refractionpcsx2 a6212f1388 GS-HW: Always preload new frames for PCRTC if none found. 2023-02-03 00:09:54 +00:00
refractionpcsx2 7bfea60b35 GS-TC: Tex In RT expand target match 2023-02-02 23:46:05 +00:00
refractionpcsx2 beee740dc8 GameDB: Add Mipmapping to Jak X, replace FMV fix on Snowboard Heaven 2023-02-02 23:46:05 +00:00
refractionpcsx2 e31387b8bc GS-TC: Don't Tex in RT on old targets/PCRTC only frames. 2023-02-02 23:46:05 +00:00
TheLastRarandrefractionpcsx2 3586a12c46 DEV9: Remove pcap dumper
Never worked in Qt
2023-02-02 21:13:52 +00:00
TheLastRarandrefractionpcsx2 bf21254b13 DEV9: Pcap loop instead of returning on invalid packet
Should improve pcap bridged performance when lots of unrelated network traffic is present.
2023-02-02 21:13:52 +00:00
TheLastRarandrefractionpcsx2 dc9f61e70a DEV9: Add classes for editing packets and make PCAP use them 2023-02-02 21:13:52 +00:00
TheLastRarandrefractionpcsx2 3028998a43 DEV9: Move most of the pcap methods into PCAPAdapter
Also includes the following changes;
pcap_io_running checks replaced with assets in send/recv
pcap_io_running checks replaces checks on hpcap being non null.
Don't cast header->len from unsigned to signed when checking for jumbo frames
Log dropping jumbo frames
Free pcap filter code
Check return value of GetMACAddress() and handle appropriately.
2023-02-02 21:13:52 +00:00
TheLastRarandrefractionpcsx2 42511ce8d8 DEV9: Remove unneeded headers
by providing a typedef for Adapter
2023-02-02 21:13:52 +00:00
RedDevilusandrefractionpcsx2 c245d2134f GameDB: Driver Parallel Lines + Godfather + ...
Godfather: remove on-screen garbage
Driver Parallel Lines: Reduce misaligned bloom issues
Simple 2000 Series Vol. 109 - The Taxi 2: name fix to be Chauffeur and not like train driver (Utenshi to Untenshu)
Rimococoron
2023-02-02 14:22:06 +00:00
JordanTheToasterandrefractionpcsx2 f01884537d GameDB: Assorted fixes
Fixes the name of GT-R 400 and adds round sprite to fix lines in menus and text and corrects the name of 40k Fire Warrior to correctly have a space.
2023-02-01 10:23:11 +00:00
Mrlinkwiiandrefractionpcsx2 b48fb0d4da Misc : label & comment fixes
Misc : remove old comments
labeler: Add labels for Translations
2023-02-01 10:22:12 +00:00
RedDevilusandrefractionpcsx2 7a6470a19d CI: Add RetroAchievements/Rcheevos labels for PRs
Beats doing it manually labels for Rcheevos.
2023-02-01 09:39:30 +00:00
Silentandrefractionpcsx2 bfd8fc771a Qt: Clear the status text after gamelist scanning
Improves UI parity with DuckStation.
2023-02-01 09:38:46 +00:00
Connor McLaughlinandlightningterror f96ca4ac1f GS/OGL: Fix syntax error in fragment shader 2023-02-01 09:04:52 +01:00
TheLastRarandrefractionpcsx2 937bfce68e DEV9: Better match HDD size per-game UI to rest of settings 2023-01-31 21:23:05 +00:00
TheLastRarandrefractionpcsx2 5869d35d85 DEV9: Consider placeholderText when opening File dialog
Per-game settings store the global setting as placeholderText.
If no per-game setting is present we can prefill the dialog with the global setting, before falling back to a default value
2023-01-31 21:23:05 +00:00
TheLastRarandrefractionpcsx2 8d3325e6cd DEV9: Fix HDD file overwrite check 2023-01-31 21:23:05 +00:00
TheLastRarandrefractionpcsx2 4badb5b975 DEV9: Fix HddEnable not enabling UI correctly in per-game settings 2023-01-31 21:23:05 +00:00
TheLastRarandrefractionpcsx2 7e4ff233ec DEV9: Fix Per-game HDD path
SettingWidgetBinder isn't capable of handling this yet
2023-01-31 21:23:05 +00:00
refractionpcsx2 0e3397239d GS: Correct GSIMR/GSCSR reg init, regression from previous release 2023-01-31 16:55:45 +00:00
refractionpcsx2 08bae3da2e GS: Fix h/vsync counters on mode change/sync. 2023-01-31 14:56:48 +00:00
refractionpcsx2 4b49c8bd6e GS: Correct CSR behaviour on mode change 2023-01-31 14:56:48 +00:00
refractionpcsx2 c1bd1fcbd4 GS: Clear Privilage registers on GS Reset via CSR 2023-01-31 14:56:48 +00:00
TheLastRarandrefractionpcsx2 1c3379f082 Qt: Connect close instead of accept for the close button 2023-01-31 02:06:09 +00:00
Mrlinkwiiandrefractionpcsx2 86c97a8ba3 GameDB: fixes for Megaman X Command Mission 2023-01-30 20:55:18 +00:00
lightningterror 8e6c18d3f4 emitter: Ignore Wmissing-braces warnings on clang. 2023-01-30 20:32:33 +01:00
Goatman13andrefractionpcsx2 f1e80c466d IPU: Reset threshold on IPU reset. 2023-01-30 19:13:50 +00:00
Mrlinkwiiandrefractionpcsx2 9a542bcb20 GameDB: fixes for Simple 2000 Series Vol. 48 2023-01-30 17:31:00 +00:00
JordanTheToasterandrefractionpcsx2 290c8ec420 GameDB: Various Tekken fixes
Disables preloading entirely on Tekken Tag Tournament and Tekken 4 and adds missing fix comments and missing db entry's for both games.
2023-01-30 14:03:58 +00:00
Mrlinkwiiandrefractionpcsx2 517ccd5e40 GameDB:fixes for Dragon Quest VIII - Journey of the Cursed King 2023-01-29 17:14:17 +00:00
157 changed files with 5939 additions and 3409 deletions
+5
View File
@@ -32,6 +32,8 @@
- '**/GameIndex.*'
'Installer | Package':
- 'build.sh'
'Translations':
- 'pcsx2-qt/Translations/*'
# Tools / Features
'Debugger':
@@ -45,6 +47,9 @@
'TAS Functionality':
- 'pcsx2/Recording/*'
- 'pcsx2/Recording/**/*'
'RetroAchievements':
- 'pcsx2/Frontend/Achievements.*'
- 'pcsx2/Achievements.*'
# Emulation Components
'Counters':
+2 -2
View File
@@ -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.
File diff suppressed because it is too large Load Diff
+6
View File
@@ -543,6 +543,7 @@
030000009b2800001e00000000000000,Raphnet Vectrex Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a1,lefty:a2,x:b2,y:b3,platform:Windows,
030000009b2800002b00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800002c00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800008000000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,
03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000321500000204000000000000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000321500000104000000000000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
@@ -849,6 +850,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000000d0f00003801000008010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Mac OS X,
030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f0000aa00000072050000,Hori Real Arcade Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
030000000d0f00000002000015010000,Hori Switch Split Pad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00006e00000000010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00006600000000000000,Horipad FPS Plus 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
@@ -991,6 +993,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e040000130b000011050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000200b000011050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000200b000013050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000200b000015050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000d102000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000005e040000dd02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000005e040000e002000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,
@@ -1175,6 +1178,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000000d0f00006a00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00006b00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00001600000000010000,Hori Real Arcade Pro EXSE,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,
030000000d0f00008501000015010000,Hori Switch Split Pad Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000000d0f00006e00000011010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00006600000011010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f0000ee00000011010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
@@ -1270,6 +1274,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e0400008902000021010000,Microsoft Xbox pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,
03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,
050000004d4f435554452d3035335800,Mocute 053X,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
05000000e80400006e0400001b010000,Mocute 053X M59,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000004d4f435554452d3035305800,Mocute 054X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000d6200000e589000001000000,Moga 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,
05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,
@@ -1546,6 +1551,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
050000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
060000005e040000120b00000b050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,y:b3,x:b2,start:b7,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:b12,dpleft:b14,dpdown:b13,dpright:b15,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
030000005e040000120b000007050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000005e040000130b000007050000,Xbox Series X Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000130b000011050000,Xbox Series X Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+47 -30
View File
@@ -21,6 +21,8 @@
#define PS_FST 0
#define PS_WMS 0
#define PS_WMT 0
#define PS_ADJS 0
#define PS_ADJT 0
#define PS_AEM_FMT FMT_32
#define PS_AEM 0
#define PS_TFX 0
@@ -36,13 +38,13 @@
#define PS_POINT_SAMPLER 0
#define PS_SHUFFLE 0
#define PS_READ_BA 0
#define PS_READ16_SRC 0
#define PS_DFMT 0
#define PS_DEPTH_FMT 0
#define PS_PAL_FMT 0
#define PS_CHANNEL_FETCH 0
#define PS_TALES_OF_ABYSS_HLE 0
#define PS_URBAN_CHAOS_HLE 0
#define PS_INVALID_TEX0 0
#define PS_SCALE_FACTOR 1.0
#define PS_HDR 0
#define PS_COLCLIP 0
@@ -158,10 +160,10 @@ cbuffer cb1
float2 TA;
float MaxDepthPS;
float Af;
uint4 MskFix;
uint4 FbMask;
float4 HalfTexel;
float4 MinMax;
float4 STRange;
int4 ChannelShuffle;
float2 TC_OffsetHack;
float2 STScale;
@@ -183,7 +185,20 @@ float4 sample_c(float2 uv, float uv_w)
// As of 2018 this issue is still present.
uv = (trunc(uv * WH.zw) + float2(0.5, 0.5)) / WH.zw;
}
#if !PS_ADJS && !PS_ADJT
uv *= STScale;
#else
#if PS_ADJS
uv.x = (uv.x - STRange.x) * STRange.z;
#else
uv.x = uv.x * STScale.x;
#endif
#if PS_ADJT
uv.y = (uv.y - STRange.y) * STRange.w;
#else
uv.y = uv.y * STScale.y;
#endif
#endif
#if PS_AUTOMATIC_LOD == 1
return Texture.Sample(TextureSampler, uv);
@@ -218,12 +233,7 @@ float4 sample_p_norm(float u)
float4 clamp_wrap_uv(float4 uv)
{
float4 tex_size;
if (PS_INVALID_TEX0 == 1)
tex_size = WH.zwzw;
else
tex_size = WH.xyxy;
float4 tex_size = WH.xyxy;
if(PS_WMS == PS_WMT)
{
@@ -238,7 +248,7 @@ float4 clamp_wrap_uv(float4 uv)
// textures. Fixes Xenosaga's hair issue.
uv = frac(uv);
#endif
uv = (float4)(((uint4)(uv * tex_size) & MskFix.xyxy) | MskFix.zwzw) / tex_size;
uv = (float4)(((uint4)(uv * tex_size) & asuint(MinMax.xyxy)) | asuint(MinMax.zwzw)) / tex_size;
}
}
else
@@ -252,7 +262,7 @@ float4 clamp_wrap_uv(float4 uv)
#if PS_FST == 0
uv.xz = frac(uv.xz);
#endif
uv.xz = (float2)(((uint2)(uv.xz * tex_size.xx) & MskFix.xx) | MskFix.zz) / tex_size.xx;
uv.xz = (float2)(((uint2)(uv.xz * tex_size.xx) & asuint(MinMax.xx)) | asuint(MinMax.zz)) / tex_size.xx;
}
if(PS_WMT == 2)
{
@@ -263,7 +273,7 @@ float4 clamp_wrap_uv(float4 uv)
#if PS_FST == 0
uv.yw = frac(uv.yw);
#endif
uv.yw = (float2)(((uint2)(uv.yw * tex_size.yy) & MskFix.yy) | MskFix.ww) / tex_size.yy;
uv.yw = (float2)(((uint2)(uv.yw * tex_size.yy) & asuint(MinMax.yy)) | asuint(MinMax.ww)) / tex_size.yy;
}
}
@@ -353,7 +363,7 @@ float4 fetch_c(int2 uv)
int2 clamp_wrap_uv_depth(int2 uv)
{
int4 mask = (int4)MskFix << 4;
int4 mask = asint(MinMax) << 4;
if (PS_WMS == PS_WMT)
{
if (PS_WMS == 2)
@@ -676,11 +686,7 @@ float4 fog(float4 c, float f)
float4 ps_color(PS_INPUT input)
{
#if PS_FST == 0 && PS_INVALID_TEX0 == 1
// Re-normalize coordinate from invalid GS to corrected texture size
float2 st = (input.t.xy * WH.xy) / (input.t.w * WH.zw);
float2 st_int = (input.ti.zw * WH.xy) / (input.t.w * WH.zw);
#elif PS_FST == 0
#if PS_FST == 0
float2 st = input.t.xy / input.t.w;
float2 st_int = input.ti.zw / input.t.w;
#else
@@ -872,26 +878,37 @@ PS_OUTPUT ps_main(PS_INPUT input)
{
uint4 denorm_c = uint4(C);
uint2 denorm_TA = uint2(float2(TA.xy) * 255.0f + 0.5f);
// Mask will take care of the correct destination
if (PS_READ_BA)
C.rb = C.bb;
else
C.rb = C.rr;
if (PS_READ_BA)
if (PS_READ16_SRC)
{
C.rb = (float2)float((denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7u) << 5));
if (denorm_c.a & 0x80u)
C.ga = (float2)(float((denorm_c.a & 0x7Fu) | (denorm_TA.y & 0x80u)));
C.ga = (float2)float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.y & 0x80u));
else
C.ga = (float2)(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
C.ga = (float2)float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u));
}
else
{
if (denorm_c.g & 0x80u)
C.ga = (float2)(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
// Mask will take care of the correct destination
if (PS_READ_BA)
C.rb = C.bb;
else
C.ga = (float2)(float((denorm_c.g & 0x7Fu) | (denorm_TA.x & 0x80u)));
C.rb = C.rr;
if (PS_READ_BA)
{
if (denorm_c.a & 0x80u)
C.ga = (float2)(float((denorm_c.a & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = (float2)(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
}
else
{
if (denorm_c.g & 0x80u)
C.ga = (float2)(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = (float2)(float((denorm_c.g & 0x7Fu) | (denorm_TA.x & 0x80u)));
}
}
}
@@ -75,13 +75,12 @@ layout(std140, binding = 0) uniform cb21
float MaxDepthPS;
float Af;
uvec4 MskFix;
uvec4 FbMask;
vec4 HalfTexel;
vec4 MinMax;
vec4 STRange;
ivec4 ChannelShuffle;
@@ -92,11 +91,6 @@ layout(std140, binding = 0) uniform cb21
};
#endif
//layout(std140, binding = 22) uniform cb22
//{
// vec4 rt_size;
//};
//////////////////////////////////////////////////////////////////////
// Default Sampler
//////////////////////////////////////////////////////////////////////
+30 -18
View File
@@ -109,7 +109,20 @@ vec4 sample_c(vec2 uv)
// As of 2018 this issue is still present.
uv = (trunc(uv * WH.zw) + vec2(0.5, 0.5)) / WH.zw;
#endif
uv *= STScale;
#if !PS_ADJS && !PS_ADJT
uv *= STScale;
#else
#if PS_ADJS
uv.x = (uv.x - STRange.x) * STRange.z;
#else
uv.x = uv.x * STScale.x;
#endif
#if PS_ADJT
uv.y = (uv.y - STRange.y) * STRange.w;
#else
uv.y = uv.y * STScale.y;
#endif
#endif
#if PS_AUTOMATIC_LOD == 1
return texture(TextureSampler, uv);
@@ -146,11 +159,7 @@ vec4 sample_p_norm(float u)
vec4 clamp_wrap_uv(vec4 uv)
{
vec4 uv_out = uv;
#if PS_INVALID_TEX0 == 1
vec4 tex_size = WH.zwzw;
#else
vec4 tex_size = WH.xyxy;
#endif
#if PS_WMS == PS_WMT
@@ -162,7 +171,7 @@ vec4 clamp_wrap_uv(vec4 uv)
// textures. Fixes Xenosaga's hair issue.
uv = fract(uv);
#endif
uv_out = vec4((uvec4(uv * tex_size) & MskFix.xyxy) | MskFix.zwzw) / tex_size;
uv_out = vec4((uvec4(uv * tex_size) & floatBitsToUint(MinMax.xyxy)) | floatBitsToUint(MinMax.zwzw)) / tex_size;
#endif
#else // PS_WMS != PS_WMT
@@ -174,7 +183,7 @@ vec4 clamp_wrap_uv(vec4 uv)
#if PS_FST == 0
uv.xz = fract(uv.xz);
#endif
uv_out.xz = vec2((uvec2(uv.xz * tex_size.xx) & MskFix.xx) | MskFix.zz) / tex_size.xx;
uv_out.xz = vec2((uvec2(uv.xz * tex_size.xx) & floatBitsToUint(MinMax.xx)) | floatBitsToUint(MinMax.zz)) / tex_size.xx;
#endif
@@ -185,7 +194,7 @@ vec4 clamp_wrap_uv(vec4 uv)
#if PS_FST == 0
uv.yw = fract(uv.yw);
#endif
uv_out.yw = vec2((uvec2(uv.yw * tex_size.yy) & MskFix.yy) | MskFix.ww) / tex_size.yy;
uv_out.yw = vec2((uvec2(uv.yw * tex_size.yy) & floatBitsToUint(MinMax.yy)) | floatBitsToUint(MinMax.ww)) / tex_size.yy;
#endif
#endif
@@ -227,7 +236,7 @@ uvec4 sample_4_index(vec4 uv)
#if PS_PAL_FMT == 1
// 4HL
return i & 0xFu
return i & 0xFu;
#elif PS_PAL_FMT == 2
// 4HH
return i >> 4u;
@@ -288,7 +297,7 @@ ivec2 clamp_wrap_uv_depth(ivec2 uv)
// Keep the full precision
// It allow to multiply the ScalingFactor before the 1/16 coeff
ivec4 mask = ivec4(MskFix) << 4;
ivec4 mask = floatBitsToInt(MinMax) << 4;
#if PS_WMS == PS_WMT
@@ -591,11 +600,7 @@ void fog(inout vec4 C, float f)
vec4 ps_color()
{
//FIXME: maybe we can set gl_Position.w = q in VS
#if (PS_FST == 0) && (PS_INVALID_TEX0 == 1)
// Re-normalize coordinate from invalid GS to corrected texture size
vec2 st = (PSin.t_float.xy * WH.xy) / (vec2(PSin.t_float.w) * WH.zw);
vec2 st_int = (PSin.t_int.zw * WH.xy) / (vec2(PSin.t_float.w) * WH.zw);
#elif (PS_FST == 0)
#if (PS_FST == 0)
vec2 st = PSin.t_float.xy / vec2(PSin.t_float.w);
vec2 st_int = PSin.t_int.zw / vec2(PSin.t_float.w);
#else
@@ -866,7 +871,13 @@ void ps_main()
#if PS_SHUFFLE
uvec4 denorm_c = uvec4(C);
uvec2 denorm_TA = uvec2(vec2(TA.xy) * 255.0f + 0.5f);
#if PS_READ16_SRC
C.rb = vec2(float((denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7u) << 5)));
if (bool(denorm_c.a & 0x80u))
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u)));
#else
// Write RB part. Mask will take care of the correct destination
#if PS_READ_BA
C.rb = C.bb;
@@ -902,9 +913,10 @@ void ps_main()
// float sel = step(128.0f, c.g);
// vec2 c_shuffle = vec2((denorm_c.gg & 0x7Fu) | (denorm_TA & 0x80u));
// c.ga = mix(c_shuffle.xx, c_shuffle.yy, sel);
#endif
#endif // PS_READ_BA
#endif
#endif // READ16_SRC
#endif // PS_SHUFFLE
// Must be done before alpha correction
+45 -33
View File
@@ -312,6 +312,8 @@ void main()
#define PS_FST 0
#define PS_WMS 0
#define PS_WMT 0
#define PS_ADJS 0
#define PS_ADJT 0
#define PS_FMT FMT_32
#define PS_AEM 0
#define PS_TFX 0
@@ -326,13 +328,13 @@ void main()
#define PS_POINT_SAMPLER 0
#define PS_SHUFFLE 0
#define PS_READ_BA 0
#define PS_READ16_SRC 0
#define PS_DFMT 0
#define PS_DEPTH_FMT 0
#define PS_PAL_FMT 0
#define PS_CHANNEL_FETCH 0
#define PS_TALES_OF_ABYSS_HLE 0
#define PS_URBAN_CHAOS_HLE 0
#define PS_INVALID_TEX0 0
#define PS_SCALE_FACTOR 1.0
#define PS_HDR 0
#define PS_COLCLIP 0
@@ -361,10 +363,10 @@ layout(std140, set = 0, binding = 1) uniform cb1
vec2 TA;
float MaxDepthPS;
float Af;
uvec4 MskFix;
uvec4 FbMask;
vec4 HalfTexel;
vec4 MinMax;
vec4 STRange;
ivec4 ChannelShuffle;
vec2 TC_OffsetHack;
vec2 STScale;
@@ -420,7 +422,20 @@ vec4 sample_c(vec2 uv)
// As of 2018 this issue is still present.
uv = (trunc(uv * WH.zw) + vec2(0.5, 0.5)) / WH.zw;
#endif
#if !PS_ADJS && !PS_ADJT
uv *= STScale;
#else
#if PS_ADJS
uv.x = (uv.x - STRange.x) * STRange.z;
#else
uv.x = uv.x * STScale.x;
#endif
#if PS_ADJT
uv.y = (uv.y - STRange.y) * STRange.w;
#else
uv.y = uv.y * STScale.y;
#endif
#endif
#if PS_AUTOMATIC_LOD == 1
return texture(Texture, uv);
@@ -455,13 +470,7 @@ vec4 sample_p_norm(float u)
vec4 clamp_wrap_uv(vec4 uv)
{
vec4 tex_size;
#if PS_INVALID_TEX0
tex_size = WH.zwzw;
#else
tex_size = WH.xyxy;
#endif
vec4 tex_size = WH.xyxy;
#if PS_WMS == PS_WMT
{
@@ -476,7 +485,7 @@ vec4 clamp_wrap_uv(vec4 uv)
// textures. Fixes Xenosaga's hair issue.
uv = fract(uv);
#endif
uv = vec4((uvec4(uv * tex_size) & MskFix.xyxy) | MskFix.zwzw) / tex_size;
uv = vec4((uvec4(uv * tex_size) & floatBitsToUint(MinMax.xyxy)) | floatBitsToUint(MinMax.zwzw)) / tex_size;
}
#endif
}
@@ -491,7 +500,7 @@ vec4 clamp_wrap_uv(vec4 uv)
#if PS_FST == 0
uv.xz = fract(uv.xz);
#endif
uv.xz = vec2((uvec2(uv.xz * tex_size.xx) & MskFix.xx) | MskFix.zz) / tex_size.xx;
uv.xz = vec2((uvec2(uv.xz * tex_size.xx) & floatBitsToUint(MinMax.xx)) | floatBitsToUint(MinMax.zz)) / tex_size.xx;
}
#endif
#if PS_WMT == 2
@@ -503,7 +512,7 @@ vec4 clamp_wrap_uv(vec4 uv)
#if PS_FST == 0
uv.yw = fract(uv.yw);
#endif
uv.yw = vec2((uvec2(uv.yw * tex_size.yy) & MskFix.yy) | MskFix.ww) / tex_size.yy;
uv.yw = vec2((uvec2(uv.yw * tex_size.yy) & floatBitsToUint(MinMax.yy)) | floatBitsToUint(MinMax.ww)) / tex_size.yy;
}
#endif
}
@@ -590,7 +599,7 @@ vec4 fetch_c(ivec2 uv)
ivec2 clamp_wrap_uv_depth(ivec2 uv)
{
ivec4 mask = ivec4(MskFix << 4);
ivec4 mask = floatBitsToInt(MinMax) << 4;
#if (PS_WMS == PS_WMT)
{
#if (PS_WMS == 2)
@@ -907,11 +916,7 @@ vec4 fog(vec4 c, float f)
vec4 ps_color()
{
#if PS_FST == 0 && PS_INVALID_TEX0 == 1
// Re-normalize coordinate from invalid GS to corrected texture size
vec2 st = (vsIn.t.xy * WH.xy) / (vsIn.t.w * WH.zw);
vec2 st_int = (vsIn.ti.zw * WH.xy) / (vsIn.t.w * WH.zw);
#elif PS_FST == 0
#if PS_FST == 0
vec2 st = vsIn.t.xy / vsIn.t.w;
vec2 st_int = vsIn.ti.zw / vsIn.t.w;
#else
@@ -1171,24 +1176,31 @@ void main()
#if PS_SHUFFLE
uvec4 denorm_c = uvec4(C);
uvec2 denorm_TA = uvec2(vec2(TA.xy) * 255.0f + 0.5f);
// Mask will take care of the correct destination
#if PS_READ_BA
C.rb = C.bb;
#else
C.rb = C.rr;
#endif
#if PS_READ_BA
#if PS_READ16_SRC
C.rb = vec2(float((denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7u) << 5)));
if ((denorm_c.a & 0x80u) != 0u)
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.y & 0x80u)));
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u)));
#else
if ((denorm_c.g & 0x80u) != 0u)
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.x & 0x80u)));
// Mask will take care of the correct destination
#if PS_READ_BA
C.rb = C.bb;
#else
C.rb = C.rr;
#endif
#if PS_READ_BA
if ((denorm_c.a & 0x80u) != 0u)
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
#else
if ((denorm_c.g & 0x80u) != 0u)
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.x & 0x80u)));
#endif
#endif
#endif
+3 -2
View File
@@ -10,8 +10,9 @@ function(detectOperatingSystem)
if(WIN32)
set(Windows TRUE PARENT_SCOPE)
elseif(UNIX AND APPLE)
# No easy way to filter out iOS.
message(WARNING "OS X/iOS isn't supported, the build will most likely fail")
if(IOS)
message(WARNING "iOS isn't supported, the build will most likely fail")
endif()
set(MacOSX TRUE PARENT_SCOPE)
elseif(UNIX)
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
+14 -3
View File
@@ -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()
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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;
+2
View File
@@ -100,6 +100,8 @@ static void SysPageFaultSignalFilter(int signal, siginfo_t* siginfo, void* ctx)
#if defined(__APPLE__) && defined(__x86_64__)
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext->__ss.__rip);
#elif defined(__FreeBSD__) && defined(__x86_64__)
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext.mc_rip);
#elif defined(__x86_64__)
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext.gregs[REG_RIP]);
#else
+14 -1
View File
@@ -153,6 +153,9 @@ namespace Vulkan
m_provoking_vertex = {};
m_provoking_vertex.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT;
m_line_rasterization_state = {};
m_line_rasterization_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT;
// set defaults
SetNoCullRasterizationState();
SetNoDepthTestState();
@@ -255,7 +258,17 @@ namespace Vulkan
m_ci.pRasterizationState = &m_rasterization_state;
}
void GraphicsPipelineBuilder::SetLineWidth(float width) { m_rasterization_state.lineWidth = width; }
void GraphicsPipelineBuilder::SetLineWidth(float width)
{
m_rasterization_state.lineWidth = width;
}
void GraphicsPipelineBuilder::SetLineRasterizationMode(VkLineRasterizationModeEXT mode)
{
Util::AddPointerToChain(&m_rasterization_state, &m_line_rasterization_state);
m_line_rasterization_state.lineRasterizationMode = mode;
}
void GraphicsPipelineBuilder::SetMultisamples(u32 multisamples, bool per_sample_shading)
{
+2
View File
@@ -96,6 +96,7 @@ namespace Vulkan
void SetRasterizationState(VkPolygonMode polygon_mode, VkCullModeFlags cull_mode, VkFrontFace front_face);
void SetLineWidth(float width);
void SetLineRasterizationMode(VkLineRasterizationModeEXT mode);
void SetMultisamples(u32 multisamples, bool per_sample_shading);
void SetNoCullRasterizationState();
@@ -157,6 +158,7 @@ namespace Vulkan
VkPipelineMultisampleStateCreateInfo m_multisample_state;
VkPipelineRasterizationProvokingVertexStateCreateInfoEXT m_provoking_vertex;
VkPipelineRasterizationLineStateCreateInfoEXT m_line_rasterization_state;
};
class ComputePipelineBuilder
+45 -13
View File
@@ -458,6 +458,8 @@ namespace Vulkan
SupportsExtension(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, false);
m_optional_extensions.vk_ext_calibrated_timestamps =
SupportsExtension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME, false);
m_optional_extensions.vk_ext_line_rasterization =
SupportsExtension(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false);
m_optional_extensions.vk_khr_driver_properties =
SupportsExtension(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, false);
m_optional_extensions.vk_arm_rasterization_order_attachment_access =
@@ -654,12 +656,19 @@ namespace Vulkan
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT};
VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesARM rasterization_order_access_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_ARM};
VkPhysicalDeviceLineRasterizationFeaturesEXT line_rasterization_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT};
if (m_optional_extensions.vk_ext_provoking_vertex)
{
provoking_vertex_feature.provokingVertexLast = VK_TRUE;
Util::AddPointerToChain(&device_info, &provoking_vertex_feature);
}
if (m_optional_extensions.vk_ext_line_rasterization)
{
line_rasterization_feature.bresenhamLines = VK_TRUE;
Util::AddPointerToChain(&device_info, &line_rasterization_feature);
}
if (m_optional_extensions.vk_arm_rasterization_order_attachment_access)
{
rasterization_order_access_feature.rasterizationOrderColorAttachmentAccess = VK_TRUE;
@@ -724,12 +733,16 @@ namespace Vulkan
VkPhysicalDeviceFeatures2 features2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2};
VkPhysicalDeviceProvokingVertexFeaturesEXT provoking_vertex_features = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT};
VkPhysicalDeviceLineRasterizationFeaturesEXT line_rasterization_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT};
VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesARM rasterization_order_access_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_ARM};
// add in optional feature structs
if (m_optional_extensions.vk_ext_provoking_vertex)
Util::AddPointerToChain(&features2, &provoking_vertex_features);
if (m_optional_extensions.vk_ext_line_rasterization)
Util::AddPointerToChain(&features2, &line_rasterization_feature);
if (m_optional_extensions.vk_arm_rasterization_order_attachment_access)
Util::AddPointerToChain(&features2, &rasterization_order_access_feature);
@@ -739,6 +752,7 @@ namespace Vulkan
// confirm we actually support it
m_optional_extensions.vk_ext_provoking_vertex &= (provoking_vertex_features.provokingVertexLast == VK_TRUE);
m_optional_extensions.vk_arm_rasterization_order_attachment_access &= (rasterization_order_access_feature.rasterizationOrderColorAttachmentAccess == VK_TRUE);
m_optional_extensions.vk_ext_line_rasterization &= (line_rasterization_feature.bresenhamLines == VK_TRUE);
VkPhysicalDeviceProperties2 properties2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2};
void** pNext = &properties2.pNext;
@@ -789,6 +803,8 @@ namespace Vulkan
Console.WriteLn("VK_EXT_provoking_vertex is %s",
m_optional_extensions.vk_ext_provoking_vertex ? "supported" : "NOT supported");
Console.WriteLn("VK_EXT_line_rasterization is %s",
m_optional_extensions.vk_ext_line_rasterization ? "supported" : "NOT supported");
Console.WriteLn("VK_EXT_calibrated_timestamps is %s",
m_optional_extensions.vk_ext_calibrated_timestamps ? "supported" : "NOT supported");
Console.WriteLn("VK_ARM_rasterization_order_attachment_access is %s",
@@ -1106,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.
@@ -1250,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)
@@ -1270,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;
}
@@ -1444,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();
@@ -1465,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)
@@ -1580,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)
{
@@ -1672,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: ");
@@ -1878,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);
}
@@ -1890,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)
{
@@ -1998,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: ");
+3
View File
@@ -52,6 +52,7 @@ namespace Vulkan
bool vk_ext_provoking_vertex : 1;
bool vk_ext_memory_budget : 1;
bool vk_ext_calibrated_timestamps : 1;
bool vk_ext_line_rasterization : 1;
bool vk_khr_driver_properties : 1;
bool vk_arm_rasterization_order_attachment_access : 1;
bool vk_khr_fragment_shader_barycentric : 1;
@@ -208,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.
@@ -372,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;
+6
View File
@@ -16,6 +16,12 @@
#include "common/emitter/internal.h"
#include "common/emitter/tools.h"
// warning: suggest braces around initialization of subobject [-Wmissing-braces]
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmissing-braces"
#endif
namespace x86Emitter
{
const xImplAVX_Move xVMOVAPS = {0x00, 0x28, 0x29};
+50 -11
View File
@@ -48,6 +48,7 @@
#include "pcsx2/HostDisplay.h"
#include "pcsx2/HostSettings.h"
#include "pcsx2/INISettingsInterface.h"
#include "pcsx2/PAD/Host/PAD.h"
#include "pcsx2/PerformanceMetrics.h"
#include "pcsx2/VMManager.h"
@@ -79,6 +80,7 @@ static std::optional<bool> s_use_window;
// Owned by the GS thread.
static u32 s_dump_frame_number = 0;
static u32 s_loop_number = s_loop_count;
bool GSRunner::InitializeConfig()
{
@@ -102,6 +104,13 @@ bool GSRunner::InitializeConfig()
// we don't need any sound output
si.SetStringValue("SPU2/Output", "OutputModule", "nullout");
// none of the bindings are going to resolve to anything
PAD::ClearPortBindings(si, 0);
si.ClearSection("Hotkeys");
// make sure any gamesettings inis in your tree don't get loaded
si.SetBoolValue("EmuCore", "EnablePerGameSettings", false);
// force logging
si.SetBoolValue("Logging", "EnableSystemConsole", true);
si.SetBoolValue("Logging", "EnableTimestamps", true);
@@ -271,21 +280,26 @@ void Host::ReleaseHostDisplay(bool clear_state)
g_host_display.reset();
}
bool Host::BeginPresentFrame(bool frame_skip)
HostDisplay::PresentResult Host::BeginPresentFrame(bool frame_skip)
{
// when we wrap around, don't race other files
GSJoinSnapshotThreads();
if (s_loop_number == 0)
{
// when we wrap around, don't race other files
GSJoinSnapshotThreads();
// queue dumping of this frame
std::string dump_path(fmt::format("{}_frame{}.png", s_output_prefix, s_dump_frame_number));
GSQueueSnapshot(dump_path);
// queue dumping of this frame
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;
const HostDisplay::PresentResult result = g_host_display->BeginPresent(frame_skip);
if (result != HostDisplay::PresentResult::OK)
{
// don't render imgui
ImGuiManager::SkipFrame();
}
// don't render imgui
ImGuiManager::NewFrame();
return false;
return result;
}
void Host::EndPresentFrame()
@@ -519,6 +533,29 @@ static bool ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& param
s_settings_interface.SetIntValue("EmuCore/GS", "Renderer", static_cast<int>(type));
continue;
}
else if (CHECK_ARG_PARAM("-renderhacks"))
{
std::string str(argv[++i]);
s_settings_interface.SetBoolValue("EmuCore/GS", "UserHacks", true);
if(str.find("af") != std::string::npos)
s_settings_interface.SetBoolValue("EmuCore/GS", "UserHacks_AutoFlush", true);
if (str.find("cpufb") != std::string::npos)
s_settings_interface.SetBoolValue("EmuCore/GS", "UserHacks_CPU_FB_Conversion", true);
if (str.find("dds") != std::string::npos)
s_settings_interface.SetBoolValue("EmuCore/GS", "UserHacks_DisableDepthSupport", true);
if (str.find("dpi") != std::string::npos)
s_settings_interface.SetBoolValue("EmuCore/GS", "UserHacks_DisablePartialInvalidation", true);
if (str.find("dsf") != std::string::npos)
s_settings_interface.SetBoolValue("EmuCore/GS", "UserHacks_DisableSafeFeatures", true);
if (str.find("tinrt") != std::string::npos)
s_settings_interface.SetBoolValue("EmuCore/GS", "UserHacks_TextureInsideRt", true);
if (str.find("plf") != std::string::npos)
s_settings_interface.SetBoolValue("EmuCore/GS", "preload_frame_with_gs_data", true);
continue;
}
else if (CHECK_ARG_PARAM("-logfile"))
{
const char* logfile = argv[++i];
@@ -627,6 +664,7 @@ int main(int argc, char* argv[])
// apply new settings (e.g. pick up renderer change)
VMManager::ApplySettings();
GSDumpReplayer::SetIsDumpRunner(true);
if (VMManager::Initialize(params))
{
@@ -651,6 +689,7 @@ void Host::CPUThreadVSync()
{
// update GS thread copy of frame number
GetMTGS().RunOnGSThread([frame_number = GSDumpReplayer::GetFrameNumber()]() { s_dump_frame_number = frame_number; });
GetMTGS().RunOnGSThread([loop_number = GSDumpReplayer::GetLoopCount()]() { s_loop_number = loop_number; });
// process any window messages (but we shouldn't really have any)
GSRunner::PumpPlatformMessages();
+10 -5
View File
@@ -16,7 +16,7 @@ def is_gs_path(path):
return False
def run_regression_test(runner, dumpdir, renderer, parallel, gspath):
def run_regression_test(runner, dumpdir, renderer, parallel, renderhacks, gspath):
args = [runner]
gsname = Path(gspath).name
while gsname.rfind('.') >= 0:
@@ -28,6 +28,10 @@ def run_regression_test(runner, dumpdir, renderer, parallel, gspath):
if renderer is not None:
args.extend(["-renderer", renderer])
if renderhacks is not None:
args.extend(["-renderhacks", renderhacks])
args.extend(["-dumpdir", real_dumpdir])
args.extend(["-logfile", os.path.join(real_dumpdir, "emulog.txt")])
@@ -46,7 +50,7 @@ def run_regression_test(runner, dumpdir, renderer, parallel, gspath):
subprocess.run(args)
def run_regression_tests(runner, gsdir, dumpdir, renderer, parallel=1):
def run_regression_tests(runner, gsdir, dumpdir, renderer, renderhacks, parallel=1):
paths = glob.glob(gsdir + "/*.*", recursive=True)
gamepaths = list(filter(is_gs_path, paths))
@@ -57,10 +61,10 @@ def run_regression_tests(runner, gsdir, dumpdir, renderer, parallel=1):
if parallel <= 1:
for game in gamepaths:
run_regression_test(runner, dumpdir, renderer, parallel, game)
run_regression_test(runner, dumpdir, renderer, parallel, renderhacks, game)
else:
print("Processing %u games on %u processors" % (len(gamepaths), parallel))
func = partial(run_regression_test, runner, dumpdir, renderer, parallel)
func = partial(run_regression_test, runner, dumpdir, renderer, parallel, renderhacks)
pool = multiprocessing.Pool(parallel)
pool.map(func, gamepaths)
pool.close()
@@ -76,10 +80,11 @@ if __name__ == "__main__":
parser.add_argument("-dumpdir", action="store", required=True, help="Base directory to dump frames to")
parser.add_argument("-renderer", action="store", required=False, help="Renderer to use")
parser.add_argument("-parallel", action="store", type=int, default=1, help="Number of proceeses to run")
parser.add_argument("-renderhacks", action="store", required=False, help="Enable HW Renering hacks")
args = parser.parse_args()
if not run_regression_tests(args.runner, os.path.realpath(args.gsdir), os.path.realpath(args.dumpdir), args.renderer, args.parallel):
if not run_regression_tests(args.runner, os.path.realpath(args.gsdir), os.path.realpath(args.dumpdir), args.renderer, args.renderhacks, args.parallel):
sys.exit(1)
else:
sys.exit(0)
+1
View File
@@ -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
+3 -6
View File
@@ -107,18 +107,15 @@ QVariant StackModel::headerData(int section, Qt::Orientation orientation, int ro
void StackModel::refreshData()
{
if (m_cpu.getCpuType() == BREAKPOINT_IOP)
return;
// Hopefully in the near future we can get a stack frame for
// each thread
beginResetModel();
for (const auto& thread : getEEThreads())
for (const auto& thread : m_cpu.GetThreadList())
{
if (thread.data.status == THS_RUN)
if (thread->Status() == ThreadStatus::THS_RUN)
{
m_stackFrames = MipsStackWalk::Walk(&m_cpu, m_cpu.getPC(), m_cpu.getRegister(0, 31), m_cpu.getRegister(0, 29),
thread.data.entry_init, thread.data.stack);
thread->EntryPoint(), thread->StackTop());
break;
}
}
+24 -28
View File
@@ -27,10 +27,7 @@ ThreadModel::ThreadModel(DebugInterface& cpu, QObject* parent)
int ThreadModel::rowCount(const QModelIndex&) const
{
if (m_cpu.getCpuType() == BREAKPOINT_EE)
return getEEThreads().size();
else
return 0;
return m_cpu.GetThreadList().size();
}
int ThreadModel::columnCount(const QModelIndex&) const
@@ -40,66 +37,65 @@ int ThreadModel::columnCount(const QModelIndex&) const
QVariant ThreadModel::data(const QModelIndex& index, int role) const
{
const auto threads = m_cpu.GetThreadList();
auto* const thread = threads.at(index.row()).get();
if (role == Qt::DisplayRole)
{
const auto thread = getEEThreads().at(index.row());
switch (index.column())
{
case ThreadModel::ID:
return thread.tid;
return thread->TID();
case ThreadModel::PC:
{
if (thread.data.status == THS_RUN)
if (thread->Status() == ThreadStatus::THS_RUN)
return QtUtils::FilledQStringFromValue(m_cpu.getPC(), 16);
else
return QtUtils::FilledQStringFromValue(thread.data.entry, 16);
return QtUtils::FilledQStringFromValue(thread->PC(), 16);
}
case ThreadModel::ENTRY:
return QtUtils::FilledQStringFromValue(thread.data.entry_init, 16);
return QtUtils::FilledQStringFromValue(thread->EntryPoint(), 16);
case ThreadModel::PRIORITY:
return QString::number(thread.data.currentPriority);
return QString::number(thread->Priority());
case ThreadModel::STATE:
{
const auto& state = ThreadStateStrings.find(thread.data.status);
const auto& state = ThreadStateStrings.find(thread->Status());
if (state != ThreadStateStrings.end())
return state->second;
else
return tr("INVALID");
return tr("INVALID");
}
case ThreadModel::WAIT_TYPE:
{
const auto& waitType = ThreadWaitStrings.find(thread.data.waitType);
const auto& waitType = ThreadWaitStrings.find(thread->Wait());
if (waitType != ThreadWaitStrings.end())
return waitType->second;
else
return tr("INVALID");
return tr("INVALID");
}
}
}
else if (role == Qt::UserRole)
{
const auto thread = getEEThreads().at(index.row());
switch (index.column())
{
case ThreadModel::ID:
return thread.tid;
return thread->TID();
case ThreadModel::PC:
{
if (thread.data.status == THS_RUN)
if (thread->Status() == ThreadStatus::THS_RUN)
return m_cpu.getPC();
else
return thread.data.entry;
return thread->PC();
}
case ThreadModel::ENTRY:
return thread.data.entry_init;
return thread->EntryPoint();
case ThreadModel::PRIORITY:
return thread.data.currentPriority;
return thread->Priority();
case ThreadModel::STATE:
return thread.data.status;
return static_cast<u32>(thread->Status());
case ThreadModel::WAIT_TYPE:
return thread.data.waitType;
return static_cast<u32>(thread->Wait());
default:
return QVariant();
}
+20 -12
View File
@@ -48,19 +48,27 @@ public:
void refreshData();
private:
const std::map<int, QString> ThreadStateStrings{
{THS_BAD, tr("BAD")},
{THS_RUN, tr("RUN")},
{THS_READY, tr("READY")},
{THS_WAIT, tr("WAIT")},
{THS_SUSPEND, tr("SUSPEND")},
{THS_WAIT_SUSPEND, tr("WAIT SUSPEND")},
{THS_DORMANT, tr("DORMANT")}};
const std::map<ThreadStatus, QString> ThreadStateStrings{
{ThreadStatus::THS_BAD, tr("BAD")},
{ThreadStatus::THS_RUN, tr("RUN")},
{ThreadStatus::THS_READY, tr("READY")},
{ThreadStatus::THS_WAIT, tr("WAIT")},
{ThreadStatus::THS_SUSPEND, tr("SUSPEND")},
{ThreadStatus::THS_WAIT_SUSPEND, tr("WAIT SUSPEND")},
{ThreadStatus::THS_DORMANT, tr("DORMANT")},
};
const std::map<int, QString> ThreadWaitStrings{
{WAIT_NONE, tr("NONE")},
{WAIT_WAKEUP_REQ, tr("WAKEUP REQUEST")},
{WAIT_SEMA, tr("SEMAPHORE")}};
const std::map<WaitState, QString> ThreadWaitStrings{
{WaitState::NONE, tr("NONE")},
{WaitState::WAKEUP_REQ, tr("WAKEUP REQUEST")},
{WaitState::SEMA, tr("SEMAPHORE")},
{WaitState::SLEEP, tr("SLEEP")},
{WaitState::DELAY, tr("DELAY")},
{WaitState::EVENTFLAG, tr("EVENTFLAG")},
{WaitState::MBOX, tr("MBOX")},
{WaitState::VPOOL, tr("VPOOL")},
{WaitState::FIXPOOL, tr("FIXPOOL")},
};
DebugInterface& m_cpu;
};
+1 -1
View File
@@ -488,7 +488,7 @@ void GameListWidget::resizeTableViewColumnsToFit()
80, // last played
80, // size
60, // region
100 // compatibility
120 // compatibility
});
}
+38 -23
View File
@@ -983,27 +983,28 @@ void MainWindow::saveStateToConfig()
if (!isVisible())
return;
bool changed = false;
const QByteArray geometry(saveGeometry());
const QByteArray geometry_b64(geometry.toBase64());
const std::string old_geometry_b64(Host::GetBaseStringSettingValue("UI", "MainWindowGeometry"));
if (old_geometry_b64 != geometry_b64.constData())
{
const QByteArray geometry = saveGeometry();
const QByteArray geometry_b64 = geometry.toBase64();
const std::string old_geometry_b64 = Host::GetBaseStringSettingValue("UI", "MainWindowGeometry");
if (old_geometry_b64 != geometry_b64.constData())
{
Host::SetBaseStringSettingValue("UI", "MainWindowGeometry", geometry_b64.constData());
Host::CommitBaseSettingChanges();
}
Host::SetBaseStringSettingValue("UI", "MainWindowGeometry", geometry_b64.constData());
changed = true;
}
const QByteArray state(saveState());
const QByteArray state_b64(state.toBase64());
const std::string old_state_b64(Host::GetBaseStringSettingValue("UI", "MainWindowState"));
if (old_state_b64 != state_b64.constData())
{
const QByteArray state = saveState();
const QByteArray state_b64 = state.toBase64();
const std::string old_state_b64 = Host::GetBaseStringSettingValue("UI", "MainWindowState");
if (old_state_b64 != state_b64.constData())
{
Host::SetBaseStringSettingValue("UI", "MainWindowState", state_b64.constData());
Host::CommitBaseSettingChanges();
}
Host::SetBaseStringSettingValue("UI", "MainWindowState", state_b64.constData());
changed = true;
}
if (changed)
Host::CommitBaseSettingChanges();
}
void MainWindow::restoreStateFromConfig()
@@ -1363,6 +1364,7 @@ void MainWindow::onGameListRefreshProgress(const QString& status, int current, i
void MainWindow::onGameListRefreshComplete()
{
m_ui.statusBar->clearMessage();
clearProgressBar();
}
@@ -1463,7 +1465,6 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
if (m_ui.menuDebug->menuAction()->isVisible())
{
// TODO: Hook this up once it's implemented.
action = menu.addAction(tr("Boot and Debug"));
connect(action, &QAction::triggered, [this, entry]() { DebugInterface::setPauseOnEntry(true); startGameListEntry(entry); getDebuggerWindow()->show(); });
}
@@ -1932,6 +1933,7 @@ void MainWindow::onVMStopped()
// If we're closing or in batch mode, quit the whole application now.
if (m_is_closing || QtHost::InBatchMode())
{
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
QCoreApplication::quit();
return;
}
@@ -1975,16 +1977,24 @@ void MainWindow::showEvent(QShowEvent* event)
void MainWindow::closeEvent(QCloseEvent* event)
{
if (!requestShutdown(true, true, EmuConfig.SaveStateOnShutdown))
// If there's no VM, we can just exit as normal.
if (!s_vm_valid)
{
event->ignore();
saveStateToConfig();
QMainWindow::closeEvent(event);
return;
}
saveStateToConfig();
m_is_closing = true;
// But if there is, we have to cancel the action, regardless of whether we ended exiting
// or not. The window still needs to be visible while GS is shutting down.
event->ignore();
QMainWindow::closeEvent(event);
// Exit cancelled?
if (!requestShutdown(true, true, EmuConfig.SaveStateOnShutdown))
return;
// Application will be exited in VM stopped handler.
m_is_closing = true;
}
static QString getFilenameFromMimeData(const QMimeData* md)
@@ -2249,7 +2259,12 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main, bool
// Don't risk doing this on Wayland, it really doesn't like window state changes,
// and positioning has no effect anyway.
if (!s_use_central_widget)
restoreDisplayWindowGeometryFromConfig();
{
if (isVisible() && g_emu_thread->shouldRenderToMain())
container->move(pos());
else
restoreDisplayWindowGeometryFromConfig();
}
if (!is_exclusive_fullscreen)
container->showFullScreen();
+2 -1
View File
@@ -110,6 +110,8 @@ public:
/// Rescans a single file. NOTE: Happens on UI thread.
void rescanFile(const std::string& path);
void openDebugger();
public Q_SLOTS:
void checkForUpdates(bool display_message, bool force_check);
void refreshGameList(bool invalidate_cache);
@@ -241,7 +243,6 @@ private:
void updateInputRecordingActions(bool started);
DebuggerWindow* getDebuggerWindow();
void openDebugger();
ControllerSettingsDialog* getControllerSettingsDialog();
void doControllerSettings(ControllerSettingsDialog::Category category = ControllerSettingsDialog::Category::Count);
+18 -5
View File
@@ -96,6 +96,7 @@ static bool s_nogui_mode = false;
static bool s_start_fullscreen_ui = false;
static bool s_start_fullscreen_ui_fullscreen = false;
static bool s_test_config_and_exit = false;
static bool s_boot_and_debug = false;
//////////////////////////////////////////////////////////////////////////
// CPU Thread
@@ -966,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()
@@ -1621,6 +1622,7 @@ void QtHost::PrintCommandLineHelp(const std::string_view& progname)
std::fprintf(stderr, " -nofullscreen: Prevents fullscreen mode from triggering if enabled.\n");
std::fprintf(stderr, " -earlyconsolelog: Forces logging of early console messages to console.\n");
std::fprintf(stderr, " -testconfig: Initializes configuration and checks version, then exits.\n");
std::fprintf(stderr, " -debugger: Open debugger and break on entry point.\n");
#ifdef ENABLE_RAINTEGRATION
std::fprintf(stderr, " -raintegration: Use RAIntegration instead of built-in achievement support.\n");
#endif
@@ -1743,6 +1745,11 @@ bool QtHost::ParseCommandLineOptions(const QStringList& args, std::shared_ptr<VM
s_test_config_and_exit = true;
continue;
}
else if (CHECK_ARG(QStringLiteral("-debugger")))
{
s_boot_and_debug = true;
continue;
}
#ifdef ENABLE_RAINTEGRATION
else if (CHECK_ARG(QStringLiteral("-raintegration")))
{
@@ -1880,6 +1887,12 @@ int main(int argc, char* argv[])
if (s_start_fullscreen_ui)
g_emu_thread->startFullscreenUI(s_start_fullscreen_ui_fullscreen);
if (s_boot_and_debug)
{
DebugInterface::setPauseOnEntry(true);
main_window->openDebugger();
}
// Skip the update check if we're booting a game directly.
if (autoboot)
g_emu_thread->startVM(std::move(autoboot));
+12 -12
View File
@@ -595,7 +595,7 @@ namespace SettingWidgetBinder
/// Binds a widget's value to a setting, updating it when the value changes.
template <typename WidgetType>
static void BindWidgetToBoolSetting(
static inline void BindWidgetToBoolSetting(
SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, bool default_value)
{
using Accessor = SettingAccessor<WidgetType>;
@@ -636,7 +636,7 @@ namespace SettingWidgetBinder
}
template <typename WidgetType>
static void BindWidgetToIntSetting(
static inline void BindWidgetToIntSetting(
SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, int default_value, int option_offset = 0)
{
using Accessor = SettingAccessor<WidgetType>;
@@ -677,7 +677,7 @@ namespace SettingWidgetBinder
}
template <typename WidgetType>
static void BindWidgetToFloatSetting(
static inline void BindWidgetToFloatSetting(
SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, float default_value)
{
using Accessor = SettingAccessor<WidgetType>;
@@ -718,7 +718,7 @@ namespace SettingWidgetBinder
}
template <typename WidgetType>
static void BindWidgetToNormalizedSetting(
static inline void BindWidgetToNormalizedSetting(
SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, float range, float default_value)
{
using Accessor = SettingAccessor<WidgetType>;
@@ -759,7 +759,7 @@ namespace SettingWidgetBinder
}
template <typename WidgetType>
static void BindWidgetToStringSetting(
static inline void BindWidgetToStringSetting(
SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, std::string default_value = std::string())
{
using Accessor = SettingAccessor<WidgetType>;
@@ -804,7 +804,7 @@ namespace SettingWidgetBinder
}
template <typename WidgetType, typename DataType>
static void BindWidgetToEnumSetting(SettingsInterface* sif, WidgetType* widget, std::string section, std::string key,
static inline void BindWidgetToEnumSetting(SettingsInterface* sif, WidgetType* widget, std::string section, std::string key,
std::optional<DataType> (*from_string_function)(const char* str), const char* (*to_string_function)(DataType value),
DataType default_value)
{
@@ -866,7 +866,7 @@ namespace SettingWidgetBinder
}
template <typename WidgetType, typename DataType>
static void BindWidgetToEnumSetting(
static inline void BindWidgetToEnumSetting(
SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, const char** enum_names, DataType default_value)
{
using Accessor = SettingAccessor<WidgetType>;
@@ -928,7 +928,7 @@ namespace SettingWidgetBinder
}
template <typename WidgetType>
static void BindWidgetToEnumSetting(SettingsInterface* sif, WidgetType* widget, std::string section, std::string key,
static inline void BindWidgetToEnumSetting(SettingsInterface* sif, WidgetType* widget, std::string section, std::string key,
const char** enum_names, const char** enum_values, const char* default_value)
{
using Accessor = SettingAccessor<WidgetType>;
@@ -992,7 +992,7 @@ namespace SettingWidgetBinder
}
template <typename WidgetType>
static void BindWidgetToFolderSetting(SettingsInterface* sif, WidgetType* widget, QAbstractButton* browse_button,
static inline void BindWidgetToFolderSetting(SettingsInterface* sif, WidgetType* widget, QAbstractButton* browse_button,
QAbstractButton* open_button, QAbstractButton* reset_button, std::string section, std::string key, std::string default_value,
bool use_relative = true)
{
@@ -1068,7 +1068,7 @@ namespace SettingWidgetBinder
}
}
[[maybe_unused]] static void BindSliderToIntSetting(SettingsInterface* sif, QSlider* slider, QLabel* label, const QString& label_suffix,
static inline void BindSliderToIntSetting(SettingsInterface* sif, QSlider* slider, QLabel* label, const QString& label_suffix,
std::string section, std::string key, s32 default_value)
{
const s32 global_value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value);
@@ -1109,8 +1109,8 @@ namespace SettingWidgetBinder
});
slider->connect(slider, &QSlider::valueChanged, slider,
[sif, label, label_suffix, section = std::move(section), key = std::move(key),
orig_font = std::move(orig_font), bold_font = std::move(bold_font)](int value) {
[sif, label, label_suffix, section = std::move(section), key = std::move(key), orig_font = std::move(orig_font),
bold_font = std::move(bold_font)](int value) {
label->setText(QStringLiteral("%1%2").arg(value).arg(label_suffix));
if (label->font() != bold_font)
+2 -2
View File
@@ -72,7 +72,7 @@ AdvancedSettingsWidget::AdvancedSettingsWidget(SettingsDialog* dialog, QWidget*
dialog->registerWidgetHelp(m_ui.eeClampMode, tr("Clamping Mode"), tr("Normal (Default)"), tr(""));
dialog->registerWidgetHelp(m_ui.eeRecompiler, tr("Enable Recompiler"), tr("Checked"),
tr("Performs just - in - time binary translation of 64 - bit MIPS - IV machine code to x86."));
tr("Performs just-in-time binary translation of 64-bit MIPS-IV machine code to x86."));
dialog->registerWidgetHelp(m_ui.eeWaitLoopDetection, tr("Wait Loop Detection"), tr("Checked"),
tr("Moderate speedup for some games, with no known side effects."));
@@ -86,7 +86,7 @@ AdvancedSettingsWidget::AdvancedSettingsWidget(SettingsDialog* dialog, QWidget*
tr("Uses backpatching to avoid register flushing on every memory access."));
dialog->registerWidgetHelp(m_ui.pauseOnTLBMiss, tr("Pause On TLB Miss"), tr("Unchecked"),
tr("Pauses the virtual machine when a TLB miss occurs, instead of ignoring it and continuing. Note the the VM will pause after the "
tr("Pauses the virtual machine when a TLB miss occurs, instead of ignoring it and continuing. Note that the VM will pause after the "
"end of the block, not on the instruction which caused the exception. Refer to the console to see the address where the invalid "
"access occurred."));
+47 -38
View File
@@ -42,10 +42,12 @@
ControllerBindingWidget::ControllerBindingWidget(QWidget* parent, ControllerSettingsDialog* dialog, u32 port)
: QWidget(parent)
, m_dialog(dialog)
, m_config_section(StringUtil::StdStringFromFormat("Pad%u", port + 1u))
, m_config_section(fmt::format("Pad{}", port + 1))
, m_port_number(port)
{
m_ui.setupUi(this);
m_ui.groupBox->setTitle(tr("Controller Port %1").arg(port + 1));
populateControllerTypes();
onTypeChanged();
@@ -113,10 +115,9 @@ void ControllerBindingWidget::onTypeChanged()
if (has_settings)
{
const QString settings_title(tr("%1 Settings").arg(qApp->translate("PAD", cinfo->display_name)));
const gsl::span<const SettingInfo> settings(cinfo->settings, cinfo->num_settings);
m_settings_widget = new ControllerCustomSettingsWidget(
settings, m_config_section, std::string(), settings_title, cinfo->name, getDialog(), m_ui.stackedWidget);
m_settings_widget =
new ControllerCustomSettingsWidget(settings, m_config_section, std::string(), cinfo->name, getDialog(), m_ui.stackedWidget);
m_ui.stackedWidget->addWidget(m_settings_widget);
}
@@ -458,8 +459,7 @@ void ControllerMacroEditWidget::updateBinds()
//////////////////////////////////////////////////////////////////////////
ControllerCustomSettingsWidget::ControllerCustomSettingsWidget(gsl::span<const SettingInfo> settings, std::string config_section,
std::string config_prefix, const QString& group_title, const char* translation_ctx, ControllerSettingsDialog* dialog,
QWidget* parent_widget)
std::string config_prefix, const char* translation_ctx, ControllerSettingsDialog* dialog, QWidget* parent_widget)
: QWidget(parent_widget)
, m_settings(settings)
, m_config_section(std::move(config_section))
@@ -469,22 +469,19 @@ ControllerCustomSettingsWidget::ControllerCustomSettingsWidget(gsl::span<const S
if (settings.empty())
return;
QGroupBox* gbox = new QGroupBox(group_title, this);
QGridLayout* gbox_layout = new QGridLayout(gbox);
createSettingWidgets(translation_ctx, gbox, gbox_layout);
QScrollArea* sarea = new QScrollArea(this);
QWidget* swidget = new QWidget(sarea);
sarea->setWidget(swidget);
sarea->setWidgetResizable(true);
sarea->setFrameShape(QFrame::StyledPanel);
sarea->setFrameShadow(QFrame::Sunken);
QGridLayout* swidget_layout = new QGridLayout(swidget);
createSettingWidgets(translation_ctx, swidget, swidget_layout);
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(gbox);
QHBoxLayout* bottom_hlayout = new QHBoxLayout();
QPushButton* restore_defaults = new QPushButton(tr("Restore Default Settings"), this);
restore_defaults->setIcon(QIcon::fromTheme(QStringLiteral("restart-line")));
connect(restore_defaults, &QPushButton::clicked, this, &ControllerCustomSettingsWidget::restoreDefaults);
bottom_hlayout->addStretch(1);
bottom_hlayout->addWidget(restore_defaults);
layout->addLayout(bottom_hlayout);
layout->addStretch(1);
layout->addWidget(sarea);
}
ControllerCustomSettingsWidget::~ControllerCustomSettingsWidget() = default;
@@ -503,8 +500,6 @@ static std::tuple<QString, QString> getPrefixAndSuffixForIntFormat(const QString
return std::tie(prefix, suffix);
}
#if 0
// Unused until we handle multiplier below.
static std::tuple<QString, QString, int> getPrefixAndSuffixForFloatFormat(const QString& format)
{
QString prefix, suffix;
@@ -532,7 +527,6 @@ static std::tuple<QString, QString, int> getPrefixAndSuffixForFloatFormat(const
return std::tie(prefix, suffix, decimals);
}
#endif
void ControllerCustomSettingsWidget::createSettingWidgets(const char* translation_ctx, QWidget* widget_parent, QGridLayout* layout)
{
@@ -569,7 +563,8 @@ void ControllerCustomSettingsWidget::createSettingWidgets(const char* translatio
sb->setPrefix(prefix);
sb->setSuffix(suffix);
}
SettingWidgetBinder::BindWidgetToIntSetting(sif, sb, m_config_section, std::move(key_name), si.IntegerDefaultValue());
ControllerSettingWidgetBinder::BindWidgetToInputProfileInt(
sif, sb, m_config_section, std::move(key_name), si.IntegerDefaultValue());
layout->addWidget(new QLabel(qApp->translate(translation_ctx, si.display_name), widget_parent), current_row, 0);
layout->addWidget(sb, current_row, 1, 1, 3);
current_row++;
@@ -582,7 +577,7 @@ void ControllerCustomSettingsWidget::createSettingWidgets(const char* translatio
cb->setObjectName(QString::fromUtf8(si.name));
for (u32 i = 0; si.options[i] != nullptr; i++)
cb->addItem(qApp->translate(translation_ctx, si.options[i]));
SettingWidgetBinder::BindWidgetToIntSetting(
ControllerSettingWidgetBinder::BindWidgetToInputProfileInt(
sif, cb, m_config_section, std::move(key_name), si.IntegerDefaultValue(), si.IntegerMinValue());
layout->addWidget(new QLabel(qApp->translate(translation_ctx, si.display_name), widget_parent), current_row, 0);
layout->addWidget(cb, current_row, 1, 1, 3);
@@ -594,11 +589,10 @@ void ControllerCustomSettingsWidget::createSettingWidgets(const char* translatio
{
QDoubleSpinBox* sb = new QDoubleSpinBox(widget_parent);
sb->setObjectName(QString::fromUtf8(si.name));
sb->setMinimum(si.FloatMinValue());
sb->setMaximum(si.FloatMaxValue());
sb->setSingleStep(si.FloatStepValue());
#if 0
// We can't use this until we handle multiplier.
sb->setMinimum(si.FloatMinValue() * si.multiplier);
sb->setMaximum(si.FloatMaxValue() * si.multiplier);
sb->setSingleStep(si.FloatStepValue() * si.multiplier);
if (si.format)
{
const auto [prefix, suffix, decimals] = getPrefixAndSuffixForFloatFormat(QString::fromUtf8(si.format));
@@ -607,8 +601,9 @@ void ControllerCustomSettingsWidget::createSettingWidgets(const char* translatio
sb->setDecimals(decimals);
sb->setSuffix(suffix);
}
#endif
SettingWidgetBinder::BindWidgetToFloatSetting(sif, sb, m_config_section, std::move(key_name), si.FloatDefaultValue());
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(
sif, sb, m_config_section, std::move(key_name), si.FloatDefaultValue(), si.multiplier);
layout->addWidget(new QLabel(qApp->translate(translation_ctx, si.display_name), widget_parent), current_row, 0);
layout->addWidget(sb, current_row, 1, 1, 3);
current_row++;
@@ -619,7 +614,8 @@ void ControllerCustomSettingsWidget::createSettingWidgets(const char* translatio
{
QLineEdit* le = new QLineEdit(widget_parent);
le->setObjectName(QString::fromUtf8(si.name));
SettingWidgetBinder::BindWidgetToStringSetting(sif, le, m_config_section, std::move(key_name), si.StringDefaultValue());
ControllerSettingWidgetBinder::BindWidgetToInputProfileString(
sif, le, m_config_section, std::move(key_name), si.StringDefaultValue());
layout->addWidget(new QLabel(qApp->translate(translation_ctx, si.display_name), widget_parent), current_row, 0);
layout->addWidget(le, current_row, 1, 1, 3);
current_row++;
@@ -641,7 +637,8 @@ void ControllerCustomSettingsWidget::createSettingWidgets(const char* translatio
for (u32 i = 0; si.options[i] != nullptr; i++)
cb->addItem(qApp->translate(translation_ctx, si.options[i]), QString::fromUtf8(si.options[i]));
}
SettingWidgetBinder::BindWidgetToStringSetting(sif, cb, m_config_section, std::move(key_name), si.StringDefaultValue());
ControllerSettingWidgetBinder::BindWidgetToInputProfileString(
sif, cb, m_config_section, std::move(key_name), si.StringDefaultValue());
layout->addWidget(new QLabel(qApp->translate(translation_ctx, si.display_name), widget_parent), current_row, 0);
layout->addWidget(cb, current_row, 1, 1, 3);
current_row++;
@@ -653,7 +650,8 @@ void ControllerCustomSettingsWidget::createSettingWidgets(const char* translatio
QLineEdit* le = new QLineEdit(widget_parent);
le->setObjectName(QString::fromUtf8(si.name));
QPushButton* browse_button = new QPushButton(tr("Browse..."), widget_parent);
SettingWidgetBinder::BindWidgetToStringSetting(sif, le, m_config_section, std::move(key_name), si.StringDefaultValue());
ControllerSettingWidgetBinder::BindWidgetToInputProfileString(
sif, le, m_config_section, std::move(key_name), si.StringDefaultValue());
connect(browse_button, &QPushButton::clicked, [this, le]() {
const QString path(QDir::toNativeSeparators(QFileDialog::getOpenFileName(this, tr("Select File"))));
if (!path.isEmpty())
@@ -677,6 +675,16 @@ void ControllerCustomSettingsWidget::createSettingWidgets(const char* translatio
layout->addItem(new QSpacerItem(1, 10, QSizePolicy::Minimum, QSizePolicy::Fixed), current_row++, 0, 1, 4);
}
QHBoxLayout* bottom_hlayout = new QHBoxLayout();
QPushButton* restore_defaults = new QPushButton(tr("Restore Default Settings"), this);
restore_defaults->setIcon(QIcon::fromTheme(QStringLiteral("restart-line")));
connect(restore_defaults, &QPushButton::clicked, this, &ControllerCustomSettingsWidget::restoreDefaults);
bottom_hlayout->addStretch(1);
bottom_hlayout->addWidget(restore_defaults);
layout->addLayout(bottom_hlayout, current_row++, 0, 1, 4);
layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding), current_row++, 0, 1, 4);
}
void ControllerCustomSettingsWidget::restoreDefaults()
@@ -715,7 +723,7 @@ void ControllerCustomSettingsWidget::restoreDefaults()
{
QDoubleSpinBox* widget = findChild<QDoubleSpinBox*>(QString::fromStdString(si.name));
if (widget)
widget->setValue(si.FloatDefaultValue());
widget->setValue(si.FloatDefaultValue() * si.multiplier);
}
break;
@@ -851,10 +859,12 @@ ControllerBindingWidget_Base* ControllerBindingWidget_DualShock2::createInstance
USBDeviceWidget::USBDeviceWidget(QWidget* parent, ControllerSettingsDialog* dialog, u32 port)
: QWidget(parent)
, m_dialog(dialog)
, m_config_section(StringUtil::StdStringFromFormat("USB%u", port + 1u))
, m_config_section(fmt::format("USB{}", port + 1))
, m_port_number(port)
{
m_ui.setupUi(this);
m_ui.groupBox->setTitle(tr("USB Port %1").arg(port + 1));
populateDeviceTypes();
populatePages();
@@ -926,9 +936,8 @@ void USBDeviceWidget::populatePages()
if (!settings.empty())
{
const QString settings_title(tr("Device Settings"));
m_settings_widget = new ControllerCustomSettingsWidget(
settings, m_config_section, m_device_type + "_", settings_title, m_device_type.c_str(), m_dialog, m_ui.stackedWidget);
settings, m_config_section, m_device_type + "_", m_device_type.c_str(), m_dialog, m_ui.stackedWidget);
m_ui.stackedWidget->addWidget(m_settings_widget);
}
+1 -1
View File
@@ -139,7 +139,7 @@ class ControllerCustomSettingsWidget : public QWidget
public:
ControllerCustomSettingsWidget(gsl::span<const SettingInfo> settings, std::string config_section, std::string config_prefix,
const QString& group_title, const char* translation_ctx, ControllerSettingsDialog* dialog, QWidget* parent_widget);
const char* translation_ctx, ControllerSettingsDialog* dialog, QWidget* parent_widget);
~ControllerCustomSettingsWidget();
private Q_SLOTS:
@@ -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/>
@@ -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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Mouse Mapping Settings&lt;/span&gt;&lt;br/&gt;These settings fine-tune the behavior when mapping a mouse to the emulated controller.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>
@@ -38,7 +38,8 @@ namespace ControllerSettingWidgetBinder
{
/// Interface specific method of BindWidgetToBoolSetting().
template <typename WidgetType>
static void BindWidgetToInputProfileBool(SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, bool default_value)
static inline void BindWidgetToInputProfileBool(
SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, bool default_value)
{
using Accessor = SettingWidgetBinder::SettingAccessor<WidgetType>;
@@ -68,19 +69,53 @@ namespace ControllerSettingWidgetBinder
}
}
/// Interface specific method of BindWidgetToIntSetting().
template <typename WidgetType>
static inline void BindWidgetToInputProfileInt(
SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, s32 default_value, s32 option_offset = 0)
{
using Accessor = SettingWidgetBinder::SettingAccessor<WidgetType>;
if (sif)
{
const s32 value = sif->GetIntValue(section.c_str(), key.c_str(), default_value);
Accessor::setIntValue(widget, value - option_offset);
Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key), option_offset]() {
const float new_value = Accessor::getIntValue(widget);
sif->SetIntValue(section.c_str(), key.c_str(), new_value + option_offset);
sif->Save();
g_emu_thread->reloadGameSettings();
});
}
else
{
const s32 value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value);
Accessor::setIntValue(widget, value - option_offset);
Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), option_offset]() {
const s32 new_value = Accessor::getIntValue(widget);
Host::SetBaseIntSettingValue(section.c_str(), key.c_str(), new_value + option_offset);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings();
});
}
}
/// Interface specific method of BindWidgetToFloatSetting().
template <typename WidgetType>
static void BindWidgetToInputProfileFloat(SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, float default_value)
static inline void BindWidgetToInputProfileFloat(
SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, float default_value, float multiplier = 1.0f)
{
using Accessor = SettingWidgetBinder::SettingAccessor<WidgetType>;
if (sif)
{
const float value = sif->GetFloatValue(section.c_str(), key.c_str(), default_value);
Accessor::setFloatValue(widget, value);
Accessor::setFloatValue(widget, value * multiplier);
Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key)]() {
const float new_value = Accessor::getFloatValue(widget);
Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key), multiplier]() {
const float new_value = Accessor::getFloatValue(widget) / multiplier;
sif->SetFloatValue(section.c_str(), key.c_str(), new_value);
sif->Save();
g_emu_thread->reloadGameSettings();
@@ -89,10 +124,10 @@ namespace ControllerSettingWidgetBinder
else
{
const float value = Host::GetBaseFloatSettingValue(section.c_str(), key.c_str(), default_value);
Accessor::setFloatValue(widget, value);
Accessor::setFloatValue(widget, value * multiplier);
Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() {
const float new_value = Accessor::getFloatValue(widget);
Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), multiplier]() {
const float new_value = Accessor::getFloatValue(widget) / multiplier;
Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value);
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings();
@@ -102,12 +137,11 @@ namespace ControllerSettingWidgetBinder
/// Interface specific method of BindWidgetToNormalizedSetting().
template <typename WidgetType>
static void BindWidgetToInputProfileNormalized(
static inline void BindWidgetToInputProfileNormalized(
SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, float range, float default_value)
{
using Accessor = SettingWidgetBinder::SettingAccessor<WidgetType>;
if (sif)
{
const float value = sif->GetFloatValue(section.c_str(), key.c_str(), default_value);
@@ -136,7 +170,7 @@ namespace ControllerSettingWidgetBinder
/// Interface specific method of BindWidgetToStringSetting().
template <typename WidgetType>
static void BindWidgetToInputProfileString(
static inline void BindWidgetToInputProfileString(
SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, std::string default_value = std::string())
{
using Accessor = SettingWidgetBinder::SettingAccessor<WidgetType>;
@@ -160,7 +194,8 @@ namespace ControllerSettingWidgetBinder
}
else
{
const QString value(QString::fromStdString(Host::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value.c_str())));
const QString value(
QString::fromStdString(Host::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value.c_str())));
Accessor::setStringValue(widget, value);
+71 -25
View File
@@ -260,9 +260,13 @@ DEV9SettingsWidget::DEV9SettingsWidget(SettingsDialog* dialog, QWidget* parent)
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hddEnabled, "DEV9/Hdd", "HddEnable", false);
connect(m_ui.hddFile, &QLineEdit::editingFinished, this, &DEV9SettingsWidget::onHddFileEdit);
SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.hddFile, "DEV9/Hdd", "HddFile", "DEV9hdd.raw");
if (m_dialog->isPerGameSettings())
{
m_ui.hddFile->setText(QString::fromUtf8(m_dialog->getStringValue("DEV9/Hdd", "HddFile", "").value().c_str()));
m_ui.hddFile->setPlaceholderText(QString::fromUtf8(Host::GetBaseStringSettingValue("DEV9/Hdd", "HddFile", "DEV9hdd.raw")));
}
else
m_ui.hddFile->setText(QString::fromUtf8(m_dialog->getStringValue("DEV9/Hdd", "HddFile", "DEV9hdd.raw").value().c_str()));
connect(m_ui.hddBrowseFile, &QPushButton::clicked, this, &DEV9SettingsWidget::onHddBrowseFileClicked);
//TODO: need a getUintValue for if 48bit support occurs
@@ -270,18 +274,27 @@ DEV9SettingsWidget::DEV9SettingsWidget(SettingsDialog* dialog, QWidget* parent)
if (m_dialog->isPerGameSettings())
{
std::optional<int> sizeOpt = std::nullopt;
if (size > 0)
sizeOpt = size;
const int sizeGlobal = (u64)Host::GetBaseIntSettingValue("DEV9/Hdd", "HddSizeSectors", 0) * 512 / (1024 * 1024 * 1024);
m_ui.hddSizeSpinBox->setMinimum(39);
m_ui.hddSizeSpinBox->setSpecialValueText(tr("Global [%1]").arg(sizeGlobal));
SettingWidgetBinder::SettingAccessor<QSpinBox>::makeNullableInt(m_ui.hddSizeSpinBox, sizeGlobal);
SettingWidgetBinder::SettingAccessor<QSpinBox>::setNullableIntValue(m_ui.hddSizeSpinBox, sizeOpt);
m_ui.hddSizeSlider->setValue(sizeOpt.value_or(sizeGlobal));
m_ui.hddSizeSlider->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_ui.hddSizeSlider, &QSlider::customContextMenuRequested, this, &DEV9SettingsWidget::onHddSizeSliderContext);
}
else
{
m_ui.hddSizeSlider->setValue(size);
SettingWidgetBinder::SettingAccessor<QSpinBox>::setIntValue(m_ui.hddSizeSpinBox, size);
}
// clang-format off
m_ui.hddSizeSlider ->setValue(size);
m_ui.hddSizeSpinBox->setValue(size);
connect(m_ui.hddSizeSlider, QOverload<int>::of(&QSlider ::valueChanged), this, &DEV9SettingsWidget::onHddSizeSlide);
connect(m_ui.hddSizeSpinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &DEV9SettingsWidget::onHddSizeSpin );
// clang-format on
connect(m_ui.hddSizeSlider, QOverload<int>::of(&QSlider::valueChanged), this, &DEV9SettingsWidget::onHddSizeSlide);
SettingWidgetBinder::SettingAccessor<QSpinBox>::connectValueChanged(m_ui.hddSizeSpinBox, [&]() { onHddSizeAccessorSpin(); });
connect(m_ui.hddCreate, &QPushButton::clicked, this, &DEV9SettingsWidget::onHddCreateClicked);
}
@@ -682,7 +695,7 @@ void DEV9SettingsWidget::onEthHostEdit(QStandardItem* item)
void DEV9SettingsWidget::onHddEnabledChanged(int state)
{
const bool enabled = state == Qt::CheckState::PartiallyChecked ? m_dialog->getEffectiveBoolValue("DEV9/Hdd", "HddEnable", false) : state;
const bool enabled = state == Qt::CheckState::PartiallyChecked ? Host::GetBaseBoolSettingValue("DEV9/Hdd", "HddEnable", false) : state;
m_ui.hddFile->setEnabled(enabled);
m_ui.hddFileLabel->setEnabled(enabled);
@@ -699,8 +712,8 @@ void DEV9SettingsWidget::onHddBrowseFileClicked()
{
QString path =
QDir::toNativeSeparators(QFileDialog::getSaveFileName(QtUtils::GetRootWidget(this), tr("HDD Image File"),
!m_ui.hddFile->text().isEmpty() ? m_ui.hddFile->text() : "DEV9hdd.raw", tr("HDD (*.raw)"), nullptr,
QFileDialog::DontConfirmOverwrite));
!m_ui.hddFile->text().isEmpty() ? m_ui.hddFile->text() : (!m_ui.hddFile->placeholderText().isEmpty() ? m_ui.hddFile->placeholderText() : "DEV9hdd.raw"),
tr("HDD (*.raw)"), nullptr, QFileDialog::DontConfirmOverwrite));
if (path.isEmpty())
return;
@@ -711,10 +724,16 @@ void DEV9SettingsWidget::onHddBrowseFileClicked()
void DEV9SettingsWidget::onHddFileEdit()
{
//Check if file exists, if so set HddSize to correct value
// Check if file exists, if so set HddSize to correct value.
// Also save the hddPath setting
std::string hddPath(m_ui.hddFile->text().toStdString());
if (hddPath.empty())
{
m_dialog->setStringSettingValue("DEV9/Hdd", "HddFile", std::nullopt);
return;
}
else
m_dialog->setStringSettingValue("DEV9/Hdd", "HddFile", hddPath.c_str());
if (!Path::IsAbsolute(hddPath))
hddPath = Path::Combine(EmuFolders::Settings, hddPath);
@@ -739,22 +758,49 @@ void DEV9SettingsWidget::onHddFileEdit()
void DEV9SettingsWidget::onHddSizeSlide(int i)
{
// We have to call onHddSizeAccessorSpin() ourself, as the value could still be considered null when the valueChanged signal is fired
QSignalBlocker sb(m_ui.hddSizeSpinBox);
m_ui.hddSizeSpinBox->setValue(i);
m_dialog->setIntSettingValue("DEV9/Hdd", "HddSizeSectors", (int)((s64)i * 1024 * 1024 * 1024 / 512));
SettingWidgetBinder::SettingAccessor<QSpinBox>::setNullableIntValue(m_ui.hddSizeSpinBox, i);
onHddSizeAccessorSpin();
}
void DEV9SettingsWidget::onHddSizeSpin(int i)
void DEV9SettingsWidget::onHddSizeSliderContext(const QPoint& pt)
{
QSignalBlocker sb(m_ui.hddSizeSlider);
m_ui.hddSizeSlider->setValue(i);
QMenu menu(m_ui.hddSizeSlider);
connect(menu.addAction(qApp->translate("SettingWidgetBinder", "Reset")), &QAction::triggered, this, &DEV9SettingsWidget::onHddSizeSliderReset);
menu.exec(m_ui.hddSizeSlider->mapToGlobal(pt));
}
//TODO: need a setUintSettingValue for if 48bit support occurs
if (i == 39)
m_dialog->setIntSettingValue("DEV9/Hdd", "HddSizeSectors", std::nullopt);
void DEV9SettingsWidget::onHddSizeSliderReset([[maybe_unused]] bool checked)
{
// We have to call onHddSizeAccessorSpin() ourself, as the value could still be considered non-null when the valueChanged signal is fired
QSignalBlocker sb(m_ui.hddSizeSpinBox);
SettingWidgetBinder::SettingAccessor<QSpinBox>::setNullableIntValue(m_ui.hddSizeSpinBox, std::nullopt);
onHddSizeAccessorSpin();
}
void DEV9SettingsWidget::onHddSizeAccessorSpin()
{
//TODO: need a getUintValue for if 48bit support occurs
QSignalBlocker sb(m_ui.hddSizeSlider);
if (m_dialog->isPerGameSettings())
{
std::optional<int> new_value = SettingWidgetBinder::SettingAccessor<QSpinBox>::getNullableIntValue(m_ui.hddSizeSpinBox);
const int sizeGlobal = (u64)Host::GetBaseIntSettingValue("DEV9/Hdd", "HddSizeSectors", 0) * 512 / (1024 * 1024 * 1024);
m_ui.hddSizeSlider->setValue(new_value.value_or(sizeGlobal));
if (new_value.has_value())
m_dialog->setIntSettingValue("DEV9/Hdd", "HddSizeSectors", new_value.value() * (1024 * 1024 * 1024 / 512));
else
m_dialog->setIntSettingValue("DEV9/Hdd", "HddSizeSectors", std::nullopt);
}
else
m_dialog->setIntSettingValue("DEV9/Hdd", "HddSizeSectors", i * (1024 * 1024 * 1024 / 512));
{
const int new_value = SettingWidgetBinder::SettingAccessor<QSpinBox>::getIntValue(m_ui.hddSizeSpinBox);
m_ui.hddSizeSlider->setValue(new_value);
m_dialog->setIntSettingValue("DEV9/Hdd", "HddSizeSectors", new_value * (1024 * 1024 * 1024 / 512));
}
}
void DEV9SettingsWidget::onHddCreateClicked()
@@ -774,7 +820,7 @@ void DEV9SettingsWidget::onHddCreateClicked()
if (!Path::IsAbsolute(hddPath))
hddPath = Path::Combine(EmuFolders::Settings, hddPath);
if (!FileSystem::FileExists(hddPath.c_str()))
if (FileSystem::FileExists(hddPath.c_str()))
{
//GHC uses UTF8 on all platforms
QMessageBox::StandardButton selection =
+5 -1
View File
@@ -49,7 +49,11 @@ private Q_SLOTS:
void onHddBrowseFileClicked();
void onHddFileEdit();
void onHddSizeSlide(int i);
void onHddSizeSpin(int i);
// Per game only.
void onHddSizeSliderContext(const QPoint& pt);
void onHddSizeSliderReset(bool checked = false);
//
void onHddSizeAccessorSpin();
void onHddCreateClicked();
public:
@@ -120,8 +120,8 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsDialog* dialog, QWidget
tr("Allows games and homebrew to access files / folders directly on the host computer."));
dialog->registerWidgetHelp(m_ui.optimalFramePacing, tr("Optimal Frame Pacing"), tr("Unchecked"),
tr("Sets the vsync queue size to 0, making every frame be completed and presented by the GS before input is polled, and the next frame begins. "
"Using this setting can reduce input lag, at the cost of measurably higher CPU and GPU requirements."));
tr("Sets the VSync queue size to 0, making every frame be completed and presented by the GS before input is polled and the next frame begins. "
"Using this setting can reduce input lag at the cost of measurably higher CPU and GPU requirements."));
dialog->registerWidgetHelp(m_ui.maxFrameLatency, tr("Maximum Frame Latency"), tr("2 Frames"),
tr("Sets the maximum number of frames that can be queued up to the GS, before the CPU thread will wait for one of them to complete before continuing. "
"Higher values can assist with smoothing out irregular frame times, but add additional input lag."));
+5
View File
@@ -346,6 +346,11 @@
<string>In-Game</string>
</property>
</item>
<item>
<property name="text">
<string>Playable</string>
</property>
</item>
<item>
<property name="text">
<string>Perfect</string>
+22 -11
View File
@@ -372,7 +372,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
dialog->registerWidgetHelp(m_ui.PCRTCOverscan, tr("Show Overscan"), tr("Unchecked"),
tr("Enables the option to show the overscan area on games which draw more than the safe area of the screen."));
dialog->registerWidgetHelp(m_ui.fmvAspectRatio, tr("FMV Aspect Ratio"), tr("Off (Default)"), tr("Overrides the FMV aspect ratio."));
dialog->registerWidgetHelp(m_ui.fmvAspectRatio, tr("FMV Aspect Ratio"), tr("Off (Default)"),
tr("Overrides the full-motion video (FMV) aspect ratio."));
dialog->registerWidgetHelp(m_ui.PCRTCAntiBlur, tr("Anti-Blur"), tr("Checked"),
tr("Enables internal Anti-Blur hacks. Less accurate to PS2 rendering but will make a lot of games look less blurry."));
@@ -402,24 +403,32 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
tr("Selects the quality at which screenshots will be compressed. Higher values preserve more detail for JPEG, and reduce file "
"size for PNG."));
dialog->registerWidgetHelp(m_ui.stretchY, tr("Stretch Height"), tr("100%"), tr(""));
dialog->registerWidgetHelp(m_ui.stretchY, tr("Stretch Height"), tr("100%"),
tr("Stretches (> 100%) or squashes (< 100%) the vertical component of the display"));
dialog->registerWidgetHelp(m_ui.fullscreenModes, tr("Fullscreen Mode"), tr("Borderless Fullscreen"),
tr("Chooses the fullscreen resolution and frequency."));
dialog->registerWidgetHelp(m_ui.cropLeft, tr("Left"), tr("0px"), tr(""));
dialog->registerWidgetHelp(m_ui.cropLeft, tr("Left"), tr("0px"),
tr("Changes number of pixels cropped from the left side of the display"));
dialog->registerWidgetHelp(m_ui.cropTop, tr("Top"), tr("0px"), tr(""));
dialog->registerWidgetHelp(m_ui.cropTop, tr("Top"), tr("0px"),
tr("Changes number of pixels cropped from the top of the display"));
dialog->registerWidgetHelp(m_ui.cropRight, tr("Right"), tr("0px"), tr(""));
dialog->registerWidgetHelp(m_ui.cropRight, tr("Right"), tr("0px"),
tr("Changes number of pixels cropped from the right side of the display"));
dialog->registerWidgetHelp(m_ui.cropBottom, tr("Bottom"), tr("0px"), tr(""));
dialog->registerWidgetHelp(m_ui.cropBottom, tr("Bottom"), tr("0px"),
tr("Changes number of pixels cropped from the bottom of the display"));
}
// Rendering tab
{
// Hardware
dialog->registerWidgetHelp(m_ui.upscaleMultiplier, tr("Internal Resolution"), tr("Native (PS2) (Default)"), tr(""));
dialog->registerWidgetHelp(m_ui.upscaleMultiplier, tr("Internal Resolution"), tr("Native (PS2) (Default)"),
tr("Control the resolution at which games are rendered. High resolutions can impact performance on"
"older or lower-end GPUs.<br>Non-native resolution may cause minor graphical issues in some games.<br>"
"FMV resolution will remain unchanged, as the video files are pre-rendered."));
dialog->registerWidgetHelp(
m_ui.mipmapping, tr("Mipmapping"), tr("Automatic (Default)"), tr("Control the accuracy level of the mipmapping emulation."));
@@ -491,7 +500,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
dialog->registerWidgetHelp(m_ui.cpuSpriteRenderBW, tr("CPU Sprite Renderer Size"), tr("0 (Disabled)"), tr(""));
dialog->registerWidgetHelp(m_ui.cpuCLUTRender, tr("Software Clut Render"), tr("0 (Disabled)"), tr(""));
dialog->registerWidgetHelp(m_ui.cpuCLUTRender, tr("Software CLUT Render"), tr("0 (Disabled)"), tr(""));
dialog->registerWidgetHelp(m_ui.skipDrawStart, tr("Skipdraw Range Start"), tr("0"),
tr("Completely skips drawing surfaces from the surface in the left box up to the surface specified in the box on the right."));
@@ -592,13 +601,14 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
dialog->registerWidgetHelp(m_ui.shadeBoostSaturation, tr("Saturation"), tr("50"), tr(""));
dialog->registerWidgetHelp(m_ui.tvShader, tr("TV Shader"), tr("None (Default)"), tr(""));
dialog->registerWidgetHelp(m_ui.tvShader, tr("TV Shader"), tr("None (Default)"),
tr("Applies a shader which replicates the visual effects of different styles of television set."));
}
// OSD tab
{
dialog->registerWidgetHelp(
m_ui.osdScale, tr("OSD Scale"), tr("100%"), tr("Scales the size of the onscreen OSD from 100% to 500%."));
m_ui.osdScale, tr("OSD Scale"), tr("100%"), tr("Scales the size of the onscreen OSD from 50% to 500%."));
dialog->registerWidgetHelp(m_ui.osdShowMessages, tr("Show OSD Messages"), tr("Checked"),
tr("Shows on-screen-display messages when events occur such as save states being "
@@ -654,7 +664,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
tr("Allows the GPU instead of just the CPU to transform lines into sprites. "
"This reduces CPU load and bandwidth requirement, but it is heavier on the GPU."));
dialog->registerWidgetHelp(m_ui.gsDumpCompression, tr("GS Dump Compression"), tr("Zstandard (zst)"), tr(""));
dialog->registerWidgetHelp(m_ui.gsDumpCompression, tr("GS Dump Compression"), tr("Zstandard (zst)"),
tr("Change the compression algorithm used when creating a GS dump."));
dialog->registerWidgetHelp(m_ui.useBlitSwapChain, tr("Use Blit Swap Chain"), tr("Unchecked"),
tr("Uses a blit presentation model instead of flipping when using the Direct3D 11 "
+1 -1
View File
@@ -1536,7 +1536,7 @@
<string>%</string>
</property>
<property name="minimum">
<number>100</number>
<number>50</number>
</property>
<property name="maximum">
<number>500</number>
+33
View File
@@ -17,6 +17,7 @@
#include "QtHost.h"
#include "QtUtils.h"
#include "Settings/ControllerSettingWidgetBinder.h"
#include "Settings/InputBindingDialog.h"
#include "Settings/InputBindingWidget.h"
#include <QtCore/QTimer>
@@ -24,6 +25,8 @@
#include <QtGui/QMouseEvent>
#include <QtGui/QWheelEvent>
#include "fmt/format.h"
// _BitScanForward()
#include "pcsx2/GS/GSIntrin.h"
@@ -45,6 +48,26 @@ InputBindingDialog::InputBindingDialog(SettingsInterface* sif, InputBindingInfo:
connect(m_ui.clearBindings, &QPushButton::clicked, this, &InputBindingDialog::onClearBindingsButtonClicked);
connect(m_ui.buttonBox, &QDialogButtonBox::rejected, [this]() { done(0); });
updateList();
// Only show the sensitivity controls for binds where it's applicable.
if (bind_type == InputBindingInfo::Type::Button || bind_type == InputBindingInfo::Type::Axis ||
bind_type == InputBindingInfo::Type::HalfAxis)
{
ControllerSettingWidgetBinder::BindWidgetToInputProfileNormalized(
sif, m_ui.sensitivity, m_section_name, fmt::format("{}Scale", m_key_name), 100.0f, 1.0f);
ControllerSettingWidgetBinder::BindWidgetToInputProfileNormalized(
sif, m_ui.deadzone, m_section_name, fmt::format("{}Deadzone", m_key_name), 100.0f, 0.0f);
connect(m_ui.sensitivity, &QSlider::valueChanged, this, &InputBindingDialog::onSensitivityChanged);
connect(m_ui.deadzone, &QSlider::valueChanged, this, &InputBindingDialog::onDeadzoneChanged);
onSensitivityChanged(m_ui.sensitivity->value());
onDeadzoneChanged(m_ui.deadzone->value());
}
else
{
m_ui.verticalLayout->removeWidget(m_ui.sensitivityWidget);
}
}
InputBindingDialog::~InputBindingDialog()
@@ -318,6 +341,16 @@ void InputBindingDialog::inputManagerHookCallback(InputBindingKey key, float val
}
}
void InputBindingDialog::onSensitivityChanged(int value)
{
m_ui.sensitivityValue->setText(tr("%1%").arg(value));
}
void InputBindingDialog::onDeadzoneChanged(int value)
{
m_ui.deadzoneValue->setText(tr("%1%").arg(value));
}
void InputBindingDialog::hookInputManager()
{
InputManager::SetHook([this](InputBindingKey key, float value) {
+3
View File
@@ -41,6 +41,9 @@ protected Q_SLOTS:
void onInputListenTimerTimeout();
void inputManagerHookCallback(InputBindingKey key, float value);
void onSensitivityChanged(int value);
void onDeadzoneChanged(int value);
protected:
enum : u32
{
+88 -1
View File
@@ -10,7 +10,7 @@
<x>0</x>
<y>0</y>
<width>533</width>
<height>283</height>
<height>266</height>
</rect>
</property>
<property name="windowTitle">
@@ -30,6 +30,93 @@
<item>
<widget class="QListWidget" name="bindingList"/>
</item>
<item>
<widget class="QWidget" name="sensitivityWidget" native="true">
<layout class="QGridLayout" name="sensitivityLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="sensitivityLabel">
<property name="text">
<string>Sensitivity:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSlider" name="sensitivity">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>200</number>
</property>
<property name="value">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="sensitivityValue">
<property name="text">
<string>100%</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="deadzoneLabel">
<property name="text">
<string>Deadzone:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSlider" name="deadzone">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>5</number>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="deadzoneValue">
<property name="text">
<string>100%</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="status">
<property name="text">
@@ -129,7 +129,7 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsDialog* dialog, QWidget
m_ui.confirmShutdown, tr("Confirm Shutdown"), tr("Checked"),
tr("Determines whether a prompt will be displayed to confirm shutting down the virtual machine "
"when the hotkey is pressed."));
dialog->registerWidgetHelp(m_ui.saveStateOnShutdown, tr("Save State On Shutdown"), tr("Checked"),
dialog->registerWidgetHelp(m_ui.saveStateOnShutdown, tr("Save State On Shutdown"), tr("Unchecked"),
tr("Automatically saves the emulator state when powering down or exiting. You can then "
"resume directly from where you left off next time."));
dialog->registerWidgetHelp(m_ui.pauseOnStart, tr("Pause On Start"), tr("Unchecked"),
@@ -58,7 +58,7 @@ MemoryCardConvertDialog::MemoryCardConvertDialog(QWidget* parent, QString select
SetType(MemoryCardType::File, MemoryCardFileType::PS2_8MB, "A standard, 8 MB memory card. Most compatible, but smallest capacity.");
break;
case 16:
SetType(MemoryCardType::File, MemoryCardFileType::PS2_16MB, "2x larger as a standard memory card. May have some compatibility issues.");
SetType(MemoryCardType::File, MemoryCardFileType::PS2_16MB, "2x larger than a standard memory card. May have some compatibility issues.");
break;
case 32:
SetType(MemoryCardType::File, MemoryCardFileType::PS2_32MB, "4x larger than a standard memory card. Likely to have compatibility issues.");
+2 -2
View File
@@ -115,7 +115,7 @@ void SettingsDialog::setupUi(const GameList::Entry* game)
{
addWidget(m_game_fix_settings_widget = new GameFixSettingsWidget(this, m_ui.settingsContainer), tr("Game Fix"),
QStringLiteral("close-line"),
tr("<strong>Game Fix Settings</strong><hr>Gamefixes can work around incorrect emulation in some titles<br>however they can "
tr("<strong>Game Fix Settings</strong><hr>Gamefixes can work around incorrect emulation in some titles.<br>However, they can "
"also cause problems in games if used incorrectly.<br>It is best to leave them all disabled unless advised otherwise."));
}
@@ -191,7 +191,7 @@ void SettingsDialog::setupUi(const GameList::Entry* game)
m_ui.settingsContainer->setCurrentIndex(0);
m_ui.helpText->setText(m_category_help_text[0]);
connect(m_ui.settingsCategory, &QListWidget::currentRowChanged, this, &SettingsDialog::onCategoryCurrentRowChanged);
connect(m_ui.closeButton, &QPushButton::clicked, this, &SettingsDialog::accept);
connect(m_ui.closeButton, &QPushButton::clicked, this, &SettingsDialog::close);
connect(m_ui.restoreDefaultsButton, &QPushButton::clicked, this, &SettingsDialog::onRestoreDefaultsClicked);
}
+4 -1
View File
@@ -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>
+3
View File
@@ -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">
@@ -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

@@ -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

+2
View File
@@ -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>
+115
View File
@@ -0,0 +1,115 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "CDVD/CDVDdiscReader.h"
#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/storage/IOMedia.h>
#include <IOKit/storage/IOCDMedia.h>
#include <IOKit/storage/IODVDMedia.h>
#include <IOKit/IOBSD.h>
#include <IOKit/IOKitLib.h>
#endif
#include <fcntl.h>
#include <unistd.h>
#ifdef __APPLE__
std::vector<std::string> GetDriveListFromClasses(CFMutableDictionaryRef classes)
{
io_iterator_t iterator = IO_OBJECT_NULL;
kern_return_t result;
std::vector<std::string> drives;
CFDictionarySetValue(classes, CFSTR(kIOMediaEjectableKey), kCFBooleanTrue);
result = IOServiceGetMatchingServices(kIOMasterPortDefault, classes, &iterator);
if (result != KERN_SUCCESS)
return drives;
while (io_object_t media = IOIteratorNext(iterator))
{
CFTypeRef path_cfstr = IORegistryEntryCreateCFProperty(media, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0);
if (path_cfstr)
{
char path[PATH_MAX] = {0};
strlcpy(path, "/dev/r", PATH_MAX);
size_t path_prefix_len = strnlen(path, PATH_MAX);
result = CFStringGetCString((CFStringRef)path_cfstr, path + path_prefix_len, PATH_MAX - path_prefix_len, kCFStringEncodingUTF8);
if (result)
{
drives.emplace_back(path);
}
CFRelease(path_cfstr);
}
IOObjectRelease(media);
}
IOObjectRelease(iterator);
return drives;
}
#endif
std::vector<std::string> GetOpticalDriveList()
{
#ifdef __APPLE__
std::vector<std::string> drives;
if (CFMutableDictionaryRef cd_classes = IOServiceMatching(kIOCDMediaClass))
{
std::vector<std::string> cd = GetDriveListFromClasses(cd_classes);
drives.insert(drives.end(), cd.begin(), cd.end());
}
if (CFMutableDictionaryRef dvd_classes = IOServiceMatching(kIODVDMediaClass))
{
std::vector<std::string> dvd = GetDriveListFromClasses(dvd_classes);
drives.insert(drives.end(), dvd.begin(), dvd.end());
}
return drives;
#else
return {};
#endif
}
void GetValidDrive(std::string& drive)
{
if (!drive.empty())
{
#ifdef __APPLE__
int fd = open(drive.c_str(), O_RDONLY | O_NONBLOCK);
if (fd != -1)
{
close(fd);
}
else
{
drive.clear();
}
#else
drive.clear();
#endif
}
if (drive.empty())
{
auto drives = GetOpticalDriveList();
if (!drives.empty())
drive = drives.front();
}
if (!drive.empty())
DevCon.WriteLn("CDVD: Opening drive '%s'...", drive.c_str());
}
+268
View File
@@ -0,0 +1,268 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2022 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "CDVD/CDVDdiscReader.h"
#include "CDVD/CDVD.h"
#ifdef __APPLE__
#include <IOKit/storage/IOCDMediaBSDClient.h>
#include <IOKit/storage/IODVDMediaBSDClient.h>
#endif
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>
IOCtlSrc::IOCtlSrc(decltype(m_filename) filename)
: m_filename(filename)
{
if (!Reopen())
throw std::runtime_error(" * CDVD: Error opening source.\n");
}
IOCtlSrc::~IOCtlSrc()
{
if (m_device != -1)
{
SetSpindleSpeed(true);
close(m_device);
}
}
bool IOCtlSrc::Reopen()
{
if (m_device != -1)
close(m_device);
// O_NONBLOCK allows a valid file descriptor to be returned even if the
// drive is empty. Probably does other things too.
m_device = open(m_filename.c_str(), O_RDONLY | O_NONBLOCK);
if (m_device == -1)
return false;
// DVD detection MUST be first on Linux - The TOC ioctls work for both
// CDs and DVDs.
if (ReadDVDInfo() || ReadCDInfo())
SetSpindleSpeed(false);
return true;
}
void IOCtlSrc::SetSpindleSpeed(bool restore_defaults) const
{
#ifdef __APPLE__
u16 speed = restore_defaults ? 0xFFFF : m_media_type >= 0 ? 5540 :
3600;
int ioctl_code = m_media_type >= 0 ? DKIOCDVDSETSPEED : DKIOCCDSETSPEED;
if (ioctl(m_device, ioctl_code, &speed) == -1)
{
DevCon.Warning("CDVD: Failed to set spindle speed: %s", strerror(errno));
}
else if (!restore_defaults)
{
DevCon.WriteLn("CDVD: Spindle speed set to %d", speed);
}
#else
// FIXME: FreeBSD equivalent for DKIOCDVDSETSPEED DKIOCCDSETSPEED.
DevCon.Warning("CDVD: Setting spindle speed not supported!");
#endif
}
u32 IOCtlSrc::GetSectorCount() const
{
return m_sectors;
}
u32 IOCtlSrc::GetLayerBreakAddress() const
{
return m_layer_break;
}
s32 IOCtlSrc::GetMediaType() const
{
return m_media_type;
}
const std::vector<toc_entry>& IOCtlSrc::ReadTOC() const
{
return m_toc;
}
bool IOCtlSrc::ReadSectors2048(u32 sector, u32 count, u8* buffer) const
{
const ssize_t bytes_to_read = 2048 * count;
ssize_t bytes_read = pread(m_device, buffer, bytes_to_read, sector * 2048ULL);
if (bytes_read == bytes_to_read)
return true;
if (bytes_read == -1)
DevCon.Warning("CDVD: read sectors %u-%u failed: %s",
sector, sector + count - 1, strerror(errno));
else
DevCon.Warning("CDVD: read sectors %u-%u: %zd bytes read, %zd bytes expected",
sector, sector + count - 1, bytes_read, bytes_to_read);
return false;
}
bool IOCtlSrc::ReadSectors2352(u32 sector, u32 count, u8* buffer) const
{
#ifdef __APPLE__
dk_cd_read_t desc;
memset(&desc, 0, sizeof(dk_cd_read_t));
desc.sectorArea = kCDSectorAreaSync | kCDSectorAreaHeader | kCDSectorAreaSubHeader | kCDSectorAreaUser | kCDSectorAreaAuxiliary;
desc.sectorType = kCDSectorTypeUnknown;
for (u32 i = 0; i < count; ++i)
{
desc.offset = (sector + i) * 2352ULL;
desc.buffer = buffer + i * 2352;
desc.bufferLength = 2352;
if (ioctl(m_device, DKIOCCDREAD, &desc) == -1)
{
DevCon.Warning("CDVD: DKIOCCDREAD sector %u failed: %s",
sector + i, strerror(errno));
return false;
}
}
return true;
#else
return false;
#endif
}
bool IOCtlSrc::ReadDVDInfo()
{
#ifdef __APPLE__
dk_dvd_read_structure_t dvdrs;
memset(&dvdrs, 0, sizeof(dk_dvd_read_structure_t));
dvdrs.format = kDVDStructureFormatPhysicalFormatInfo;
dvdrs.layer = 0;
DVDPhysicalFormatInfo layer0;
dvdrs.buffer = &layer0;
dvdrs.bufferLength = sizeof(DVDPhysicalFormatInfo);
int ret = ioctl(m_device, DKIOCDVDREADSTRUCTURE, &dvdrs);
if (ret == -1)
{
return false;
}
u32 start_sector = *(u32*)layer0.startingPhysicalSectorNumberOfDataArea;
u32 end_sector = *(u32*)layer0.endPhysicalSectorNumberOfDataArea;
if (layer0.numberOfLayers == 0)
{
// Single layer
m_media_type = 0;
m_layer_break = 0;
m_sectors = end_sector - start_sector + 1;
}
else if (layer0.trackPath == 0)
{
// Dual layer, Parallel Track Path
DVDPhysicalFormatInfo layer1;
dvdrs.layer = 1;
dvdrs.buffer = &layer1;
dvdrs.bufferLength = sizeof(DVDPhysicalFormatInfo);
ret = ioctl(m_device, DKIOCDVDREADSTRUCTURE, &dvdrs);
if (ret == -1)
return false;
u32 layer1_start_sector = *(u32*)layer1.startingPhysicalSectorNumberOfDataArea;
u32 layer1_end_sector = *(u32*)layer1.endPhysicalSectorNumberOfDataArea;
m_media_type = 1;
m_layer_break = end_sector - start_sector;
m_sectors = end_sector - start_sector + 1 + layer1_end_sector - layer1_start_sector + 1;
}
else
{
// Dual layer, Opposite Track Path
u32 end_sector_layer0 = *(u32*)layer0.endSectorNumberInLayerZero;
m_media_type = 2;
m_layer_break = end_sector_layer0 - start_sector;
m_sectors = end_sector_layer0 - start_sector + 1 + end_sector - (~end_sector_layer0 & 0xFFFFFFU) + 1;
}
return true;
#else
return false;
#endif
}
bool IOCtlSrc::ReadCDInfo()
{
#ifdef __APPLE__
u8* buffer = (u8*)malloc(2048);
dk_cd_read_toc_t cdrt;
memset(&cdrt, 0, sizeof(dk_cd_read_toc_t));
cdrt.format = kCDTOCFormatTOC;
cdrt.formatAsTime = 1;
cdrt.address.track = 0;
cdrt.buffer = buffer;
cdrt.bufferLength = 2048;
memset(buffer, 0, 2048);
if (ioctl(m_device, DKIOCCDREADTOC, &cdrt) == -1)
{
DevCon.Warning("CDVD: DKIOCCDREADTOC failed: %s\n", strerror(errno));
return false;
}
CDTOC* toc = (CDTOC*)buffer;
u32 desc_count = CDTOCGetDescriptorCount(toc);
for (u32 i = 0; i < desc_count; ++i)
{
CDTOCDescriptor desc = toc->descriptors[i];
if (desc.point < 0xa0 && desc.adr == 1)
{
u32 lba = CDConvertMSFToLBA(desc.p);
m_toc.push_back({lba, desc.point, desc.adr, desc.control});
}
else if (desc.point == 0xa2) // lead out, use to get total sector count
{
m_sectors = CDConvertMSFToLBA(desc.p);
}
}
m_media_type = -1;
free(buffer);
return true;
#else
return false;
#endif
}
bool IOCtlSrc::DiscReady()
{
#ifdef __APPLE__
if (m_device == -1)
return false;
if (!m_sectors)
{
Reopen();
}
return !!m_sectors;
#else
return false;
#endif
}
-11
View File
@@ -16,18 +16,14 @@
#include "PrecompiledHeader.h"
#include "CDVD/CDVDdiscReader.h"
#ifdef __linux__
#include <libudev.h>
#include <linux/cdrom.h>
#endif
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
std::vector<std::string> GetOpticalDriveList()
{
#ifdef __linux__
udev* udev_context = udev_new();
if (!udev_context)
return {};
@@ -56,16 +52,12 @@ std::vector<std::string> GetOpticalDriveList()
udev_unref(udev_context);
return drives;
#else
return {};
#endif
}
void GetValidDrive(std::string& drive)
{
if (!drive.empty())
{
#ifdef __linux__
int fd = open(drive.c_str(), O_RDONLY | O_NONBLOCK);
if (fd != -1)
{
@@ -77,9 +69,6 @@ void GetValidDrive(std::string& drive)
{
drive.clear();
}
#else
drive.clear();
#endif
}
if (drive.empty())
{
-19
View File
@@ -17,10 +17,7 @@
#include "CDVD/CDVDdiscReader.h"
#include "CDVD/CDVD.h"
#ifdef __linux__
#include <linux/cdrom.h>
#endif
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
@@ -108,7 +105,6 @@ bool IOCtlSrc::ReadSectors2048(u32 sector, u32 count, u8* buffer) const
bool IOCtlSrc::ReadSectors2352(u32 sector, u32 count, u8* buffer) const
{
#ifdef __linux__
union
{
cdrom_msf msf;
@@ -130,14 +126,10 @@ bool IOCtlSrc::ReadSectors2352(u32 sector, u32 count, u8* buffer) const
}
return true;
#else
return false;
#endif
}
bool IOCtlSrc::ReadDVDInfo()
{
#ifdef __linux__
dvd_struct dvdrs;
dvdrs.type = DVD_STRUCT_PHYSICAL;
dvdrs.physical.layer_num = 0;
@@ -180,14 +172,10 @@ bool IOCtlSrc::ReadDVDInfo()
}
return true;
#else
return false;
#endif
}
bool IOCtlSrc::ReadCDInfo()
{
#ifdef __linux__
cdrom_tochdr header;
if (ioctl(m_device, CDROMREADTOCHDR, &header) == -1)
@@ -214,14 +202,10 @@ bool IOCtlSrc::ReadCDInfo()
m_media_type = -1;
return true;
#else
return false;
#endif
}
bool IOCtlSrc::DiscReady()
{
#ifdef __linux__
if (m_device == -1)
return false;
@@ -239,7 +223,4 @@ bool IOCtlSrc::DiscReady()
}
return !!m_sectors;
#else
return false;
#endif
}
+8 -4
View File
@@ -329,6 +329,7 @@ set(pcsx2DEV9Sources
DEV9/InternalServers/DNS_Logger.cpp
DEV9/InternalServers/DNS_Server.cpp
DEV9/PacketReader/ARP/ARP_Packet.cpp
DEV9/PacketReader/ARP/ARP_PacketEditor.cpp
DEV9/PacketReader/IP/ICMP/ICMP_Packet.cpp
DEV9/PacketReader/IP/TCP/TCP_Options.cpp
DEV9/PacketReader/IP/TCP/TCP_Packet.cpp
@@ -340,6 +341,7 @@ set(pcsx2DEV9Sources
DEV9/PacketReader/IP/IP_Options.cpp
DEV9/PacketReader/IP/IP_Packet.cpp
DEV9/PacketReader/EthernetFrame.cpp
DEV9/PacketReader/EthernetFrameEditor.cpp
DEV9/Sessions/BaseSession.cpp
DEV9/Sessions/ICMP_Session/ICMP_Session.cpp
DEV9/Sessions/TCP_Session/TCP_Session.cpp
@@ -366,6 +368,7 @@ set(pcsx2DEV9Headers
DEV9/InternalServers/DNS_Server.h
DEV9/net.h
DEV9/PacketReader/ARP/ARP_Packet.h
DEV9/PacketReader/ARP/ARP_PacketEditor.h
DEV9/PacketReader/IP/ICMP/ICMP_Packet.h
DEV9/PacketReader/IP/TCP/TCP_Options.h
DEV9/PacketReader/IP/TCP/TCP_Packet.h
@@ -380,6 +383,7 @@ set(pcsx2DEV9Headers
DEV9/PacketReader/IP/IP_Packet.h
DEV9/PacketReader/IP/IP_Payload.h
DEV9/PacketReader/EthernetFrame.h
DEV9/PacketReader/EthernetFrameEditor.h
DEV9/PacketReader/MAC_Address.h
DEV9/PacketReader/NetLib.h
DEV9/PacketReader/Payload.h
@@ -912,14 +916,14 @@ set(pcsx2LinuxSources
)
set(pcsx2OSXSources
CDVD/Linux/DriveUtility.cpp
CDVD/Linux/IOCtlSrc.cpp
CDVD/Darwin/DriveUtility.cpp
CDVD/Darwin/IOCtlSrc.cpp
Darwin/DarwinFlatFileReader.cpp
)
set(pcsx2FreeBSDSources
CDVD/Linux/DriveUtility.cpp
CDVD/Linux/IOCtlSrc.cpp
CDVD/Darwin/DriveUtility.cpp
CDVD/Darwin/IOCtlSrc.cpp
Darwin/DarwinFlatFileReader.cpp
)
+15 -8
View File
@@ -445,6 +445,10 @@ u32 UpdateVSyncRate()
if (vSyncInfo.Framerate != frames_per_second || vSyncInfo.VideoMode != gsVideoMode)
{
// NBA Jam 2004 PAL will fail to display 3D on the menu if this value isn't correct on reset.
if (video_mode_initialized && vSyncInfo.VideoMode != gsVideoMode)
CSRreg.FIELD = 1;
vSyncInfo.VideoMode = gsVideoMode;
vSyncInfoCalc(&vSyncInfo, frames_per_second, total_scanlines);
@@ -457,7 +461,9 @@ u32 UpdateVSyncRate()
hsyncCounter.CycleT = vSyncInfo.hRender; // Amount of cycles before the counter will be updated
vsyncCounter.CycleT = vSyncInfo.Render; // Amount of cycles before the counter will be updated
hsyncCounter.sCycle = cpuRegs.cycle;
vsyncCounter.sCycle = cpuRegs.cycle;
vsyncCounter.Mode = MODE_VRENDER;
cpuRcntSet();
}
@@ -537,12 +543,16 @@ static __fi void DoFMVSwitch()
RendererSwitched = false;
}
// Convenience function to update UI thread and set patches.
static __fi void frameLimitUpdateCore()
// Convenience function to update UI thread and set patches.
static __fi void VSyncUpdateCore()
{
DoFMVSwitch();
VMManager::Internal::VSyncOnCPUThread();
}
static __fi void VSyncCheckExit()
{
if (VMManager::Internal::IsExecutionInterrupted())
Cpu->ExitExecution();
}
@@ -553,10 +563,7 @@ static __fi void frameLimit()
{
// Framelimiter off in settings? Framelimiter go brrr.
if (EmuConfig.GS.LimitScalar == 0.0f || s_use_vsync_for_timing)
{
frameLimitUpdateCore();
return;
}
const u64 uExpectedEnd = m_iStart + m_iTicks; // Compute when we would expect this frame to end, assuming everything goes perfectly perfect.
const u64 iEnd = GetCPUTicks(); // The current tick we actually stopped on.
@@ -567,7 +574,6 @@ static __fi void frameLimit()
{
// ... Fudge the next frame start over a bit. Prevents fast forward zoomies.
m_iStart += (sDeltaTime / m_iTicks) * m_iTicks;
frameLimitUpdateCore();
return;
}
@@ -591,16 +597,17 @@ static __fi void frameLimit()
// Finally, set our next frame start to when this one ends
m_iStart = uExpectedEnd;
frameLimitUpdateCore();
}
static __fi void VSyncStart(u32 sCycle)
{
// Update vibration at the end of a frame.
VSyncUpdateCore();
PAD::Update();
frameLimit(); // limit FPS
gsPostVsyncStart(); // MUST be after framelimit; doing so before causes funk with frame times!
VSyncCheckExit();
if(EmuConfig.Trace.Enabled && EmuConfig.Trace.EE.m_EnableAll)
SysTrace.EE.Counters.Write( " ================ EE COUNTER VSYNC START (frame: %d) ================", g_FrameCount );
-2
View File
@@ -62,8 +62,6 @@ struct EECNT_MODE
u32 OverflowReached:1;
};
// fixme: Cycle and sCycleT members are unused.
// But they can't be removed without making a new savestate version.
struct Counter
{
u32 count;
@@ -0,0 +1,79 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "ARP_PacketEditor.h"
#ifdef _WIN32
#include "winsock.h"
#else
#include <arpa/inet.h>
#endif
namespace PacketReader::ARP
{
ARP_PacketEditor::ARP_PacketEditor(PayloadPtr* pkt)
: basePkt{pkt}
{
}
u16 ARP_PacketEditor::GetHardwareType()
{
return ntohs(*(u16*)&basePkt->data[0]);
}
u16 ARP_PacketEditor::GetProtocol()
{
return ntohs(*(u16*)&basePkt->data[2]);
}
u8 ARP_PacketEditor::GetHardwareAddressLength()
{
return basePkt->data[4];
}
u8 ARP_PacketEditor::GetProtocolAddressLength()
{
return basePkt->data[5];
}
u16 ARP_PacketEditor::GetOp()
{
return ntohs(*(u16*)&basePkt->data[6]);
}
u8* ARP_PacketEditor::SenderHardwareAddress()
{
return &basePkt->data[8];
}
u8* ARP_PacketEditor::SenderProtocolAddress()
{
int offset = 8 + GetHardwareAddressLength();
return &basePkt->data[offset];
}
u8* ARP_PacketEditor::TargetHardwareAddress()
{
int offset = 8 + GetHardwareAddressLength() + GetProtocolAddressLength();
return &basePkt->data[offset];
}
u8* ARP_PacketEditor::TargetProtocolAddress()
{
int offset = 8 + 2 * GetHardwareAddressLength() + GetProtocolAddressLength();
return &basePkt->data[offset];
}
} // namespace PacketReader::ARP
@@ -0,0 +1,42 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "DEV9/PacketReader/Payload.h"
namespace PacketReader::ARP
{
class ARP_PacketEditor
{
private:
PayloadPtr* basePkt;
public:
ARP_PacketEditor(PayloadPtr* pkt);
u16 GetHardwareType();
u16 GetProtocol();
u8 GetHardwareAddressLength();
u8 GetProtocolAddressLength();
u16 GetOp();
u8* SenderHardwareAddress();
u8* SenderProtocolAddress();
u8* TargetHardwareAddress();
u8* TargetProtocolAddress();
};
} // namespace PacketReader::ARP
@@ -0,0 +1,66 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "EthernetFrameEditor.h"
#ifdef _WIN32
#include "winsock.h"
#else
#include <arpa/inet.h>
#endif
namespace PacketReader
{
EthernetFrameEditor::EthernetFrameEditor(NetPacket* pkt)
: basePkt{pkt}
{
headerLength = 14; //(6+6+2)
//Note: we don't have to worry about the Ethernet Frame CRC as it is not included in the packet
//Note: We don't support tagged frames
payload = std::make_unique<PayloadPtr>((u8*)&basePkt->buffer[14], pkt->size - headerLength);
}
MAC_Address EthernetFrameEditor::GetDestinationMAC()
{
return *(MAC_Address*)&basePkt->buffer[0];
}
void EthernetFrameEditor::SetDestinationMAC(MAC_Address value)
{
*(MAC_Address*)&basePkt->buffer[0] = value;
}
MAC_Address EthernetFrameEditor::GetSourceMAC()
{
return *(MAC_Address*)&basePkt->buffer[6];
}
void EthernetFrameEditor::SetSourceMAC(MAC_Address value)
{
*(MAC_Address*)&basePkt->buffer[6] = value;
}
u16 EthernetFrameEditor::GetProtocol()
{
return ntohs(*(u16*)&basePkt->buffer[12]);
}
PayloadPtr* EthernetFrameEditor::GetPayload()
{
return payload.get();
}
} // namespace PacketReader
@@ -0,0 +1,45 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "DEV9/net.h"
#include "MAC_Address.h"
#include "Payload.h"
namespace PacketReader
{
class EthernetFrameEditor
{
public:
int headerLength = 14;
//Length
private:
NetPacket* basePkt;
std::unique_ptr<PayloadPtr> payload;
public:
EthernetFrameEditor(NetPacket* pkt);
MAC_Address GetDestinationMAC();
void SetDestinationMAC(MAC_Address value);
MAC_Address GetSourceMAC();
void SetSourceMAC(MAC_Address value);
u16 GetProtocol();
PayloadPtr* GetPayload();
};
} // namespace PacketReader
+216 -249
View File
@@ -23,18 +23,16 @@
#include "common/StringUtil.h"
#include <WinSock2.h>
#include <iphlpapi.h>
#elif defined(__POSIX__)
#include <sys/types.h>
#include <ifaddrs.h>
#endif
#include <stdio.h>
#include <stdarg.h>
#include "pcap_io.h"
#include "DEV9.h"
#include "AdapterUtils.h"
#include "net.h"
#include "DEV9/PacketReader/MAC_Address.h"
#include "PacketReader/EthernetFrame.h"
#include "PacketReader/EthernetFrameEditor.h"
#include "PacketReader/ARP/ARP_PacketEditor.h"
#ifndef PCAP_NETMASK_UNKNOWN
#define PCAP_NETMASK_UNKNOWN 0xffffffff
#endif
@@ -43,203 +41,9 @@
#define PCAPPREFIX "\\Device\\NPF_"
#endif
pcap_t* adhandle;
pcap_dumper_t* dump_pcap = nullptr;
char errbuf[PCAP_ERRBUF_SIZE];
int pcap_io_running = 0;
bool pcap_io_switched;
bool pcap_io_blocking;
extern u8 eeprom[];
char namebuff[256];
ip_address ps2_ip;
mac_address ps2_mac;
mac_address host_mac;
int pcap_io_init(const std::string& adapter, bool switched, mac_address virtual_mac)
{
struct bpf_program fp;
char filter[1024] = "ether broadcast or ether dst ";
int dlt;
char* dlt_name;
Console.WriteLn("DEV9: Opening adapter '%s'...", adapter.c_str());
pcap_io_switched = switched;
#ifdef _WIN32
std::string pcapAdapter = PCAPPREFIX + adapter;
#else
std::string pcapAdapter = adapter;
#endif
/* Open the adapter */
if ((adhandle = pcap_open_live(pcapAdapter.c_str(), // name of the device
65536, // portion of the packet to capture.
// 65536 grants that the whole packet will be captured on all the MACs.
switched ? 1 : 0,
1, // read timeout
errbuf // error buffer
)) == NULL)
{
Console.Error("DEV9: %s", errbuf);
Console.Error("DEV9: Unable to open the adapter. %s is not supported by pcap", adapter.c_str());
return -1;
}
if (switched)
{
char virtual_mac_str[18];
sprintf(virtual_mac_str, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", virtual_mac.bytes[0], virtual_mac.bytes[1], virtual_mac.bytes[2], virtual_mac.bytes[3], virtual_mac.bytes[4], virtual_mac.bytes[5]);
strcat(filter, virtual_mac_str);
// fprintf(stderr, "Trying pcap filter: %s\n", filter);
if (pcap_compile(adhandle, &fp, filter, 1, PCAP_NETMASK_UNKNOWN) == -1)
{
Console.Error("DEV9: Error calling pcap_compile: %s", pcap_geterr(adhandle));
return -1;
}
if (pcap_setfilter(adhandle, &fp) == -1)
{
Console.Error("DEV9: Error setting filter: %s", pcap_geterr(adhandle));
return -1;
}
}
if (pcap_setnonblock(adhandle, 1, errbuf) == -1)
{
Console.Error("DEV9: Error setting non-blocking: %s", pcap_geterr(adhandle));
Console.Error("DEV9: Continuing in blocking mode");
pcap_io_blocking = true;
}
else
pcap_io_blocking = false;
dlt = pcap_datalink(adhandle);
dlt_name = (char*)pcap_datalink_val_to_name(dlt);
Console.Error("DEV9: Device uses DLT %d: %s", dlt, dlt_name);
switch (dlt)
{
case DLT_EN10MB:
//case DLT_IEEE802_11:
break;
default:
Console.Error("ERROR: Unsupported DataLink Type (%d): %s", dlt, dlt_name);
pcap_close(adhandle);
return -1;
}
#ifdef DEBUG
const std::string plfile(s_strLogPath + "/pkt_log.pcap");
dump_pcap = pcap_dump_open(adhandle, plfile.c_str());
#endif
pcap_io_running = 1;
Console.WriteLn("DEV9: Adapter Ok.");
return 0;
}
#ifdef _WIN32
int gettimeofday(struct timeval* tv, void* tz)
{
unsigned __int64 ns100; /*time since 1 Jan 1601 in 100ns units */
GetSystemTimeAsFileTime((LPFILETIME)&ns100);
tv->tv_usec = (long)((ns100 / 10L) % 1000000L);
tv->tv_sec = (long)((ns100 - 116444736000000000L) / 10000000L);
return (0);
}
#endif
int pcap_io_send(void* packet, int plen)
{
if (pcap_io_running <= 0)
return -1;
if (!pcap_io_switched)
{
if (((ethernet_header*)packet)->protocol == 0x0008) //IP
{
ps2_ip = ((ip_header*)((u8*)packet + sizeof(ethernet_header)))->src;
}
if (((ethernet_header*)packet)->protocol == 0x0608) //ARP
{
ps2_ip = ((arp_packet*)((u8*)packet + sizeof(ethernet_header)))->p_src;
((arp_packet*)((u8*)packet + sizeof(ethernet_header)))->h_src = host_mac;
}
((ethernet_header*)packet)->src = host_mac;
}
if (dump_pcap)
{
static struct pcap_pkthdr ph;
gettimeofday(&ph.ts, NULL);
ph.caplen = plen;
ph.len = plen;
pcap_dump((u_char*)dump_pcap, &ph, (u_char*)packet);
}
return pcap_sendpacket(adhandle, (u_char*)packet, plen);
}
int pcap_io_recv(void* packet, int max_len)
{
static struct pcap_pkthdr* header;
static const u_char* pkt_data1;
if (pcap_io_running <= 0)
return -1;
if ((pcap_next_ex(adhandle, &header, &pkt_data1)) > 0)
{
if ((int)header->len > max_len)
return -1;
memcpy(packet, pkt_data1, header->len);
if (!pcap_io_switched)
{
{
if (((ethernet_header*)packet)->protocol == 0x0008)
{
ip_header* iph = ((ip_header*)((u8*)packet + sizeof(ethernet_header)));
if (ip_compare(iph->dst, ps2_ip) == 0)
{
((ethernet_header*)packet)->dst = ps2_mac;
}
}
if (((ethernet_header*)packet)->protocol == 0x0608)
{
arp_packet* aph = ((arp_packet*)((u8*)packet + sizeof(ethernet_header)));
if (ip_compare(aph->p_dst, ps2_ip) == 0)
{
((ethernet_header*)packet)->dst = ps2_mac;
((arp_packet*)((u8*)packet + sizeof(ethernet_header)))->h_dst = ps2_mac;
}
}
}
}
if (dump_pcap)
pcap_dump((u_char*)dump_pcap, header, (u_char*)packet);
return header->len;
}
return -1;
}
void pcap_io_close()
{
if (dump_pcap)
pcap_dump_close(dump_pcap);
if (adhandle)
pcap_close(adhandle);
pcap_io_running = 0;
}
using namespace PacketReader;
using namespace PacketReader::ARP;
using namespace PacketReader::IP;
PCAPAdapter::PCAPAdapter()
: NetAdapter()
@@ -251,44 +55,62 @@ PCAPAdapter::PCAPAdapter()
return;
#endif
AdapterUtils::Adapter adapter;
AdapterUtils::AdapterBuffer buffer;
if (AdapterUtils::GetAdapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer))
{
std::optional<PacketReader::MAC_Address> adMAC = AdapterUtils::GetAdapterMAC(&adapter);
if (adMAC.has_value())
{
mac_address hostMAC = *(mac_address*)&adMAC.value();
mac_address newMAC;
memcpy(&newMAC, &ps2MAC, 6);
#ifdef _WIN32
std::string pcapAdapter = PCAPPREFIX + EmuConfig.DEV9.EthDevice;
#else
std::string pcapAdapter = EmuConfig.DEV9.EthDevice;
#endif
//Lets take the hosts last 2 bytes to make it unique on Xlink
newMAC.bytes[5] = hostMAC.bytes[4];
newMAC.bytes[4] = hostMAC.bytes[5];
switched = EmuConfig.DEV9.EthApi == Pcsx2Config::DEV9Options::NetApi::PCAP_Switched;
SetMACAddress((PacketReader::MAC_Address*)&newMAC);
host_mac = hostMAC;
ps2_mac = newMAC; //Needed outside of this class
}
else
{
Console.Error("DEV9: Failed to get MAC address for adapter");
return;
}
}
else
{
Console.Error("DEV9: Failed to get adapter information");
return;
}
if (pcap_io_init(EmuConfig.DEV9.EthDevice, EmuConfig.DEV9.EthApi == Pcsx2Config::DEV9Options::NetApi::PCAP_Switched, ps2_mac) == -1)
if (!InitPCAP(pcapAdapter, switched))
{
Console.Error("DEV9: Can't open Device '%s'", EmuConfig.DEV9.EthDevice.c_str());
return;
}
InitInternalServer(&adapter);
AdapterUtils::Adapter adapter;
AdapterUtils::AdapterBuffer buffer;
std::optional<MAC_Address> adMAC = std::nullopt;
const bool foundAdapter = AdapterUtils::GetAdapter(EmuConfig.DEV9.EthDevice, &adapter, &buffer);
if (foundAdapter)
adMAC = AdapterUtils::GetAdapterMAC(&adapter);
else
Console.Error("DEV9: Failed to get adapter information");
if (adMAC.has_value())
{
hostMAC = adMAC.value();
MAC_Address newMAC = ps2MAC;
//Lets take the hosts last 2 bytes to make it unique on Xlink
newMAC.bytes[5] = hostMAC.bytes[4];
newMAC.bytes[4] = hostMAC.bytes[5];
SetMACAddress(&newMAC);
}
else if (switched)
Console.Error("DEV9: Failed to get MAC address for adapter, proceeding with hardcoded MAC address");
else
{
Console.Error("DEV9: Failed to get MAC address for adapter");
pcap_close(hpcap);
hpcap = nullptr;
return;
}
if (switched && !SetMACSwitchedFilter(ps2MAC))
{
pcap_close(hpcap);
hpcap = nullptr;
Console.Error("DEV9: Can't open Device '%s'", EmuConfig.DEV9.EthDevice.c_str());
return;
}
if (foundAdapter)
InitInternalServer(&adapter);
else
InitInternalServer(nullptr);
}
AdapterOptions PCAPAdapter::GetAdapterOptions()
{
@@ -296,43 +118,69 @@ AdapterOptions PCAPAdapter::GetAdapterOptions()
}
bool PCAPAdapter::blocks()
{
pxAssert(pcap_io_running);
return pcap_io_blocking;
pxAssert(hpcap);
return blocking;
}
bool PCAPAdapter::isInitialised()
{
return !!pcap_io_running;
return hpcap != nullptr;
}
//gets a packet.rv :true success
bool PCAPAdapter::recv(NetPacket* pkt)
{
if (!pcap_io_blocking && NetAdapter::recv(pkt))
pxAssert(hpcap);
if (!blocking && NetAdapter::recv(pkt))
return true;
int size = pcap_io_recv(pkt->buffer, sizeof(pkt->buffer));
if (size > 0 && VerifyPkt(pkt, size))
pcap_pkthdr* header;
const u_char* pkt_data;
// pcap bridged will pick up packets not intended for us, returning false on those packets will incur a 1ms wait.
// This delays getting packets we need, so instead loop untill a valid packet, or no packet, is returned from pcap_next_ex.
while (pcap_next_ex(hpcap, &header, &pkt_data) > 0)
{
InspectRecv(pkt);
return true;
if (header->len > sizeof(pkt->buffer))
{
Console.Error("DEV9: Dropped jumbo frame of size: %u", header->len);
continue;
}
pxAssert(header->len == header->caplen);
memcpy(pkt->buffer, pkt_data, header->len);
pkt->size = (int)header->len;
if (!switched)
SetMACBridgedRecv(pkt);
if (VerifyPkt(pkt, header->len))
{
InspectRecv(pkt);
return true;
}
// continue.
}
else
return false;
return false;
}
//sends the packet .rv :true success
bool PCAPAdapter::send(NetPacket* pkt)
{
pxAssert(hpcap);
InspectSend(pkt);
if (NetAdapter::send(pkt))
return true;
if (pcap_io_send(pkt->buffer, pkt->size))
{
// TODO: loopback broadcast packets to host pc in switched mode.
if (!switched)
SetMACBridgedSend(pkt);
if (pcap_sendpacket(hpcap, (u_char*)pkt->buffer, pkt->size))
return false;
}
else
{
return true;
}
}
void PCAPAdapter::reloadSettings()
@@ -347,7 +195,11 @@ void PCAPAdapter::reloadSettings()
PCAPAdapter::~PCAPAdapter()
{
pcap_io_close();
if (hpcap)
{
pcap_close(hpcap);
hpcap = nullptr;
}
}
std::vector<AdapterEntry> PCAPAdapter::GetAdapters()
@@ -359,6 +211,7 @@ std::vector<AdapterEntry> PCAPAdapter::GetAdapters()
return nic;
#endif
char errbuf[PCAP_ERRBUF_SIZE];
pcap_if_t* alldevs;
pcap_if_t* d;
@@ -414,3 +267,117 @@ std::vector<AdapterEntry> PCAPAdapter::GetAdapters()
return nic;
}
// Opens device for capture and sets non-blocking.
bool PCAPAdapter::InitPCAP(const std::string& adapter, bool promiscuous)
{
char errbuf[PCAP_ERRBUF_SIZE];
Console.WriteLn("DEV9: Opening adapter '%s'...", adapter.c_str());
// Open the adapter.
if ((hpcap = pcap_open_live(adapter.c_str(), // Name of the device.
65536, // portion of the packet to capture.
// 65536 grants that the whole packet will be captured on all the MACs.
promiscuous ? 1 : 0,
1, // Read timeout.
errbuf // Error buffer.
)) == nullptr)
{
Console.Error("DEV9: %s", errbuf);
Console.Error("DEV9: Unable to open the adapter. %s is not supported by pcap", adapter.c_str());
return false;
}
if (pcap_setnonblock(hpcap, 1, errbuf) == -1)
{
Console.Error("DEV9: Error setting non-blocking: %s", pcap_geterr(hpcap));
Console.Error("DEV9: Continuing in blocking mode");
blocking = true;
}
else
blocking = false;
// Validate.
const int dlt = pcap_datalink(hpcap);
const char* dlt_name = pcap_datalink_val_to_name(dlt);
Console.Error("DEV9: Device uses DLT %d: %s", dlt, dlt_name);
switch (dlt)
{
case DLT_EN10MB:
//case DLT_IEEE802_11:
break;
default:
Console.Error("ERROR: Unsupported DataLink Type (%d): %s", dlt, dlt_name);
pcap_close(hpcap);
hpcap = nullptr;
return false;
}
Console.WriteLn("DEV9: Adapter Ok.");
return true;
}
bool PCAPAdapter::SetMACSwitchedFilter(MAC_Address mac)
{
bpf_program fp;
char filter[1024] = "ether broadcast or ether dst ";
char virtual_mac_str[18];
sprintf(virtual_mac_str, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", mac.bytes[0], mac.bytes[1], mac.bytes[2], mac.bytes[3], mac.bytes[4], mac.bytes[5]);
strcat(filter, virtual_mac_str);
if (pcap_compile(hpcap, &fp, filter, 1, PCAP_NETMASK_UNKNOWN) == -1)
{
Console.Error("DEV9: Error calling pcap_compile: %s", pcap_geterr(hpcap));
return false;
}
int setFilterRet;
if ((setFilterRet = pcap_setfilter(hpcap, &fp)) == -1)
Console.Error("DEV9: Error setting filter: %s", pcap_geterr(hpcap));
pcap_freecode(&fp);
return setFilterRet != -1;
}
void PCAPAdapter::SetMACBridgedRecv(NetPacket* pkt)
{
EthernetFrameEditor frame(pkt);
if (frame.GetProtocol() == (u16)EtherType::IPv4) // IP
{
// Compare DEST IP in IP with the PS2's IP, if they match, change DEST MAC to ps2MAC.
PayloadPtr* payload = frame.GetPayload();
IP_Packet ippkt(payload->data, payload->GetLength());
if (ippkt.destinationIP == ps2IP)
frame.SetDestinationMAC(ps2MAC);
}
if (frame.GetProtocol() == (u16)EtherType::ARP) // ARP
{
// Compare DEST IP in ARP with the PS2's IP, if they match, DEST MAC to ps2MAC on both ARP and ETH Packet headers.
ARP_PacketEditor arpPkt(frame.GetPayload());
if (*(IP_Address*)arpPkt.TargetProtocolAddress() == ps2IP)
{
frame.SetDestinationMAC(ps2MAC);
*(MAC_Address*)arpPkt.TargetHardwareAddress() = ps2MAC;
}
}
}
void PCAPAdapter::SetMACBridgedSend(NetPacket* pkt)
{
EthernetFrameEditor frame(pkt);
if (frame.GetProtocol() == (u16)EtherType::IPv4) // IP
{
PayloadPtr* payload = frame.GetPayload();
IP_Packet ippkt(payload->data, payload->GetLength());
ps2IP = ippkt.sourceIP;
}
if (frame.GetProtocol() == (u16)EtherType::ARP) // ARP
{
ARP_PacketEditor arpPkt(frame.GetPayload());
ps2IP = *(IP_Address*)arpPkt.SenderProtocolAddress();
*(MAC_Address*)arpPkt.SenderHardwareAddress() = hostMAC;
}
frame.SetSourceMAC(hostMAC);
}
+18 -149
View File
@@ -16,163 +16,24 @@
#pragma once
#include "pcap.h"
#include "net.h"
#ifdef __cplusplus
extern "C" {
#endif
//#ifndef _WIN32
#pragma pack(push, 1)
typedef struct _ip_address
{
u_char bytes[4];
} ip_address;
typedef struct _mac_address
{
u_char bytes[6];
} mac_address;
typedef struct _ethernet_header
{
mac_address dst;
mac_address src;
u_short protocol;
} ethernet_header;
typedef struct _arp_packet
{
u_short hw_type;
u_short protocol;
u_char h_addr_len;
u_char p_addr_len;
u_short operation;
mac_address h_src;
ip_address p_src;
mac_address h_dst;
ip_address p_dst;
} arp_packet;
typedef struct _ip_header
{
u_char ver_hlen; /* version << 4 | header length >> 2 */
u_char type; /* type of service */
u_short len; /* total length */
u_short id; /* identification */
u_short offset; /* fragment offset field */
u_char ttl; /* time to live */
u_char proto; /* protocol */
u_short hdr_csum; /* checksum */
ip_address src; /* source and dest address */
ip_address dst;
} ip_header;
/* Internet Control Message Protocol Constants and Packet Format */
/* ic_type field */
#define ICT_ECHORP 0 /* Echo reply */
#define ICT_DESTUR 3 /* Destination unreachable */
#define ICT_SRCQ 4 /* Source quench */
#define ICT_REDIRECT 5 /* Redirect message type */
#define ICT_ECHORQ 8 /* Echo request */
#define ICT_TIMEX 11 /* Time exceeded */
#define ICT_PARAMP 12 /* Parameter Problem */
#define ICT_TIMERQ 13 /* Timestamp request */
#define ICT_TIMERP 14 /* Timestamp reply */
#define ICT_INFORQ 15 /* Information request */
#define ICT_INFORP 16 /* Information reply */
#define ICT_MASKRQ 17 /* Mask request */
#define ICT_MASKRP 18 /* Mask reply */
/* ic_code field */
#define ICC_NETUR 0 /* dest unreachable, net unreachable */
#define ICC_HOSTUR 1 /* dest unreachable, host unreachable */
#define ICC_PROTOUR 2 /* dest unreachable, proto unreachable */
#define ICC_PORTUR 3 /* dest unreachable, port unreachable */
#define ICC_FNADF 4 /* dest unr, frag needed & don't frag */
#define ICC_SRCRT 5 /* dest unreachable, src route failed */
#define ICC_NETRD 0 /* redirect: net */
#define ICC_HOSTRD 1 /* redirect: host */
#define IC_TOSNRD 2 /* redirect: type of service, net */
#define IC_TOSHRD 3 /* redirect: type of service, host */
#define ICC_TIMEX 0 /* time exceeded, ttl */
#define ICC_FTIMEX 1 /* time exceeded, frag */
#define IC_HLEN 8 /* octets */
#define IC_PADLEN 3 /* pad length (octets) */
#define IC_RDTTL 300 /* ttl for redirect routes */
/* ICMP packet format (following the IP header) */
typedef struct _icmp_header
{ /* ICMP packet */
char type; /* type of message (ICT_* above)*/
char code; /* code (ICC_* above) */
short csum; /* checksum of ICMP header+data */
union
{
struct
{
int ic1_id : 16; /* echo type, a message id */
int ic1_seq : 16; /* echo type, a seq. number */
} ic1;
ip_address ic2_gw; /* for redirect, gateway */
struct
{
char ic3_ptr; /* pointer, for ICT_PARAMP */
char ic3_pad[IC_PADLEN];
} ic3;
int ic4_mbz; /* must be zero */
} icu;
} icmp_header;
/*typedef struct _udp_header {
u16 src_port;
u16 dst_port;
u16 len;
u16 csum;
} udp_header;*/
typedef struct _full_arp_packet
{
ethernet_header header;
arp_packet arp;
} full_arp_packet;
#pragma pack(pop)
#define ARP_REQUEST 0x0100 //values are big-endian
#define mac_compare(a, b) (memcmp(&(a), &(b), 6))
#define ip_compare(a, b) (memcmp(&(a), &(b), 4))
//#endif
/*
int pcap_io_init(char *adapter);
int pcap_io_send(void* packet, int plen);
int pcap_io_recv(void* packet, int max_len);
void pcap_io_close();
int pcap_io_get_dev_num();
char* pcap_io_get_dev_desc(int num);
char* pcap_io_get_dev_name(int num);
*/
#include "PacketReader/MAC_Address.h"
#ifdef _WIN32
bool load_pcap();
void unload_pcap();
#endif
#ifdef __cplusplus
}
#endif
class PCAPAdapter : public NetAdapter
{
private:
pcap_t* hpcap = nullptr;
bool switched;
bool blocking;
PacketReader::IP::IP_Address ps2IP{};
PacketReader::MAC_Address hostMAC;
public:
PCAPAdapter();
virtual bool blocks();
@@ -185,4 +46,12 @@ public:
virtual ~PCAPAdapter();
static std::vector<AdapterEntry> GetAdapters();
static AdapterOptions GetAdapterOptions();
private:
bool InitPCAP(const std::string& adapter, bool promiscuous);
void InitPCAPDumper();
bool SetMACSwitchedFilter(PacketReader::MAC_Address mac);
void SetMACBridgedRecv(NetPacket* pkt);
void SetMACBridgedSend(NetPacket* pkt);
};
+51 -10
View File
@@ -15,29 +15,70 @@
#include "PrecompiledHeader.h"
#include "BiosDebugData.h"
#include "IopMem.h"
#include "Memory.h"
std::vector<EEThread> getEEThreads()
{
std::vector<EEThread> threads;
if (CurrentBiosInformation.threadListAddr <= 0)
std::vector<std::unique_ptr<BiosThread>> getEEThreads()
{
std::vector<std::unique_ptr<BiosThread>> threads;
if (CurrentBiosInformation.eeThreadListAddr <= 0)
return threads;
const u32 start = CurrentBiosInformation.threadListAddr & 0x3fffff;
const u32 start = CurrentBiosInformation.eeThreadListAddr & 0x3fffff;
for (int tid = 0; tid < 256; tid++)
{
EEThread thread;
EEInternalThread* internal = static_cast<EEInternalThread*>(PSM(start + tid * sizeof(EEInternalThread)));
if (internal->status != THS_BAD)
if (internal->status != (int)ThreadStatus::THS_BAD)
{
thread.tid = tid;
thread.data = *internal;
threads.push_back(thread);
auto thread = std::make_unique<EEThread>(tid, *internal);
threads.push_back(std::move(thread));
}
}
return threads;
}
std::vector<std::unique_ptr<BiosThread>> getIOPThreads()
{
std::vector<std::unique_ptr<BiosThread>> threads;
if (CurrentBiosInformation.iopThreadListAddr <= 0)
return threads;
u32 item = iopMemRead32(CurrentBiosInformation.iopThreadListAddr);
while (item != 0)
{
IOPInternalThread data{};
u16 tag = iopMemRead16(item + 0x8);
if (tag != 0x7f01)
{
// something went wrong
return {};
}
data.stackTop = iopMemRead32(item + 0x3c);
data.status = iopMemRead8(item + 0xc);
data.tid = iopMemRead16(item + 0xa);
data.entrypoint = iopMemRead32(item + 0x38);
data.waitstate = iopMemRead16(item + 0xe);
data.initPriority = iopMemRead16(item + 0x2e);
data.SavedSP = iopMemRead32(item + 0x10);
data.PC = iopMemRead32(data.SavedSP + 0x8c);
auto thread = std::make_unique<IOPThread>(data);
threads.push_back(std::move(thread));
item = iopMemRead32(item + 0x24);
}
return threads;
}
+149 -20
View File
@@ -15,9 +15,24 @@
#pragma once
#include "common/Pcsx2Types.h"
#include <memory>
#include <vector>
#include "ps2/BiosTools.h"
struct EEInternalThread { // internal struct
enum class ThreadStatus
{
THS_BAD = 0x00,
THS_RUN = 0x01,
THS_READY = 0x02,
THS_WAIT = 0x04,
THS_SUSPEND = 0x08,
THS_WAIT_SUSPEND = 0x0C,
THS_DORMANT = 0x10,
};
struct EEInternalThread
{ // internal struct
u32 prev;
u32 next;
int status;
@@ -40,26 +55,140 @@ struct EEInternalThread { // internal struct
u32 heap_base;
};
enum {
THS_BAD = 0x00,
THS_RUN = 0x01,
THS_READY = 0x02,
THS_WAIT = 0x04,
THS_SUSPEND = 0x08,
THS_WAIT_SUSPEND = 0x0C,
THS_DORMANT = 0x10,
};
enum {
WAIT_NONE = 0,
WAIT_WAKEUP_REQ = 1,
WAIT_SEMA = 2,
};
struct EEThread
// Not the full struct, just what we care about
struct IOPInternalThread
{
int tid;
u32 tid;
u32 PC;
u32 stackTop;
u32 SavedSP;
u32 status;
u32 entrypoint;
u32 waitstate;
u32 initPriority;
};
enum class IOPWaitStatus
{
TSW_SLEEP = 1,
TSW_DELAY = 2,
TSW_SEMA = 3,
TSW_EVENTFLAG = 4,
TSW_MBX = 5,
TSW_VPL = 6,
TSW_FPL = 7,
};
enum class EEWaitStatus
{
WAIT_NONE = 0,
WAIT_WAKEUP_REQ = 1,
WAIT_SEMA = 2,
};
enum class WaitState
{
NONE,
WAKEUP_REQ,
SEMA,
SLEEP,
DELAY,
EVENTFLAG,
MBOX,
VPOOL,
FIXPOOL,
};
class BiosThread
{
public:
virtual ~BiosThread() = default;
[[nodiscard]] virtual u32 TID() const = 0;
[[nodiscard]] virtual u32 PC() const = 0;
[[nodiscard]] virtual ThreadStatus Status() const = 0;
[[nodiscard]] virtual WaitState Wait() const = 0;
[[nodiscard]] virtual u32 EntryPoint() const = 0;
[[nodiscard]] virtual u32 StackTop() const = 0;
[[nodiscard]] virtual u32 Priority() const = 0;
};
class EEThread : public BiosThread
{
public:
EEThread(int tid, EEInternalThread th)
: tid(tid)
, data(th)
{
}
~EEThread() override = default;
[[nodiscard]] u32 TID() const override { return tid; };
[[nodiscard]] u32 PC() const override { return data.entry; };
[[nodiscard]] ThreadStatus Status() const override { return static_cast<ThreadStatus>(data.status); };
[[nodiscard]] WaitState Wait() const override
{
auto wait = static_cast<EEWaitStatus>(data.waitType);
switch (wait)
{
case EEWaitStatus::WAIT_NONE:
return WaitState::NONE;
case EEWaitStatus::WAIT_WAKEUP_REQ:
return WaitState::WAKEUP_REQ;
case EEWaitStatus::WAIT_SEMA:
return WaitState::SEMA;
}
return WaitState::NONE;
};
[[nodiscard]] u32 EntryPoint() const override { return data.entry_init; };
[[nodiscard]] u32 StackTop() const override { return data.stack; };
[[nodiscard]] u32 Priority() const override { return data.currentPriority; };
private:
u32 tid;
EEInternalThread data;
};
std::vector<EEThread> getEEThreads();
class IOPThread : public BiosThread
{
public:
IOPThread(IOPInternalThread th)
: data(th)
{
}
~IOPThread() override = default;
[[nodiscard]] u32 TID() const override { return data.tid; };
[[nodiscard]] u32 PC() const override { return data.PC; };
[[nodiscard]] ThreadStatus Status() const override { return static_cast<ThreadStatus>(data.status); };
[[nodiscard]] WaitState Wait() const override
{
auto wait = static_cast<IOPWaitStatus>(data.waitstate);
switch (wait)
{
case IOPWaitStatus::TSW_DELAY:
return WaitState::DELAY;
case IOPWaitStatus::TSW_EVENTFLAG:
return WaitState::EVENTFLAG;
case IOPWaitStatus::TSW_SLEEP:
return WaitState::SLEEP;
case IOPWaitStatus::TSW_SEMA:
return WaitState::SEMA;
case IOPWaitStatus::TSW_MBX:
return WaitState::MBOX;
case IOPWaitStatus::TSW_VPL:
return WaitState::VPOOL;
case IOPWaitStatus::TSW_FPL:
return WaitState::FIXPOOL;
}
return WaitState::NONE;
};
[[nodiscard]] u32 EntryPoint() const override { return data.entrypoint; };
[[nodiscard]] u32 StackTop() const override { return data.stackTop; };
[[nodiscard]] u32 Priority() const override { return data.initPriority; };
private:
IOPInternalThread data;
};
std::vector<std::unique_ptr<BiosThread>> getIOPThreads();
std::vector<std::unique_ptr<BiosThread>> getEEThreads();
+11
View File
@@ -728,6 +728,12 @@ SymbolMap& R5900DebugInterface::GetSymbolMap() const
return R5900SymbolMap;
}
std::vector<std::unique_ptr<BiosThread>> R5900DebugInterface::GetThreadList() const
{
return getEEThreads();
}
//
// R3000DebugInterface
//
@@ -1000,3 +1006,8 @@ SymbolMap& R3000DebugInterface::GetSymbolMap() const
{
return R3000SymbolMap;
}
std::vector<std::unique_ptr<BiosThread>> R3000DebugInterface::GetThreadList() const
{
return getIOPThreads();
}
+4
View File
@@ -14,6 +14,7 @@
*/
#pragma once
#include "DebugTools/BiosDebugData.h"
#include "MemoryTypes.h"
#include "ExpressionParser.h"
#include "SymbolMap.h"
@@ -84,6 +85,7 @@ public:
virtual u32 getCycles() = 0;
virtual BreakPointCpu getCpuType() = 0;
[[nodiscard]] virtual SymbolMap& GetSymbolMap() const = 0;
[[nodiscard]] virtual std::vector<std::unique_ptr<BiosThread>> GetThreadList() const = 0;
bool initExpression(const char* exp, PostfixExpression& dest);
bool parseExpression(PostfixExpression& exp, u64& dest);
@@ -130,6 +132,7 @@ public:
void setPc(u32 newPc) override;
void setRegister(int cat, int num, u128 newValue) override;
[[nodiscard]] SymbolMap& GetSymbolMap() const override;
[[nodiscard]] std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
std::string disasm(u32 address, bool simplify) override;
bool isValidAddress(u32 address) override;
@@ -168,6 +171,7 @@ public:
void setPc(u32 newPc) override;
void setRegister(int cat, int num, u128 newValue) override;
[[nodiscard]] SymbolMap& GetSymbolMap() const override;
[[nodiscard]] std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
std::string disasm(u32 address, bool simplify) override;
bool isValidAddress(u32 address) override;
+2 -2
View File
@@ -169,8 +169,8 @@ The clamp modes are also numerically based.
* alignSprite [`0` or `1`] {Off or On} Default: Off (`0`)
* mergeSprite [`0` or `1`] {Off or On} Default: Off (`0`)
* wildArmsHack [`0` or `1`] {Off or On} Default: Off (`0`)
* skipDrawStart [Value between `0` to `100000`] {0-100000} Default: Off (`0`)
* skipDrawEnd [Value between `0` to `100000`] {0-100000} Default: Off (`0`)
* skipDrawStart [Value between `0` to `10000`] {0-10000} Default: Off (`0`)
* skipDrawEnd [Value between `0` to `10000`] {0-10000} Default: Off (`0`)
* halfPixelOffset [`0` or `1` or `2` or `3`] {Off, Normal Vertex, Special Texture or Special Texture Aggressive} Default: Off (`0`)
* roundSprite [`0` or `1` or `2`] {Off, Half or Full} Default: Off (`0`)
+1 -1
View File
@@ -160,7 +160,7 @@ void CommonHost::SetDataDirectory()
EmuFolders::DataRoot = Path::Combine(StringUtil::WideStringToUTF8String(documents_directory), "PCSX2");
CoTaskMemFree(documents_directory);
}
#elif defined(__linux__)
#elif defined(__linux__) || defined(__FreeBSD__)
// Use $XDG_CONFIG_HOME/PCSX2 if it exists.
const char* xdg_config_home = getenv("XDG_CONFIG_HOME");
if (xdg_config_home && Path::IsAbsolute(xdg_config_home))
+3 -6
View 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()
+1 -1
View File
@@ -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;
+11 -7
View File
@@ -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)
+2 -1
View File
@@ -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;
};
+8 -2
View File
@@ -90,7 +90,7 @@ bool ImGuiManager::Initialize()
return false;
}
s_global_scale = std::max(1.0f, g_host_display->GetWindowScale() * (EmuConfig.GS.OsdScale / 100.0f));
s_global_scale = std::max(0.5f, g_host_display->GetWindowScale() * (EmuConfig.GS.OsdScale / 100.0f));
ImGui::CreateContext();
@@ -185,7 +185,7 @@ void ImGuiManager::WindowResized()
void ImGuiManager::UpdateScale()
{
const float window_scale = g_host_display ? g_host_display->GetWindowScale() : 1.0f;
const float scale = std::max(window_scale * (EmuConfig.GS.OsdScale / 100.0f), 1.0f);
const float scale = std::max(window_scale * (EmuConfig.GS.OsdScale / 100.0f), 0.5f);
if (scale == s_global_scale && (!HasFullscreenFonts() || !ImGuiFullscreen::UpdateLayoutScale()))
return;
@@ -229,6 +229,12 @@ void ImGuiManager::NewFrame()
}
}
void ImGuiManager::SkipFrame()
{
ImGui::EndFrame();
NewFrame();
}
void ImGuiManager::SetStyle()
{
ImGuiStyle& style = ImGui::GetStyle();
+3
View File
@@ -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();
+58 -18
View File
@@ -105,6 +105,7 @@ namespace InputManager
static bool ParseBindingAndGetSource(const std::string_view& binding, InputBindingKey* key, InputSource** source);
static bool IsAxisHandler(const InputEventHandler& handler);
static float ApplySingleBindingScale(float sensitivity, float deadzone, float value);
static void AddHotkeyBindings(SettingsInterface& si);
static void AddPadBindings(SettingsInterface& si, u32 pad, const char* default_type);
@@ -159,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)>;
@@ -585,6 +590,12 @@ std::string InputManager::GetPointerDeviceName(u32 pointer_index)
// Binding Enumeration
// ------------------------------------------------------------------------
float InputManager::ApplySingleBindingScale(float scale, float deadzone, float value)
{
const float svalue = std::clamp(value * scale, 0.0f, 1.0f);
return (deadzone > 0.0f && svalue < deadzone) ? 0.0f : svalue;
}
std::vector<const HotkeyInfo*> InputManager::GetHotkeyList()
{
std::vector<const HotkeyInfo*> ret;
@@ -613,7 +624,7 @@ void InputManager::AddHotkeyBindings(SettingsInterface& si)
void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const char* default_type)
{
const std::string section(StringUtil::StdStringFromFormat("Pad%u", pad_index + 1));
const std::string section(fmt::format("Pad{}", pad_index + 1));
const std::string type(si.GetStringValue(section.c_str(), "Type", default_type));
if (type.empty() || type == "None")
return;
@@ -636,9 +647,12 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const ch
if (!bindings.empty())
{
// we use axes for all pad bindings to simplify things, and because they are pressure sensitive
AddBindings(bindings, InputAxisEventHandler{[pad_index, bind_index = bi.bind_index](float value) {
PAD::SetControllerState(pad_index, bind_index, value);
}});
const float sensitivity = si.GetFloatValue(section.c_str(), fmt::format("{}Scale", bi.name).c_str(), 1.0f);
const float deadzone = si.GetFloatValue(section.c_str(), fmt::format("{}Deadzone", bi.name).c_str(), 0.0f);
AddBindings(
bindings, InputAxisEventHandler{[pad_index, bind_index = bi.bind_index, sensitivity, deadzone](float value) {
PAD::SetControllerState(pad_index, bind_index, ApplySingleBindingScale(sensitivity, deadzone, value));
}});
}
}
break;
@@ -717,8 +731,11 @@ void InputManager::AddUSBBindings(SettingsInterface& si, u32 port)
const std::vector<std::string> bindings(si.GetStringList(section.c_str(), bind_name.c_str()));
if (!bindings.empty())
{
AddBindings(bindings, InputAxisEventHandler{[port, bind_index = bi.bind_index](
float value) { USB::SetDeviceBindValue(port, bind_index, value); }});
const float sensitivity = si.GetFloatValue(section.c_str(), fmt::format("{}Scale", bi.name).c_str(), 1.0f);
const float deadzone = si.GetFloatValue(section.c_str(), fmt::format("{}Deadzone", bi.name).c_str(), 0.0f);
AddBindings(bindings, InputAxisEventHandler{[port, bind_index = bi.bind_index, sensitivity, deadzone](float value) {
USB::SetDeviceBindValue(port, bind_index, ApplySingleBindingScale(sensitivity, deadzone, value));
}});
}
}
break;
@@ -937,15 +954,34 @@ 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 <= static_cast<u32>(InputPointerAxis::Y))
{
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
{
// ImGui can consume mouse wheel events when the mouse is over a UI element.
if (delta != 0.0f && ImGuiManager::ProcessPointerAxisEvent(key, delta))
continue;
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;
@@ -1190,14 +1226,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);
+1 -1
View File
@@ -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;
+4 -4
View File
@@ -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()
+3 -6
View File
@@ -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()
+1 -1
View File
@@ -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;
+9 -8
View File
@@ -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()
+1 -1
View File
@@ -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;
+9 -10
View File
@@ -32,19 +32,15 @@ void gsSetVideoMode(GS_VideoMode mode)
{
gsVideoMode = mode;
UpdateVSyncRate();
CSRreg.FIELD = 1;
}
// Make sure framelimiter options are in sync with GS capabilities.
void gsReset()
{
GetMTGS().ResetGS(true);
UpdateVSyncRate();
gsVideoMode = GS_VideoMode::Uninitialized;
memzero(g_RealGSMem);
CSRreg.Reset();
GSIMR.reset();
UpdateVSyncRate();
}
void gsUpdateFrequency(Pcsx2Config& config)
@@ -85,11 +81,14 @@ static __fi void gsCSRwrite( const tGS_CSR& csr )
//Console.Warning( "csr.RESET" );
//gifUnit.Reset(true); // Don't think gif should be reset...
gifUnit.gsSIGNAL.queued = false;
GetMTGS().SendSimplePacket(GS_RINGTYPE_RESET, 0, 0, 0);
const u32 field = CSRreg.FIELD;
CSRreg.Reset();
gifUnit.gsFINISH.gsFINISHFired = true;
// Privilage registers also reset.
memzero(g_RealGSMem);
GSIMR.reset();
CSRreg.FIELD = field;
CSRreg.Reset();
gsVideoMode = GS_VideoMode::Uninitialized;
UpdateVSyncRate();
GetMTGS().SendSimplePacket(GS_RINGTYPE_RESET, 0, 0, 0);
}
if(csr.FLUSH)
-1
View File
@@ -153,7 +153,6 @@ union tGS_CSR
void Reset()
{
_u64 = 0;
FIFO = CSR_FIFO_EMPTY;
REV = 0x1B; // GS Revision
ID = 0x55; // GS ID
+54 -35
View File
@@ -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;
@@ -783,11 +800,13 @@ void GSSwitchRenderer(GSRendererType new_renderer)
if (existing_api == RenderAPI::OpenGLES)
existing_api = RenderAPI::OpenGL;
g_gs_renderer->PurgeTextureCache();
const bool is_software_switch = (new_renderer == GSRendererType::SW || GSConfig.Renderer == GSRendererType::SW);
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.");
}
+1 -1
View File
@@ -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);
+9
View File
@@ -149,6 +149,15 @@ bool GSClut::InvalidateRange(u32 start_block, u32 end_block, bool is_draw)
GIFRegTEX0 next_cbp;
next_cbp.U64 = m_write.next_tex0;
// Handle wrapping writes. Star Wars Battlefront 2 does this.
if ((end_block & 0xFFE0) < (start_block & 0xFFE0))
{
if ((next_cbp.CBP + 3U) <= end_block)
next_cbp.CBP += 0x4000;
end_block += 0x4000;
}
if ((next_cbp.CBP + 3U) >= start_block && end_block >= next_cbp.CBP)
{
m_write.dirty |= is_draw ? 2 : 1;
-9
View File
@@ -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 */},
-1
View File
@@ -23,7 +23,6 @@ public:
enum Title : u32
{
NoTitle,
FFX2,
GetawayGames,
HauntingGround,
ICO,
+1 -48
View File
@@ -130,7 +130,7 @@ GIFRegTEX0 GSDrawingContext::GetSizeFixedTEX0(const GSVector4& st, bool linear,
res.TW = tw > 10 ? 0 : tw;
res.TH = th > 10 ? 0 : th;
if (GSConfig.Renderer == GSRendererType::SW && (TEX0.TW != res.TW || TEX0.TH != res.TH))
if (TEX0.TW != res.TW || TEX0.TH != res.TH)
{
GL_DBG("FixedTEX0 %05x %d %d tw %d=>%d th %d=>%d st (%.0f,%.0f,%.0f,%.0f) uvmax %d,%d wm %d,%d (%d,%d,%d,%d)",
(int)TEX0.TBP0, (int)TEX0.TBW, (int)TEX0.PSM,
@@ -142,50 +142,3 @@ GIFRegTEX0 GSDrawingContext::GetSizeFixedTEX0(const GSVector4& st, bool linear,
return res;
}
void GSDrawingContext::ComputeFixedTEX0(const GSVector4& st)
{
// It is quite complex to handle rescaling so this function is less stricter than GetSizeFixedTEX0,
// therefore we remove the reduce optimization and we don't handle bilinear filtering which might create wrong interpolation at the border.
int tw = TEX0.TW;
int th = TEX0.TH;
int wms = (int)CLAMP.WMS;
int wmt = (int)CLAMP.WMT;
int minu = (int)CLAMP.MINU;
int minv = (int)CLAMP.MINV;
int maxu = (int)CLAMP.MAXU;
int maxv = (int)CLAMP.MAXV;
if (wms != CLAMP_REGION_CLAMP)
tw = tw > 10 ? 0 : tw;
if (wmt != CLAMP_REGION_CLAMP)
th = th > 10 ? 0 : th;
GSVector4i uv = GSVector4i(st.floor().xyzw(st.ceil()));
uv.x = findmax(uv.x, uv.z, (1 << tw) - 1, wms, minu, maxu);
uv.y = findmax(uv.y, uv.w, (1 << th) - 1, wmt, minv, maxv);
if (wms == CLAMP_REGION_CLAMP || wms == CLAMP_REGION_REPEAT)
tw = extend(uv.x, tw);
if (wmt == CLAMP_REGION_CLAMP || wmt == CLAMP_REGION_REPEAT)
th = extend(uv.y, th);
tw = std::clamp<int>(tw, 0, 10);
th = std::clamp<int>(th, 0, 10);
if ((tw != (int)TEX0.TW) || (th != (int)TEX0.TH))
{
m_fixed_tex0 = true;
TEX0.TW = tw;
TEX0.TH = th;
GL_DBG("FixedTEX0 TW %d=>%d, TH %d=>%d wm %d,%d",
(int)stack.TEX0.TW, (int)TEX0.TW, (int)stack.TEX0.TH, (int)TEX0.TH,
(int)CLAMP.WMS, (int)CLAMP.WMT);
}
}
-9
View File
@@ -69,12 +69,8 @@ public:
GIFRegZBUF ZBUF;
} stack;
bool m_fixed_tex0;
GSDrawingContext()
{
m_fixed_tex0 = false;
memset(&offset, 0, sizeof(offset));
Reset();
@@ -140,8 +136,6 @@ public:
}
GIFRegTEX0 GetSizeFixedTEX0(const GSVector4& st, bool linear, bool mipmap = false) const;
void ComputeFixedTEX0(const GSVector4& st);
bool HasFixedTEX0() const { return m_fixed_tex0; }
// Save & Restore before/after draw allow to correct/optimize current register for current draw
// Note: we could avoid the restore part if all renderer code is updated to use a local copy instead
@@ -159,9 +153,6 @@ public:
stack.FBA = FBA;
stack.FRAME = FRAME;
stack.ZBUF = ZBUF;
// This function is called before the draw so take opportunity to reset m_fixed_tex0
m_fixed_tex0 = false;
}
void RestoreReg()
+1 -1
View File
@@ -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;
+1
View File
@@ -823,6 +823,7 @@ union
REG_END2
__forceinline bool IsRepeating() const
{
// This is actually "does the texture span more than one page".
if (TBW < 2)
{
if (PSM == PSM_PSMT8)
+199 -395
View File
@@ -26,6 +26,7 @@
#include <iomanip> // Dump Verticles
int GSState::s_n = 0;
int GSState::s_transfer_n = 0;
static __fi bool IsAutoFlushEnabled()
{
@@ -37,14 +38,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.
@@ -56,58 +104,12 @@ GSState::GSState()
memset(&m_v, 0, sizeof(m_v));
memset(&m_vertex, 0, sizeof(m_vertex));
memset(&m_index, 0, sizeof(m_index));
memset(m_mem.m_vm8, 0, m_mem.m_vmsize);
m_v.RGBAQ.Q = 1.0f;
GrowVertexBuffer();
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);
m_draw_transfers.clear();
PRIM = &m_env.PRIM;
//CSR->rREV = 0x20;
@@ -140,8 +142,6 @@ void GSState::Reset(bool hardware_reset)
Flush(GSFlushReason::RESET);
// FIXME: bios logo not shown cut in half after reset, missing graphics in GoW after first FMV
if (hardware_reset)
memset(m_mem.m_vm8, 0, m_mem.m_vmsize);
memset(&m_path, 0, sizeof(m_path));
memset(&m_v, 0, sizeof(m_v));
@@ -155,7 +155,7 @@ void GSState::Reset(bool hardware_reset)
m_env.UpdateDIMX();
for (size_t i = 0; i < 2; i++)
for (u32 i = 0; i < 2; i++)
{
m_env.CTXT[i].UpdateScissor();
@@ -285,6 +285,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,254 +348,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::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();
@@ -673,14 +433,14 @@ void GSState::DumpVertices(const std::string& filename)
file << std::endl << std::endl;
const size_t count = m_index.tail;
const u32 count = m_index.tail;
GSVertex* buffer = &m_vertex.buff[0];
const char* DEL = ", ";
file << "VERTEX COORDS (XYZ)" << std::endl;
file << std::fixed << std::setprecision(4);
for (size_t i = 0; i < count; ++i)
for (u32 i = 0; i < count; ++i)
{
file << "\t" << "v" << i << ": ";
GSVertex v = buffer[m_index.buff[i]];
@@ -698,7 +458,7 @@ void GSState::DumpVertices(const std::string& filename)
file << "VERTEX COLOR (RGBA)" << std::endl;
file << std::fixed << std::setprecision(6);
for (size_t i = 0; i < count; ++i)
for (u32 i = 0; i < count; ++i)
{
file << "\t" << "v" << i << ": ";
GSVertex v = buffer[m_index.buff[i]];
@@ -716,7 +476,7 @@ void GSState::DumpVertices(const std::string& filename)
const std::string qualifier = use_uv ? "UV" : "STQ";
file << "TEXTURE COORDS (" << qualifier << ")" << std::endl;;
for (size_t i = 0; i < count; ++i)
for (u32 i = 0; i < count; ++i)
{
file << "\t" << "v" << i << ": ";
const GSVertex v = buffer[m_index.buff[i]];
@@ -1062,7 +822,7 @@ void GSState::ApplyTEX0(GIFRegTEX0& TEX0)
if (TEX0.CBP != m_mem.m_clut.GetCLUTCBP())
{
m_mem.m_clut.ClearDrawInvalidity();
CLUTAutoFlush();
CLUTAutoFlush(PRIM->PRIM);
}
Flush(GSFlushReason::CLUTCHANGE);
}
@@ -1689,7 +1449,6 @@ inline void GSState::CopyEnv(GSDrawingEnvironment* dest, GSDrawingEnvironment* s
{
memcpy(dest, src, 88);
memcpy(&dest->CTXT[ctx], &src->CTXT[ctx], 96);
dest->CTXT[ctx].m_fixed_tex0 = src->CTXT[ctx].m_fixed_tex0;
}
void GSState::Flush(GSFlushReason reason)
@@ -1762,6 +1521,7 @@ void GSState::FlushWrite()
m_tr.start += len;
g_perfmon.Put(GSPerfMon::Swizzle, len);
s_transfer_n++;
}
// This function decides if the context has changed in a way which warrants flushing the draw.
@@ -1844,10 +1604,10 @@ void GSState::FlushPrim()
GSVertex buff[2];
s_n++;
const size_t head = m_vertex.head;
const size_t tail = m_vertex.tail;
const size_t next = m_vertex.next;
size_t unused = 0;
const u32 head = m_vertex.head;
const u32 tail = m_vertex.tail;
const u32 next = m_vertex.next;
u32 unused = 0;
if (tail > head)
{
@@ -1864,7 +1624,7 @@ void GSState::FlushPrim()
break;
case GS_TRIANGLELIST:
case GS_TRIANGLESTRIP:
unused = std::min<size_t>(tail - head, 2);
unused = std::min<u32>(tail - head, 2);
memcpy(buff, &m_vertex.buff[tail - unused], sizeof(GSVertex) * 2);
break;
case GS_TRIANGLEFAN:
@@ -1932,7 +1692,7 @@ void GSState::FlushPrim()
// Jak 3 shadows get spikey (with autoflush) if you don't.
if (PRIM->PRIM == GS_TRIANGLEFAN)
{
for (size_t i = 0; i < unused; i++)
for (u32 i = 0; i < unused; i++)
{
GSVector4i* RESTRICT vert_ptr = (GSVector4i*)&m_vertex.buff[i];
GSVector4i v = vert_ptr[1];
@@ -1984,8 +1744,6 @@ void GSState::Write(const u8* mem, int len)
if (!m_tr.Update(w, h, psm.trbpp, len))
return;
GIFRegTEX0& prev_tex0 = m_prev_env.CTXT[m_prev_env.PRIM.CTXT].TEX0;
const u32 write_start_bp = m_mem.m_psm[blit.DPSM].info.bn(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, blit.DBP, blit.DBW); // (m_mem.*psm.pa)(static_cast<int>(m_env.TRXPOS.DSAX), static_cast<int>(m_env.TRXPOS.DSAY), blit.DBP, blit.DBW) >> 6;
@@ -2000,6 +1758,20 @@ void GSState::Write(const u8* mem, int len)
// Invalid the CLUT if it crosses paths.
m_mem.m_clut.InvalidateRange(write_start_bp, write_end_bp);
GSVector4i r;
r.left = m_env.TRXPOS.DSAX;
r.top = m_env.TRXPOS.DSAY;
r.right = r.left + m_env.TRXREG.RRW;
r.bottom = r.top + m_env.TRXREG.RRH;
// Store the transfer for preloading new RT's.
if (m_draw_transfers.size() == 0 || (m_draw_transfers.size() > 0 && blit.DBP != m_draw_transfers.back().blit.DBP))
{
GSUploadQueue new_transfer = { blit, r, s_n };
m_draw_transfers.push_back(new_transfer);
}
GL_CACHE("Write! ... => 0x%x W:%d F:%s (DIR %d%d), dPos(%d %d) size(%d %d)",
blit.DBP, blit.DBW, psm_str(blit.DPSM),
m_env.TRXPOS.DIRX, m_env.TRXPOS.DIRY,
@@ -2008,12 +1780,6 @@ void GSState::Write(const u8* mem, int len)
if (m_tr.end == 0 && len >= m_tr.total)
{
// received all data in one piece, no need to buffer it
GSVector4i r;
r.left = m_env.TRXPOS.DSAX;
r.top = m_env.TRXPOS.DSAY;
r.right = r.left + m_env.TRXREG.RRW;
r.bottom = r.top + m_env.TRXREG.RRH;
ExpandTarget(m_env.BITBLTBUF, r);
InvalidateVideoMem(blit, r, true);
@@ -2022,6 +1788,7 @@ void GSState::Write(const u8* mem, int len)
m_tr.start = m_tr.end = m_tr.total;
g_perfmon.Put(GSPerfMon::Swizzle, len);
s_transfer_n++;
}
else
{
@@ -2088,6 +1855,7 @@ void GSState::Move()
{
// ffxii uses this to move the top/bottom of the scrolling menus offscreen and then blends them back over the text to create a shading effect
// guitar hero copies the far end of the board to do a similar blend too
s_transfer_n++;
int sx = m_env.TRXPOS.SSAX;
int sy = m_env.TRXPOS.SSAY;
@@ -2146,6 +1914,21 @@ void GSState::Move()
{
Flush(GSFlushReason::LOCALTOLOCALMOVE);
}
// Store the transfer for preloading new RT's.
if (m_draw_transfers.size() == 0 || (m_draw_transfers.size() > 0 && dbp != m_draw_transfers.back().blit.DBP))
{
GSVector4i r;
r.left = m_env.TRXPOS.DSAX;
r.top = m_env.TRXPOS.DSAY;
r.right = r.left + m_env.TRXREG.RRW;
r.bottom = r.top + m_env.TRXREG.RRH;
GSUploadQueue new_transfer = { m_env.BITBLTBUF, r, s_n };
m_draw_transfers.push_back(new_transfer);
}
// Invalid the CLUT if it crosses paths.
m_mem.m_clut.InvalidateRange(write_start_bp, write_end_bp);
@@ -2553,18 +2336,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);
@@ -2632,7 +2416,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;
@@ -2641,7 +2425,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;
@@ -2733,7 +2517,7 @@ int GSState::Defrost(const freezeData* fd)
m_env.UpdateDIMX();
for (size_t i = 0; i < 2; i++)
for (u32 i = 0; i < 2; i++)
{
m_env.CTXT[i].UpdateScissor();
@@ -2748,6 +2532,8 @@ int GSState::Defrost(const freezeData* fd)
g_perfmon.SetFrame(5000);
ResetPCRTC();
return 0;
}
@@ -2802,7 +2588,7 @@ void GSState::UpdateVertexKick()
void GSState::GrowVertexBuffer()
{
const size_t maxcount = std::max<size_t>(m_vertex.maxcount * 3 / 2, 10000);
const u32 maxcount = std::max<u32>(m_vertex.maxcount * 3 / 2, 10000);
GSVertex* vertex = (GSVertex*)_aligned_malloc(sizeof(GSVertex) * maxcount, 32);
// Worst case index list is a list of points with vs expansion, 6 indices per point
@@ -2810,8 +2596,8 @@ void GSState::GrowVertexBuffer()
if (vertex == NULL || index == NULL)
{
const size_t vert_byte_count = sizeof(GSVertex) * maxcount;
const size_t idx_byte_count = sizeof(u32) * maxcount * 3;
const u32 vert_byte_count = sizeof(GSVertex) * maxcount;
const u32 idx_byte_count = sizeof(u32) * maxcount * 3;
Console.Error("GS: failed to allocate %zu bytes for verticles and %zu for indices.",
vert_byte_count, idx_byte_count);
@@ -2849,12 +2635,12 @@ GSState::PRIM_OVERLAP GSState::PrimitiveOverlap()
return PRIM_OVERLAP_UNKNOW; // maybe, maybe not
// Check intersection of sprite primitive only
const size_t count = m_vertex.next;
const u32 count = m_vertex.next;
PRIM_OVERLAP overlap = PRIM_OVERLAP_NO;
const GSVertex* v = m_vertex.buff;
m_drawlist.clear();
size_t i = 0;
u32 i = 0;
while (i < count)
{
// In order to speed up comparison a bounding-box is accumulated. It removes a
@@ -2868,7 +2654,7 @@ GSState::PRIM_OVERLAP GSState::PrimitiveOverlap()
GSVector4i all = GSVector4i(v[i].m[1]).upl16(GSVector4i(v[i + 1].m[1])).upl16().xzyw();
all = all.xyxy().blend(all.zwzw(), all > all.zwxy());
size_t j = i + 2;
u32 j = i + 2;
while (j < count)
{
GSVector4i sprite = GSVector4i(v[j].m[1]).upl16(GSVector4i(v[j + 1].m[1])).upl16().xzyw();
@@ -2906,12 +2692,12 @@ GSState::PRIM_OVERLAP GSState::PrimitiveOverlap()
// Some safe-guard will be added in the outer-loop to avoid corruption with a limited perf impact
if (v[1].XYZ.Y < v[0].XYZ.Y) {
// First vertex is Top-Left
for (size_t i = 0; i < count; i += 2) {
for (u32 i = 0; i < count; i += 2) {
if (v[i + 1].XYZ.Y > v[i].XYZ.Y) {
return PRIM_OVERLAP_UNKNOW;
}
GSVector4i vi(v[i].XYZ.X, v[i + 1].XYZ.Y, v[i + 1].XYZ.X, v[i].XYZ.Y);
for (size_t j = i + 2; j < count; j += 2) {
for (u32 j = i + 2; j < count; j += 2) {
GSVector4i vj(v[j].XYZ.X, v[j + 1].XYZ.Y, v[j + 1].XYZ.X, v[j].XYZ.Y);
GSVector4i inter = vi.rintersect(vj);
if (!inter.rempty()) {
@@ -2922,12 +2708,12 @@ GSState::PRIM_OVERLAP GSState::PrimitiveOverlap()
}
else {
// First vertex is Bottom-Left
for (size_t i = 0; i < count; i += 2) {
for (u32 i = 0; i < count; i += 2) {
if (v[i + 1].XYZ.Y < v[i].XYZ.Y) {
return PRIM_OVERLAP_UNKNOW;
}
GSVector4i vi(v[i].XYZ.X, v[i].XYZ.Y, v[i + 1].XYZ.X, v[i + 1].XYZ.Y);
for (size_t j = i + 2; j < count; j += 2) {
for (u32 j = i + 2; j < count; j += 2) {
GSVector4i vj(v[j].XYZ.X, v[j].XYZ.Y, v[j + 1].XYZ.X, v[j + 1].XYZ.Y);
GSVector4i inter = vi.rintersect(vj);
if (!inter.rempty()) {
@@ -2960,14 +2746,14 @@ __forceinline bool GSState::IsAutoFlushDraw()
return false;
}
__forceinline void GSState::CLUTAutoFlush()
__forceinline void GSState::CLUTAutoFlush(u32 prim)
{
if (m_mem.m_clut.IsInvalid() & 2)
return;
size_t n = 1;
u32 n = 1;
switch (PRIM->PRIM)
switch (prim)
{
case GS_POINTLIST:
n = 1;
@@ -3000,7 +2786,7 @@ __forceinline void GSState::CLUTAutoFlush()
// If it's a point, then we only have one coord, so the address for start and end will be the same, which is bad for the following check.
u32 endbp = startbp;
// otherwise calculate the end.
if (PRIM->PRIM != GS_POINTLIST || (m_index.tail > 1))
if (prim != GS_POINTLIST || (m_index.tail > 1))
endbp = psm.info.bn(temp_draw_rect.z - 1, temp_draw_rect.w - 1, m_context->FRAME.Block(), m_context->FRAME.FBW);
m_mem.m_clut.InvalidateRange(startbp, endbp, true);
@@ -3008,6 +2794,7 @@ __forceinline void GSState::CLUTAutoFlush()
}
}
template<u32 prim, bool index_swap>
__forceinline void GSState::HandleAutoFlush()
{
// Kind of a cheat, making the assumption that 2 consecutive fan/strip triangles won't overlap each other (*should* be safe)
@@ -3020,13 +2807,14 @@ __forceinline void GSState::HandleAutoFlush()
if (IsAutoFlushDraw())
{
int n = 1;
size_t buff[3];
const size_t head = m_vertex.head;
const size_t tail = m_vertex.tail;
u32 buff[3];
const u32 head = m_vertex.head;
const u32 tail = m_vertex.tail;
switch (PRIM->PRIM)
switch (prim)
{
case GS_POINTLIST:
buff[0] = tail - 1;
n = 1;
break;
case GS_LINELIST:
@@ -3099,7 +2887,7 @@ __forceinline void GSState::HandleAutoFlush()
}
// Get the last texture position from the last draw.
const GSVertex* v = &m_vertex.buff[m_index.buff[m_index.tail - 1]];
const GSVertex* v = &m_vertex.buff[m_index.buff[m_index.tail - (index_swap ? n : 1)]];
if (PRIM->FST)
{
@@ -3135,7 +2923,7 @@ __forceinline void GSState::HandleAutoFlush()
const GSVector2i offset = GSVector2i(m_context->XYOFFSET.OFX, m_context->XYOFFSET.OFY);
const GSVector4i scissor = GSVector4i(m_context->scissor.in);
GSVector4i old_tex_rect = GSVector4i(0, 0, 0, 0);
GSVector4i old_tex_rect = GSVector4i::zero();
int current_draw_end = m_index.tail;
while (current_draw_end >= n)
@@ -3203,40 +2991,43 @@ __forceinline void GSState::HandleAutoFlush()
}
}
template <u32 prim, bool auto_flush, bool index_swap>
__forceinline void GSState::VertexKick(u32 skip)
static constexpr u32 NumIndicesForPrim(u32 prim)
{
size_t n = 0;
switch (prim)
{
case GS_POINTLIST:
case GS_INVALID:
n = 1;
break;
return 1;
case GS_LINELIST:
case GS_SPRITE:
case GS_LINESTRIP:
n = 2;
break;
return 2;
case GS_TRIANGLELIST:
case GS_TRIANGLESTRIP:
case GS_TRIANGLEFAN:
n = 3;
break;
return 3;
default:
return 0;
}
}
template <u32 prim, bool auto_flush, bool index_swap>
__forceinline void GSState::VertexKick(u32 skip)
{
constexpr u32 n = NumIndicesForPrim(prim);
static_assert(n > 0);
ASSERT(m_vertex.tail < m_vertex.maxcount + 3);
if (auto_flush && skip == 0 && m_index.tail > 0 && ((m_vertex.tail + 1) - m_vertex.head) >= n)
{
HandleAutoFlush();
HandleAutoFlush<prim, index_swap>();
}
size_t head = m_vertex.head;
size_t tail = m_vertex.tail;
size_t next = m_vertex.next;
size_t xy_tail = m_vertex.xy_tail;
u32 head = m_vertex.head;
u32 tail = m_vertex.tail;
u32 next = m_vertex.next;
u32 xy_tail = m_vertex.xy_tail;
// callers should write XYZUVF to m_v.m[1] in one piece to have this load store-forwarded, either by the cpu or the compiler when this function is inlined
@@ -3255,7 +3046,7 @@ __forceinline void GSState::VertexKick(u32 skip)
m_vertex.tail = ++tail;
m_vertex.xy_tail = ++xy_tail;
const size_t m = tail - head;
const u32 m = tail - head;
if (m < n)
return;
@@ -3437,41 +3228,54 @@ __forceinline void GSState::VertexKick(u32 skip)
break;
case GS_INVALID:
m_vertex.tail = head;
break;
return;
default:
__assume(0);
}
GSVector4i draw_coord;
const GSVector2i offset = GSVector2i(m_context->XYOFFSET.OFX, m_context->XYOFFSET.OFY);
for (size_t i = 0; i < n; i++)
{
const GSVertex* v = &m_vertex.buff[m_index.buff[(m_index.tail - n) + i]];
draw_coord.x = (static_cast<int>(v->XYZ.X) - offset.x) >> 4;
draw_coord.y = (static_cast<int>(v->XYZ.Y) - offset.y) >> 4;
const GSVector4i voffset(GSVector4i::loadl(&m_context->XYOFFSET));
if (m_vertex.tail == n && i == 0)
auto get_vertex = [&](u32 i) {
GSVector4i v(GSVector4i::loadl(&m_vertex.buff[m_index.buff[(m_index.tail - n) + (i)]].XYZ));
v = v.upl16(); // 16->32
v = v.sub32(voffset); // -= (OFX, OFY)
v = v.sra32(4); // >> 4
return v;
};
const GSVector4i xy0(get_vertex(0));
GSVector4i min, max;
if (m_vertex.tail == n)
{
temp_draw_rect.x = draw_coord.x;
temp_draw_rect.y = draw_coord.y;
temp_draw_rect = temp_draw_rect.xyxy();
min = xy0;
max = xy0;
}
else
{
temp_draw_rect.x = std::min(draw_coord.x, temp_draw_rect.x);
temp_draw_rect.y = std::min(draw_coord.y, temp_draw_rect.y);
temp_draw_rect.z = std::max(draw_coord.x, temp_draw_rect.z);
temp_draw_rect.w = std::max(draw_coord.y, temp_draw_rect.w);
min = temp_draw_rect.min_i32(xy0);
max = temp_draw_rect.zwzw().max_i32(xy0);
}
if constexpr (n > 1)
{
const GSVector4i xy1(get_vertex(1));
min = min.min_i32(xy1);
max = max.max_i32(xy1);
if constexpr (n > 2)
{
const GSVector4i xy2(get_vertex(2));
min = min.min_i32(xy2);
max = max.max_i32(xy2);
}
}
const GSVector4i scissor = GSVector4i(m_context->scissor.in);
temp_draw_rect = min.upl64(max).rintersect(scissor);
}
const GSVector4i scissor = GSVector4i(m_context->scissor.in);
temp_draw_rect.rintersect(scissor);
CLUTAutoFlush();
CLUTAutoFlush(prim);
}
/// Checks if region repeat is used (applying it does something to at least one of the values in min...max)
@@ -3539,8 +3343,11 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(const GIFRegTEX0& TEX0, c
const int minu = (int)CLAMP.MINU;
const int minv = (int)CLAMP.MINV;
const int maxu = (int)CLAMP.MAXU;
const int maxv = (int)CLAMP.MAXV;
// For the FixedTEX0 case, in hardware, we handle this in the texture cache. Don't OR the bits in here, otherwise
// we'll end up with an invalid rectangle, we want the passed-in rectangle to be relative to the normalized size.
const int maxu = (wms != CLAMP_REGION_REPEAT || (int)CLAMP.MAXU < w) ? (int)CLAMP.MAXU : 0;
const int maxv = (wmt != CLAMP_REGION_REPEAT || (int)CLAMP.MAXV < h) ? (int)CLAMP.MAXV : 0;
GSVector4i vr = tr;
@@ -4050,9 +3857,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));
}
+623 -82
View File
@@ -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,28 +138,28 @@ 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
{
GSVertex* buff;
size_t head, tail, next, maxcount; // head: first vertex, tail: last vertex + 1, next: last indexed + 1
size_t xy_tail;
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;
size_t tail;
} m_index;
u32 tail;
} m_index = {};
void UpdateContext();
void UpdateScissor();
@@ -169,8 +168,9 @@ protected:
void GrowVertexBuffer();
bool IsAutoFlushDraw();
template<u32 prim, bool index_swap>
void HandleAutoFlush();
void CLUTAutoFlush();
void CLUTAutoFlush(u32 prim);
template <u32 prim, bool auto_flush, bool index_swap>
void VertexKick(u32 skip);
@@ -206,24 +206,34 @@ protected:
bool IsCoverageAlpha();
public:
GIFPath m_path[4];
GIFRegPRIM* PRIM;
GSPrivRegSet* m_regs;
struct GSUploadQueue
{
GIFRegBITBLTBUF blit;
GSVector4i rect;
int draw;
};
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;
static int s_n;
static int s_transfer_n;
static constexpr u32 STATE_VERSION = 8;
@@ -269,7 +279,7 @@ public:
GSREOPEN = 1 << 13,
};
GSFlushReason m_state_flush_reason;
GSFlushReason m_state_flush_reason = UNKNOWN;
enum PRIM_OVERLAP
{
@@ -278,61 +288,592 @@ 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()
{
const 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
const 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()
{
const 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)
{
const 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)
{
int max_height = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode].y : VideoModeOffsetsOverscan[videomode].y;
if (!(FFMD && interlaced))
{
max_height *= 2;
}
if (display == -1)
{
GSVector4i combined_rect = PCRTCDisplays[0].framebufferRect.runion(PCRTCDisplays[1].framebufferRect);
if (combined_rect.z >= 2048)
{
const 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)
{
const 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;
}
// Cap the framebuffer read to the maximum display height, otherwise the hardware renderer gets messy.
const int min_mag = std::max(1, std::min(PCRTCDisplays[0].magnification.y, PCRTCDisplays[1].magnification.y));
int offset = PCRTCDisplays[0].displayRect.runion(PCRTCDisplays[1].displayRect).y;
if (FFMD && interlaced)
{
offset = (offset - 1) / 2;
}
// Hardware mode needs a wider framebuffer as it can't offset the read.
if (GSConfig.UseHardwareRenderer())
{
combined_rect.z += std::max(PCRTCDisplays[0].framebufferOffsets.x, PCRTCDisplays[1].framebufferOffsets.x);
combined_rect.w += std::max(PCRTCDisplays[0].framebufferOffsets.y, PCRTCDisplays[1].framebufferOffsets.y);
}
offset = (max_height / min_mag) - offset;
combined_rect.w = std::min(combined_rect.w, offset);
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;
}
// Cap the framebuffer read to the maximum display height, otherwise the hardware renderer gets messy.
const int min_mag = std::max(1, PCRTCDisplays[display].magnification.y);
int offset = PCRTCDisplays[display].displayRect.y;
if (FFMD && interlaced)
{
offset = (offset - 1) / 2;
}
offset = (max_height / min_mag) - offset;
out_rect.w = std::min(out_rect.w, offset);
// Hardware mode needs a wider framebuffer as it can't offset the read.
if (GSConfig.UseHardwareRenderer())
{
out_rect.z += PCRTCDisplays[display].framebufferOffsets.x;
out_rect.w += PCRTCDisplays[display].framebufferOffsets.y;
}
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 && PCRTCSameSrc)
{
if (abs(PCRTCDisplays[1].framebufferOffsets.y - PCRTCDisplays[0].framebufferOffsets.y) == 1
&& PCRTCDisplays[0].displayRect.y == PCRTCDisplays[1].displayRect.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;
}
if (abs(PCRTCDisplays[1].framebufferOffsets.x - PCRTCDisplays[0].framebufferOffsets.x) == 1
&& PCRTCDisplays[0].displayRect.x == PCRTCDisplays[1].displayRect.x)
{
if (PCRTCDisplays[1].framebufferOffsets.x < PCRTCDisplays[0].framebufferOffsets.x)
PCRTCDisplays[0].framebufferOffsets.x = PCRTCDisplays[1].framebufferOffsets.x;
else
PCRTCDisplays[1].framebufferOffsets.x = PCRTCDisplays[0].framebufferOffsets.x;
}
}
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];
// Software mode - See note below.
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
{
// Software Mode Note:
// 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)
{
const bool both_enabled = PCRTCDisplays[0].enabled && PCRTCDisplays[1].enabled;
// 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;
if (both_enabled)
{
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 (both_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 };
GSVector2i zeroDisplay = NearestToZeroOffset();
if (both_enabled)
{
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;
}
if (both_enabled)
{
GSVector2i offset;
offset.x = (PCRTCDisplays[1 - zeroDisplay.x].displayRect.x - PCRTCDisplays[zeroDisplay.x].displayRect.x);
offset.y = (PCRTCDisplays[1 - zeroDisplay.y].displayRect.y - PCRTCDisplays[zeroDisplay.y].displayRect.y);
if (offset.x > 0 && offset.x < 4 && GSConfig.PCRTCAntiBlur)
{
PCRTCDisplays[1 - zeroDisplay.x].displayRect.x -= offset.x;
PCRTCDisplays[1 - zeroDisplay.x].displayRect.z -= offset.x;
}
if (offset.y > 0 && offset.y < 4 && GSConfig.PCRTCAntiBlur)
{
PCRTCDisplays[1 - zeroDisplay.y].displayRect.y -= offset.y;
PCRTCDisplays[1 - zeroDisplay.y].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 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();
+2 -9
View File
@@ -187,14 +187,6 @@ std::unique_ptr<GSDownloadTexture> GSDevice::CreateDownloadTexture(u32 width, u3
return {};
}
void GSDevice::EndScene()
{
m_vertex.start += m_vertex.count;
m_vertex.count = 0;
m_index.start += m_index.count;
m_index.count = 0;
}
void GSDevice::Recycle(GSTexture* t)
{
if (!t)
@@ -307,7 +299,7 @@ void GSDevice::Merge(GSTexture* sTex[3], GSVector4* sRect, GSVector4* dRect, con
}
}
DoMerge(tex, sRect, m_merge, dRect, PMODE, EXTBUF, c);
DoMerge(tex, sRect, m_merge, dRect, PMODE, EXTBUF, c, GSConfig.PCRTCOffsets);
for (size_t i = 0; i < std::size(tex); i++)
{
@@ -364,6 +356,7 @@ void GSDevice::Interlace(const GSVector2i& ds, int field, int mode, float yoffse
break;
default:
m_current = m_merge;
break;
}
}

Some files were not shown because too many files have changed in this diff Show More