Compare commits

...
72 Commits
Author SHA1 Message Date
Stenzekandrefractionpcsx2 2bf74622a5 GS/DX12: Fix potential crash in PrimID DATE setup 2023-02-26 03:49:40 +00:00
refractionpcsx2 6bcaef9325 GS-HW: Allow swapping of start/end block on overlap check within page 2023-02-26 01:29:49 +00:00
refractionpcsx2 0b3aac3d91 GS-HW: Add channel masks to dirty rects, allows partial updates 2023-02-26 01:29:49 +00:00
refractionpcsx2 9a53f0f853 GS-HW: Improve Local->Host and preload accuracy. 2023-02-26 01:29:49 +00:00
refractionpcsx2 a97df14064 GS-HW: Set scale on temporary depth stencil 2023-02-26 01:29:34 +00:00
RedDevilusandrefractionpcsx2 9a0cd1157f GameDB: Timesplitters Series + minor maintenance
- Timesplitters 2 (normal vertex screwing up bottom)
- Timesplitters Future Perfect (remove depth lines)
- Dragon Ball Z - Infinite World (dash
2023-02-26 01:13:24 +00:00
Mrlinkwiiandrefractionpcsx2 16c41255d0 Gamedb: remove disable partial invalidation from fatal frame 2023-02-26 00:24:24 +00:00
Stenzekandrefractionpcsx2 c2d1e5bd18 GameDB: Switch CPU CLUT for GPU CLUT in The Godfather
It can do 10+ readbacks in one frame, which is way too much.
2023-02-25 12:31:28 +00:00
Stenzekandrefractionpcsx2 3c85ba3eb8 GS/HW: Use 1023 for skip-target-creation threshold
Fixes target blowout in The Godfather (it does a 1023x1023 draw on
boot).
2023-02-25 12:31:28 +00:00
JordanTheToasterandrefractionpcsx2 f3b67b158c GameDB: Gorwlanser 5&6 and Poinies Poin Fixes
Adds Texture in RT to Growlanser 5 and 6 to fix a layering issue and to Poinies Poin to fix text corruption after an FMV.
2023-02-25 12:28:05 +00:00
kamfretozandrefractionpcsx2 8dac10ae36 Qt: Add a new theme, Cobalt Sky. 2023-02-25 11:00:56 +00:00
JordanTheToasterandrefractionpcsx2 6b81050283 GameDB: 007 EON Fixes
Adds mipmapping full and trilinear PS2 to 007 Everything Or Nothing to fix up texture detail and removes mipmapping from Fifa games to prevent players from going bald or having rainbow vomit shirts.
2023-02-25 10:55:53 +00:00
Stenzekandrefractionpcsx2 b4beacfc43 GS/OGL: Avoid framebuffer swaps when depth is being alternated 2023-02-25 08:18:34 +00:00
Stenzekandrefractionpcsx2 1b607a8c4d GameDB: Add partial target invalidation to affected games 2023-02-25 08:18:34 +00:00
Stenzekandrefractionpcsx2 4583c64ff7 GS/HW: Add partial target invalidation option
Eventually hopefully we can make this the default, but it breaks too
much at the moment.

Fixes missing/corrupted textures in True Crime: New York City.
2023-02-25 08:18:34 +00:00
Stenzekandrefractionpcsx2 a06a07d961 GS/HW: Allow sw prim render when texture rect is dirty
Gets rid of an unnecessary readback in True Crime: New York City.
2023-02-25 08:18:34 +00:00
Stenzekandrefractionpcsx2 520a369872 GS/HW: Prevent out of bounds copy in OI_BlitFMV() 2023-02-25 08:18:34 +00:00
Stenzekandrefractionpcsx2 1eaec773e2 GS/HW: Don't allocate Z buffer if tests always pass
.. and Z is masked.

Stops some offscreen targets from expanding in size.

Partial fix for regressions in True Crime: New York City.
2023-02-25 08:18:34 +00:00
Stenzekandrefractionpcsx2 3433a42e42 GS/DX12: Fix incorrect pipeline RT format 2023-02-25 03:04:50 +00:00
lightningterror e01eac615d GS-tc: Fix Wreorder-ctor warning. 2023-02-24 11:29:53 +01:00
lightningterror 8116646dee GS: Bump shader cache version. 2023-02-24 02:06:00 +01:00
lightningterror 7f7950cd6b GS-d3d: Check each channel individually if it overflows and do corrections. 2023-02-24 02:06:00 +01:00
lightningterror 2eb11ded52 GS-metal: Check each channel individually if it overflows and do corrections. 2023-02-24 02:06:00 +01:00
lightningterror bd64ad510b GS-vk: Check each channel individually if it overflows and do corrections. 2023-02-24 02:06:00 +01:00
lightningterror 4e9b7e61a7 GS-ogl: Check each channel individually if it overflows and do corrections. 2023-02-24 02:06:00 +01:00
lightningterror 37b19495a8 GS-hw: Use rgb color for second output instead of alpha.
We want different alpha value for each channel.
2023-02-24 02:06:00 +01:00
refractionpcsx2 c12b412e87 GS-HW: Remove debug message, partial Clang format GSRendererHW.cpp 2023-02-23 23:21:53 +00:00
refractionpcsx2 17f137f8be GS-HW: Slightly better check for target expanding 2023-02-23 20:13:01 +00:00
JordanTheToasterandrefractionpcsx2 fcc627c65c GameDB: Various fixes
Fixes for missing bloom intensity in Ferrari Challenge mipmapping in Dark Cloud and adjusts HPO on The Dog Island and adding missing Scandinavia entrys.
2023-02-23 19:37:29 +00:00
refractionpcsx2 5bb3d8e60d GS-HW: Improve GS read target detection, avoid reading dirty targets. 2023-02-23 18:27:01 +00:00
refractionpcsx2 264086e0aa GS-HW: Improve clear detection and avoid making bad render targets 2023-02-23 18:27:01 +00:00
refractionpcsx2 6013d7172a GS-HW: Drop to CPU CLUT w/e invalidate only if dirty covers whole tex 2023-02-23 18:27:01 +00:00
refractionpcsx2 a7714b2725 MCD: Stop Memcard Folders spamming the console/OSD when saving a game 2023-02-23 18:02:07 +00:00
Stenzekandrefractionpcsx2 f9dcac8cd0 GS/HW: Make readback-on-close a HW fix
Unfortunately it's too risky to enable by default all the time. So,
we'll make it a hw fix, and hopefully one day can make it default on.

Also makes save states readback the TC as well.
2023-02-23 17:25:03 +00:00
Stenzekandrefractionpcsx2 2487322e47 GS/HW: Track which bits are actually written to targets
Fixes Burnout 3 sky getting corrupted when flushing.
2023-02-23 17:25:03 +00:00
Stenzekandrefractionpcsx2 c7e9c9542e GS/HW: Don't try to dirty targets which don't overlap
The current invalidation code sucks, but at least this stops things
which are blatently wrong from happening.
2023-02-23 10:12:05 +00:00
Stenzekandrefractionpcsx2 c1bc1af302 GS/HW: Don't mess with the buffer width of the target on invalidate 2023-02-23 10:12:05 +00:00
Stenzekandrefractionpcsx2 739f9ec758 GS/HW: Set valid rect on targets created by Move 2023-02-23 10:12:05 +00:00
refractionpcsx2 f70a140f42 GS-HW: Fix Download readbacks and limit FB resizing 2023-02-22 11:50:11 +00:00
refractionpcsx2 a716e69dc0 GameDB: Remove no longer needed SW FMV switch fixes 2023-02-21 16:15:29 +00:00
refractionpcsx2 0c6e1a4d56 GS-HW: Invalidate overlapped target when expanding. 2023-02-21 16:15:29 +00:00
Stenzekandrefractionpcsx2 faf36ecba6 GS/D3D: Default to D3D11 for Intel 2023-02-21 15:49:59 +00:00
Stenzekandrefractionpcsx2 49f2900e1f GS/D3D: Combine driver version lookup for D3D11+D3D12 2023-02-21 15:49:59 +00:00
AKuHAKandrefractionpcsx2 8bd522d283 BiosTools: Implement SysGetBiosDiscID function, for Bios Serial used EXTINFO build unique timestamp 2023-02-21 15:24:36 +00:00
AKuHAKandrefractionpcsx2 e9034a1ba1 Biostools: remove unused external variable BiosZone 2023-02-21 15:24:36 +00:00
Stenzekandrefractionpcsx2 03feacd69a GS/HW: Use FB size instead of output size for target lookup
Fixes render target height doubling unnecessarily in Ridge Racer V.
2023-02-21 15:22:09 +00:00
Stenzekandrefractionpcsx2 0c9f44d8a4 GS/HW: Don't allow region textures to make sources larger
Currently not handled. Fixes Ridge Racer V intro again.
2023-02-21 15:22:09 +00:00
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
62 changed files with 1642 additions and 942 deletions
File diff suppressed because it is too large Load Diff
+6 -3
View File
@@ -543,7 +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,x:b0,y:b5,back:b2,guide:b10,start:b3,leftshoulder:b6,rightshoulder:b7,dpup:b12,dpleft:b14,dpdown:b13,dpright:b15,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b8,righttrigger:b9,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,
@@ -850,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,
@@ -992,7 +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,x:b3,y:b4,back:b10,guide:b12,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,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,
@@ -1177,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,
@@ -1272,8 +1274,8 @@ 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,
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,
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,
05000000d62000007162000001000000,Moga Pro 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,
@@ -1549,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,
+36 -23
View File
@@ -38,6 +38,7 @@
#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
@@ -762,8 +763,10 @@ void ps_color_clamp_wrap(inout float3 C)
}
}
void ps_blend(inout float4 Color, inout float As, float2 pos_xy)
void ps_blend(inout float4 Color, inout float4 As_rgba, float2 pos_xy)
{
float As = As_rgba.a;
if (SW_BLEND)
{
// PABE
@@ -812,15 +815,14 @@ void ps_blend(inout float4 Color, inout float As, float2 pos_xy)
{
// Replace Af with As so we can do proper compensation for Alpha.
if (PS_BLEND_C == 2)
As = Af;
As_rgba = (float4)Af;
// Subtract 1 for alpha to compensate for the changed equation,
// if c.rgb > 255.0f then we further need to adjust alpha accordingly,
// we pick the lowest overflow from all colors because it's the safest,
// we divide by 255 the color because we don't know Cd value,
// changed alpha should only be done for hw blend.
float min_color = min(min(Color.r, Color.g), Color.b);
float alpha_compensate = max(1.0f, min_color / 255.0f);
As -= alpha_compensate;
float3 alpha_compensate = max((float3)1.0f, Color.rgb / (float3)255.0f);
As_rgba.rgb -= alpha_compensate;
}
else if (PS_CLR_HW == 2)
{
@@ -877,26 +879,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)));
}
}
}
@@ -908,15 +921,15 @@ PS_OUTPUT ps_main(PS_INPUT input)
C.a = 128.0f;
}
float alpha_blend;
float4 alpha_blend;
if (PS_BLEND_C == 1 && PS_CLR_HW > 3)
{
float4 RT = trunc(RtTexture.Load(int3(input.p.xy, 0)) * 255.0f + 0.1f);
alpha_blend = RT.a / 128.0f;
alpha_blend = (float4)(RT.a / 128.0f);
}
else
{
alpha_blend = C.a / 128.0f;
alpha_blend = (float4)(C.a / 128.0f);
}
// Alpha correction
@@ -966,12 +979,12 @@ PS_OUTPUT ps_main(PS_INPUT input)
#if !PS_NO_COLOR
output.c0 = PS_HDR ? float4(C.rgb / 65535.0f, C.a / 255.0f) : C / 255.0f;
#if !PS_NO_COLOR1
output.c1 = (float4)(alpha_blend);
output.c1 = alpha_blend;
#endif
#if PS_NO_ABLEND
// write alpha blend factor into col0
output.c0.a = alpha_blend;
output.c0.a = alpha_blend.a;
#endif
#if PS_ONLY_ALPHA
// rgb isn't used
+20 -12
View File
@@ -686,8 +686,10 @@ void ps_color_clamp_wrap(inout vec3 C)
#endif
}
void ps_blend(inout vec4 Color, inout float As)
void ps_blend(inout vec4 Color, inout vec4 As_rgba)
{
float As = As_rgba.a;
#if SW_BLEND
// PABE
@@ -768,16 +770,15 @@ void ps_blend(inout vec4 Color, inout float As)
#if PS_CLR_HW == 1
// Replace Af with As so we can do proper compensation for Alpha.
#if PS_BLEND_C == 2
As = Af;
As_rgba = vec4(Af);
#endif
// Subtract 1 for alpha to compensate for the changed equation,
// if c.rgb > 255.0f then we further need to adjust alpha accordingly,
// we pick the lowest overflow from all colors because it's the safest,
// we divide by 255 the color because we don't know Cd value,
// changed alpha should only be done for hw blend.
float min_color = min(min(Color.r, Color.g), Color.b);
float alpha_compensate = max(1.0f, min_color / 255.0f);
As -= alpha_compensate;
vec3 alpha_compensate = max(vec3(1.0f), Color.rgb / vec3(255.0f));
As_rgba.rgb -= alpha_compensate;
#elif PS_CLR_HW == 2
// Compensate slightly for Cd*(As + 1) - Cs*As.
// The initial factor we chose is 1 (0.00392)
@@ -871,7 +872,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;
@@ -907,9 +914,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
@@ -920,9 +928,9 @@ void ps_main()
#if SW_AD_TO_HW
vec4 RT = trunc(fetch_rt() * 255.0f + 0.1f);
float alpha_blend = RT.a / 128.0f;
vec4 alpha_blend = vec4(RT.a / 128.0f);
#else
float alpha_blend = C.a / 128.0f;
vec4 alpha_blend = vec4(C.a / 128.0f);
#endif
// Correct the ALPHA value based on the output format
@@ -962,12 +970,12 @@ void ps_main()
SV_Target0 = C / 255.0f;
#endif
#if !defined(DISABLE_DUAL_SOURCE) && !PS_NO_COLOR1
SV_Target1 = vec4(alpha_blend);
SV_Target1 = alpha_blend;
#endif
#if PS_NO_ABLEND
// write alpha blend factor into col0
SV_Target0.a = alpha_blend;
SV_Target0.a = alpha_blend.a;
#endif
#if PS_ONLY_ALPHA
// rgb isn't used
+33 -24
View File
@@ -328,6 +328,7 @@ 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
@@ -1001,8 +1002,10 @@ void ps_color_clamp_wrap(inout vec3 C)
#endif
}
void ps_blend(inout vec4 Color, inout float As)
void ps_blend(inout vec4 Color, inout vec4 As_rgba)
{
float As = As_rgba.a;
#if SW_BLEND
// PABE
@@ -1086,16 +1089,15 @@ void ps_blend(inout vec4 Color, inout float As)
#if PS_CLR_HW == 1
// Replace Af with As so we can do proper compensation for Alpha.
#if PS_BLEND_C == 2
As = Af;
As_rgba = vec4(Af);
#endif
// Subtract 1 for alpha to compensate for the changed equation,
// if c.rgb > 255.0f then we further need to adjust alpha accordingly,
// we pick the lowest overflow from all colors because it's the safest,
// we divide by 255 the color because we don't know Cd value,
// changed alpha should only be done for hw blend.
float min_color = min(min(Color.r, Color.g), Color.b);
float alpha_compensate = max(1.0f, min_color / 255.0f);
As -= alpha_compensate;
vec3 alpha_compensate = max(vec3(1.0f), Color.rgb / vec3(255.0f));
As_rgba.rgb -= alpha_compensate;
#elif PS_CLR_HW == 2
// Compensate slightly for Cd*(As + 1) - Cs*As.
// The initial factor we chose is 1 (0.00392)
@@ -1175,24 +1177,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
@@ -1205,9 +1214,9 @@ void main()
#if (PS_BLEND_C == 1 && PS_CLR_HW > 3)
vec4 RT = trunc(subpassLoad(RtSampler) * 255.0f + 0.1f);
float alpha_blend = RT.a / 128.0f;
vec4 alpha_blend = vec4(RT.a / 128.0f);
#else
float alpha_blend = C.a / 128.0f;
vec4 alpha_blend = vec4(C.a / 128.0f);
#endif
// Correct the ALPHA value based on the output format
@@ -1249,12 +1258,12 @@ void main()
o_col0 = C / 255.0f;
#endif
#if !defined(DISABLE_DUAL_SOURCE) && !PS_NO_COLOR1
o_col1 = vec4(alpha_blend);
o_col1 = alpha_blend;
#endif
#if PS_NO_ABLEND
// write alpha blend factor into col0
o_col0.a = alpha_blend;
o_col0.a = alpha_blend.a;
#endif
#if PS_ONLY_ALPHA
// rgb isn't used
+53 -17
View File
@@ -737,6 +737,41 @@ void MainWindow::setStyleFromSettings()
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "CobaltSky")
{
// Custom palette by KamFretoZ, A soothing deep royal blue
// that are meant to be easy on the eyes as the main color.
// Alternative dark theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor gray(192, 192, 192);
const QColor royalBlue(29, 41, 81);
const QColor darkishBlue(17, 30, 108);
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, royalBlue);
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, royalBlue.lighter());
darkPalette.setColor(QPalette::AlternateBase, royalBlue);
darkPalette.setColor(QPalette::ToolTipBase, darkishBlue);
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Button, royalBlue.darker());
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Link, Qt::white);
darkPalette.setColor(QPalette::Highlight, darkishBlue.lighter());
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::Active, QPalette::Button, darkishBlue);
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Light, gray.darker());
qApp->setPalette(darkPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "VioletAngelPurple")
{
// Custom palette by RedDevilus, Blue as main color and Purple as complimentary.
@@ -983,27 +1018,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()
@@ -1979,6 +2015,7 @@ void MainWindow::closeEvent(QCloseEvent* event)
// If there's no VM, we can just exit as normal.
if (!s_vm_valid)
{
saveStateToConfig();
QMainWindow::closeEvent(event);
return;
}
@@ -1992,7 +2029,6 @@ void MainWindow::closeEvent(QCloseEvent* event)
return;
// Application will be exited in VM stopped handler.
saveStateToConfig();
m_is_closing = true;
}
+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."));
@@ -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."));
+48 -15
View File
@@ -178,8 +178,11 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
&GraphicsSettingsWidget::onTrilinearFilteringChanged);
connect(m_ui.gpuPaletteConversion, QOverload<int>::of(&QCheckBox::stateChanged), this,
&GraphicsSettingsWidget::onGpuPaletteConversionChanged);
connect(m_ui.textureInsideRt, QOverload<int>::of(&QCheckBox::stateChanged), this,
&GraphicsSettingsWidget::onTextureInsideRtChanged);
onTrilinearFilteringChanged();
onGpuPaletteConversionChanged(m_ui.gpuPaletteConversion->checkState());
onTextureInsideRtChanged(m_ui.textureInsideRt->checkState());
//////////////////////////////////////////////////////////////////////////
// HW Renderer Fixes
@@ -198,6 +201,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
SettingWidgetBinder::BindWidgetToBoolSetting(
sif, m_ui.disablePartialInvalidation, "EmuCore/GS", "UserHacks_DisablePartialInvalidation", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.textureInsideRt, "EmuCore/GS", "UserHacks_TextureInsideRt", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.readTCOnClose, "EmuCore/GS", "UserHacks_ReadTCOnClose", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.targetPartialInvalidation, "EmuCore/GS", "UserHacks_TargetPartialInvalidation", false);
//////////////////////////////////////////////////////////////////////////
// HW Upscaling Fixes
@@ -372,7 +377,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 +408,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 +505,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."));
@@ -513,7 +527,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
"Disables accurate GS Memory Clearing to be done on the CPU, and let the GPU handle it, which can help Kingdom Hearts "
"games."));
dialog->registerWidgetHelp(m_ui.disablePartialInvalidation, tr("Disable Partial Invalidation"), tr("Unchecked"),
dialog->registerWidgetHelp(m_ui.disablePartialInvalidation, tr("Disable Partial Source Invalidation"), tr("Unchecked"),
tr("By default, the texture cache handles partial invalidations. Unfortunately it is very costly to compute CPU wise. "
"This hack replaces the partial invalidation with a complete deletion of the texture to reduce the CPU load. "
"It helps snowblind engine games."));
@@ -526,8 +540,15 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
"Fixes black screen issues in games like Armored Core: Last Raven."));
dialog->registerWidgetHelp(m_ui.textureInsideRt, tr("Texture Inside RT"), tr("Unchecked"),
tr("Allows the texture cache to reuse as an input texture the inner portion of a previous framebuffer. "
"In some selected games this is enabled by default regardless of this setting."));
tr("Allows the texture cache to reuse as an input texture the inner portion of a previous framebuffer."));
dialog->registerWidgetHelp(m_ui.readTCOnClose, tr("Read Targets When Closing"), tr("Unchecked"),
tr("Flushes all targets in the texture cache back to local memory when shutting down. Can prevent lost visuals when saving "
"state or switching renderers, but can also cause graphical corruption."));
dialog->registerWidgetHelp(m_ui.targetPartialInvalidation, tr("Target Partial Invalidation"), tr("Unchecked"),
tr("Allows partial invalidation of render targets, which can fix graphical errors in some games. Texture Inside Render Target "
"automatically enables this option."));
}
// Upscaling Fixes tab
@@ -592,7 +613,8 @@ 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
@@ -654,7 +676,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 "
@@ -828,9 +851,19 @@ void GraphicsSettingsWidget::onEnableAudioCaptureArgumentsChanged()
void GraphicsSettingsWidget::onGpuPaletteConversionChanged(int state)
{
const bool enabled = state == Qt::CheckState::PartiallyChecked ? Host::GetBaseBoolSettingValue("EmuCore/GS", "paltex", false) : state;
const bool disabled =
state == Qt::CheckState::PartiallyChecked ? Host::GetBaseBoolSettingValue("EmuCore/GS", "paltex", false) : (state != 0);
m_ui.anisotropicFiltering->setEnabled(!enabled);
m_ui.anisotropicFiltering->setDisabled(disabled);
}
void GraphicsSettingsWidget::onTextureInsideRtChanged(int state)
{
const bool disabled = state == Qt::CheckState::PartiallyChecked ?
Host::GetBaseBoolSettingValue("EmuCore/GS", "UserHacks_TextureInsideRt", false) :
(state != 0);
m_ui.targetPartialInvalidation->setDisabled(disabled);
}
GSRendererType GraphicsSettingsWidget::getEffectiveRenderer() const
@@ -41,6 +41,7 @@ private Q_SLOTS:
void onAdapterChanged(int index);
void onTrilinearFilteringChanged();
void onGpuPaletteConversionChanged(int state);
void onTextureInsideRtChanged(int state);
void onFullscreenModeChanged(int index);
void onShadeBoostChanged();
void onCaptureContainerChanged();
+95 -81
View File
@@ -920,84 +920,13 @@
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_12">
<item row="2" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Skipdraw Range:</string>
<string>Software CLUT Render:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSpinBox" name="skipDrawStart">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="skipDrawEnd">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="hwAutoFlush">
<property name="text">
<string>Auto Flush</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="frameBufferConversion">
<property name="text">
<string>Frame Buffer Conversion</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="disableDepthEmulation">
<property name="text">
<string>Disable Depth Emulation</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="disableSafeFeatures">
<property name="text">
<string>Disable Safe Features</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="disablePartialInvalidation">
<property name="text">
<string>Disable Partial Invalidation</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="preloadFrameData">
<property name="text">
<string>Preload Frame Data</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="textureInsideRt">
<property name="text">
<string>Texture Inside RT</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="cpuCLUTRender">
<property name="currentText">
@@ -1023,13 +952,6 @@
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Software CLUT Render:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_47">
<property name="text">
@@ -1056,6 +978,98 @@
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Skipdraw Range:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSpinBox" name="skipDrawStart">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="skipDrawEnd">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<widget class="QCheckBox" name="preloadFrameData">
<property name="text">
<string>Preload Frame Data</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="textureInsideRt">
<property name="text">
<string>Texture Inside RT</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="disableDepthEmulation">
<property name="text">
<string>Disable Depth Emulation</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="disablePartialInvalidation">
<property name="text">
<string>Disable Partial Source Invalidation</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="disableSafeFeatures">
<property name="text">
<string>Disable Safe Features</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="targetPartialInvalidation">
<property name="text">
<string>Target Partial Invalidation</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="frameBufferConversion">
<property name="text">
<string>Frame Buffer Conversion</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="hwAutoFlush">
<property name="text">
<string>Auto Flush</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="readTCOnClose">
<property name="text">
<string>Read Targets When Closing</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QGroupBox" name="upscalingFixesTab">
@@ -31,6 +31,7 @@ static const char* THEME_NAMES[] = {
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "PCSX2 (White/Blue) [Light]"),
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Scarlet Devil (Red/Purple) [Dark]"),
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Violet Angel (Blue/Purple) [Dark]"),
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Cobalt Sky (Royal Blue) [Dark]"),
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Ruby (Black/Red) [Dark]"),
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Sapphire (Black/Blue) [Dark]"),
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Custom.qss [Drop in PCSX2 Folder]"),
@@ -46,6 +47,7 @@ static const char* THEME_VALUES[] = {
"PCSX2Blue",
"ScarletDevilRed",
"VioletAngelPurple",
"CobaltSky",
"Ruby",
"Sapphire",
"Custom",
@@ -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.");
+1 -1
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."));
}
+2
View File
@@ -664,12 +664,14 @@ struct Pcsx2Config
UserHacks_AlignSpriteX : 1,
UserHacks_AutoFlush : 1,
UserHacks_CPUFBConversion : 1,
UserHacks_ReadTCOnClose : 1,
UserHacks_DisableDepthSupport : 1,
UserHacks_DisablePartialInvalidation : 1,
UserHacks_DisableSafeFeatures : 1,
UserHacks_MergePPSprite : 1,
UserHacks_WildHack : 1,
UserHacks_TextureInsideRt : 1,
UserHacks_TargetPartialInvalidation : 1,
FXAA : 1,
ShadeBoost : 1,
DumpGSData : 1,
+1 -1
View File
@@ -602,9 +602,9 @@ static __fi void frameLimit()
static __fi void VSyncStart(u32 sCycle)
{
// Update vibration at the end of a frame.
VSyncUpdateCore();
PAD::Update();
VSyncUpdateCore();
frameLimit(); // limit FPS
gsPostVsyncStart(); // MUST be after framelimit; doing so before causes funk with frame times!
VSyncCheckExit();
+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`)
+10
View File
@@ -126,6 +126,11 @@
"minimum": 0,
"maximum": 1
},
"readTCOnClose": {
"type": "integer",
"minimum": 0,
"maximum": 1
},
"disableDepthSupport": {
"type": "integer",
"minimum": 0,
@@ -146,6 +151,11 @@
"minimum": 0,
"maximum": 1
},
"partialTargetInvalidation": {
"type": "integer",
"minimum": 0,
"maximum": 1
},
"textureInsideRT": {
"type": "integer",
"minimum": 0,
+2 -44
View File
@@ -16,6 +16,7 @@
#include "PrecompiledHeader.h"
#include "Frontend/D3D11HostDisplay.h"
#include "GS/Renderers/DX11/D3D.h"
#include "imgui.h"
#include "imgui_impl_dx11.h"
@@ -439,49 +440,6 @@ void D3D11HostDisplay::DestroySurface()
m_swap_chain.reset();
}
static std::string GetDriverVersionFromLUID(const LUID& luid)
{
std::string ret;
HKEY hKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\DirectX", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
DWORD max_key_len = 0, adapter_count = 0;
if (RegQueryInfoKeyW(hKey, nullptr, nullptr, nullptr, &adapter_count, &max_key_len,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
{
std::vector<TCHAR> current_name(max_key_len + 1);
for (DWORD i = 0; i < adapter_count; ++i)
{
DWORD subKeyLength = static_cast<DWORD>(current_name.size());
if (RegEnumKeyExW(hKey, i, current_name.data(), &subKeyLength, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
{
LUID current_luid = {};
DWORD current_luid_size = sizeof(uint64_t);
if (RegGetValueW(hKey, current_name.data(), L"AdapterLuid", RRF_RT_QWORD, nullptr, &current_luid, &current_luid_size) == ERROR_SUCCESS &&
current_luid.HighPart == luid.HighPart && current_luid.LowPart == luid.LowPart)
{
LARGE_INTEGER driver_version = {};
DWORD driver_version_size = sizeof(driver_version);
if (RegGetValueW(hKey, current_name.data(), L"DriverVersion", RRF_RT_QWORD, nullptr, &driver_version, &driver_version_size) == ERROR_SUCCESS)
{
WORD nProduct = HIWORD(driver_version.HighPart);
WORD nVersion = LOWORD(driver_version.HighPart);
WORD nSubVersion = HIWORD(driver_version.LowPart);
WORD nBuild = LOWORD(driver_version.LowPart);
ret = StringUtil::StdStringFromFormat("%u.%u.%u.%u", nProduct, nVersion, nSubVersion, nBuild);
}
}
}
}
}
RegCloseKey(hKey);
}
return ret;
}
std::string D3D11HostDisplay::GetDriverInfo() const
{
std::string ret = "Unknown Feature Level";
@@ -518,7 +476,7 @@ std::string D3D11HostDisplay::GetDriverInfo() const
ret += StringUtil::WideStringToUTF8String(desc.Description);
ret += "\n";
const std::string driver_version(GetDriverVersionFromLUID(desc.AdapterLuid));
const std::string driver_version(D3D::GetDriverVersionFromLUID(desc.AdapterLuid));
if (!driver_version.empty())
{
ret += "Driver Version: ";
+3 -44
View File
@@ -16,6 +16,8 @@
#include "PrecompiledHeader.h"
#include "Frontend/D3D12HostDisplay.h"
#include "GS/Renderers/DX11/D3D.h"
#include "common/Assertions.h"
#include "common/Console.h"
#include "common/D3D12/Context.h"
@@ -343,49 +345,6 @@ void D3D12HostDisplay::DestroySurface()
m_swap_chain.reset();
}
static std::string GetDriverVersionFromLUID(const LUID& luid)
{
std::string ret;
HKEY hKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\DirectX", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
DWORD max_key_len = 0, adapter_count = 0;
if (RegQueryInfoKey(hKey, nullptr, nullptr, nullptr, &adapter_count, &max_key_len,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
{
std::vector<TCHAR> current_name(max_key_len + 1);
for (DWORD i = 0; i < adapter_count; ++i)
{
DWORD subKeyLength = static_cast<DWORD>(current_name.size());
if (RegEnumKeyExW(hKey, i, current_name.data(), &subKeyLength, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
{
LUID current_luid = {};
DWORD current_luid_size = sizeof(uint64_t);
if (RegGetValueW(hKey, current_name.data(), L"AdapterLuid", RRF_RT_QWORD, nullptr, &current_luid, &current_luid_size) == ERROR_SUCCESS &&
current_luid.HighPart == luid.HighPart && current_luid.LowPart == luid.LowPart)
{
LARGE_INTEGER driver_version = {};
DWORD driver_version_size = sizeof(driver_version);
if (RegGetValueW(hKey, current_name.data(), L"DriverVersion", RRF_RT_QWORD, nullptr, &driver_version, &driver_version_size) == ERROR_SUCCESS)
{
WORD nProduct = HIWORD(driver_version.HighPart);
WORD nVersion = LOWORD(driver_version.HighPart);
WORD nSubVersion = HIWORD(driver_version.LowPart);
WORD nBuild = LOWORD(driver_version.LowPart);
ret = StringUtil::StdStringFromFormat("%u.%u.%u.%u", nProduct, nVersion, nSubVersion, nBuild);
}
}
}
}
}
RegCloseKey(hKey);
}
return ret;
}
std::string D3D12HostDisplay::GetDriverInfo() const
{
std::string ret = "Unknown Feature Level";
@@ -417,7 +376,7 @@ std::string D3D12HostDisplay::GetDriverInfo() const
ret += StringUtil::WideStringToUTF8String(desc.Description);
ret += "\n";
const std::string driver_version(GetDriverVersionFromLUID(desc.AdapterLuid));
const std::string driver_version(D3D::GetDriverVersionFromLUID(desc.AdapterLuid));
if (!driver_version.empty())
{
ret += "Driver Version: ";
+7
View File
@@ -3196,6 +3196,13 @@ void FullscreenUI::DrawGraphicsSettingsPage()
DrawToggleSetting(bsi, "Texture Inside Render Target",
"Allows the texture cache to reuse as an input texture the inner portion of a previous framebuffer.", "EmuCore/GS",
"UserHacks_TextureInsideRt", false, manual_hw_fixes);
DrawToggleSetting(bsi, "Target Partial Invalidation",
"Allows partial invalidation of render targets, which can fix graphical errors in some games.", "EmuCore/GS",
"UserHacks_TargetPartialInvalidation", false,
!GetEffectiveBoolSetting(bsi, "EmuCore/GS", "UserHacks_TextureInsideRt", false));
DrawToggleSetting(bsi, "Read Targets When Closing",
"Flushes all targets in the texture cache back to local memory when shutting down.", "EmuCore/GS",
"UserHacks_ReadTCOnClose", false, manual_hw_fixes);
MenuHeading("Upscaling Fixes");
DrawIntListSetting(bsi, "Half-Pixel Offset", "Adjusts vertices relative to upscaling.", "EmuCore/GS",
+4
View File
@@ -421,10 +421,14 @@ void ImGuiManager::DrawSettingsOverlay()
APPEND("AF ");
if (GSConfig.UserHacks_CPUFBConversion)
APPEND("FBC ");
if (GSConfig.UserHacks_ReadTCOnClose)
APPEND("FTC ");
if(GSConfig.UserHacks_DisableDepthSupport)
APPEND("DDE ");
if (GSConfig.UserHacks_DisablePartialInvalidation)
APPEND("DPIV ");
if (GSConfig.UserHacks_TargetPartialInvalidation)
APPEND("TPV ");
if (GSConfig.UserHacks_DisableSafeFeatures)
APPEND("DSF ");
if (GSConfig.WrapGSMem)
+6 -1
View File
@@ -252,6 +252,9 @@ bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::
if (recreate_renderer)
g_gs_renderer->Flush(GSState::GSFlushReason::GSREOPEN);
if (GSConfig.UserHacks_ReadTCOnClose)
g_gs_renderer->ReadbackTextureCache();
freezeData fd = {};
std::unique_ptr<u8[]> fd_data;
if (recreate_renderer)
@@ -273,8 +276,8 @@ bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::
else
{
// Make sure nothing is left over.
g_gs_renderer->PurgePool();
g_gs_renderer->PurgeTextureCache();
g_gs_renderer->PurgePool();
}
if (recreate_display)
@@ -762,6 +765,8 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
GSConfig.UserHacks_CPUCLUTRender != old_config.UserHacks_CPUCLUTRender ||
GSConfig.UserHacks_GPUTargetCLUTMode != old_config.UserHacks_GPUTargetCLUTMode)
{
if (GSConfig.UserHacks_ReadTCOnClose)
g_gs_renderer->ReadbackTextureCache();
g_gs_renderer->PurgeTextureCache();
g_gs_renderer->PurgePool();
}
+6 -1
View File
@@ -981,10 +981,15 @@ public:
off.loopPixels(r, vm32(), (u32*)src, pitch, [&](u32* dst, u32* src) { *dst = *src; });
}
void WritePixel32(u8* RESTRICT src, u32 pitch, const GSOffset& off, const GSVector4i& r, u32 write_mask)
{
off.loopPixels(r, vm32(), (u32*)src, pitch, [&](u32* dst, u32* src) { *dst = (*dst & ~write_mask) | (*src & write_mask); });
}
void WritePixel24(u8* RESTRICT src, u32 pitch, const GSOffset& off, const GSVector4i& r)
{
off.loopPixels(r, vm32(), (u32*)src, pitch,
[&](u32* dst, u32* src)
[&](u32* dst, u32* src)
{
*dst = (*dst & 0xff000000) | (*src & 0x00ffffff);
});
+39 -9
View File
@@ -1766,7 +1766,15 @@ void GSState::Write(const u8* mem, int len)
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))
if ((m_draw_transfers.size() > 0 && blit.DBP == m_draw_transfers.back().blit.DBP))
{
// Same BP, let's update the rect.
GSUploadQueue transfer = m_draw_transfers.back();
m_draw_transfers.pop_back();
transfer.rect = transfer.rect.runion(r);
m_draw_transfers.push_back(transfer);
}
else
{
GSUploadQueue new_transfer = { blit, r, s_n };
m_draw_transfers.push_back(new_transfer);
@@ -1915,16 +1923,22 @@ void GSState::Move()
Flush(GSFlushReason::LOCALTOLOCALMOVE);
}
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 && dbp != m_draw_transfers.back().blit.DBP))
if ((m_draw_transfers.size() > 0 && m_env.BITBLTBUF.DBP == m_draw_transfers.back().blit.DBP))
{
// Same BP, let's update the rect.
GSUploadQueue transfer = m_draw_transfers.back();
m_draw_transfers.pop_back();
transfer.rect = transfer.rect.runion(r);
m_draw_transfers.push_back(transfer);
}
else
{
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);
}
@@ -2126,6 +2140,19 @@ void GSState::ReadLocalMemoryUnsync(u8* mem, int qwc, GIFRegBITBLTBUF BITBLTBUF,
m_mem.ReadImageX(tb.x, tb.y, mem, len, BITBLTBUF, TRXPOS, TRXREG);
}
void GSState::PurgePool()
{
g_gs_device->PurgePool();
}
void GSState::PurgeTextureCache()
{
}
void GSState::ReadbackTextureCache()
{
}
template void GSState::Transfer<0>(const u8* mem, u32 size);
template void GSState::Transfer<1>(const u8* mem, u32 size);
template void GSState::Transfer<2>(const u8* mem, u32 size);
@@ -2345,6 +2372,9 @@ int GSState::Freeze(freezeData* fd, bool sizeonly)
Flush(GSFlushReason::SAVESTATE);
if (GSConfig.UserHacks_ReadTCOnClose)
ReadbackTextureCache();
u8* data = fd->data;
const u32 version = STATE_VERSION;
+36 -4
View File
@@ -491,7 +491,7 @@ public:
GSVector2i GetFramebufferSize(int display)
{
int max_height = VideoModeOffsets[videomode].y;
int max_height = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode].y : VideoModeOffsetsOverscan[videomode].y;
if (!(FFMD && interlaced))
{
@@ -518,7 +518,21 @@ public:
// 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));
combined_rect.w = std::min(combined_rect.w, combined_rect.y + (max_height / min_mag));
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
@@ -538,7 +552,23 @@ public:
}
// Cap the framebuffer read to the maximum display height, otherwise the hardware renderer gets messy.
out_rect.w = std::min(out_rect.w, out_rect.y + (max_height * PCRTCDisplays[display].magnification.y));
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);
}
}
@@ -856,7 +886,9 @@ public:
bool TestDrawChanged();
void FlushWrite();
virtual void Draw() = 0;
virtual void PurgePool() = 0;
virtual void PurgePool();
virtual void PurgeTextureCache();
virtual void ReadbackTextureCache();
virtual void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool eewrite = false) {}
virtual void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut = false) {}
virtual void ExpandTarget(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r) {}
+32 -4
View File
@@ -130,21 +130,44 @@ bool GSUtil::HasSharedBits(u32 spsm, const u32* RESTRICT ptr)
return (ptr[spsm >> 5] & (1 << (spsm & 0x1f))) == 0;
}
// Pixels can NOT coexist in the same 32bits of space.
// Example: Using PSMT8H or PSMT4HL/HH with CT24 would fail this check.
bool GSUtil::HasSharedBits(u32 spsm, u32 dpsm)
{
return (s_maps.SharedBitsField[dpsm][spsm >> 5] & (1 << (spsm & 0x1f))) == 0;
}
// Pixels can NOT coexist in the same 32bits of space.
// Example: Using PSMT8H or PSMT4HL/HH with CT24 would fail this check.
// SBP and DBO must match.
bool GSUtil::HasSharedBits(u32 sbp, u32 spsm, u32 dbp, u32 dpsm)
{
return ((sbp ^ dbp) | (s_maps.SharedBitsField[dpsm][spsm >> 5] & (1 << (spsm & 0x1f)))) == 0;
}
// Shares bit depths, only detects 16/24/32 bit formats.
// 24/32bit cross compatible, 16bit compatbile with 16bit.
bool GSUtil::HasCompatibleBits(u32 spsm, u32 dpsm)
{
return (s_maps.CompatibleBitsField[spsm][dpsm >> 5] & (1 << (dpsm & 0x1f))) != 0;
}
u32 GSUtil::GetChannelMask(u32 spsm)
{
switch (spsm)
{
case PSM_PSMCT24:
case PSM_PSMZ24:
return 0x7;
case PSM_PSMT8H:
case PSM_PSMT4HH: // This sucks, I'm sorry, but we don't have a way to do half channels
case PSM_PSMT4HL: // So uuhh TODO I guess.
return 0x8;
default:
return 0xf;
}
}
CRCHackLevel GSUtil::GetRecommendedCRCHackLevel(GSRendererType type)
{
return (type == GSRendererType::DX11 || type == GSRendererType::DX12) ? CRCHackLevel::Full : CRCHackLevel::Partial;
@@ -156,14 +179,19 @@ GSRendererType GSUtil::GetPreferredRenderer()
// Mac: Prefer Metal hardware.
return GSRendererType::Metal;
#elif defined(_WIN32)
if (D3D::ShouldPreferRenderer() == D3D::Renderer::Vulkan)
const u8 preferred = D3D::ShouldPreferRenderer();
#if defined(ENABLE_VULKAN)
if (preferred == D3D::Renderer::Vulkan)
return GSRendererType::VK;
#endif
#if defined(ENABLE_OPENGL)
else if (D3D::ShouldPreferRenderer() == D3D::Renderer::OpenGL)
if (preferred == D3D::Renderer::OpenGL)
return GSRendererType::OGL;
#endif
else
return GSRendererType::DX11;
if (preferred == D3D::Renderer::Direct3D12)
return GSRendererType::DX12;
return GSRendererType::DX11;
#else
// Linux: Prefer GL/Vulkan, whatever is available.
#if defined(ENABLE_OPENGL)
+1
View File
@@ -32,6 +32,7 @@ public:
static bool HasSharedBits(u32 spsm, u32 dpsm);
static bool HasSharedBits(u32 sbp, u32 spsm, u32 dbp, u32 dpsm);
static bool HasCompatibleBits(u32 spsm, u32 dpsm);
static u32 GetChannelMask(u32 spsm);
static CRCHackLevel GetRecommendedCRCHackLevel(GSRendererType type);
static GSRendererType GetPreferredRenderer();
+3 -2
View File
@@ -314,6 +314,7 @@ struct alignas(16) GSHWDrawConfig
u32 ltf : 1;
// Shuffle and fbmask effect
u32 shuffle : 1;
u32 real16src: 1;
u32 read_ba : 1;
u32 write_rg : 1;
u32 fbmask : 1;
@@ -857,8 +858,8 @@ public:
__fi static constexpr bool IsDualSourceBlendFactor(u8 factor)
{
return (factor == SRC1_ALPHA || factor == INV_SRC1_ALPHA
/*|| factor == SRC1_COLOR || factor == INV_SRC1_COLOR*/); // not used
return (factor == SRC1_ALPHA || factor == INV_SRC1_ALPHA || factor == SRC1_COLOR
/* || factor == INV_SRC1_COLOR*/); // not used
}
__fi static constexpr bool IsConstantBlendFactor(u16 factor)
{
+20 -3
View File
@@ -20,14 +20,16 @@
GSDirtyRect::GSDirtyRect() :
r(GSVector4i::zero()),
psm(PSM_PSMCT32),
bw(1)
bw(1),
rgba({})
{
}
GSDirtyRect::GSDirtyRect(GSVector4i& r, u32 psm, u32 bw) :
GSDirtyRect::GSDirtyRect(GSVector4i& r, u32 psm, u32 bw, RGBAMask rgba) :
r(r),
psm(psm),
bw(bw)
bw(bw),
rgba(rgba)
{
}
@@ -73,6 +75,21 @@ GSVector4i GSDirtyRectList::GetTotalRect(GIFRegTEX0 TEX0, const GSVector2i& size
return GSVector4i::zero();
}
u32 GSDirtyRectList::GetDirtyChannels()
{
u32 channels = 0;
if (!empty())
{
for (auto& dirty_rect : *this)
{
channels |= dirty_rect.rgba._u32;
}
}
return channels;
}
GSVector4i GSDirtyRectList::GetDirtyRect(size_t index, GIFRegTEX0 TEX0, const GSVector4i& clamp) const
{
const GSVector4i r = (*this)[index].GetDirtyRect(TEX0);
+15 -1
View File
@@ -17,15 +17,28 @@
#include "GS/GSLocalMemory.h"
union RGBAMask
{
struct
{
u32 r : 1;
u32 g : 1;
u32 b : 1;
u32 a : 1;
} c;
u32 _u32;
};
class GSDirtyRect
{
public:
GSVector4i r;
u32 psm;
u32 bw;
RGBAMask rgba;
GSDirtyRect();
GSDirtyRect(GSVector4i& r, u32 psm, u32 bw);
GSDirtyRect(GSVector4i& r, u32 psm, u32 bw, RGBAMask rgba);
GSVector4i GetDirtyRect(GIFRegTEX0 TEX0) const;
};
@@ -34,5 +47,6 @@ class GSDirtyRectList : public std::vector<GSDirtyRect>
public:
GSDirtyRectList() {}
GSVector4i GetTotalRect(GIFRegTEX0 TEX0, const GSVector2i& size) const;
u32 GetDirtyChannels();
GSVector4i GetDirtyRect(size_t index, GIFRegTEX0 TEX0, const GSVector4i& clamp) const;
};
-9
View File
@@ -829,15 +829,6 @@ void GSRenderer::EndCapture()
GSCapture::EndCapture();
}
void GSRenderer::PurgePool()
{
g_gs_device->PurgePool();
}
void GSRenderer::PurgeTextureCache()
{
}
GSTexture* GSRenderer::LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, const GSVector2i& size)
{
return nullptr;
+1 -3
View File
@@ -34,6 +34,7 @@ private:
protected:
GSVector2i m_real_size{0, 0};
bool m_texture_shuffle = false;
bool m_copy_16bit_to_target_shuffle = false;
virtual GSTexture* GetOutput(int i, int& y_offset) = 0;
virtual GSTexture* GetFeedbackOutput() { return nullptr; }
@@ -52,9 +53,6 @@ public:
virtual GSVector2 GetTextureScaleFactor() { return { 1.0f, 1.0f }; }
GSVector2i GetInternalResolution();
virtual void PurgePool() override;
virtual void PurgeTextureCache();
virtual GSTexture* LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, const GSVector2i& size);
bool SaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,
+1
View File
@@ -88,6 +88,7 @@ public:
int GetWidth() const { return m_size.x; }
int GetHeight() const { return m_size.y; }
GSVector2i GetSize() const { return m_size; }
GSVector4i GetRect() const { return GSVector4i(m_size).zwxy(); }
int GetMipmapLevels() const { return m_mipmap_levels; }
bool IsMipmap() const { return m_mipmap_levels > 1; }
+71 -6
View File
@@ -17,8 +17,12 @@
#include "GS/Renderers/DX11/D3D.h"
#include "GS/GSExtra.h"
#include "common/StringUtil.h"
#include <d3d11.h>
#include "fmt/format.h"
namespace D3D
{
wil::com_ptr_nothrow<IDXGIFactory2> CreateFactory(bool debug)
@@ -69,6 +73,52 @@ namespace D3D
return adapter;
}
std::string GetDriverVersionFromLUID(const LUID& luid)
{
std::string ret;
HKEY hKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\DirectX", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
DWORD max_key_len = 0, adapter_count = 0;
if (RegQueryInfoKeyW(hKey, nullptr, nullptr, nullptr, &adapter_count, &max_key_len, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
{
std::vector<TCHAR> current_name(max_key_len + 1);
for (DWORD i = 0; i < adapter_count; ++i)
{
DWORD subKeyLength = static_cast<DWORD>(current_name.size());
if (RegEnumKeyExW(hKey, i, current_name.data(), &subKeyLength, nullptr, nullptr, nullptr,
nullptr) == ERROR_SUCCESS)
{
LUID current_luid = {};
DWORD current_luid_size = sizeof(uint64_t);
if (RegGetValueW(hKey, current_name.data(), L"AdapterLuid", RRF_RT_QWORD, nullptr,
&current_luid, &current_luid_size) == ERROR_SUCCESS &&
current_luid.HighPart == luid.HighPart && current_luid.LowPart == luid.LowPart)
{
LARGE_INTEGER driver_version = {};
DWORD driver_version_size = sizeof(driver_version);
if (RegGetValueW(hKey, current_name.data(), L"DriverVersion", RRF_RT_QWORD, nullptr,
&driver_version, &driver_version_size) == ERROR_SUCCESS)
{
WORD nProduct = HIWORD(driver_version.HighPart);
WORD nVersion = LOWORD(driver_version.HighPart);
WORD nSubVersion = HIWORD(driver_version.LowPart);
WORD nBuild = LOWORD(driver_version.LowPart);
ret = fmt::format("{}.{}.{}.{}", nProduct, nVersion, nSubVersion, nBuild);
}
}
}
}
}
RegCloseKey(hKey);
}
return ret;
}
u8 Vendor()
{
auto factory = CreateFactory(false);
@@ -124,22 +174,37 @@ namespace D3D
switch (Vendor())
{
case VendorID::Nvidia:
{
if (feature_level == D3D_FEATURE_LEVEL_12_0)
return Renderer::Vulkan;
else if (feature_level == D3D_FEATURE_LEVEL_11_0)
return Renderer::OpenGL;
[[fallthrough]];
else
return Renderer::Direct3D11;
}
case VendorID::AMD:
{
if (feature_level == D3D_FEATURE_LEVEL_12_0)
return Renderer::Vulkan;
[[fallthrough]];
else
return Renderer::Direct3D11;
}
case VendorID::Intel:
if (feature_level == D3D_FEATURE_LEVEL_12_0)
return Renderer::OpenGL;
[[fallthrough]];
{
// Older Intel GPUs prior to Xe seem to have broken OpenGL drivers which choke
// on some of our shaders, causing what appears to be GPU timeouts+device removals.
// Vulkan has broken barriers, also prior to Xe. So just fall back to DX11 everywhere,
// unless we can find some way of differentiating Xe.
return Renderer::Direct3D11;
}
default:
{
// Default is D3D11
return Renderer::Default;
return Renderer::Direct3D11;
}
}
}
}
+3
View File
@@ -32,6 +32,9 @@ namespace D3D
// however in the event that the adapter is not found due to the above, use the default
wil::com_ptr_nothrow<IDXGIAdapter1> GetAdapterFromIndex(IDXGIFactory2* factory, int index);
// returns the driver version from the registry as a string
std::string GetDriverVersionFromLUID(const LUID& luid);
// this is sort of a legacy thing that doesn't have much to do with d3d (just the easiest way)
// checks to see if the adapter at 0 is NV and thus we should prefer OpenGL
enum VendorID
@@ -160,6 +160,7 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
sm.AddMacro("PS_POINT_SAMPLER", sel.point_sampler);
sm.AddMacro("PS_SHUFFLE", sel.shuffle);
sm.AddMacro("PS_READ_BA", sel.read_ba);
sm.AddMacro("PS_READ16_SRC", sel.real16src);
sm.AddMacro("PS_CHANNEL_FETCH", sel.channel);
sm.AddMacro("PS_TALES_OF_ABYSS_HLE", sel.tales_of_abyss_hle);
sm.AddMacro("PS_URBAN_CHAOS_HLE", sel.urban_chaos_hle);
+14 -10
View File
@@ -1501,6 +1501,7 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector&
sm.AddMacro("PS_POINT_SAMPLER", sel.point_sampler);
sm.AddMacro("PS_SHUFFLE", sel.shuffle);
sm.AddMacro("PS_READ_BA", sel.read_ba);
sm.AddMacro("PS_READ16_SRC", sel.real16src);
sm.AddMacro("PS_CHANNEL_FETCH", sel.channel);
sm.AddMacro("PS_TALES_OF_ABYSS_HLE", sel.tales_of_abyss_hle);
sm.AddMacro("PS_URBAN_CHAOS_HLE", sel.urban_chaos_hle);
@@ -1562,10 +1563,13 @@ GSDevice12::ComPtr<ID3D12PipelineState> GSDevice12::CreateTFXPipeline(const Pipe
gpb.SetRasterizationState(D3D12_FILL_MODE_SOLID, D3D12_CULL_MODE_NONE, false);
if (p.rt)
{
gpb.SetRenderTarget(0,
IsDATEModePrimIDInit(p.ps.date) ? DXGI_FORMAT_R32_FLOAT :
p.ps.hdr ? DXGI_FORMAT_R32G32B32A32_FLOAT :
DXGI_FORMAT_R8G8B8A8_UNORM);
const GSTexture::Format format = IsDATEModePrimIDInit(p.ps.date) ?
GSTexture::Format::PrimID :
(p.ps.hdr ? GSTexture::Format::HDRColor : GSTexture::Format::Color);
DXGI_FORMAT native_format;
LookupNativeFormat(format, nullptr, nullptr, &native_format, nullptr);
gpb.SetRenderTarget(0, native_format);
}
if (p.ds)
gpb.SetDepthStencilFormat(DXGI_FORMAT_D32_FLOAT_S8X24_UINT);
@@ -2346,13 +2350,12 @@ GSTexture12* GSDevice12::SetupPrimitiveTrackingDATE(GSHWDrawConfig& config, Pipe
OMSetRenderTargets(image, config.ds, config.drawarea);
// if the depth target has been cleared, we need to preserve that clear
BeginRenderPass(
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
GetLoadOpForTexture(static_cast<GSTexture12*>(config.ds)),
BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
config.ds ? GetLoadOpForTexture(static_cast<GSTexture12*>(config.ds)) :
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS,
config.ds ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE : D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
GSVector4::zero(),
static_cast<GSTexture12*>(config.ds)->GetClearDepth());
GSVector4::zero(), config.ds ? static_cast<GSTexture12*>(config.ds)->GetClearDepth() : 0.0f);
// draw the quad to prefill the image
const GSVector4 src = GSVector4(config.drawarea) / GSVector4(rtsize).xyxy();
@@ -2536,7 +2539,8 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
}
// avoid restarting the render pass just to switch from rt+depth to rt and vice versa
if (m_in_render_pass && !hdr_rt && !draw_ds && m_current_depth_target && m_current_render_target == draw_rt && config.tex != m_current_depth_target)
if (m_in_render_pass && !hdr_rt && !draw_ds && m_current_depth_target && m_current_render_target == draw_rt &&
config.tex != m_current_depth_target && m_current_depth_target->GetSize() == draw_rt->GetSize())
{
draw_ds = m_current_depth_target;
m_pipeline_selector.ds = true;
+292 -85
View File
@@ -71,10 +71,14 @@ void GSRendererHW::Destroy()
void GSRendererHW::PurgeTextureCache()
{
GSRenderer::PurgeTextureCache();
m_tc->RemoveAll();
}
void GSRendererHW::ReadbackTextureCache()
{
m_tc->ReadbackAll();
}
GSTexture* GSRendererHW::LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, const GSVector2i& size)
{
return m_tc->LookupPaletteSource(CBP, CPSM, CBW, offset, size);
@@ -115,8 +119,8 @@ bool GSRendererHW::UpdateTexIsFB(GSTextureCache::Target* dst, const GIFRegTEX0&
bool GSRendererHW::IsPossibleTextureShuffle(GSTextureCache::Target* dst, const GIFRegTEX0& TEX0) const
{
return (PRIM->TME && m_vt.m_primclass == GS_SPRITE_CLASS &&
dst->m_32_bits_fmt && GSLocalMemory::m_psm[TEX0.PSM].bpp == 16 &&
GSLocalMemory::m_psm[m_context->FRAME.PSM].bpp == 16);
dst->m_32_bits_fmt && GSLocalMemory::m_psm[TEX0.PSM].bpp == 16 &&
GSLocalMemory::m_psm[m_context->FRAME.PSM].bpp == 16);
}
void GSRendererHW::SetGameCRC(u32 crc)
@@ -169,7 +173,6 @@ void GSRendererHW::VSync(u32 field, bool registers_written)
{
iter++;
}
}
}
}
@@ -227,7 +230,9 @@ GSTexture* GSRendererHW::GetOutput(int i, int& y_offset)
TEX0.TBW = curFramebuffer.FBW;
TEX0.PSM = curFramebuffer.PSM;
if (GSTextureCache::Target* rt = m_tc->LookupDisplayTarget(TEX0, GetOutputSize(fb_height) * GSConfig.UpscaleMultiplier, fb_width, fb_height))
const GSVector2i scaled_size(static_cast<int>(static_cast<float>(fb_width) * GSConfig.UpscaleMultiplier),
static_cast<int>(static_cast<float>(fb_height) * GSConfig.UpscaleMultiplier));
if (GSTextureCache::Target* rt = m_tc->LookupDisplayTarget(TEX0, scaled_size, fb_width, fb_height))
{
t = rt->m_texture;
@@ -478,7 +483,7 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
int maxvert = 0;
int minvert = 4096;
for (u32 i = 0; i < count; i ++)
for (u32 i = 0; i < count; i++)
{
int YCord = 0;
@@ -750,7 +755,7 @@ GSVector2i GSRendererHW::GetTargetSize(GSVector2i* unscaled_size)
}
}
u32 width = m_context->FRAME.FBW * 64u;
u32 width = std::min(m_context->FRAME.FBW * 64u, static_cast<u32>(m_context->scissor.in.z));
// If it's a channel shuffle, it'll likely be just a single page, so assume full screen.
if (m_channel_shuffle)
@@ -766,7 +771,7 @@ GSVector2i GSRendererHW::GetTargetSize(GSVector2i* unscaled_size)
// Align to even lines, reduces the chance of tiny resizes.
min_height = Common::AlignUpPow2(min_height, 2);
u32 height = m_tc->GetTargetHeight(m_context->FRAME.FBP, m_context->FRAME.FBW, m_context->FRAME.PSM, min_height);
u32 height = m_tc->GetTargetHeight(m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, min_height);
if (unscaled_size)
{
@@ -774,7 +779,7 @@ GSVector2i GSRendererHW::GetTargetSize(GSVector2i* unscaled_size)
unscaled_size->y = static_cast<int>(height);
}
GL_INS("Target size for %x %u %u: %ux%u", m_context->FRAME.FBP, m_context->FRAME.FBW, m_context->FRAME.PSM, width, height);
GL_INS("Target size for %x %u %u: %ux%u", m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, width, height);
return GSVector2i(static_cast<int>(static_cast<float>(width) * GSConfig.UpscaleMultiplier),
static_cast<int>(static_cast<float>(height) * GSConfig.UpscaleMultiplier));
@@ -789,7 +794,37 @@ void GSRendererHW::InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GS
{
// printf("[%d] InvalidateVideoMem %d,%d - %d,%d %05x (%d)\n", static_cast<int>(g_perfmon.GetFrame()), r.left, r.top, r.right, r.bottom, static_cast<int>(BITBLTBUF.DBP), static_cast<int>(BITBLTBUF.DPSM));
m_tc->InvalidateVideoMem(m_mem.GetOffset(BITBLTBUF.DBP, BITBLTBUF.DBW, BITBLTBUF.DPSM), r, eewrite);
// This is gross, but if the EE write loops, we need to split it on the 2048 border.
GSVector4i rect = r;
bool loop_h = false;
bool loop_w = false;
if (r.w > 2048)
{
rect.w = 2048;
loop_h = true;
}
if (r.z > 2048)
{
rect.z = 2048;
loop_w = true;
}
if (loop_h || loop_w)
{
m_tc->InvalidateVideoMem(m_mem.GetOffset(BITBLTBUF.DBP, BITBLTBUF.DBW, BITBLTBUF.DPSM), rect, eewrite);
if (loop_h)
{
rect.y = 0;
rect.w = r.w - 2048;
}
if (loop_w)
{
rect.x = 0;
rect.z = r.w - 2048;
}
m_tc->InvalidateVideoMem(m_mem.GetOffset(BITBLTBUF.DBP, BITBLTBUF.DBW, BITBLTBUF.DPSM), rect, eewrite);
}
else
m_tc->InvalidateVideoMem(m_mem.GetOffset(BITBLTBUF.DBP, BITBLTBUF.DBW, BITBLTBUF.DPSM), r, eewrite);
}
void GSRendererHW::InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut)
@@ -799,7 +834,51 @@ void GSRendererHW::InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GS
if (clut)
return; // FIXME
m_tc->InvalidateLocalMem(m_mem.GetOffset(BITBLTBUF.SBP, BITBLTBUF.SBW, BITBLTBUF.SPSM), r);
const u32 incoming_end = GSLocalMemory::m_psm[BITBLTBUF.SPSM].info.bn(r.z - 1, r.w - 1, BITBLTBUF.SBP, BITBLTBUF.SBW);
std::vector<GSState::GSUploadQueue>::iterator iter = GSRendererHW::GetInstance()->m_draw_transfers.end();
bool skip = false;
// If the EE write overlaps the readback and was done since the last draw, there's no need to read it back.
// Dog's life and Ratchet Gladiator do this.
while (iter != GSRendererHW::GetInstance()->m_draw_transfers.begin())
{
--iter;
if (!GSUtil::HasSharedBits(iter->blit.DPSM, BITBLTBUF.SPSM) || iter->draw != s_n)
continue;
// Make sure write covers the read area.
const u32 ee_write_end = GSLocalMemory::m_psm[iter->blit.DPSM].info.bn(iter->rect.z - 1, iter->rect.w - 1, iter->blit.DBP, iter->blit.DBW);
if (!(iter->blit.DBP < incoming_end && ee_write_end > BITBLTBUF.SBP))
continue;
GSTextureCache::SurfaceOffsetKey sok;
sok.elems[0].bp = BITBLTBUF.SBP;
sok.elems[0].bw = BITBLTBUF.SBW;
sok.elems[0].psm = BITBLTBUF.SPSM;
sok.elems[0].rect = r;
sok.elems[1].bp = iter->blit.DBP;
sok.elems[1].bw = iter->blit.DBW;
sok.elems[1].psm = iter->blit.DPSM;
sok.elems[1].rect = iter->rect;
// Calculate the rect offset if the BP doesn't match.
const GSVector4i targetr = GSUtil::HasCompatibleBits(iter->blit.DPSM, BITBLTBUF.SPSM) ? r : m_tc->ComputeSurfaceOffset(sok).b2a_offset;
// Possibly incompatible or missed, we don't know, so let's assume it's a fail.
if (targetr.rempty())
continue;
//u32 ee_write_end = GSLocalMemory::m_psm[iter->blit.DPSM].info.bn(iter->rect.z - 1, iter->rect.w - 1, iter->blit.DBP, iter->blit.DBW);
// If the format, and location doesn't match, but also the upload is at least the size of the target, don't preload.
if (iter->rect.rintersect(targetr).eq(targetr))
{
skip = true;
}
}
if(!skip)
m_tc->InvalidateLocalMem(m_mem.GetOffset(BITBLTBUF.SBP, BITBLTBUF.SBW, BITBLTBUF.SPSM), r);
}
void GSRendererHW::Move()
@@ -1244,10 +1323,6 @@ void GSRendererHW::Draw()
// skip alpha test if possible
// Note: do it first so we know if frame/depth writes are masked
const GIFRegTEST TEST = context->TEST;
const GIFRegFRAME FRAME = context->FRAME;
const GIFRegZBUF ZBUF = context->ZBUF;
u32 fm = context->FRAME.FBMSK;
u32 zm = context->ZBUF.ZMSK || context->TEST.ZTE == 0 ? 0xffffffff : 0;
const u32 fm_mask = GSLocalMemory::m_psm[m_context->FRAME.PSM].fmsk;
@@ -1307,13 +1382,25 @@ void GSRendererHW::Draw()
// 2/ SuperMan really draws (0,0,0,0) color and a (0) 32-bits depth
// 3/ 50cents really draws (0,0,0,128) color and a (0) 24 bits depth
// Note: FF DoC has both buffer at same location but disable the depth test (write?) with ZTE = 0
const bool no_rt = (context->ALPHA.IsCd() && PRIM->ABE && (context->FRAME.PSM == 1));
const bool no_ds = !no_rt && (
// Depth is always pass/fail (no read) and write are discarded (tekken 5). (Note: DATE is currently implemented with a stencil buffer => a depth/stencil buffer)
(zm != 0 && m_context->TEST.ZTST <= ZTST_ALWAYS && !m_context->TEST.DATE) ||
// Depth will be written through the RT
(context->FRAME.FBP == context->ZBUF.ZBP && !PRIM->TME && zm == 0 && (fm & fm_mask) == 0 && context->TEST.ZTE)
);
const u32 max_z = (0xFFFFFFFF >> (GSLocalMemory::m_psm[context->ZBUF.PSM].fmt * 8));
const bool no_rt = (context->ALPHA.IsCd() && PRIM->ABE && (context->FRAME.PSM == 1))
|| (!context->TEST.DATE && (context->FRAME.FBMSK & GSLocalMemory::m_psm[context->FRAME.PSM].fmsk) == GSLocalMemory::m_psm[context->FRAME.PSM].fmsk);
const bool no_ds = (
// Depth is always pass/fail (no read) and write are discarded.
(zm != 0 && context->TEST.ZTST <= ZTST_ALWAYS) ||
// Depth test will always pass
(zm != 0 && context->TEST.ZTST == ZTST_GEQUAL && m_vt.m_eq.z && std::min(m_vertex.buff[0].XYZ.Z, max_z) == max_z) ||
// Depth will be written through the RT
(!no_rt && context->FRAME.FBP == context->ZBUF.ZBP && !PRIM->TME && zm == 0 && (fm & fm_mask) == 0 && context->TEST.ZTE));
// No Z test if no z buffer.
if (no_ds)
{
if (context->TEST.ZTST != ZTST_ALWAYS)
GL_CACHE("Disabling Z buffer because all tests will pass.");
context->TEST.ZTST = ZTST_ALWAYS;
}
if (no_rt && no_ds)
{
@@ -1328,7 +1415,8 @@ void GSRendererHW::Draw()
// We trigger the sw prim render here super early, to avoid creating superfluous render targets.
if (CanUseSwPrimRender(no_rt, no_ds, draw_sprite_tex) && SwPrimRender(*this, true))
{
GL_CACHE("Possible texture decompression, drawn with SwPrimRender()");
GL_CACHE("Possible texture decompression, drawn with SwPrimRender() (BP %x BW %u TBP0 %x TBW %u)",
context->FRAME.Block(), context->FRAME.FBMSK, context->TEX0.TBP0, context->TEX0.TBW);
return;
}
@@ -1395,6 +1483,7 @@ void GSRendererHW::Draw()
m_src = nullptr;
m_texture_shuffle = false;
m_copy_16bit_to_target_shuffle = false;
m_tex_is_fb = false;
// The rectangle of the draw
@@ -1548,9 +1637,7 @@ void GSRendererHW::Draw()
tmm = GetTextureMinMax(TEX0, MIP_CLAMP, m_vt.IsLinear());
m_src = tex_psm.depth ? m_tc->LookupDepthSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage) :
m_tc->LookupSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, (GSConfig.HWMipmap >= HWMipmapLevel::Basic ||
GSConfig.TriFilter == TriFiltering::Forced) ? &hash_lod_range : nullptr);
m_tc->LookupSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, (GSConfig.HWMipmap >= HWMipmapLevel::Basic || GSConfig.TriFilter == TriFiltering::Forced) ? &hash_lod_range : nullptr);
}
GSVector2i unscaled_target_size;
@@ -1565,7 +1652,20 @@ void GSRendererHW::Draw()
GSTextureCache::Target* rt = nullptr;
if (!no_rt)
rt = m_tc->LookupTarget(TEX0, t_size, GSTextureCache::RenderTarget, true, fm, false, unscaled_target_size.x, unscaled_target_size.y, force_preload);
{
// Normally we would use 1024 here to match the clear above, but The Godfather does a 1023x1023 draw instead
// (very close to 1024x1024, but apparently the GS rounds down..). So, catch that here, we don't want to
// create that target, because the clear isn't black, it'll hang around and never get invalidated.
const bool is_square = (unscaled_target_size.y == unscaled_target_size.x) && m_r.w >= 1023 && m_vertex.next == 2;
rt = m_tc->LookupTarget(TEX0, t_size, GSTextureCache::RenderTarget, true, fm, false, unscaled_target_size.x, unscaled_target_size.y, force_preload, IsConstantDirectWriteMemClear(false) && is_square);
// Draw skipped because it was a clear and there was no target.
if (!rt)
{
OI_GsMemClear();
return;
}
}
TEX0.TBP0 = context->ZBUF.Block();
TEX0.TBW = context->FRAME.FBW;
@@ -1573,18 +1673,19 @@ void GSRendererHW::Draw()
GSTextureCache::Target* ds = nullptr;
if (!no_ds)
{
ds = m_tc->LookupTarget(TEX0, t_size, GSTextureCache::DepthStencil, context->DepthWrite(), 0, false, unscaled_target_size.x, unscaled_target_size.y, force_preload);
}
if (process_texture)
{
GIFRegCLAMP MIP_CLAMP = context->CLAMP;
bool copy_16bit_to_target_shuffle = false;
if (rt)
{
// copy of a 16bit source in to this target, make sure it's opaque and not bilinear to reduce false positives.
copy_16bit_to_target_shuffle = context->TEX0.TBP0 != context->FRAME.Block() && rt->m_32_bits_fmt == true && IsOpaque() && !(context->TEX1.MMIN & 1);
m_copy_16bit_to_target_shuffle = context->TEX0.TBP0 != context->FRAME.Block() && rt->m_32_bits_fmt == true && IsOpaque()
&& !(context->TEX1.MMIN & 1) && !m_src->m_32_bits_fmt && context->FRAME.FBMSK;
}
// Hypothesis: texture shuffle is used as a postprocessing effect so texture will be an old target.
@@ -1592,7 +1693,7 @@ void GSRendererHW::Draw()
//
// Both input and output are 16 bits and texture was initially 32 bits!
m_texture_shuffle = (GSLocalMemory::m_psm[context->FRAME.PSM].bpp == 16) && (tex_psm.bpp == 16)
&& draw_sprite_tex && (m_src->m_32_bits_fmt || copy_16bit_to_target_shuffle);
&& draw_sprite_tex && (m_src->m_32_bits_fmt || m_copy_16bit_to_target_shuffle);
// Okami mustn't call this code
if (m_texture_shuffle && m_vertex.next < 3 && PRIM->FST && ((m_context->FRAME.FBMSK & fm_mask) == 0))
@@ -1721,7 +1822,7 @@ void GSRendererHW::Draw()
m_vt.m_max.t = tmax;
}
}
if (rt)
{
// Be sure texture shuffle detection is properly propagated
@@ -1730,6 +1831,9 @@ void GSRendererHW::Draw()
rt->m_32_bits_fmt = m_texture_shuffle || (GSLocalMemory::m_psm[context->FRAME.PSM].bpp != 16);
}
const bool is_mem_clear = IsConstantDirectWriteMemClear(false);
const bool can_update_size = !is_mem_clear && !m_texture_shuffle && !m_channel_shuffle;
const GSVector2i resolution = PCRTCDisplays.GetResolution();
{
// We still need to make sure the dimensions of the targets match.
const GSVector2 up_s(GetTextureScaleFactor());
@@ -1738,15 +1842,78 @@ void GSRendererHW::Draw()
if (rt)
{
const u32 old_end_block = rt->m_end_block;
const bool new_rect = rt->m_valid.rempty();
const bool new_height = new_h > rt->m_texture->GetHeight();
const int old_height = rt->m_texture->GetHeight();
pxAssert(rt->m_texture->GetScale() == up_s);
rt->ResizeTexture(new_w, new_h, up_s);
rt->UpdateValidity(m_r);
if (!m_texture_shuffle && !m_channel_shuffle)
{
const GSVector2i tex_size = rt->m_texture->GetSize();
const GSVector4i new_valid = GSVector4i(0, 0, tex_size.x / up_s.x, tex_size.y / up_s.y);
rt->ResizeValidity(new_valid);
}
// Limit to 2x the vertical height of the resolution (for double buffering)
rt->UpdateValidity(m_r, can_update_size || m_r.w <= (resolution.y * 2));
// Probably changing to double buffering, so invalidate any old target that was next to it.
// This resolves an issue where the PCRTC will find the old target in FMV's causing flashing.
// Grandia Xtreme, Onimusha Warlord.
if (!new_rect && new_height && old_end_block != rt->m_end_block)
{
GSTextureCache::Target* old_rt = nullptr;
old_rt = m_tc->FindTargetOverlap(old_end_block, rt->m_end_block, GSTextureCache::RenderTarget, context->FRAME.PSM);
if (old_rt && old_rt != rt && GSUtil::HasSharedBits(old_rt->m_TEX0.PSM, rt->m_TEX0.PSM))
{
const int copy_width = (old_rt->m_texture->GetWidth()) > (rt->m_texture->GetWidth()) ? (rt->m_texture->GetWidth()) : old_rt->m_texture->GetWidth();
const int copy_height = (old_rt->m_texture->GetHeight()) > (rt->m_texture->GetHeight() - old_height) ? (rt->m_texture->GetHeight() - old_height) : old_rt->m_texture->GetHeight();
g_gs_device->CopyRect(old_rt->m_texture, rt->m_texture, GSVector4i(0, 0, copy_width, copy_height), 0, old_height);
m_tc->InvalidateVideoMemType(GSTextureCache::RenderTarget, old_rt->m_TEX0.TBP0);
old_rt = nullptr;
}
}
}
if (ds)
{
const u32 old_end_block = ds->m_end_block;
const bool new_rect = ds->m_valid.rempty();
const bool new_height = new_h > ds->m_texture->GetHeight();
const int old_height = ds->m_texture->GetHeight();
pxAssert(ds->m_texture->GetScale() == up_s);
ds->ResizeTexture(new_w, new_h, up_s);
ds->UpdateValidity(m_r);
if (!m_texture_shuffle && !m_channel_shuffle)
{
const GSVector2i tex_size = ds->m_texture->GetSize();
const GSVector4i new_valid = GSVector4i(0, 0, tex_size.x / up_s.x, tex_size.y / up_s.y);
ds->ResizeValidity(new_valid);
}
// Limit to 2x the vertical height of the resolution (for double buffering)
ds->UpdateValidity(m_r, can_update_size || m_r.w <= (resolution.y * 2));
if (!new_rect && new_height && old_end_block != ds->m_end_block)
{
GSTextureCache::Target* old_ds = nullptr;
old_ds = m_tc->FindTargetOverlap(old_end_block, ds->m_end_block, GSTextureCache::DepthStencil, context->ZBUF.PSM);
if (old_ds && old_ds != ds && GSUtil::HasSharedBits(old_ds->m_TEX0.PSM, ds->m_TEX0.PSM))
{
const int copy_width = (old_ds->m_texture->GetWidth()) > (ds->m_texture->GetWidth()) ? (ds->m_texture->GetWidth()) : old_ds->m_texture->GetWidth();
const int copy_height = (old_ds->m_texture->GetHeight()) > (ds->m_texture->GetHeight() - old_height) ? (ds->m_texture->GetHeight() - old_height) : old_ds->m_texture->GetHeight();
g_gs_device->CopyRect(old_ds->m_texture, ds->m_texture, GSVector4i(0, 0, copy_width, copy_height), 0, old_height);
m_tc->InvalidateVideoMemType(GSTextureCache::DepthStencil, old_ds->m_TEX0.TBP0);
old_ds = nullptr;
}
}
}
}
@@ -1870,12 +2037,6 @@ void GSRendererHW::Draw()
//
context->TEST = TEST;
context->FRAME = FRAME;
context->ZBUF = ZBUF;
//
// Temporary source *must* be invalidated before normal, because otherwise it'll be double freed.
m_tc->InvalidateTemporarySource();
@@ -1884,7 +2045,10 @@ void GSRendererHW::Draw()
if ((fm & fm_mask) != fm_mask && rt)
{
//rt->m_valid = rt->m_valid.runion(r);
rt->UpdateValidity(m_r);
// Limit to 2x the vertical height of the resolution (for double buffering)
rt->UpdateValidity(m_r, can_update_size || m_r.w <= (resolution.y * 2));
rt->UpdateValidBits(~fm & fm_mask);
m_tc->InvalidateVideoMem(context->offset.fb, m_r, false, false);
@@ -1894,7 +2058,10 @@ void GSRendererHW::Draw()
if (zm != 0xffffffff && ds)
{
//ds->m_valid = ds->m_valid.runion(r);
ds->UpdateValidity(m_r);
// Limit to 2x the vertical height of the resolution (for double buffering)
ds->UpdateValidity(m_r, can_update_size || m_r.w <= (resolution.y * 2));
ds->UpdateValidBits(GSLocalMemory::m_psm[context->ZBUF.PSM].fmsk);
m_tc->InvalidateVideoMem(context->offset.zb, m_r, false, false);
@@ -1922,7 +2089,7 @@ void GSRendererHW::Draw()
s = GetDrawDumpPath("%05d_f%lld_rz1_%05x_%s.bmp", s_n, frame, context->ZBUF.Block(), psm_str(context->ZBUF.PSM));
if (ds)
rt->m_texture->Save(s);
ds->m_texture->Save(s);
}
if (GSConfig.SaveL > 0 && (s_n - GSConfig.SaveN) > GSConfig.SaveL)
@@ -2147,14 +2314,6 @@ void GSRendererHW::EmulateZbuffer()
m_conf.ps.zclamp = 1;
}
}
const GSVertex* v = &m_vertex.buff[0];
// Minor optimization of a corner case (it allow to better emulate some alpha test effects)
if (m_conf.depth.ztst == ZTST_GEQUAL && m_vt.m_eq.z && v[0].XYZ.Z == max_z)
{
GL_DBG("Optimize Z test GEQUAL to ALWAYS (%s)", psm_str(m_context->ZBUF.PSM));
m_conf.depth.ztst = ZTST_ALWAYS;
}
}
void GSRendererHW::EmulateTextureShuffleAndFbmask()
@@ -2208,7 +2367,7 @@ void GSRendererHW::EmulateTextureShuffleAndFbmask()
m_conf.ps.write_rg = !write_ba && features.texture_barrier && m_context->TEST.DATE;
m_conf.ps.read_ba = read_ba;
m_conf.ps.real16src = m_copy_16bit_to_target_shuffle;
// Please bang my head against the wall!
// 1/ Reduce the frame mask to a 16 bit format
const u32 m = m_context->FRAME.FBMSK & GSLocalMemory::m_psm[m_context->FRAME.PSM].fmsk;
@@ -2216,7 +2375,7 @@ void GSRendererHW::EmulateTextureShuffleAndFbmask()
// fbmask is converted to a 16bit version to represent the 2 32bit channels it's writing to.
// The lower 8 bits represents the Red/Blue channels, the top 8 bits is Green/Alpha, depending on write_ba.
const u32 fbmask = ((m >> 3) & 0x1F) | ((m >> 6) & 0x3E0) | ((m >> 9) & 0x7C00) | ((m >> 16) & 0x8000);
// r = rb mask, g = ga mask
// r = rb mask, g = ga mask
const GSVector2i rb_ga_mask = GSVector2i(fbmask & 0xFF, (fbmask >> 8) & 0xFF);
m_conf.colormask.wrgba = 0;
@@ -2635,11 +2794,10 @@ void GSRendererHW::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool&
// BLEND_C_CLR1 with Ad, BLEND_C_CLR3 Cs > 0.5f will require sw blend.
// BLEND_C_CLR1 with As/F, BLEND_C_CLR2_AF, BLEND_C_CLR2_AS can be done in hw.
const bool clr_blend = !!(blend_flag & (BLEND_C_CLR1 | BLEND_C_CLR2_AF | BLEND_C_CLR2_AS | BLEND_C_CLR3));
bool clr_blend1_2 = (blend_flag & (BLEND_C_CLR1 | BLEND_C_CLR2_AF | BLEND_C_CLR2_AS))
&& (m_conf.ps.blend_c != 1) // Make sure it isn't an Ad case
&& !m_env.PABE.PABE // No PABE as it will require sw blending.
&& (m_env.COLCLAMP.CLAMP) // Let's add a colclamp check too, hw blend will clamp to 0-1.
&& !(one_barrier || m_conf.require_full_barrier); // Also don't run if there are barriers present.
bool clr_blend1_2 = (blend_flag & (BLEND_C_CLR1 | BLEND_C_CLR2_AF | BLEND_C_CLR2_AS)) && (m_conf.ps.blend_c != 1) // Make sure it isn't an Ad case
&& !m_env.PABE.PABE // No PABE as it will require sw blending.
&& (m_env.COLCLAMP.CLAMP) // Let's add a colclamp check too, hw blend will clamp to 0-1.
&& !(one_barrier || m_conf.require_full_barrier); // Also don't run if there are barriers present.
// Warning no break on purpose
// Note: the [[fallthrough]] attribute tell compilers not to complain about not having breaks.
@@ -2954,10 +3112,9 @@ void GSRendererHW::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool&
// Replace Cs*F + Cd*(1 - F) with Cs*F - Cd*(F - 1).
// As - 1 or F - 1 subtraction is only done for the dual source output (hw blending part) since we are changing the equation.
// Af will be replaced with As in shader and send it to dual source output.
m_conf.blend = {true, GSDevice::CONST_ONE, GSDevice::SRC1_ALPHA, GSDevice::OP_SUBTRACT, false, 0};
m_conf.blend = {true, GSDevice::CONST_ONE, GSDevice::SRC1_COLOR, GSDevice::OP_SUBTRACT, false, 0};
// clr_hw 1 will disable alpha clamp, we can reuse the old bits.
m_conf.ps.clr_hw = 1;
//m_conf.ps.blend_mix = 0;
// DSB output will always be used.
m_conf.ps.no_color1 = false;
}
@@ -3066,7 +3223,7 @@ void GSRendererHW::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool&
// Remove second color output when unused. Works around bugs in some drivers (e.g. Intel).
m_conf.ps.no_color1 |= !GSDevice::IsDualSourceBlendFactor(m_conf.blend.src_factor) &&
!GSDevice::IsDualSourceBlendFactor(m_conf.blend.dst_factor);
!GSDevice::IsDualSourceBlendFactor(m_conf.blend.dst_factor);
}
// DATE_PRIMID interact very badly with sw blending. DATE_PRIMID uses the primitiveID to find the primitive
@@ -3090,7 +3247,7 @@ __ri static constexpr bool IsRedundantClamp(u8 clamp, u32 clamp_min, u32 clamp_m
// That way trilinear etc still works.
const u32 textent = (1u << tsize) - 1u;
if (clamp == CLAMP_REGION_CLAMP)
return (clamp_min == 0 && clamp_max == textent);
return (clamp_min == 0 && clamp_max >= textent);
else if (clamp == CLAMP_REGION_REPEAT)
return (clamp_max == 0 && clamp_min == textent);
else
@@ -3113,7 +3270,7 @@ void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Source* tex)
// Redundant clamp tests are restricted to local memory/1x sources only, if we're from a target,
// we keep the shader clamp. See #5851 on github, and the note in Draw().
[[maybe_unused]] static constexpr const char* clamp_modes[] = { "REPEAT", "CLAMP", "REGION_CLAMP", "REGION_REPEAT" };
[[maybe_unused]] static constexpr const char* clamp_modes[] = {"REPEAT", "CLAMP", "REGION_CLAMP", "REGION_REPEAT"};
const bool redundant_wms = !tex->m_target && IsRedundantClamp(m_context->CLAMP.WMS, m_context->CLAMP.MINU,
m_context->CLAMP.MAXU, tex->m_TEX0.TW);
const bool redundant_wmt = !tex->m_target && IsRedundantClamp(m_context->CLAMP.WMT, m_context->CLAMP.MINV,
@@ -3121,10 +3278,10 @@ void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Source* tex)
const u8 wms = EffectiveClamp(m_context->CLAMP.WMS, tex->m_region.HasX() || redundant_wms);
const u8 wmt = EffectiveClamp(m_context->CLAMP.WMT, tex->m_region.HasY() || redundant_wmt);
const bool complex_wms_wmt = !!((wms | wmt) & 2);
GL_CACHE("WMS: %s [%s%s] WMT: %s [%s%s] Complex: %d MINU: %d MINV: %d MINV: %d MAXV: %d",
GL_CACHE("WMS: %s [%s%s] WMT: %s [%s%s] Complex: %d MINU: %d MAXU: %d MINV: %d MAXV: %d",
clamp_modes[m_context->CLAMP.WMS], redundant_wms ? "redundant," : "", clamp_modes[wms],
clamp_modes[m_context->CLAMP.WMT], redundant_wmt ? "redundant," : "", clamp_modes[wmt],
complex_wms_wmt, m_context->CLAMP.MINU, m_context->CLAMP.MINV, m_context->CLAMP.MAXU, m_context->CLAMP.MAXV);
complex_wms_wmt, m_context->CLAMP.MINU, m_context->CLAMP.MAXU, m_context->CLAMP.MINV, m_context->CLAMP.MAXV);
const bool need_mipmap = IsMipMapDraw();
const bool shader_emulated_sampler = tex->m_palette || cpsm.fmt != 0 || complex_wms_wmt || psm.depth;
@@ -3492,7 +3649,7 @@ void GSRendererHW::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
const bool DATE = m_context->TEST.DATE && m_context->FRAME.PSM != PSM_PSMCT24;
bool DATE_PRIMID = false;
bool DATE_BARRIER = false;
bool DATE_one = false;
bool DATE_one = false;
const bool ate_first_pass = m_context->TEST.DoFirstPass();
const bool ate_second_pass = m_context->TEST.DoSecondPass();
@@ -3657,6 +3814,16 @@ void GSRendererHW::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
m_conf.datm = m_context->TEST.DATM;
// If we're doing stencil DATE and we don't have a depth buffer, we need to allocate a temporary one.
GSTexture* temp_ds = nullptr;
if (m_conf.destination_alpha >= GSHWDrawConfig::DestinationAlphaMode::Stencil &&
m_conf.destination_alpha <= GSHWDrawConfig::DestinationAlphaMode::StencilOne && !m_conf.ds)
{
temp_ds = g_gs_device->CreateDepthStencil(rt->GetWidth(), rt->GetHeight(), GSTexture::Format::DepthStencil, false);
temp_ds->SetScale(m_conf.rt->GetScale());
m_conf.ds = temp_ds;
}
// vs
m_conf.vs.tme = PRIM->TME;
@@ -3965,6 +4132,42 @@ void GSRendererHW::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
m_conf.drawlist = (m_conf.require_full_barrier && m_vt.m_primclass == GS_SPRITE_CLASS) ? &m_drawlist : nullptr;
g_gs_device->RenderHW(m_conf);
if (temp_ds)
g_gs_device->Recycle(temp_ds);
}
// If the EE uploaded a new CLUT since the last draw, use that.
bool GSRendererHW::HasEEUpload(GSVector4i r)
{
std::vector<GSState::GSUploadQueue>::iterator iter;
for (iter = m_draw_transfers.begin(); iter != m_draw_transfers.end();)
{
if (iter->draw == (s_n - 1) && iter->blit.DBP == m_context->TEX0.TBP0 && GSUtil::HasSharedBits(iter->blit.DPSM, m_context->TEX0.PSM))
{
GSVector4i rect = r;
if (!GSUtil::HasCompatibleBits(iter->blit.DPSM, m_context->TEX0.PSM))
{
GSTextureCache::SurfaceOffsetKey sok;
sok.elems[0].bp = iter->blit.DBP;
sok.elems[0].bw = iter->blit.DBW;
sok.elems[0].psm = iter->blit.DPSM;
sok.elems[0].rect = iter->rect;
sok.elems[1].bp = m_context->TEX0.TBP0;
sok.elems[1].bw = m_context->TEX0.TBW;
sok.elems[1].psm = m_context->TEX0.PSM;
sok.elems[1].rect = r;
rect = m_tc->ComputeSurfaceOffset(sok).b2a_offset;
}
if (rect.rintersect(r).eq(r))
return true;
}
iter++;
}
return false;
}
GSRendererHW::CLUTDrawTestResult GSRendererHW::PossibleCLUTDraw()
@@ -4020,11 +4223,12 @@ GSRendererHW::CLUTDrawTestResult GSRendererHW::PossibleCLUTDraw()
// Make sure it's a division of 8 in width to avoid bad draws. Points will go from 0-7 inclusive, but sprites etc will do 0-16 exclusive.
int draw_divder_match = false;
const int valid_sizes[] = { 8, 16, 32, 64 };
const int valid_sizes[] = {8, 16, 32, 64};
for (int i = 0; i < 4; i++) {
for (int i = 0; i < 4; i++)
{
draw_divder_match = ((m_vt.m_primclass == GS_POINT_CLASS) ? ((static_cast<int>(m_vt.m_max.p.x + 1) & ~1) == valid_sizes[i]) : (static_cast<int>(m_vt.m_max.p.x) == valid_sizes[i]));
if (draw_divder_match)
break;
}
@@ -4048,13 +4252,8 @@ GSRendererHW::CLUTDrawTestResult GSRendererHW::PossibleCLUTDraw()
// If we have GPU CLUT enabled, don't do a CPU draw when it would result in a download.
if (GSConfig.UserHacks_GPUTargetCLUTMode != GSGPUTargetCLUTMode::Disabled)
{
std::vector<GSState::GSUploadQueue>::iterator iter;
for (iter = m_draw_transfers.begin(); iter != m_draw_transfers.end(); ) {
if (iter->blit.DBP == m_context->TEX0.TBP0 && GSUtil::HasSharedBits(iter->blit.DPSM, m_context->TEX0.PSM))
return CLUTDrawTestResult::CLUTDrawOnCPU;
iter++;
}
if (HasEEUpload(r))
return CLUTDrawTestResult::CLUTDrawOnCPU;
GSTextureCache::Target* tgt = m_tc->GetExactTarget(m_context->TEX0.TBP0, m_context->TEX0.TBW, m_context->TEX0.PSM);
if (tgt)
@@ -4077,20 +4276,15 @@ GSRendererHW::CLUTDrawTestResult GSRendererHW::PossibleCLUTDraw()
}
else
{
std::vector<GSState::GSUploadQueue>::iterator iter;
for (iter = m_draw_transfers.begin(); iter != m_draw_transfers.end(); ) {
if (iter->blit.DBP == m_context->TEX0.TBP0 && GSUtil::HasSharedBits(iter->blit.DPSM, m_context->TEX0.PSM))
return CLUTDrawTestResult::CLUTDrawOnCPU;
iter++;
}
if (HasEEUpload(r))
return CLUTDrawTestResult::CLUTDrawOnCPU;
}
GIFRegBITBLTBUF BITBLTBUF = {};
BITBLTBUF.SBP = m_context->TEX0.TBP0;
BITBLTBUF.SBW = m_context->TEX0.TBW;
BITBLTBUF.SPSM = m_context->TEX0.PSM;
InvalidateLocalMem(BITBLTBUF, r);
}
// Debugging stuff..
@@ -4159,7 +4353,20 @@ bool GSRendererHW::CanUseSwPrimRender(bool no_rt, bool no_ds, bool draw_sprite_t
// will use a much smaller block size than the framebuffer.
GSTextureCache::Target* src_target = m_tc->GetTargetWithSharedBits(m_context->TEX0.TBP0, m_context->TEX0.PSM);
if (src_target && src_target->m_TEX0.TBW == m_context->TEX0.TBW)
{
// If the EE has written over our sample area, we're fine to do this on the CPU, despite the target.
if (!src_target->m_dirty.empty())
{
const GSVector4i tr(GetTextureMinMax(m_context->TEX0, m_context->CLAMP, m_vt.IsLinear()).coverage);
for (GSDirtyRect& rc : src_target->m_dirty)
{
if (!rc.GetDirtyRect(m_context->TEX0).rintersect(tr).rempty())
return true;
}
}
return false;
}
// We can use the sw prim render path!
return true;
@@ -4397,7 +4604,7 @@ bool GSRendererHW::OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Sourc
// Copy back the texture into the GS mem. I don't know why but it will be
// reuploaded again later
m_tc->Read(tex, r_texture);
m_tc->Read(tex, r_texture.rintersect(tex->m_texture->GetRect()));
m_tc->InvalidateVideoMemSubTarget(_rt);
+2
View File
@@ -75,6 +75,7 @@ private:
CLUTDrawOnGPU,
};
bool HasEEUpload(GSVector4i r);
CLUTDrawTestResult PossibleCLUTDraw();
CLUTDrawTestResult PossibleCLUTDrawAggressive();
bool CanUseSwPrimRender(bool no_rt, bool no_ds, bool draw_sprite_tex);
@@ -166,6 +167,7 @@ public:
void Draw() override;
void PurgeTextureCache() override;
void ReadbackTextureCache() override;
GSTexture* LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, const GSVector2i& size) override;
// Called by the texture cache to know if current texture is useful
+310 -109
View File
@@ -46,6 +46,15 @@ GSTextureCache::~GSTextureCache()
_aligned_free(s_unswizzle_buffer);
}
void GSTextureCache::ReadbackAll()
{
for (int type = 0; type < 2; type++)
{
for (auto t : m_dst[type])
Read(t, t->m_drawn_since_read);
}
}
void GSTextureCache::RemoveAll()
{
m_src.RemoveAll();
@@ -74,15 +83,16 @@ void GSTextureCache::RemoveAll()
m_surface_offset_cache.clear();
}
void GSTextureCache::AddDirtyRectTarget(Target* target, GSVector4i rect, u32 psm, u32 bw)
void GSTextureCache::AddDirtyRectTarget(Target* target, GSVector4i rect, u32 psm, u32 bw, RGBAMask rgba)
{
bool skipdirty = false;
bool canskip = true;
std::vector<GSDirtyRect>::iterator it = target->m_dirty.end();
while (it != target->m_dirty.begin())
{
--it;
if (it[0].bw == bw && it[0].psm == psm)
if (it[0].bw == bw && it[0].psm == psm && it[0].rgba._u32 == rgba._u32)
{
if (it[0].r.rintersect(rect).eq(rect) && canskip)
{
@@ -103,7 +113,7 @@ void GSTextureCache::AddDirtyRectTarget(Target* target, GSVector4i rect, u32 psm
}
if (!skipdirty)
target->m_dirty.push_back(GSDirtyRect(rect, psm, bw));
target->m_dirty.push_back(GSDirtyRect(rect, psm, bw, rgba));
}
GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, bool palette)
@@ -216,7 +226,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
__ri static GSTextureCache::Source* FindSourceInMap(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA,
const GSLocalMemory::psm_t& psm_s, const u32* clut, const GSTexture* gpu_clut, const GSVector2i& compare_lod,
const GSTextureCache::SourceRegion& region, FastList<GSTextureCache::Source*>& map)
const GSTextureCache::SourceRegion& region, u32 fixed_tex0, FastList<GSTextureCache::Source*>& map)
{
for (auto i = map.begin(); i != map.end(); ++i)
{
@@ -248,7 +258,10 @@ __ri static GSTextureCache::Source* FindSourceInMap(const GIFRegTEX0& TEX0, cons
continue;
}
if (s->m_region.bits != 0 && s->m_region.bits != region.bits)
// When fixed tex0 is used, we must find a matching region texture. The base likely
// doesn't contain to the correct region. Bit cheeky here, avoid a logical or by
// adding the invalid tex0 bit in.
if (((s->m_region.bits | fixed_tex0) != 0) && s->m_region.bits != region.bits)
continue;
// Same base mip texture, but we need to check that MXL was the same as well.
@@ -279,7 +292,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
{
// Another Lupin case here, it uses region clamp with UV (not ST), puts a clamp region further
// into the texture, but a smaller TW/TH. Catch this by looking for a clamp range above TW.
const u32 rw = CLAMP.MAXU - CLAMP.MAXU + 1;
const u32 rw = CLAMP.MAXU - CLAMP.MINU + 1;
if (rw < (1u << TEX0.TW) || CLAMP.MAXU >= (1u << TEX0.TW))
{
region.SetX(CLAMP.MINU, CLAMP.MAXU + 1);
@@ -292,7 +305,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
// to offset the actual texture data to elsewhere. So, we'll just force any cases like this down
// the region texture path.
const u32 rw = ((CLAMP.MINU | CLAMP.MAXU) - CLAMP.MAXU) + 1;
if (rw < (1u << TEX0.TW) || CLAMP.MAXU != 0)
if (rw < (1u << TEX0.TW) || (CLAMP.MAXU != 0 && (rw <= (1u << TEX0.TW))))
{
region.SetX(CLAMP.MAXU, (CLAMP.MINU | CLAMP.MAXU) + 1);
GL_CACHE("TC: Region repeat optimization: %d width -> %d", 1 << TEX0.TW, region.GetWidth());
@@ -310,7 +323,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
else if (CLAMP.WMT == CLAMP_REGION_REPEAT && CLAMP.MINV != 0)
{
const u32 rh = ((CLAMP.MINV | CLAMP.MAXV) - CLAMP.MAXV) + 1;
if (rh < (1u << TEX0.TH) || CLAMP.MAXV != 0)
if (rh < (1u << TEX0.TH) || (CLAMP.MAXV != 0 && (rh <= (1u << TEX0.TH))))
{
region.SetY(CLAMP.MAXV, (CLAMP.MINV | CLAMP.MAXV) + 1);
GL_CACHE("TC: Region repeat optimization: %d height -> %d", 1 << TEX0.TW, region.GetHeight());
@@ -322,15 +335,16 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
// Region textures might be placed in a different page, so check that first.
const u32 lookup_page = TEX0.TBP0 >> 5;
const bool is_fixed_tex0 = region.IsFixedTEX0(1 << TEX0.TW, 1 << TEX0.TH);
if (region.GetMinX() != 0 || region.GetMinY() != 0)
{
const GSOffset offset(psm_s.info, TEX0.TBP0, TEX0.TBW, TEX0.PSM);
const u32 region_page = offset.bn(region.GetMinX(), region.GetMinY()) >> 5;
if (lookup_page != region_page)
src = FindSourceInMap(TEX0, TEXA, psm_s, clut, gpu_clut, compare_lod, region, m_src.m_map[region_page]);
src = FindSourceInMap(TEX0, TEXA, psm_s, clut, gpu_clut, compare_lod, region, is_fixed_tex0, m_src.m_map[region_page]);
}
if (!src)
src = FindSourceInMap(TEX0, TEXA, psm_s, clut, gpu_clut, compare_lod, region, m_src.m_map[lookup_page]);
src = FindSourceInMap(TEX0, TEXA, psm_s, clut, gpu_clut, compare_lod, region, is_fixed_tex0, m_src.m_map[lookup_page]);
Target* dst = nullptr;
@@ -369,7 +383,11 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
// Solution: consider the RT as 32 bits if the alpha was used in the past
const u32 t_psm = (t->m_dirty_alpha) ? t->m_TEX0.PSM & ~0x1 : t->m_TEX0.PSM;
const bool t_clean = t->m_dirty.empty();
const u32 channels = t->m_dirty.GetDirtyChannels() & GSUtil::GetChannelMask(psm);
// If not all channels are clean/dirty, we need to update the target.
if (channels != 0 && channels != GSUtil::GetChannelMask(psm))
t->Update(false);
const bool t_clean = (t->m_dirty.GetDirtyChannels() & GSUtil::GetChannelMask(psm)) == 0;
const bool t_wraps = t->m_end_block > GSTextureCache::MAX_BP;
// Match if we haven't already got a tex in rt
@@ -460,6 +478,8 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
if (tex_in_rt)
{
GSVector2i size_delta = { ((x_offset + (1 << TEX0.TW)) - dst->m_valid.z), ((y_offset + (1 << TEX0.TH)) - dst->m_valid.w) };
RGBAMask rgba;
rgba._u32 = GSUtil::GetChannelMask(psm);
if (size_delta.x > 0)
{
@@ -470,7 +490,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
{
dst->UpdateValidity(dirty_rect);
AddDirtyRectTarget(dst, dirty_rect, dst->m_TEX0.PSM, dst->m_TEX0.TBW);
AddDirtyRectTarget(dst, dirty_rect, dst->m_TEX0.PSM, dst->m_TEX0.TBW, rgba);
}
}
@@ -483,7 +503,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
{
dst->UpdateValidity(dirty_rect);
AddDirtyRectTarget(dst, dirty_rect, dst->m_TEX0.PSM, dst->m_TEX0.TBW);
AddDirtyRectTarget(dst, dirty_rect, dst->m_TEX0.PSM, dst->m_TEX0.TBW, rgba);
}
}
const GSVector2 up_s(dst->m_texture->GetScale());
@@ -573,7 +593,20 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
return src;
}
GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask, const bool is_frame, const int real_w, const int real_h, bool preload)
GSTextureCache::Target* GSTextureCache::FindTargetOverlap(u32 bp, u32 end_block, int type, int psm)
{
u32 end_block_bp = end_block < bp ? (MAX_BP + 1) : end_block;
for (auto t : m_dst[type])
{
// Only checks that the texure starts at the requested bp, which shares data. Size isn't considered.
if (t->m_TEX0.TBP0 >= bp && t->m_TEX0.TBP0 < end_block_bp && GSUtil::HasSharedBits(t->m_TEX0.PSM, psm))
return t;
}
return nullptr;
}
GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask, const bool is_frame, const int real_w, const int real_h, bool preload, bool is_clear)
{
const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[TEX0.PSM];
const GSVector2& new_s = static_cast<GSRendererHW*>(g_gs_renderer.get())->GetTextureScaleFactor();
@@ -614,7 +647,18 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, con
dst = t;
dst->m_32_bits_fmt |= (psm_s.bpp != 16);
dst->m_TEX0 = TEX0;
// Nicktoons Unite tries to change the width from 10 to 8 and breaks FMVs.
// Haunting ground has some messed textures if you don't modify the rest.
if (!dst->m_is_frame)
{
dst->m_TEX0 = TEX0;
}
else
{
u32 width = dst->m_TEX0.TBW;
dst->m_TEX0 = TEX0;
dst->m_TEX0.TBW = width;
}
break;
}
@@ -691,7 +735,10 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, con
}
if (dst)
{
dst->m_TEX0.TBW = TEX0.TBW; // Fix Jurassic Park - Operation Genesis loading disk logo.
dst->m_is_frame |= is_frame; // Nicktoons Unite tries to change the width from 10 to 8 and breaks FMVs.
}
}
if (dst)
@@ -766,6 +813,13 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, con
if (!dst)
{
// Skip full screen clears from making massive targets.
if (is_clear)
{
GL_CACHE("TC: Create RT skipped on clear draw");
return nullptr;
}
GL_CACHE("TC: Lookup %s(%s) %dx%d, miss (0x%x, %s)", is_frame ? "Frame" : "Target", to_string(type), size.x, size.y, bp, psm_str(TEX0.PSM));
dst = CreateTarget(TEX0, size.x, size.y, type, true);
@@ -786,28 +840,61 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(const GIFRegTEX0& TEX0, con
{
const bool forced_preload = GSRendererHW::GetInstance()->m_force_preload > 0;
const GSVector4i newrect = GSVector4i(0, 0, real_w, real_h);
const u32 rect_end = GSLocalMemory::m_psm[TEX0.PSM].info.bn(newrect.z - 1, newrect.w - 1, TEX0.TBP0, TEX0.TBW);
RGBAMask rgba;
rgba._u32 = GSUtil::GetChannelMask(TEX0.PSM);
if (!is_frame && !forced_preload && !preload)
{
std::vector<GSState::GSUploadQueue>::iterator iter;
GSVector4i eerect = GSVector4i::zero();
for (iter = GSRendererHW::GetInstance()->m_draw_transfers.begin(); iter != GSRendererHW::GetInstance()->m_draw_transfers.end(); )
{
// If the format, and location doesn't match, but also the upload is at least the size of the target, don't preload.
if (iter->blit.DBP == TEX0.TBP0 && GSUtil::HasCompatibleBits(iter->blit.DPSM, TEX0.PSM) && iter->rect.rintersect(newrect).eq(newrect))
// If the format, and location doesn't overlap
if (iter->blit.DBP >= TEX0.TBP0 && iter->blit.DBP <= rect_end && GSUtil::HasCompatibleBits(iter->blit.DPSM, TEX0.PSM))
{
GSRendererHW::GetInstance()->m_draw_transfers.erase(iter);
GL_INS("Preloading the RT DATA");
AddDirtyRectTarget(dst, newrect, TEX0.PSM, TEX0.TBW);
dst->Update(true);
break;
GSTextureCache::SurfaceOffsetKey sok;
sok.elems[0].bp = iter->blit.DBP;
sok.elems[0].bw = iter->blit.DBW;
sok.elems[0].psm = iter->blit.DPSM;
sok.elems[0].rect = iter->rect;
sok.elems[1].bp = TEX0.TBP0;
sok.elems[1].bw = TEX0.TBW;
sok.elems[1].psm = TEX0.PSM;
sok.elems[1].rect = newrect;
// Calculate the rect offset if the BP doesn't match.
const GSVector4i targetr = (iter->blit.DBP == TEX0.TBP0) ? iter->rect : ComputeSurfaceOffset(sok).b2a_offset;
if (eerect.rempty())
eerect = targetr;
else
eerect = eerect.runion(targetr);
iter = GSRendererHW::GetInstance()->m_draw_transfers.erase(iter);
if (eerect.rintersect(newrect).eq(newrect))
break;
else
continue;
}
iter++;
}
if (!eerect.rempty())
{
GL_INS("Preloading the RT DATA");
eerect = eerect.rintersect(newrect);
dst->UpdateValidity(newrect);
AddDirtyRectTarget(dst, eerect, TEX0.PSM, TEX0.TBW, rgba);
dst->Update(true);
}
}
else
{
GL_INS("Preloading the RT DATA");
AddDirtyRectTarget(dst, newrect, TEX0.PSM, TEX0.TBW);
dst->UpdateValidity(newrect);
AddDirtyRectTarget(dst, newrect, TEX0.PSM, TEX0.TBW, rgba);
dst->Update(true);
}
}
@@ -885,21 +972,23 @@ void GSTextureCache::ScaleTargetForDisplay(Target* t, const GIFRegTEX0& dispfb,
g_gs_device->Recycle(old_texture);
t->m_texture = new_texture;
RGBAMask rgba;
rgba._u32 = GSUtil::GetChannelMask(t->m_TEX0.PSM);
// We unconditionally preload the frame here, because otherwise we'll end up with blackness for one frame (when the expand happens).
const int preload_width = t->m_TEX0.TBW * 64;
if (old_width < preload_width && old_height < needed_height)
{
const GSVector4i right(old_width, 0, preload_width, needed_height);
const GSVector4i bottom(0, old_height, old_width, needed_height);
AddDirtyRectTarget(t, right, t->m_TEX0.PSM, t->m_TEX0.TBW);
AddDirtyRectTarget(t, bottom, t->m_TEX0.PSM, t->m_TEX0.TBW);
AddDirtyRectTarget(t, right, t->m_TEX0.PSM, t->m_TEX0.TBW, rgba);
AddDirtyRectTarget(t, bottom, t->m_TEX0.PSM, t->m_TEX0.TBW, rgba);
}
else
{
const GSVector4i newrect = GSVector4i((old_height < scaled_needed_height) ? 0 : old_width,
(old_width < preload_width) ? 0 : old_height,
preload_width, needed_height);
AddDirtyRectTarget(t, newrect, t->m_TEX0.PSM, t->m_TEX0.TBW);
AddDirtyRectTarget(t, newrect, t->m_TEX0.PSM, t->m_TEX0.TBW, rgba);
}
// Inject the new height back into the cache.
@@ -935,6 +1024,8 @@ void GSTextureCache::ExpandTarget(const GIFRegBITBLTBUF& BITBLTBUF, const GSVect
TEX0.PSM = BITBLTBUF.DPSM;
Target* dst = nullptr;
auto& list = m_dst[RenderTarget];
RGBAMask rgba;
rgba._u32 = GSUtil::GetChannelMask(TEX0.PSM);
for (auto i = list.begin(); i != list.end(); ++i)
{
@@ -967,9 +1058,10 @@ void GSTextureCache::ExpandTarget(const GIFRegBITBLTBUF& BITBLTBUF, const GSVect
// which are being expanded one-line-at-a-time.
if (dst->ResizeTexture(upsc_width, upsc_height, false))
{
AddDirtyRectTarget(dst, r, TEX0.PSM, TEX0.TBW);
AddDirtyRectTarget(dst, r, TEX0.PSM, TEX0.TBW, rgba);
GetTargetHeight(TEX0.TBP0, TEX0.TBW, TEX0.PSM, aligned_height);
dst->UpdateValidity(r);
dst->UpdateValidBits(GSLocalMemory::m_psm[TEX0.PSM].fmsk);
}
}
}
@@ -978,8 +1070,9 @@ void GSTextureCache::ExpandTarget(const GIFRegBITBLTBUF& BITBLTBUF, const GSVect
const GSVector4i clamped_r(
r.rintersect(GSVector4i(0, 0, static_cast<int>(dst->m_texture->GetWidth() / dst->m_texture->GetScale().x),
static_cast<int>(dst->m_texture->GetHeight() / dst->m_texture->GetScale().y))));
AddDirtyRectTarget(dst, clamped_r, TEX0.PSM, TEX0.TBW);
AddDirtyRectTarget(dst, clamped_r, TEX0.PSM, TEX0.TBW, rgba);
dst->UpdateValidity(clamped_r);
dst->UpdateValidBits(GSLocalMemory::m_psm[TEX0.PSM].fmsk);
}
}
// Goal: Depth And Target at the same address is not possible. On GS it is
@@ -1157,6 +1250,15 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
if (!target)
return;
// Handle the case where the transfer wrapped around the end of GS memory.
const u32 end_bp = off.bn(rect.z - 1, rect.w - 1);
const u32 unwrapped_end_bp = end_bp + ((end_bp < bp) ? MAX_BLOCKS : 0);
// Ideally in the future we can turn this on unconditionally, but for now it breaks too much.
const bool check_inside_target = (GSConfig.UserHacks_TargetPartialInvalidation || GSConfig.UserHacks_TextureInsideRt);
RGBAMask rgba;
rgba._u32 = GSUtil::GetChannelMask(psm);
for (int type = 0; type < 2; type++)
{
auto& list = m_dst[type];
@@ -1165,6 +1267,13 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
auto j = i;
Target* t = *j;
// Don't bother checking any further if the target doesn't overlap with the write/invalidation.
if ((bp < t->m_TEX0.TBP0 && unwrapped_end_bp < t->m_TEX0.TBP0) || bp > t->UnwrappedEndBlock())
{
++i;
continue;
}
// GH: (I think) this code is completely broken. Typical issue:
// EE write an alpha channel into 32 bits texture
// Results: the target is deleted (because HasCompatibleBits is false)
@@ -1183,7 +1292,7 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
if (eewrite)
t->m_age = 0;
AddDirtyRectTarget(t, r, psm, bw);
AddDirtyRectTarget(t, r, psm, bw, rgba);
}
else
{
@@ -1198,7 +1307,7 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
// Compatible formats and same width, probably updating the same texture, so just mark it as dirty.
if (bw == t->m_TEX0.TBW && GSLocalMemory::m_psm[psm].bpp == GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp)
{
AddDirtyRectTarget(t, rect, psm, bw);
AddDirtyRectTarget(t, rect, psm, bw, rgba);
GL_CACHE("TC: Direct Dirty in the middle [aggressive] of Target(%s) %d [PSM:%s BP:0x%x->0x%x BW:%u rect(%d,%d=>%d,%d)] write[PSM:%s BP:0x%x BW:%u rect(%d,%d=>%d,%d)]",
to_string(type),
t->m_texture ? t->m_texture->GetID() : 0,
@@ -1232,7 +1341,7 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
SurfaceOffset so = ComputeSurfaceOffset(off, r, t);
if (so.is_valid)
{
AddDirtyRectTarget(t, so.b2a_offset, psm, bw);
AddDirtyRectTarget(t, so.b2a_offset, psm, bw, rgba);
GL_CACHE("TC: Dirty in the middle [aggressive] of Target(%s) %d [PSM:%s BP:0x%x->0x%x BW:%u rect(%d,%d=>%d,%d)] write[PSM:%s BP:0x%x BW:%u rect(%d,%d=>%d,%d)]",
to_string(type),
t->m_texture ? t->m_texture->GetID() : 0,
@@ -1295,29 +1404,21 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
const u32 rowsize = bw * 8192;
const u32 offset = static_cast<u32>((t->m_TEX0.TBP0 - bp) * 256);
// This grossness is needed to fix incorrect invalidations in True Crime: New York City.
// Because it's writing tiny texture blocks (which are later decompressed) over previous targets,
// we need to be ensure said targets are invalidated, otherwise the SW prim render path won't be
// triggered. This whole thing needs rewriting anyway, because it can't handle non-page-aligned
// writes, but for now we'll just use the unsafer logic when the TC hack is enabled.
const bool start_of_page = rowsize > 0 && (offset % rowsize == 0);
if (start_of_page || (rowsize > 0 && GSConfig.UserHacks_CPUSpriteRenderBW != 0))
if (rowsize > 0 && offset % rowsize == 0)
{
int y = GSLocalMemory::m_psm[psm].pgs.y * offset / rowsize;
if (r.bottom > y && (start_of_page || r.top >= y))
if (r.bottom > y)
{
GL_CACHE("TC: Dirty After Target(%s) %d (0x%x)", to_string(type),
t->m_texture ? t->m_texture->GetID() : 0,
t->m_TEX0.TBP0);
// TODO: do not add this rect above too
t->m_TEX0.TBW = bw;
if (eewrite)
t->m_age = 0;
const GSVector4i dirty_r = GSVector4i(r.left, r.top - y, r.right, r.bottom - y);
AddDirtyRectTarget(t, dirty_r, psm, bw);
AddDirtyRectTarget(t, dirty_r, psm, bw, rgba);
continue;
}
}
@@ -1347,14 +1448,12 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
if (eewrite)
t->m_age = 0;
t->m_TEX0.TBW = bw;
const GSVector4i dirty_r = GSVector4i(r.left, r.top + y, r.right, r.bottom + y);
AddDirtyRectTarget(t, dirty_r, psm, bw);
AddDirtyRectTarget(t, dirty_r, psm, bw, rgba);
continue;
}
}
else if (GSConfig.UserHacks_TextureInsideRt && t->Overlaps(bp, bw, psm, rect) && GSUtil::HasCompatibleBits(psm, t->m_TEX0.PSM))
else if (check_inside_target && t->Overlaps(bp, bw, psm, rect) && GSUtil::HasSharedBits(psm, t->m_TEX0.PSM))
{
SurfaceOffsetKey sok;
sok.elems[0].bp = bp;
@@ -1372,7 +1471,7 @@ void GSTextureCache::InvalidateVideoMem(const GSOffset& off, const GSVector4i& r
if (eewrite)
t->m_age = 0;
AddDirtyRectTarget(t, so.b2a_offset, t->m_TEX0.PSM, t->m_TEX0.TBW);
AddDirtyRectTarget(t, so.b2a_offset, t->m_TEX0.PSM, t->m_TEX0.TBW, rgba);
}
}
#endif
@@ -1388,6 +1487,9 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
const u32 bp = off.bp();
const u32 psm = off.psm();
[[maybe_unused]] const u32 bw = off.bw();
const u32 read_start = GSLocalMemory::m_psm[psm].info.bn(r.x, r.y, bp, bw);
const u32 read_end = GSLocalMemory::m_psm[psm].info.bn(r.x, r.y, bp, bw);
const bool read_paltex = GSLocalMemory::m_psm[psm].pal > 0;
GL_CACHE("TC: InvalidateLocalMem off(0x%x, %u, %s) r(%d, %d => %d, %d)",
bp,
@@ -1414,18 +1516,22 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
for (auto it = dss.rbegin(); it != dss.rend(); ++it) // Iterate targets from LRU to MRU.
{
Target* t = *it;
if (GSUtil::HasSharedBits(bp, psm, t->m_TEX0.TBP0, t->m_TEX0.PSM))
{
if (GSUtil::HasCompatibleBits(psm, t->m_TEX0.PSM))
{
const GSVector4i draw_rect = (t->readbacks_since_draw > 0) ? t->m_drawn_since_read : r.rintersect(t->m_drawn_since_read);
Read(t, draw_rect);
t->readbacks_since_draw++;
if(draw_rect.rintersect(t->m_drawn_since_read).eq(t->m_drawn_since_read))
t->m_drawn_since_read = GSVector4i::zero();
}
}
// Check the offset of the read, if they're not pointing at or inside this texture, it's probably not what we want.
const bool expecting_this_tex = ((bp < t->m_TEX0.TBP0 && read_start >= t->m_TEX0.TBP0) || bp >= t->m_TEX0.TBP0) && read_end <= t->m_end_block;
if (!expecting_this_tex || !t->Overlaps(bp, bw, psm, r) || !GSUtil::HasSharedBits(psm, t->m_TEX0.PSM) || ((bp != t->m_TEX0.TBP0) && !GSUtil::HasCompatibleBits(psm, t->m_TEX0.PSM)))
continue;
const bool bpp_match = GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == GSLocalMemory::m_psm[psm].bpp;
const bool format_match = (bp == t->m_TEX0.TBP0 && bw == t->m_TEX0.TBW && bpp_match);
// Calculate the rect offset if the BP doesn't match.
const GSVector4i targetr = (format_match) ? r.rintersect(t->m_valid) : ComputeSurfaceOffset(bp, bw, psm, r, t).b2a_offset;
const GSVector4i draw_rect = (t->readbacks_since_draw > 0) ? t->m_drawn_since_read : targetr.rintersect(t->m_drawn_since_read);
Read(t, draw_rect);
t->readbacks_since_draw++;
if (draw_rect.rintersect(t->m_drawn_since_read).eq(t->m_drawn_since_read))
t->m_drawn_since_read = GSVector4i::zero();
}
}
return;
@@ -1436,22 +1542,53 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
// (Busen0: Wizardry and Chaos Legion).
// Also in a few games the below code ran the Grandia3 case when it shouldn't :p
auto& rts = m_dst[RenderTarget];
for (auto it = rts.rbegin(); it != rts.rend(); ++it) // Iterate targets from LRU to MRU.
for (auto it = rts.rbegin(); it != rts.rend(); it++) // Iterate targets from LRU to MRU.
{
Target* t = *it;
if (t->m_TEX0.PSM != PSM_PSMZ32 && t->m_TEX0.PSM != PSM_PSMZ24 && t->m_TEX0.PSM != PSM_PSMZ16 && t->m_TEX0.PSM != PSM_PSMZ16S)
{
// Some games like to offset their GS download memory addresses by
// using overly big source Y position values.
// Checking for targets that overlap with the requested memory region
// instead of just comparing TBPs should fix that.
// For example, this fixes Judgement ring rendering in Shadow Hearts 2.
// Be wary of old targets being misdetected, set a sensible range of 30 frames (like Display source lookups).
if (t->Overlaps(bp, bw, psm, r) && t->m_TEX0.TBP0 >= bp && GSUtil::HasSharedBits(psm, t->m_TEX0.PSM) && t->m_age <= 30)
{
// Enforce full invalidation if BP's don't match.
const GSVector4i targetr = (bp == t->m_TEX0.TBP0) ? r : t->m_valid;
// propagate the format from the result of a channel effect
// texture is 16/8 bit but the real data is 32
// common use for shuffling is moving data into the alpha channel
// the game can then draw using 8H format
// in the case of silent hill blit 8H -> 8P
// this will matter later when the data ends up in GS memory in the wrong format
// Be careful to avoid 24 bit textures which are technically 32bit, as you could lose alpha (8H) data.
if (t->m_32_bits_fmt && t->m_TEX0.PSM > PSM_PSMCT24)
t->m_TEX0.PSM = PSM_PSMCT32;
// Check the offset of the read, if they're not pointing at or inside this texture, it's probably not what we want.
const bool expecting_this_tex = ((bp < t->m_TEX0.TBP0 && read_start >= t->m_TEX0.TBP0) || bp >= t->m_TEX0.TBP0) && read_end <= t->m_end_block;
const bool target_paltex = GSLocalMemory::m_psm[t->m_TEX0.PSM].pal > 0;
// Only allow an indexed format on a 32bit colour, if it's alpha channel.
const bool alpha_read = t->m_TEX0.PSM == PSM_PSMCT32 && psm >= PSM_PSMT8H;
// Okay this is a nightmare of a check, so these are the conditions:
// 1. Check if it's expecting this texture, so the read must be inside this texture, even if the BP doesn't match.
// 2. It must overlap (okay maybe redundant).
// 3. If it's a paltex (indexed format), the target must also be paltex, unless it's only reading the alpha channel.
// 4. They share bits in some capacity.
// 5. If the BP doesn't match, make sure the formats are compatible, at very least (like CT32 + CT24 for example).
if (!expecting_this_tex || !t->Overlaps(bp, bw, psm, r) || (read_paltex != target_paltex && !alpha_read) || !GSUtil::HasSharedBits(psm, t->m_TEX0.PSM) || (bp != t->m_TEX0.TBP0 && !GSUtil::HasCompatibleBits(psm, t->m_TEX0.PSM)))
continue;
const bool bpp_match = GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == GSLocalMemory::m_psm[psm].bpp;
const bool format_match = (bp == t->m_TEX0.TBP0 && bw == t->m_TEX0.TBW && bpp_match);
SurfaceOffsetKey sok;
sok.elems[0].bp = bp;
sok.elems[0].bw = bw;
sok.elems[0].psm = psm;
sok.elems[0].rect = r;
sok.elems[1].bp = t->m_TEX0.TBP0;
sok.elems[1].bw = t->m_TEX0.TBW;
sok.elems[1].psm = t->m_TEX0.PSM;
sok.elems[1].rect = t->m_valid;
// Calculate the rect offset if the BP doesn't match.
const GSVector4i targetr = GSVector4i((format_match) ? r.rintersect(t->m_valid) : ComputeSurfaceOffset(sok).b2a_offset).rintersect(t->m_drawn_since_read);
if (!targetr.rempty())
{
// GH Note: Read will do a StretchRect and then will sizzle data to the GS memory
// t->m_valid will do the full target texture whereas r.intersect(t->m_valid) will be limited
// to the useful part for the transfer.
@@ -1462,21 +1599,12 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
// note: r.rintersect breaks Wizardry and Chaos Legion
// Read(t, t->m_valid) works in all tested games but is very slow in GUST titles ><
// Update: 18/02/2023: Chaos legion breaks because it reads the width at half of the real width.
// Surface offset deals with this.
// If the game has been spamming downloads, we've already read the whole texture back at this point.
if (t->m_drawn_since_read.rempty())
if (t->m_drawn_since_read.rempty() || !t->m_dirty.empty())
continue;
// propagate the format from the result of a channel effect
// texture is 16/8 bit but the real data is 32
// common use for shuffling is moving data into the alpha channel
// the game can then draw using 8H format
// in the case of silent hill blit 8H -> 8P
// this will matter later when the data ends up in GS memory in the wrong format
// Be careful to avoid 24 bit textures which are technically 32bit, as you could lose alpha (8H) data.
if (t->m_32_bits_fmt && t->m_TEX0.PSM > PSM_PSMCT24)
t->m_TEX0.PSM = PSM_PSMCT32;
if (GSConfig.HWDownloadMode != GSHardwareDownloadMode::Enabled)
{
const GSVector4i rb_rc((!GSConfig.UserHacks_DisablePartialInvalidation && targetr.x == 0 && targetr.y == 0) ? t->m_valid : targetr.rintersect(t->m_valid));
@@ -1493,24 +1621,31 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
}
else if(!targetr.rintersect(t->m_drawn_since_read).rempty()) // Block level read?
{
// Read full width of drawn area, it's not much slower and makes invalidation easier.
GSVector4i full_lines = GSVector4i(0, targetr.y, t->m_valid.z, targetr.w);
full_lines = full_lines.rintersect(t->m_drawn_since_read);
Read(t, full_lines);
// Read the width of the draw, reading too much could wipe out dirty memory.
Read(t, targetr);
// After reading, try to cut down our "dirty" rect.
if (full_lines.rintersect(t->m_drawn_since_read).eq(t->m_drawn_since_read))
if (targetr.rintersect(t->m_drawn_since_read).eq(t->m_drawn_since_read))
t->m_drawn_since_read = GSVector4i::zero();
else
{
// Try to cut down how much we read next, if we can.
if (full_lines.w >= t->m_drawn_since_read.y)
// Fatal Frame reads in vertical strips, SOCOM 2 does horizontal, so we can handle that below.
if (targetr.width() == t->m_drawn_since_read.width()
&& targetr.w >= t->m_drawn_since_read.y)
{
if (full_lines.y <= t->m_drawn_since_read.y)
t->m_drawn_since_read.y = full_lines.w;
else if (full_lines.w >= t->m_drawn_since_read.w)
t->m_drawn_since_read.w = full_lines.y;
if (targetr.y <= t->m_drawn_since_read.y)
t->m_drawn_since_read.y = targetr.w;
else if (targetr.w >= t->m_drawn_since_read.w)
t->m_drawn_since_read.w = targetr.y;
}
else if (targetr.height() == t->m_drawn_since_read.height()
&& targetr.z >= t->m_drawn_since_read.x)
{
if (targetr.x <= t->m_drawn_since_read.x)
t->m_drawn_since_read.x = targetr.z;
else if (targetr.z >= t->m_drawn_since_read.z)
t->m_drawn_since_read.z = targetr.x;
}
}
t->readbacks_since_draw++;
@@ -1635,7 +1770,11 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
const GSVector2 scale(src->m_texture->GetScale());
dst = LookupTarget(new_TEX0, GSVector2i(static_cast<int>(w * scale.x), static_cast<int>(real_height * scale.y)), src->m_type, true);
if (dst)
{
dst->m_texture->SetScale(scale);
dst->UpdateValidity(GSVector4i(dx, dy, dx + w, dy + h));
dst->m_valid_bits = src->m_valid_bits;
}
}
if (!src || !dst || src->m_texture->GetScale() != dst->m_texture->GetScale())
@@ -1687,7 +1826,7 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
// Make sure the copy doesn't go out of bounds (it shouldn't).
if ((scaled_dx + scaled_w) > dst->m_texture->GetWidth() || (scaled_dy + scaled_h) > dst->m_texture->GetHeight())
return false;
GL_CACHE("HW Move 0x%x to 0x%x <%d,%d->%d,%d> -> <%d,%d->%d,%d>", SBP, DBP, sx, sy, sx + w, sy, h, dx, dy, dx + w, dy + h);
g_gs_device->CopyRect(src->m_texture, dst->m_texture,
GSVector4i(scaled_sx, scaled_sy, scaled_sx + scaled_w, scaled_sy + scaled_h),
scaled_dx, scaled_dy);
@@ -2662,6 +2801,16 @@ void GSTextureCache::Read(Target* t, const GSVector4i& r)
return;
}
// Don't overwrite bits which aren't used in the target's format.
// Stops Burnout 3's sky from breaking when flushing targets to local memory.
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[TEX0.PSM];
const u32 write_mask = t->m_valid_bits & psm.fmsk;
if (psm.bpp > 16 && write_mask == 0)
{
Console.Warning("Not reading back target %x PSM %s due to no write mask", TEX0.TBP0, psm_str(TEX0.PSM));
return;
}
// Yes lots of logging, but I'm not confident with this code
GL_PUSH("Texture Cache Read. Format(0x%x)", TEX0.PSM);
@@ -2707,11 +2856,9 @@ void GSTextureCache::Read(Target* t, const GSVector4i& r)
{
case PSM_PSMCT32:
case PSM_PSMZ32:
g_gs_renderer->m_mem.WritePixel32(bits, pitch, off, r);
break;
case PSM_PSMCT24:
case PSM_PSMZ24:
g_gs_renderer->m_mem.WritePixel24(bits, pitch, off, r);
g_gs_renderer->m_mem.WritePixel32(bits, pitch, off, r, write_mask);
break;
case PSM_PSMCT16:
case PSM_PSMCT16S:
@@ -2730,6 +2877,9 @@ void GSTextureCache::Read(Target* t, const GSVector4i& r)
void GSTextureCache::Read(Source* t, const GSVector4i& r)
{
if (r.rempty())
return;
const GSVector4i drc(0, 0, r.width(), r.height());
if (!PrepareDownloadTexture(drc.z, drc.w, GSTexture::Format::Color, &m_color_download_texture))
@@ -2777,8 +2927,13 @@ bool GSTextureCache::Surface::Inside(u32 bp, u32 bw, u32 psm, const GSVector4i&
bool GSTextureCache::Surface::Overlaps(u32 bp, u32 bw, u32 psm, const GSVector4i& rect)
{
// Valid only for color formats.
const u32 end_block = GSLocalMemory::m_psm[psm].info.bn(rect.z - 1, rect.w - 1, bp, bw);
const u32 start_block = GSLocalMemory::m_psm[psm].info.bn(rect.x, rect.y, bp, bw);
u32 end_block = GSLocalMemory::m_psm[psm].info.bn(rect.z - 1, rect.w - 1, bp, bw);
u32 start_block = GSLocalMemory::m_psm[psm].info.bn(rect.x, rect.y, bp, bw);
// Due to block ordering, end can be below start in a page, so if it's within a page, swap them.
if (end_block < start_block && ((start_block - end_block) < (1 << 5)))
{
std::swap(start_block, end_block);
}
const bool overlap = GSTextureCache::CheckOverlap(m_TEX0.TBP0, m_end_block, start_block, end_block);
return overlap;
}
@@ -3086,9 +3241,9 @@ bool GSTextureCache::Source::ClutMatch(const PaletteKey& palette_key)
GSTextureCache::Target::Target(const GIFRegTEX0& TEX0, const bool depth_supported, const int type)
: m_type(type)
, m_depth_supported(depth_supported)
, m_used(false)
, m_valid(GSVector4i::zero())
, m_depth_supported(depth_supported)
{
m_TEX0 = TEX0;
m_32_bits_fmt |= (GSLocalMemory::m_psm[TEX0.PSM].trbpp != 16);
@@ -3182,19 +3337,25 @@ void GSTextureCache::Target::Update(bool reset_age)
for (size_t i = 0; i < m_dirty.size(); i++)
{
const GSVector4i r(m_dirty.GetDirtyRect(i, m_TEX0, total_rect));
if (r.rempty())
continue;
const GSVector4 sRect(GSVector4(r - t_offset) / t_sizef);
const GSVector4 dRect(GSVector4(r) * GSVector4(m_texture->GetScale()).xyxy());
// Copy the new GS memory content into the destination texture.
if (m_type == RenderTarget)
{
GL_INS("ERROR: Update RenderTarget 0x%x bw:%d (%d,%d => %d,%d)", m_TEX0.TBP0, m_TEX0.TBW, r.x, r.y, r.z, r.w);
// Bilinear filtering this is probably not a good thing, at least in native, but upscaling Nearest can be gross and messy.
g_gs_device->StretchRect(t, sRect, m_texture, dRect, ShaderConvert::COPY, g_gs_renderer->CanUpscale());
if (m_dirty[i].rgba._u32 != 0xf)
{
g_gs_device->StretchRect(t, sRect, m_texture, dRect, m_dirty[i].rgba.c.r, m_dirty[i].rgba.c.g, m_dirty[i].rgba.c.b, m_dirty[i].rgba.c.a);
}
else
{
// Bilinear filtering this is probably not a good thing, at least in native, but upscaling Nearest can be gross and messy.
g_gs_device->StretchRect(t, sRect, m_texture, dRect, ShaderConvert::COPY, g_gs_renderer->CanUpscale());
}
}
else if (m_type == DepthStencil)
{
@@ -3228,14 +3389,18 @@ void GSTextureCache::Target::UpdateIfDirtyIntersects(const GSVector4i& rc)
}
}
void GSTextureCache::Target::UpdateValidity(const GSVector4i& rect)
void GSTextureCache::Target::ResizeValidity(const GSVector4i& rect)
{
if (m_valid.eq(GSVector4i::zero()))
m_valid = rect;
if (!m_valid.eq(GSVector4i::zero()))
{
m_valid = m_valid.rintersect(rect);
m_drawn_since_read = m_drawn_since_read.rintersect(rect);
}
else
m_valid = m_valid.runion(rect);
m_drawn_since_read = m_drawn_since_read.runion(rect);
{
// No valid size, so need to resize down.
return;
}
// Block of the bottom right texel of the validity rectangle, last valid block of the texture
// TODO: This is not correct when the PSM changes. e.g. a 512x448 target being shuffled will become 512x896 temporarily, and
// at the moment, we blow the valid rect out to twice the size. The only thing stopping everything breaking is the fact
@@ -3245,6 +3410,40 @@ void GSTextureCache::Target::UpdateValidity(const GSVector4i& rect)
// GL_CACHE("UpdateValidity (0x%x->0x%x) from R:%d,%d Valid: %d,%d", m_TEX0.TBP0, m_end_block, rect.z, rect.w, m_valid.z, m_valid.w);
}
void GSTextureCache::Target::UpdateValidity(const GSVector4i& rect, bool can_resize)
{
if (m_valid.runion(rect).eq(m_valid))
{
m_drawn_since_read = m_drawn_since_read.runion(rect);
return;
}
if (can_resize)
{
if (m_valid.eq(GSVector4i::zero()))
m_valid = rect;
else
m_valid = m_valid.runion(rect);
}
if (m_drawn_since_read.eq(GSVector4i::zero()) || !can_resize)
{
m_drawn_since_read = rect.rintersect(m_valid);
}
else
m_drawn_since_read = m_drawn_since_read.runion(rect);
// Block of the bottom right texel of the validity rectangle, last valid block of the texture
// TODO: This is not correct when the PSM changes. e.g. a 512x448 target being shuffled will become 512x896 temporarily, and
// at the moment, we blow the valid rect out to twice the size. The only thing stopping everything breaking is the fact
// that we clamp the draw rect to the target size in GSRendererHW::Draw().
m_end_block = GSLocalMemory::m_psm[m_TEX0.PSM].info.bn(m_valid.z - 1, m_valid.w - 1, m_TEX0.TBP0, m_TEX0.TBW); // Valid only for color formats
// GL_CACHE("UpdateValidity (0x%x->0x%x) from R:%d,%d Valid: %d,%d", m_TEX0.TBP0, m_end_block, rect.z, rect.w, m_valid.z, m_valid.w);
}
void GSTextureCache::Target::UpdateValidBits(u32 bits_written)
{
m_valid_bits |= bits_written;
}
bool GSTextureCache::Target::ResizeTexture(int new_width, int new_height, bool recycle_old)
{
@@ -3261,6 +3460,7 @@ bool GSTextureCache::Target::ResizeTexture(int new_width, int new_height, GSVect
// These exceptions *really* need to get lost. This gets called outside of draws, which just crashes
// when it tries to propogate the exception back.
const bool clear = (new_width > width || new_height > height);
GSTexture* tex = nullptr;
try
{
@@ -3302,6 +3502,7 @@ bool GSTextureCache::Target::ResizeTexture(int new_width, int new_height, GSVect
delete m_texture;
m_texture = tex;
return true;
}
+18 -6
View File
@@ -235,20 +235,30 @@ public:
{
public:
const int m_type = 0;
const bool m_depth_supported = false;
bool m_dirty_alpha = true;
bool m_is_frame = false;
bool m_used = false;
GSDirtyRectList m_dirty;
GSVector4i m_valid{};
GSVector4i m_drawn_since_read{};
const bool m_depth_supported = false;
bool m_dirty_alpha = true;
bool m_is_frame = false;
u32 m_valid_bits = 0;
int readbacks_since_draw = 0;
public:
Target(const GIFRegTEX0& TEX0, const bool depth_supported, const int type);
~Target();
void UpdateValidity(const GSVector4i& rect);
/// Returns true if the target wraps around the end of GS memory.
bool Wraps() const { return (m_end_block < m_TEX0.TBP0); }
/// Returns the end block for the target, but doesn't wrap at 0x3FFF.
/// Can be used for overlap tests.
u32 UnwrappedEndBlock() const { return (m_end_block + (Wraps() ? MAX_BLOCKS : 0)); }
void ResizeValidity(const GSVector4i& rect);
void UpdateValidity(const GSVector4i& rect, bool can_resize = true);
void UpdateValidBits(u32 bits_written);
void Update(bool reset_age);
@@ -397,14 +407,16 @@ public:
void Read(Target* t, const GSVector4i& r);
void Read(Source* t, const GSVector4i& r);
void RemoveAll();
void AddDirtyRectTarget(Target* target, GSVector4i rect, u32 psm, u32 bw);
void ReadbackAll();
void AddDirtyRectTarget(Target* target, GSVector4i rect, u32 psm, u32 bw, RGBAMask rgba);
GSTexture* LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, const GSVector2i& size);
Source* LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const GSVector2i* lod);
Source* LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, bool palette = false);
Target* LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask = 0, const bool is_frame = false, const int real_w = 0, const int real_h = 0, bool preload = GSConfig.PreloadFrameWithGSData);
Target* FindTargetOverlap(u32 bp, u32 end_block, int type, int psm);
Target* LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask = 0, const bool is_frame = false, const int real_w = 0, const int real_h = 0, bool preload = GSConfig.PreloadFrameWithGSData, bool is_clear = false);
Target* LookupDisplayTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, const int real_w, const int real_h);
/// Looks up a target in the cache, and only returns it if the BP/BW/PSM match exactly.
+1
View File
@@ -1379,6 +1379,7 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
setFnConstantB(m_fn_constants, pssel.ltf, GSMTLConstantIndex_PS_LTF);
setFnConstantB(m_fn_constants, pssel.shuffle, GSMTLConstantIndex_PS_SHUFFLE);
setFnConstantB(m_fn_constants, pssel.read_ba, GSMTLConstantIndex_PS_READ_BA);
setFnConstantB(m_fn_constants, pssel.real16src, GSMTLConstantIndex_PS_READ16_SRC);
setFnConstantB(m_fn_constants, pssel.write_rg, GSMTLConstantIndex_PS_WRITE_RG);
setFnConstantB(m_fn_constants, pssel.fbmask, GSMTLConstantIndex_PS_FBMASK);
setFnConstantI(m_fn_constants, pssel.blend_a, GSMTLConstantIndex_PS_BLEND_A);
@@ -175,6 +175,7 @@ enum GSMTLFnConstants
GSMTLConstantIndex_PS_LTF,
GSMTLConstantIndex_PS_SHUFFLE,
GSMTLConstantIndex_PS_READ_BA,
GSMTLConstantIndex_PS_READ16_SRC,
GSMTLConstantIndex_PS_WRITE_RG,
GSMTLConstantIndex_PS_FBMASK,
GSMTLConstantIndex_PS_BLEND_A,
+23 -10
View File
@@ -42,6 +42,7 @@ constant bool PS_ADJT [[function_constant(GSMTLConstantIndex_PS_AD
constant bool PS_LTF [[function_constant(GSMTLConstantIndex_PS_LTF)]];
constant bool PS_SHUFFLE [[function_constant(GSMTLConstantIndex_PS_SHUFFLE)]];
constant bool PS_READ_BA [[function_constant(GSMTLConstantIndex_PS_READ_BA)]];
constant bool PS_READ16_SRC [[function_constant(GSMTLConstantIndex_PS_READ16_SRC)]];
constant bool PS_WRITE_RG [[function_constant(GSMTLConstantIndex_PS_WRITE_RG)]];
constant bool PS_FBMASK [[function_constant(GSMTLConstantIndex_PS_FBMASK)]];
constant uint PS_BLEND_A [[function_constant(GSMTLConstantIndex_PS_BLEND_A)]];
@@ -824,8 +825,10 @@ struct PSMain
return selector == 0 ? zero : selector == 1 ? one : two;
}
void ps_blend(thread float4& Color, thread float& As)
void ps_blend(thread float4& Color, thread float4& As_rgba)
{
float As = As_rgba.a;
if (SW_BLEND)
{
// PABE
@@ -872,15 +875,14 @@ struct PSMain
{
// Replace Af with As so we can do proper compensation for Alpha.
if (PS_BLEND_C == 2)
As = cb.alpha_fix;
As_rgba = float4(cb.alpha_fix);
// Subtract 1 for alpha to compensate for the changed equation,
// if c.rgb > 255.0f then we further need to adjust alpha accordingly,
// we pick the lowest overflow from all colors because it's the safest,
// we divide by 255 the color because we don't know Cd value,
// changed alpha should only be done for hw blend.
float min_color = min(min(Color.r, Color.g), Color.b);
float alpha_compensate = max(1.f, min_color / 255.f);
As -= alpha_compensate;
float3 alpha_compensate = max(float3(1.f), Color.rgb / float3(255.f));
As_rgba.rgb -= alpha_compensate;
}
else if (PS_CLR_HW == 2)
{
@@ -950,11 +952,22 @@ struct PSMain
uint4 denorm_c = uint4(C);
uint2 denorm_TA = uint2(cb.ta * 255.5f);
C.rb = PS_READ_BA ? C.bb : C.rr;
if (PS_READ_BA)
C.ga = (denorm_c.a & 0x7F) | (denorm_c.a & 0x80 ? denorm_TA.y & 0x80 : denorm_TA.x & 0x80);
if (PS_READ16_SRC)
{
C.rb = (denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7u) << 5);
if (denorm_c.a & 0x80)
C.ga = (denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.y & 0x80);
else
C.ga = (denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80);
}
else
C.ga = (denorm_c.g & 0x7F) | (denorm_c.g & 0x80 ? denorm_TA.y & 0x80 : denorm_TA.x & 0x80);
{
C.rb = PS_READ_BA ? C.bb : C.rr;
if (PS_READ_BA)
C.ga = (denorm_c.a & 0x7F) | (denorm_c.a & 0x80 ? denorm_TA.y & 0x80 : denorm_TA.x & 0x80);
else
C.ga = (denorm_c.g & 0x7F) | (denorm_c.g & 0x80 ? denorm_TA.y & 0x80 : denorm_TA.x & 0x80);
}
}
// Must be done before alpha correction
@@ -965,7 +978,7 @@ struct PSMain
C.a = 128.0f;
}
float alpha_blend = SW_AD_TO_HW ? (trunc(current_color.a * 255.5f) / 128.f) : (C.a / 128.f);
float4 alpha_blend = SW_AD_TO_HW ? float4(trunc(current_color.a * 255.5f) / 128.f) : float4(C.a / 128.f);
if (PS_DFMT == FMT_16)
{
+4 -4
View File
@@ -42,8 +42,8 @@ namespace GLState
GLuint ps_ss;
GLuint rt;
GLuint ds;
GSTextureOGL* rt = nullptr;
GSTextureOGL* ds = nullptr;
GLuint tex_unit[8];
GLuint64 tex_handle[8];
@@ -72,8 +72,8 @@ namespace GLState
ps_ss = 0;
rt = 0;
ds = 0;
rt = nullptr;
ds = nullptr;
std::fill(std::begin(tex_unit), std::end(tex_unit), 0);
std::fill(std::begin(tex_handle), std::end(tex_handle), 0);
+4 -2
View File
@@ -18,6 +18,8 @@
#include "GS/Renderers/OpenGL/GLLoader.h"
#include "GS/GSVector.h"
class GSTextureOGL;
namespace GLState
{
extern GLuint fbo; // frame buffer object
@@ -44,8 +46,8 @@ namespace GLState
extern GLuint ps_ss; // sampler
extern GLuint rt; // render target
extern GLuint ds; // Depth-Stencil
extern GSTextureOGL* rt; // render target
extern GSTextureOGL* ds; // Depth-Stencil
extern GLuint tex_unit[8]; // shader input texture
extern GLuint64 tex_handle[8]; // shader input texture
+29 -15
View File
@@ -1060,6 +1060,7 @@ std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
+ fmt::format("#define PS_IIP {}\n", sel.iip)
+ fmt::format("#define PS_SHUFFLE {}\n", sel.shuffle)
+ fmt::format("#define PS_READ_BA {}\n", sel.read_ba)
+ fmt::format("#define PS_READ16_SRC {}\n", sel.real16src)
+ fmt::format("#define PS_WRITE_RG {}\n", sel.write_rg)
+ fmt::format("#define PS_FBMASK {}\n", sel.fbmask)
+ fmt::format("#define PS_HDR {}\n", sel.hdr)
@@ -1599,35 +1600,27 @@ bool GSDeviceOGL::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, con
void GSDeviceOGL::OMAttachRt(GSTextureOGL* rt)
{
GLuint id = 0;
if (rt)
{
rt->WasAttached();
id = rt->GetID();
}
if (GLState::rt != id)
if (GLState::rt != rt)
{
GLState::rt = id;
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, id, 0);
GLState::rt = rt;
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt ? rt->GetID() : 0, 0);
}
}
void GSDeviceOGL::OMAttachDs(GSTextureOGL* ds)
{
GLuint id = 0;
if (ds)
{
ds->WasAttached();
id = ds->GetID();
}
if (GLState::ds != id)
if (GLState::ds != ds)
{
GLState::ds = id;
GLState::ds = ds;
const GLenum target = m_features.framebuffer_fetch ? GL_DEPTH_ATTACHMENT : GL_DEPTH_STENCIL_ATTACHMENT;
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, target, GL_TEXTURE_2D, id, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, target, GL_TEXTURE_2D, ds ? ds->GetID() : 0, 0);
}
}
@@ -1656,6 +1649,14 @@ void GSDeviceOGL::OMSetColorMaskState(OMColorMaskSelector sel)
}
}
void GSDeviceOGL::OMUnbindTexture(GSTextureOGL* tex)
{
if (GLState::rt == tex)
OMAttachRt();
if (GLState::ds == tex)
OMAttachDs();
}
void GSDeviceOGL::OMSetBlendState(bool enable, GLenum src_factor, GLenum dst_factor, GLenum op, bool is_constant, u8 constant)
{
if (enable)
@@ -1967,8 +1968,21 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
s_gl_blend_factors[config.blend.dst_factor], s_gl_blend_ops[config.blend.op],
config.blend.constant_enable, config.blend.constant);
OMSetColorMaskState(config.colormask);
// avoid changing framebuffer just to switch from rt+depth to rt and vice versa
GSTexture* draw_rt = hdr_rt ? hdr_rt : config.rt;
GSTexture* draw_ds = config.ds;
if (!draw_ds && GLState::ds && GLState::rt == draw_rt && config.tex != GLState::ds &&
GLState::ds->GetSize() == draw_rt->GetSize())
{
// should already be always-pass.
draw_ds = GLState::ds;
config.depth.ztst = ZTST_ALWAYS;
config.depth.zwe = false;
}
OMSetRenderTargets(draw_rt, draw_ds, &config.scissor);
SetupOM(config.depth);
OMSetRenderTargets(hdr_rt ? hdr_rt : config.rt, config.ds, &config.scissor);
SendHWDraw(config, psel.ps.IsFeedbackLoop());
+1
View File
@@ -357,6 +357,7 @@ public:
void OMSetBlendState(bool enable = false, GLenum src_factor = GL_ONE, GLenum dst_factor = GL_ZERO, GLenum op = GL_FUNC_ADD, bool is_constant = false, u8 constant = 0);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = NULL);
void OMSetColorMaskState(OMColorMaskSelector sel = OMColorMaskSelector());
void OMUnbindTexture(GSTextureOGL* tex);
bool CreateTextureFX();
std::string GetShaderSource(const std::string_view& entry, GLenum type, const std::string_view& common_header, const std::string_view& glsl_h_code, const std::string_view& macro_sel);
+3 -5
View File
@@ -174,12 +174,10 @@ GSTextureOGL::GSTextureOGL(Type type, int width, int height, int levels, Format
GSTextureOGL::~GSTextureOGL()
{
/* Unbind the texture from our local state */
// Textures aren't cleared from attachments on deletion.
GSDeviceOGL::GetInstance()->OMUnbindTexture(this);
if (m_texture_id == GLState::rt)
GLState::rt = 0;
if (m_texture_id == GLState::ds)
GLState::ds = 0;
// But they are unbound.
for (GLuint& tex : GLState::tex_unit)
{
if (m_texture_id == tex)
+7 -7
View File
@@ -189,7 +189,7 @@ GSTexture* GSRendererSW::GetOutput(int i, int& y_offset)
{
if (GSConfig.SaveFrame && s_n >= GSConfig.SaveN)
{
m_texture[i]->Save(GetDrawDumpPath("%05d_f%lld_fr%d_%05x_%s.bmp", s_n, g_perfmon.GetFrame(), i, (int)curFramebuffer.Block(), psm_str(curFramebuffer.PSM)));
m_texture[index]->Save(GetDrawDumpPath("%05d_f%lld_fr%d_%05x_%s.bmp", s_n, g_perfmon.GetFrame(), i, (int)curFramebuffer.Block(), psm_str(curFramebuffer.PSM)));
}
}
}
@@ -469,18 +469,18 @@ void GSRendererSW::Draw()
{
// Dump the RT in 32 bits format. It helps to debug texture shuffle effect
s = GetDrawDumpPath("%05d_f%lld_rt0_%05x_32bits.bmp", s_n, frame, m_context->FRAME.Block());
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, 0, PCRTCDisplays.GetFramebufferRect(-1).width(), 512);
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, 0, r.width(), r.height());
}
s = GetDrawDumpPath("%05d_f%lld_rt0_%05x_%s.bmp", s_n, frame, m_context->FRAME.Block(), psm_str(m_context->FRAME.PSM));
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, PCRTCDisplays.GetFramebufferRect(-1).width(), 512);
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, r.width(), r.height());
}
if (GSConfig.SaveDepth && s_n >= GSConfig.SaveN)
{
s = GetDrawDumpPath("%05d_f%lld_rz0_%05x_%s.bmp", s_n, frame, m_context->ZBUF.Block(), psm_str(m_context->ZBUF.PSM));
m_mem.SaveBMP(s, m_context->ZBUF.Block(), m_context->FRAME.FBW, m_context->ZBUF.PSM, PCRTCDisplays.GetFramebufferRect(-1).width(), 512);
m_mem.SaveBMP(s, m_context->ZBUF.Block(), m_context->FRAME.FBW, m_context->ZBUF.PSM, r.width(), r.height());
}
Queue(data);
@@ -493,18 +493,18 @@ void GSRendererSW::Draw()
{
// Dump the RT in 32 bits format. It helps to debug texture shuffle effect
s = GetDrawDumpPath("%05d_f%lld_rt1_%05x_32bits.bmp", s_n, frame, m_context->FRAME.Block());
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, 0, PCRTCDisplays.GetFramebufferRect(-1).width(), 512);
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, 0, r.width(), r.height());
}
s = GetDrawDumpPath("%05d_f%lld_rt1_%05x_%s.bmp", s_n, frame, m_context->FRAME.Block(), psm_str(m_context->FRAME.PSM));
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, PCRTCDisplays.GetFramebufferRect(-1).width(), 512);
m_mem.SaveBMP(s, m_context->FRAME.Block(), m_context->FRAME.FBW, m_context->FRAME.PSM, r.width(), r.height());
}
if (GSConfig.SaveDepth && s_n >= GSConfig.SaveN)
{
s = GetDrawDumpPath("%05d_f%lld_rz1_%05x_%s.bmp", s_n, frame, m_context->ZBUF.Block(), psm_str(m_context->ZBUF.PSM));
m_mem.SaveBMP(s, m_context->ZBUF.Block(), m_context->FRAME.FBW, m_context->ZBUF.PSM, PCRTCDisplays.GetFramebufferRect(-1).width(), 512);
m_mem.SaveBMP(s, m_context->ZBUF.Block(), m_context->FRAME.FBW, m_context->ZBUF.PSM, r.width(), r.height());
}
if (GSConfig.SaveL > 0 && (s_n - GSConfig.SaveN) > GSConfig.SaveL)
+3 -1
View File
@@ -1983,6 +1983,7 @@ VkShaderModule GSDeviceVK::GetTFXFragmentShader(const GSHWDrawConfig::PSSelector
AddMacro(ss, "PS_IIP", sel.iip);
AddMacro(ss, "PS_SHUFFLE", sel.shuffle);
AddMacro(ss, "PS_READ_BA", sel.read_ba);
AddMacro(ss, "PS_READ16_SRC", sel.real16src);
AddMacro(ss, "PS_WRITE_RG", sel.write_rg);
AddMacro(ss, "PS_FBMASK", sel.fbmask);
AddMacro(ss, "PS_HDR", sel.hdr);
@@ -3033,7 +3034,8 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
{
// avoid restarting the render pass just to switch from rt+depth to rt and vice versa
if (!draw_ds && m_current_depth_target && m_current_render_target == draw_rt &&
config.tex != m_current_depth_target && !(pipe.feedback_loop && !CurrentFramebufferHasFeedbackLoop()))
config.tex != m_current_depth_target && m_current_depth_target->GetSize() == draw_rt->GetSize() &&
!(pipe.feedback_loop && !CurrentFramebufferHasFeedbackLoop()))
{
draw_ds = m_current_depth_target;
m_pipeline_selector.ds = true;
+16
View File
@@ -342,10 +342,12 @@ void GameDatabase::parseAndInsert(const std::string_view& serial, const c4::yml:
static const char* s_gs_hw_fix_names[] = {
"autoFlush",
"cpuFramebufferConversion",
"readTCOnClose",
"disableDepthSupport",
"wrapGSMem",
"preloadFrameData",
"disablePartialInvalidation",
"partialTargetInvalidation",
"textureInsideRT",
"alignSprite",
"mergeSprite",
@@ -548,6 +550,9 @@ bool GameDatabaseSchema::GameEntry::configMatchesHWFix(const Pcsx2Config::GSOpti
case GSHWFixId::CPUFramebufferConversion:
return (static_cast<int>(config.UserHacks_CPUFBConversion) == value);
case GSHWFixId::FlushTCOnClose:
return (static_cast<int>(config.UserHacks_ReadTCOnClose) == value);
case GSHWFixId::DisableDepthSupport:
return (static_cast<int>(config.UserHacks_DisableDepthSupport) == value);
@@ -560,6 +565,9 @@ bool GameDatabaseSchema::GameEntry::configMatchesHWFix(const Pcsx2Config::GSOpti
case GSHWFixId::DisablePartialInvalidation:
return (static_cast<int>(config.UserHacks_DisablePartialInvalidation) == value);
case GSHWFixId::TargetPartialInvalidation:
return (static_cast<int>(config.UserHacks_TargetPartialInvalidation) == value);
case GSHWFixId::TextureInsideRT:
return (static_cast<int>(config.UserHacks_TextureInsideRt) == value);
@@ -654,6 +662,10 @@ u32 GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions&
config.UserHacks_CPUFBConversion = (value > 0);
break;
case GSHWFixId::FlushTCOnClose:
config.UserHacks_ReadTCOnClose = (value > 0);
break;
case GSHWFixId::DisableDepthSupport:
config.UserHacks_DisableDepthSupport = (value > 0);
break;
@@ -670,6 +682,10 @@ u32 GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions&
config.UserHacks_DisablePartialInvalidation = (value > 0);
break;
case GSHWFixId::TargetPartialInvalidation:
config.UserHacks_TargetPartialInvalidation = (value > 0);
break;
case GSHWFixId::TextureInsideRT:
config.UserHacks_TextureInsideRt = (value > 0);
break;
+2
View File
@@ -62,10 +62,12 @@ namespace GameDatabaseSchema
// boolean settings
AutoFlush,
CPUFramebufferConversion,
FlushTCOnClose,
DisableDepthSupport,
WrapGSMem,
PreloadFrameData,
DisablePartialInvalidation,
TargetPartialInvalidation,
TextureInsideRT,
AlignSprite,
MergeSprite,
+11 -3
View File
@@ -89,6 +89,7 @@ static void SaveYAMLToFile(const char* filename, const ryml::NodeRef& node)
}
static constexpr time_t MEMORY_CARD_FILE_ENTRY_DATE_TIME_OFFSET = 60 * 60 * 9; // 9 hours from UTC
static auto last = std::chrono::time_point<std::chrono::system_clock>();
MemoryCardFileEntryDateTime MemoryCardFileEntryDateTime::FromTime(time_t time)
{
@@ -2353,9 +2354,16 @@ s32 FolderMemoryCardAggregator::Save(uint slot, const u8* src, u32 adr, int size
const s32 saveResult = m_cards[slot].Save(src, adr, size);
if (saveResult)
{
const std::string_view filename = Path::GetFileName(m_cards[slot].GetFolderName());
Host::AddIconOSDMessage(fmt::format("MemoryCardSave{}", slot), ICON_FA_SD_CARD,
fmt::format("Memory card '{}' was saved to storage.", filename), Host::OSD_INFO_DURATION);
std::chrono::duration<float> elapsed = std::chrono::system_clock::now() - last;
if (elapsed > std::chrono::seconds(5))
{
const std::string_view filename = Path::GetFileName(m_cards[slot].GetFolderName());
Host::AddIconOSDMessage(fmt::format("MemoryCardSave{}", slot), ICON_FA_SD_CARD,
fmt::format("Memory card '{}' was saved to storage.", filename), Host::OSD_INFO_DURATION);
last = std::chrono::system_clock::now();
}
}
return saveResult;
+5
View File
@@ -429,6 +429,7 @@ Pcsx2Config::GSOptions::GSOptions()
UserHacks_AlignSpriteX = false;
UserHacks_AutoFlush = false;
UserHacks_CPUFBConversion = false;
UserHacks_ReadTCOnClose = false;
UserHacks_DisableDepthSupport = false;
UserHacks_DisablePartialInvalidation = false;
UserHacks_DisableSafeFeatures = false;
@@ -640,12 +641,14 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
GSSettingBoolEx(UserHacks_AlignSpriteX, "UserHacks_align_sprite_X");
GSSettingBoolEx(UserHacks_AutoFlush, "UserHacks_AutoFlush");
GSSettingBoolEx(UserHacks_CPUFBConversion, "UserHacks_CPU_FB_Conversion");
GSSettingBoolEx(UserHacks_ReadTCOnClose, "UserHacks_ReadTCOnClose");
GSSettingBoolEx(UserHacks_DisableDepthSupport, "UserHacks_DisableDepthSupport");
GSSettingBoolEx(UserHacks_DisablePartialInvalidation, "UserHacks_DisablePartialInvalidation");
GSSettingBoolEx(UserHacks_DisableSafeFeatures, "UserHacks_Disable_Safe_Features");
GSSettingBoolEx(UserHacks_MergePPSprite, "UserHacks_merge_pp_sprite");
GSSettingBoolEx(UserHacks_WildHack, "UserHacks_WildHack");
GSSettingBoolEx(UserHacks_TextureInsideRt, "UserHacks_TextureInsideRt");
GSSettingBoolEx(UserHacks_TargetPartialInvalidation, "UserHacks_TargetPartialInvalidation");
GSSettingBoolEx(FXAA, "fxaa");
GSSettingBool(ShadeBoost);
GSSettingBoolEx(DumpGSData, "dump");
@@ -770,7 +773,9 @@ void Pcsx2Config::GSOptions::MaskUserHacks()
UserHacks_DisablePartialInvalidation = false;
UserHacks_DisableDepthSupport = false;
UserHacks_CPUFBConversion = false;
UserHacks_ReadTCOnClose = false;
UserHacks_TextureInsideRt = false;
UserHacks_TargetPartialInvalidation = false;
UserHacks_TCOffsetX = 0;
UserHacks_TCOffsetY = 0;
UserHacks_CPUSpriteRenderBW = 0;
+1 -1
View File
@@ -15,4 +15,4 @@
/// Version number for GS and other shaders. Increment whenever any of the contents of the
/// shaders change, to invalidate the cache.
static constexpr u32 SHADER_CACHE_VERSION = 12;
static constexpr u32 SHADER_CACHE_VERSION = 14;
+15 -12
View File
@@ -27,6 +27,7 @@
#include "common/Perf.h"
#include "common/StringUtil.h"
#include "CDVD/CDVD.h"
#include "ps2/BiosTools.h"
#include "GS/Renderers/Common/GSFunctionMap.h"
#include "common/emitter/x86_intrin.h"
@@ -123,8 +124,10 @@ void SysLogMachineCaps()
std::string features;
if (x86caps.hasAVX) features += "AVX ";
if (x86caps.hasAVX2) features += "AVX2 ";
if (x86caps.hasAVX)
features += "AVX ";
if (x86caps.hasAVX2)
features += "AVX2 ";
StringUtil::StripWhitespace(&features);
@@ -340,18 +343,18 @@ void SysClearExecutionCache()
}
}
// This function returns part of EXTINFO data of the BIOS rom
// This module contains information about Sony build environment at offst 0x10
// first 15 symbols is build date/time that is unique per rom and can be used as unique serial
// Example for romver 0160EC20010704
// 20010704-160707,ROMconf,PS20160EC20010704.bin,kuma@rom-server/~/f10k/g/app/rom
// 20010704-160707 can be used as unique ID for Bios
std::string SysGetBiosDiscID()
{
// FIXME: we should return a serial based on
// the BIOS being run (either a checksum of the BIOS roms, and/or a string based on BIOS
// region and revision).
// Good candidate can be first part of EXTINFO data in the BIOS rom:
// Example for romver 0160EC20010704
// 20010704-160707,ROMconf,PS20160EC20010704.bin,kuma@rom-server/~/f10k/g/app/rom
// 20010704-160707 can be used as unique ID for Bios
// rom0:EXTINFO first 15 bytes
return {};
if (!BiosSerial.empty())
return BiosSerial;
else
return {};
}
// This function always returns a valid DiscID -- using the Sony serial when possible, and
+2 -2
View File
@@ -513,9 +513,9 @@ namespace usb_lightgun
{SettingInfo::Type::Float, "scale_y", "Y Scale (Sensitivity)",
"Scales the position to simulate CRT curvature.", "100", "0", "200", "0.1", "%.2f%%", nullptr, nullptr,
1.0f},
{SettingInfo::Type::Float, "center_x", "Center X", "Sets the center position of the simulated screen.",
{SettingInfo::Type::Float, "center_x", "Center X", "Sets the horizontal center position of the simulated screen.",
"320", "0", "1024", "1", "%.0fpx", nullptr, nullptr, 1.0f},
{SettingInfo::Type::Float, "center_y", "Center Y", "Sets the center position of the simulated screen.",
{SettingInfo::Type::Float, "center_y", "Center Y", "Sets the vertical center position of the simulated screen.",
"120", "0", "1024", "1", "%.0fpx", nullptr, nullptr, 1.0f},
{SettingInfo::Type::Integer, "screen_width", "Screen Width", "Sets the width of the simulated screen.",
"640", "1", "1024", "1", "%dpx", nullptr, nullptr, 1.0f},
+10 -6
View File
@@ -366,11 +366,15 @@ namespace usb_pad
{"StickUp", "Stick Up", InputBindingInfo::Type::HalfAxis, CID_THROTTLE, GenericInputBinding::LeftStickUp},
{"StickDown", "Stick Down", InputBindingInfo::Type::HalfAxis, CID_BRAKE, GenericInputBinding::LeftStickDown},
{"A", "A", InputBindingInfo::Type::Button, CID_BUTTON0, GenericInputBinding::Cross},
{"B", "B", InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::Circle},
{"X", "X", InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Square},
{"Y", "Y", InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Triangle},
{"Z", "Z", InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::L1},
{"C", "C", InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::R1},
{"B", "B", InputBindingInfo::Type::Button, CID_BUTTON1, GenericInputBinding::Circle},
{"C", "C", InputBindingInfo::Type::Button, CID_BUTTON2, GenericInputBinding::R2},
{"X", "X", InputBindingInfo::Type::Button, CID_BUTTON3, GenericInputBinding::Square},
{"Y", "Y", InputBindingInfo::Type::Button, CID_BUTTON4, GenericInputBinding::Triangle},
{"Z", "Z", InputBindingInfo::Type::Button, CID_BUTTON5, GenericInputBinding::L2},
{"L", "L", InputBindingInfo::Type::Button, CID_BUTTON6, GenericInputBinding::L1},
{"R", "R", InputBindingInfo::Type::Button, CID_BUTTON7, GenericInputBinding::R1},
{"Select", "Select", InputBindingInfo::Type::Button, CID_BUTTON8, GenericInputBinding::Select},
{"Start", "Start", InputBindingInfo::Type::Button, CID_BUTTON9, GenericInputBinding::Start},
{"DPadUp", "D-Pad Up", InputBindingInfo::Type::Button, CID_DPAD_UP, GenericInputBinding::DPadUp},
{"DPadDown", "D-Pad Down", InputBindingInfo::Type::Button, CID_DPAD_DOWN, GenericInputBinding::DPadDown},
{"DPadLeft", "D-Pad Left", InputBindingInfo::Type::Button, CID_DPAD_LEFT, GenericInputBinding::DPadLeft},
@@ -436,7 +440,7 @@ namespace usb_pad
bool SeamicDevice::Freeze(USBDevice* dev, StateWrapper& sw) const
{
pxFailRel("Not implemented!");
Console.Warning("Not implemented!");
return true;
// SeamicState *s = (SeamicState *)dev;
// switch (mode)
+70 -56
View File
@@ -54,11 +54,11 @@ bool NoOSD;
bool AllowParams1;
bool AllowParams2;
std::string BiosDescription;
std::string BiosZone;
std::string BiosSerial;
std::string BiosPath;
BiosDebugInformation CurrentBiosInformation;
static bool LoadBiosVersion(std::FILE* fp, u32& version, std::string& description, u32& region, std::string& zone)
static bool LoadBiosVersion(std::FILE* fp, u32& version, std::string& description, u32& region, std::string& zone, std::string& serial)
{
romdir rd;
for (u32 i = 0; i < 512 * 1024; i++)
@@ -73,13 +73,25 @@ static bool LoadBiosVersion(std::FILE* fp, u32& version, std::string& descriptio
s64 fileOffset = 0;
s64 fileSize = FileSystem::FSize64(fp);
bool foundRomVer = false;
char romver[14 + 1] = {}; // ascii version loaded from disk.
char extinfo[15 + 1] = {}; // ascii version loaded from disk.
// ensure it's a null-terminated and not zero-length string
while (rd.fileName[0] != '\0' && strnlen(rd.fileName, sizeof(rd.fileName)) != sizeof(rd.fileName))
{
if (std::strncmp(rd.fileName, "EXTINFO", sizeof(rd.fileName)) == 0)
{
s64 pos = FileSystem::FTell64(fp);
if (FileSystem::FSeek64(fp, fileOffset + 0x10, SEEK_SET) != 0 ||
std::fread(extinfo, 15, 1, fp) != 1 || FileSystem::FSeek64(fp, pos, SEEK_SET) != 0)
{
break;
}
serial = extinfo;
}
if (std::strncmp(rd.fileName, "ROMVER", sizeof(rd.fileName)) == 0)
{
char romver[14 + 1] = {}; // ascii version loaded from disk.
s64 pos = FileSystem::FTell64(fp);
if (FileSystem::FSeek64(fp, fileOffset, SEEK_SET) != 0 ||
@@ -88,56 +100,7 @@ static bool LoadBiosVersion(std::FILE* fp, u32& version, std::string& descriptio
break;
}
// TODO: some regions can be detected only from rom1
/*
switch (rom1:DVDID[4])
{
// clang-format off
case 'O': zone = "Oceania";region = 3; break;
case 'R': zone = "Russia"; region = 5; break;
case 'M': zone = "Mexico"; region = 7; break;
// clang-format on
}
*/
switch (romver[4])
{
// clang-format off
case 'J': zone = "Japan"; region = 0; break;
case 'A': zone = "USA"; region = 1; break;
case 'E': zone = "Europe"; region = 2; break;
// case 'E': zone = "Oceania";region = 3; break; // Not implemented
case 'H': zone = "Asia"; region = 4; break;
// case 'E': zone = "Russia"; region = 3; break; // Not implemented
case 'C': zone = "China"; region = 6; break;
// case 'A': zone = "Mexico"; region = 7; break; // Not implemented
case 'T': zone = "T10K"; region = 8; break;
case 'X': zone = "Test"; region = 9; break;
case 'P': zone = "Free"; region = 10; break;
// clang-format on
default:
zone.clear();
zone += romver[4];
region = 0;
break;
}
char vermaj[3] = {romver[0], romver[1], 0};
char vermin[3] = {romver[2], romver[3], 0};
description = StringUtil::StdStringFromFormat("%-7s v%s.%s(%c%c/%c%c/%c%c%c%c) %s",
zone.c_str(),
vermaj, vermin,
romver[12], romver[13], // day
romver[10], romver[11], // month
romver[6], romver[7], romver[8], romver[9], // year!
(romver[5] == 'C') ? "Console" : (romver[5] == 'D') ? "Devel" : "");
version = strtol(vermaj, (char**)NULL, 0) << 8;
version |= strtol(vermin, (char**)NULL, 0);
foundRomVer = true;
Console.WriteLn("Bios Found: %s", description.c_str());
}
if ((rd.fileSize % 0x10) == 0)
@@ -151,7 +114,57 @@ static bool LoadBiosVersion(std::FILE* fp, u32& version, std::string& descriptio
fileOffset -= ((rd.fileSize + 0x10) & 0xfffffff0) - rd.fileSize;
if (!foundRomVer)
if (foundRomVer)
{
switch (romver[4])
{
// clang-format off
case 'J': zone = "Japan"; region = 0; break;
case 'A': zone = "USA"; region = 1; break;
case 'E': zone = "Europe"; region = 2; break;
// case 'E': zone = "Oceania";region = 3; break; // Not implemented
case 'H': zone = "Asia"; region = 4; break;
// case 'E': zone = "Russia"; region = 3; break; // Not implemented
case 'C': zone = "China"; region = 6; break;
// case 'A': zone = "Mexico"; region = 7; break; // Not implemented
case 'T': zone = "T10K"; region = 8; break;
case 'X': zone = "Test"; region = 9; break;
case 'P': zone = "Free"; region = 10; break;
// clang-format on
default:
zone.clear();
zone += romver[4];
region = 0;
break;
}
// TODO: some regions can be detected only from rom1
/* switch (rom1:DVDID[4])
{
// clang-format off
case 'O': zone = "Oceania";region = 3; break;
case 'R': zone = "Russia"; region = 5; break;
case 'M': zone = "Mexico"; region = 7; break;
// clang-format on
} */
char vermaj[3] = {romver[0], romver[1], 0};
char vermin[3] = {romver[2], romver[3], 0};
description = StringUtil::StdStringFromFormat("%-7s v%s.%s(%c%c/%c%c/%c%c%c%c) %s %s",
zone.c_str(),
vermaj, vermin,
romver[12], romver[13], // day
romver[10], romver[11], // month
romver[6], romver[7], romver[8], romver[9], // year!
(romver[5] == 'C') ? "Console" : (romver[5] == 'D') ? "Devel" :
"",
serial.c_str());
version = strtol(vermaj, (char**)NULL, 0) << 8;
version |= strtol(vermin, (char**)NULL, 0);
Console.WriteLn("Bios Found: %s", description.c_str());
}
else
return false;
if (fileSize < (int)fileOffset)
@@ -284,7 +297,8 @@ bool LoadBIOS()
if (filesize <= 0)
return false;
LoadBiosVersion(fp.get(), BiosVersion, BiosDescription, BiosRegion, BiosZone);
std::string zone;
LoadBiosVersion(fp.get(), BiosVersion, BiosDescription, BiosRegion, zone, BiosSerial);
if (FileSystem::FSeek64(fp.get(), 0, SEEK_SET) ||
std::fread(eeMem->ROM, static_cast<size_t>(std::min<s64>(Ps2MemSize::Rom, filesize)), 1, fp.get()) != 1)
@@ -317,14 +331,14 @@ bool LoadBIOS()
bool IsBIOS(const char* filename, u32& version, std::string& description, u32& region, std::string& zone)
{
const std::string bios_path(Path::Combine(EmuFolders::Bios, filename));
std::string serial;
const auto fp = FileSystem::OpenManagedCFile(filename, "rb");
if (!fp)
return false;
// FPS2BIOS is smaller and of variable size
//if (inway.Length() < 512*1024) return false;
return LoadBiosVersion(fp.get(), version, description, region, zone);
return LoadBiosVersion(fp.get(), version, description, region, zone, serial);
}
bool IsBIOSAvailable(const std::string& full_path)
+1 -1
View File
@@ -38,7 +38,7 @@ extern bool AllowParams1;
extern bool AllowParams2;
extern u32 BiosChecksum;
extern std::string BiosDescription;
extern std::string BiosZone;
extern std::string BiosSerial;
extern std::string BiosPath;
extern bool LoadBIOS();
extern bool IsBIOS(const char* filename, u32& version, std::string& description, u32& region, std::string& zone);