Compare commits

...

62 Commits

Author SHA1 Message Date
refractionpcsx2 6b752a888e GS/HW: Add new method of rounding sprites 2026-02-03 21:03:20 +00:00
PCSX2 Bot fe7935ce0e [ci skip] Qt: Update Base Translation. 2026-02-02 01:59:37 +01:00
lightningterror 2fba420339 GS/DX11: Preemptively bind shader resources if possible.
Idea:
PSSetShaderResource binds local state only (ps_sr_views).
PSUnbindConflictingSRVs binds/updates local and gpu state (ps_sr_views, ps_cached_sr_views and srv gpu state)

What we can do is before checking for conflicts we can preemptively bind new tex on the local state if it's not already bound to the current render target, depth target, then PSUnbindConflictingSRVs will update the gpu state when it checks for conflicts.
Then the following update that happens during the Draw which calls for a gpu upgrade will be ignored since it's already been updated.
2026-02-02 00:13:02 +01:00
lightningterror e35c84c2c6 GS: Update Shader/texture copies counter handling.
Add the copy counters in the function themselves, easier to track.
This also tracks other shader copies such as setupdate, primid date,
post processing such as shade boost and others which weren't tracked before.
2026-02-01 13:35:24 +01:00
PCSX2 Bot 4b79645774 [ci skip] Qt: Update Base Translation. 2026-02-01 01:05:44 +01:00
lightningterror df4be2c170 GS/DX11: Add console logs for supported features. 2026-02-01 00:48:11 +01:00
lightningterror ede25a5ecc GS/DX11: Check if RGBA16 UNORM supports hw blending.
If not then use DXGI_FORMAT_R16G16B16A16_FLOAT,
it will be less precise but will still work on feature level 10.0 and some 10.1 gpus and allow for hardware blending.
2026-02-01 00:48:11 +01:00
lightningterror 76977da05a GS/DX11: Check if conservative depth is supported.
Fixes dx10 gpus being broken without  conservative depth.
2026-02-01 00:48:11 +01:00
lightningterror aca37d14a1 GS/DX11: Clear out render/depth stencil views before swap chain present.
Fixes api warning in specific scenario when force running through dxcpl:
D3D11 INFO: ID3D11DeviceContext::OMSetRenderTargets: Forcing OM Render Target slot 0 to NULL , since calling Present for DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL SwapChains unbinds backbuffer 0 from all GPU writeable bind points, except for DXGI_PRESENT_DO_NOT_SEQUENCE or DXGI_PRESENT_TEST usage. [ STATE_SETTING INFO 49: OMSETRENDERTARGETS_UNBINDDELETINGOBJECT]
2026-02-01 00:48:11 +01:00
Ty eee315718f Darwin: Implement Mach exceptions ports instead of signals
Credit goes to the Julia language and Dolphin emulator for their free and open source implementations
2026-01-31 13:45:02 -05:00
TheLastRar a14d99e93c GS/DX12: Check for typed casting support 2026-01-31 19:16:35 +01:00
TheLastRar af3877ac7a GS/DX12: Init enhanced barriers to false 2026-01-31 19:16:35 +01:00
lightningterror c7f7bd6f3d GS/DX11: Align drawlist bounding box to 4 pixel boundaries.
Will be faster using Direct Memory Access, otherwise it may stall as more commands need to be issued.
2026-01-31 16:57:42 +01:00
PCSX2 Bot dd81fbbd5f [ci skip] Qt: Update Base Translation. 2026-01-31 02:58:58 +01:00
Mrlinkwii b4293a40d2 Docs: update Gamedb documentation 2026-01-30 18:06:59 +01:00
Mrlinkwii 9acadb21fe GameDB: remove left over schemea options 2026-01-30 18:06:02 +01:00
TJnotJT e82fa0bba5 GS/HW: Require 32 bit RT for accumulation blend on max blend. 2026-01-30 11:06:13 +01:00
Ziemas 45490d903a SPU: Slow down DMA 2026-01-29 22:26:28 -05:00
PCSX2 Bot 76dadf792a [ci skip] Qt: Update Base Translation. 2026-01-30 02:03:26 +01:00
wxvu 204829865d GameDB: Add fixes for Puchi Copter 2 2026-01-29 14:21:14 +01:00
lightningterror 84dc2959c5 GSDumpRunner: Use utf-8 encoding for opening files.
Allows the dump compare to work normally with unique characters.
2026-01-29 14:17:02 +01:00
refractionpcsx2 a85b203689 GS/TC: More fixes for dst_matches and old target deletion. 2026-01-29 14:17:02 +01:00
lightningterror 135d40fb7f GS/TC: Remove targets in reverse lookup if the targets are old. 2026-01-29 14:17:02 +01:00
lightningterror a0bc7a5d0e GS/TC: Update depth lookup when looking up targets. 2026-01-29 14:17:02 +01:00
SternXD 955b925633 FullscreenUI: Run translation script
Signed-off-by: SternXD <stern@sidestore.io>
2026-01-29 14:15:17 +01:00
SternXD cc338cdd9d Tools: Refactor translation string extraction to support multiple source files
Signed-off-by: SternXD <stern@sidestore.io>

f
2026-01-29 14:15:17 +01:00
SternXD 082a28dc13 FullscreenUI: Cleanup headers
Signed-off-by: SternXD <stern@sidestore.io>
2026-01-29 14:15:17 +01:00
SternXD 664e14bd6c FullscreenUI: Extract Settings into separate source and add internal header
Signed-off-by: SternXD <stern@sidestore.io>
2026-01-29 14:15:17 +01:00
refractionpcsx2 7ea33400a9 GS: Update the stored transfer rect if worked out to be different 2026-01-29 14:13:39 +01:00
refractionpcsx2 32a3e8e62d GS/HW: On EE->GS transfer only invalidate area actually transferred 2026-01-29 14:13:39 +01:00
refractionpcsx2 fa953d7bb3 GS/TC: Allow creation of target during GS->GS transfer with offset 2026-01-29 14:12:45 +01:00
TheLastRar 66cd51bcd5 GS/DX: Fix per game fullscreen mode setting 2026-01-29 14:11:58 +01:00
PCSX2 Bot ed7ebb77ca [ci skip] Qt: Update Base Translation. 2026-01-29 02:44:25 +01:00
Ziemas 10fc9a790d SPU: Emulate voice decode buffers
This makes the timing of NAX advancing more similar to console since it
emulates the decode buffer behaviour of it rushing ahead of playback
until the buffer is full.

It also makes interpolation of the first four samples more correct by
using real data instead of the zero filled previous values.

[SAVEVERSION+]
2026-01-28 12:18:43 -05:00
Ziemas c42330eebf SPU: Remove unused voice struct members
Might as well If the saveversion is already being bumped.

[SAVEVERSION+]
2026-01-28 12:18:43 -05:00
Ziemas 680e05fead SPU: clang-format mixer.cpp 2026-01-28 12:18:43 -05:00
chaoticgd 1f7b98bf6b Debugger: Allow removing conditions from the breakpoint dialog 2026-01-28 12:13:39 -05:00
TheLastRar c3a20d421e GS/DX12: Fix recreating swapchain failing on vsync mode changes 2026-01-28 01:30:12 +01:00
TheLastRar f03ab6f728 Deps: Bump ffnvcodec to 13.0.19.0 2026-01-28 01:14:08 +01:00
PCSX2 Bot e9275d78b5 [ci skip] Qt: Update Base Translation. 2026-01-28 01:04:00 +01:00
lightningterror 8ababb3890 GS/DX/VK: Properly align uniform buffers.
Make sure uniform buffers are 16 bytes aligned.
Make sure full size is 32 bytes with padding, was 28 previously which can be bad for cpu cache.
2026-01-28 01:00:36 +01:00
Mrlinkwii 422aba4b20 UI : rename fast boot option heading 2026-01-27 12:01:20 +01:00
wxvu 045b9bbf40 GameDB: Add EE Nearest Rounding to Steambot Chronicles (Bumpy Trot)
GameDB: Add EE Nearest Rounding to Steambot Chronicles (Bumpy Trot)

GameDB: Add EE Nearest Rounding to Steambot Chronicles (Bumpy Trot)
2026-01-27 12:00:25 +01:00
PCSX2 Bot 1f519acf92 [ci skip] Qt: Update Base Translation. 2026-01-27 11:59:55 +01:00
lightningterror ac9ebdecba GS/DX12: Check if D3D12GetInterface is supported first.
On older versions of Windows 10 (example 2019 LTSC) D3D12GetInterface may fail because it doesn't exist, in such case we can check if D3D12GetInterface exists first.
2026-01-27 00:09:43 +01:00
Ariel Nogueira Kovaljski 1861394216 Qt: Fix shortcut creation when special folders have been moved.
Use SHGetKnownFolderPath to get the path of special folders instead of building the path from %USERPROFILE%.

Special folders like "Desktop" and "Start Menu\Programs" can be moved from their default paths, which breaks the shortcut creation due to the assumption that they will always be present in the user's home directory (%USERPROFILE%).
2026-01-26 23:59:24 +01:00
PCSX2 Bot 11cc884c96 [ci skip] PAD: Update to latest controller database. 2026-01-26 23:34:42 +01:00
TJnotJT 5710c2740c GS/HW: Enable Z floor only when needed. 2026-01-26 23:30:09 +01:00
TJnotJT ec96feb22e GS/HW: Use conservative depth for shader depth output. 2026-01-26 23:30:09 +01:00
TJnotJT a1173c53d3 GS/HW: Reduce barriers in triangle strips/fans.
Account for triangles forming non-overlapping quads in triangles strips/fans.
2026-01-26 11:57:20 +01:00
JordanTheToaster ac0deff9b2 Memcard: Purge automatic save management 2026-01-26 03:35:44 +01:00
refractionpcsx2 3ae707464c GS/HW: Ignore point draws for a single black pixel in the top left 2026-01-26 03:19:49 +01:00
refractionpcsx2 00ef419023 GS/TC: Enhance depth clear behaviour to improve heuristics 2026-01-26 03:19:49 +01:00
refractionpcsx2 448a279cd4 GS/HW: Further improve clear behaviour 2026-01-26 03:19:49 +01:00
refractionpcsx2 70e13adfde GS/TC: Improve wrapping behaviour for inside target lookups 2026-01-26 03:19:49 +01:00
refractionpcsx2 25bc280818 GS/HW: Clear GS memory if not zero or target overwrite 2026-01-26 03:19:49 +01:00
TheLastRar 32e073002a [ci skip] CI/Linux: Fix contribute link 2026-01-25 19:55:48 +01:00
PCSX2 Bot 911314e948 [ci skip] Qt: Update Base Translation. 2026-01-25 02:38:54 +01:00
lightningterror a73fcb343c GS: Default to DX12 on NV/AMD.
DX12 trades blows with Vulkan on AMD depending on cpu usage and will be stable on RDNA 3 so let's default to it.

NVIDIA: 590 drivers on Nvidia are bad causing performance regressions so let's switch to DX12 as the default.
2026-01-24 20:28:04 +01:00
TheLastRar 251b2960f8 Revert "GS/VK: Use the compute queues for present" 2026-01-24 20:27:30 +01:00
refractionpcsx2 5bb99105c3 GS/HW: Clear downscale source on draw to avoid cross game corruption 2026-01-24 18:32:43 +01:00
lightningterror fa6e1b0949 GS/DX12: Don't end render passes on enhanced barriers. 2026-01-23 11:19:11 +01:00
57 changed files with 9118 additions and 8358 deletions
@@ -25,7 +25,7 @@ LIBBACKTRACE=ad106d5fdd5d960bd33fae1c48a351af567fd075
LIBJPEGTURBO=3.1.2
LIBPNG=1.6.53
LIBWEBP=1.6.0
NVENC=11.1.5.3
NVENC=13.0.19.0
SDL=SDL3-3.4.0
QT=6.10.1
QTAPNG=1.3.0
@@ -57,7 +57,7 @@ e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWE
082cbf5f429e0d80820f68dc2b507a94d4cc1b4e70817b119bbb8ec6a69584b8 $SDL.tar.gz
452a1a290bd0cf18737fad0057dc17b7fdf10a73eda2d6d4f31ba04fda25ef2c libpng-$LIBPNG-apng.patch.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
2974b91062197e0527dffa3aadd8fe3bfa6681ae45f5ff9181bc0ca6479abd59 nv-codec-headers-$NVENC.tar.gz
13da39edb3a40ed9713ae390ca89faa2f1202c9dda869ef306a8d4383e242bee nv-codec-headers-$NVENC.tar.gz
c465aa56757e7746ac707f582b6e2d51546569a4a2488c1172fb543aa5fdfc2c vulkan-sdk-$VULKAN.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
5a6226f7e23db51fdc3223121eba53f3f5447cf0cc4d6cb82a3a2df7a65d265d qtbase-everywhere-src-$QT.tar.xz
@@ -20,7 +20,7 @@
<url type="donation">https://github.com/sponsors/PCSX2</url>
<url type="faq">https://pcsx2.net/docs/</url>
<url type="help">https://pcsx2.net/discord</url>
<url type="contribute">https://github.com/PCSX2/pcsx2/blob/master/.github/CONTRIBUTING.md</url>
<url type="contribute">https://pcsx2.net/docs/category/contributing</url>
<url type="translate">https://crowdin.com/project/pcsx2-emulator</url>
<url type="contact">https://mastodon.social/@PCSX2</url>
<screenshots>
Binary file not shown.
+32
View File
@@ -2075,6 +2075,8 @@ SCAJ-20128:
SCAJ-20129:
name: "Ponkotsu Roman Daikatsugeki Bumpy Trot"
region: "NTSC-Unk"
roundModes:
eeRoundMode: 0 # Fixes broken load triggers.
gsHWFixes:
getSkipCount: "GSC_IRem"
halfPixelOffset: 2 # Aligns effects.
@@ -7614,6 +7616,8 @@ SCKA-20058:
name: "액션 로망 범피 트롯"
name-en: "Action Romance Bumpy Trot"
region: "NTSC-K"
roundModes:
eeRoundMode: 0 # Fixes broken load triggers.
gsHWFixes:
getSkipCount: "GSC_IRem"
halfPixelOffset: 2 # Aligns effects.
@@ -24777,6 +24781,11 @@ SLES-53820:
SLES-53821:
name: "Radio Helicopter II"
region: "PAL-E"
patches:
9A695202:
content: |-
comment=Patch that nops a branch instruction causing a freeze.
patch=1,EE,001799AC,word,00000000
SLES-53824:
name: "Trapt"
region: "PAL-E"
@@ -25578,6 +25587,8 @@ SLES-54137:
SLES-54138:
name: "Steambot Chronicles"
region: "PAL-E"
roundModes:
eeRoundMode: 0 # Fixes broken load triggers.
gsHWFixes:
getSkipCount: "GSC_IRem"
halfPixelOffset: 2 # Aligns effects.
@@ -26228,6 +26239,8 @@ SLES-54333:
SLES-54335:
name: "Steambot Chronicles"
region: "PAL-F"
roundModes:
eeRoundMode: 0 # Fixes broken load triggers.
gsHWFixes:
getSkipCount: "GSC_IRem"
halfPixelOffset: 2 # Aligns effects.
@@ -35853,6 +35866,8 @@ SLPM-60255:
name-sort: "ぽんこつろまんだいかつげきばんぴーとろっと [たいけんばん]"
name-en: "Ponkotsu Roeman Daikatsugeki Bumpy Trot [Trial]"
region: "NTSC-J"
roundModes:
eeRoundMode: 0 # Fixes broken load triggers.
gsHWFixes:
getSkipCount: "GSC_IRem"
halfPixelOffset: 2 # Aligns effects.
@@ -35924,6 +35939,8 @@ SLPM-60266:
name-sort: "ぽんこつろまんだいかつげきばんぴーとろっと [たいけんばん]"
name-en: "Ponkotsu Roeman Daikatsugeki Bumpy Trot [Trial]"
region: "NTSC-J"
roundModes:
eeRoundMode: 0 # Fixes broken load triggers.
gsHWFixes:
getSkipCount: "GSC_IRem"
halfPixelOffset: 2 # Aligns effects.
@@ -40077,6 +40094,11 @@ SLPM-62624:
name-sort: "ぷちこぷたー2"
name-en: "Petit Copter 2"
region: "NTSC-J"
patches:
9A695202:
content: |-
comment=Patch that nops a branch instruction causing a freeze.
patch=1,EE,001799B8,word,00000000
SLPM-62625:
name: "鬼浜爆走愚連隊 激闘編"
name-sort: "おにはまばくそうぐれんたい げきとうへん"
@@ -59824,6 +59846,8 @@ SLPS-25457:
name-sort: "ぽんこつろまんだいかつげきばんぴーとろっと"
name-en: "Ponkotsu Roeman Daikatsugeki Bumpy Trot"
region: "NTSC-J"
roundModes:
eeRoundMode: 0 # Fixes broken load triggers.
gsHWFixes:
getSkipCount: "GSC_IRem"
halfPixelOffset: 2 # Aligns effects.
@@ -61228,6 +61252,8 @@ SLPS-25683:
name-sort: "ぽんこつろまんだいかつげきばんぴーとろっと [Irem COLLECTION]"
name-en: "Ponkotsu Roman Daikatsugeki Bumpy Trot [Irem Collection]"
region: "NTSC-J"
roundModes:
eeRoundMode: 0 # Fixes broken load triggers.
gsHWFixes:
getSkipCount: "GSC_IRem"
halfPixelOffset: 2 # Aligns effects.
@@ -71360,6 +71386,8 @@ SLUS-21344:
name: "Steambot Chronicles"
region: "NTSC-U"
compat: 5
roundModes:
eeRoundMode: 0 # Fixes broken load triggers.
gsHWFixes:
getSkipCount: "GSC_IRem"
halfPixelOffset: 2 # Aligns effects.
@@ -74901,6 +74929,8 @@ SLUS-28059:
SLUS-28061:
name: "Steambot Chronicles [Trade Demo]"
region: "NTSC-U"
roundModes:
eeRoundMode: 0 # Fixes broken load triggers.
gsHWFixes:
getSkipCount: "GSC_IRem"
halfPixelOffset: 2 # Aligns effects.
@@ -75679,6 +75709,8 @@ SLUS-29185:
SLUS-29188:
name: "Steambot Chronicles [Regular Demo]"
region: "NTSC-U"
roundModes:
eeRoundMode: 0 # Fixes broken load triggers.
gsHWFixes:
getSkipCount: "GSC_IRem"
halfPixelOffset: 2 # Aligns effects.
+14 -11
View File
@@ -236,6 +236,7 @@
03000000ac0500005b05000000000000,GameSir G3w,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:Windows,
03000000ac0500002d02000000000000,GameSir G4,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:a5,start:b11,x:b3,y:b4,platform:Windows,
03000000ac0500004d04000000000000,GameSir G4,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:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000373500002210000000000000,GameSir G7 Pro,a:b0,b:b1,x:b3,y:b4,back:b10,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,lefty:a2,rightx:a3,righty:a4,lefttrigger:b8,righttrigger:b9,platform:Window,
03000000ac0500001a06000000000000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000373500009410000000000000,GameSir Tegenaria Lite,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:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
030000004c0e00001035000000000000,Gamester,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,
@@ -245,8 +246,8 @@
03000000b62500000100000000000000,Gametel GT004 01,a:b3,b:b0,dpdown:b10,dpleft:b9,dpright:b8,dpup:b11,leftshoulder:b4,rightshoulder:b5,start:b7,x:b1,y:b2,platform:Windows,
030000008f0e00001411000000000000,Gamo2 Divaller,a:b1,b:b2,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:b0,y:b3,platform:Windows,
03000000120c0000a857000000000000,Gator Claw,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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,
03000000c21100000791000000000000,Be1 GC101 Controller 1.03,a:b2,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:b3,y:b0,platform:Windows,
03000000c9110000f055000000000000,Be1 GC100XF Controller,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:Windows,
03000000c21100000791000000000000,Nacon GC101 1.03,a:b2,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:b3,y:b0,platform:Windows,
03000000c9110000f055000000000000,Nacon GC100XF,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:Windows,
030000008305000009a0000000000000,Genius,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:Windows,
030000008305000031b0000000000000,Genius Maxfire Blaze 3,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:Windows,
03000000451300000010000000000000,Genius Maxfire Grandias 12,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:Windows,
@@ -463,7 +464,7 @@
03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
03000000091200004488000000000000,MUSIA PlayStation 2 Input Display,a:b0,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:b11,rightx:a2,righty:a3,start:b5,x:b1,y:b3,platform:Windows,
03000000f70600000100000000000000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,platform:Windows,
030000006b140000010c000000000000,Nacon GC 400ES,a:b0,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:b2,y:b3,platform:Windows,
030000006b140000010c000000000000,Nacon GC400ES,a:b0,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:b2,y:b3,platform:Windows,
030000006b1400001106000000000000,Nacon Revolution 3 PS4 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:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,
0300000085320000170d000000000000,Nacon Revolution 5 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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,
0300000085320000190d000000000000,Nacon Revolution 5 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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,
@@ -1305,7 +1306,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000632500007a05000001020000,Cosmic Byte Ares Wired Controller,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:Linux,
03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux,
03000000a306000022f6000011010000,Cyborg V3 Rumble,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:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,
030000005e0400008e02000002010000,Data Frog S80,a:b1,b:b0,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:b3,y:b2,platform:Linux,
03000000791d00000103000010010000,Dual Box Wii Classic Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
030000006f0e00003001000001010000,EA Sports 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:Linux,
03000000c11100000191000011010000,EasySMX,a:b2,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:b3,y:b0,platform:Linux,
@@ -1512,9 +1512,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e0400008e02000010020000,MSI GC20 V2,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,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,
03000000f70600000100000000010000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,platform:Linux,
030000006b1400000906000014010000,Nacon Asymmetric Wireless PS4 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,
030000006b140000010c000010010000,Nacon GC 400ES,a:b0,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:b2,y:b3,platform:Linux,
03000000853200000706000012010000,Nacon GC-100,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,
05000000853200000503000000010000,Nacon MG-X Pro,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:Linux,
030000006b140000010c000010010000,Nacon GC400ES,a:b0,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:b2,y:b3,platform:Linux,
0300000085320000030c000011010000,Nacon GC100,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:Linux,
03000000853200000706000012010000,Nacon GC100,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,
05000000853200000503000000010000,Nacon MGX Pro,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:Linux,
0300000085320000170d000011010000,Nacon Revolution 5 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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
0300000085320000190d000011010000,Nacon Revolution 5 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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
030000000d0f00000900000010010000,Natec Genesis P44,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,
@@ -1799,18 +1800,20 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000434f4d4d414e440000000000,VX Gaming Command Series,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
0000000058626f782033363020576900,Xbox 360 Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,
030000005e0400001907000000010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,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,
030000005e0400008e02000010010000,Xbox 360 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,
030000005e0400008e02000014010000,Xbox 360 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,
030000005e0400009102000007010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,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,
030000005e040000a102000000010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,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,
030000005e040000a102000007010000,Xbox 360 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,
030000005e040000a102000030060000,Xbox 360 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,
030000006f0e00001503000000020000,Xbox 360 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,
030000005e0400008e02000000010000,Xbox 360 EasySMX,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,
030000005e0400008e02000000010000,Xbox 360 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,
030000005e0400008e02000010010000,Xbox 360 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,
030000005e0400008e02000002010000,Xbox 360 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,
030000005e0400008e02000014010000,Xbox 360 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,
030000005e0400008e02000047010000,Xbox 360 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,
030000005e0400008e02000072050000,Xbox 360 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,
030000005e040000a102000014010000,Xbox 360 Receiver,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,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,
0000000058626f782047616d65706100,Xbox 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:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
030000005e0400000202000000010000,Xbox Controller,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,
030000005e0400008e02000072050000,Xbox 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,
030000006f0e00001304000000010000,Xbox 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,
03000000ffff0000ffff000000010000,Xbox Controller,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,
030000005e0400000a0b000005040000,Xbox One Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,
+7 -3
View File
@@ -112,7 +112,7 @@ struct VS_OUTPUT
struct PS_INPUT
{
float4 p : SV_Position;
noperspective centroid float4 p : SV_Position;
float4 t : TEXCOORD0;
float4 ti : TEXCOORD2;
#if VS_IIP != 0 || GS_IIP != 0 || PS_IIP != 0
@@ -139,8 +139,12 @@ struct PS_OUTPUT
#endif
#endif
#endif
#if PS_ZCLAMP || PS_ZFLOOR
float depth : SV_Depth;
#if (PS_ZFLOOR || PS_ZCLAMP)
#if PS_HAS_CONSERVATIVE_DEPTH
float depth : SV_DepthLessEqual;
#else
float depth : SV_Depth;
#endif
#endif
};
+4
View File
@@ -113,6 +113,10 @@ layout(binding = 3) uniform sampler2D img_prim_min;
//layout(pixel_center_integer) in vec4 gl_FragCoord;
#endif
#if (PS_ZFLOOR || PS_ZCLAMP) && PS_HAS_CONSERVATIVE_DEPTH
layout(depth_less) out float gl_FragDepth;
#endif
vec4 sample_from_rt()
{
#if !NEEDS_RT
+4
View File
@@ -361,6 +361,10 @@ layout(set = 1, binding = 1) uniform texture2D Palette;
layout(set = 1, binding = 3) uniform texture2D PrimMinTexture;
#endif
#if PS_ZFLOOR || PS_ZCLAMP
layout(depth_less) out float gl_FragDepth;
#endif
#if NEEDS_TEX
vec4 sample_c(vec2 uv)
+197 -1
View File
@@ -11,6 +11,7 @@
#include "common/Threading.h"
#include "common/WindowInfo.h"
#include "common/HostSys.h"
#include "fmt/format.h"
#include <csignal>
#include <cstring>
@@ -19,12 +20,15 @@
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <thread>
#include <time.h>
#include <mach/mach_init.h>
#include <mach/mach_port.h>
#include <mach/mach_time.h>
#include <mach/mach_vm.h>
#include <mach/message.h>
#include <mach/task.h>
#include <mach/thread_state.h>
#include <mach/vm_map.h>
#include <mutex>
#include <ApplicationServices/ApplicationServices.h>
@@ -294,7 +298,7 @@ static CPUInfo CalcCPUInfo()
std::vector<DarwinMisc::CPUClass> classes = DarwinMisc::GetCPUClasses();
out.num_clusters = static_cast<u32>(classes.size());
out.num_big_cores = classes.empty() ? 0 : classes[0].num_physical;
out.num_threads = classes.empty() ? 0 : classes[0].num_logical;
out.num_threads = classes.empty() ? 0 : classes[0].num_logical;
out.num_small_cores = 0;
for (std::size_t i = 1; i < classes.size(); i++)
{
@@ -568,15 +572,206 @@ void HostSys::EndCodeWrite()
#endif // _M_ARM64
#define USE_MACH_EXCEPTION_PORTS
namespace PageFaultHandler
{
#ifdef USE_MACH_EXCEPTION_PORTS
static void SignalHandler(mach_port_t port);
#else
static void SignalHandler(int sig, siginfo_t* info, void* ctx);
#endif
static std::recursive_mutex s_exception_handler_mutex;
static bool s_in_exception_handler = false;
static bool s_installed = false;
} // namespace PageFaultHandler
#ifdef USE_MACH_EXCEPTION_PORTS
#if defined(_M_X86)
#define THREAD_STATE64_COUNT x86_THREAD_STATE64_COUNT
#define THREAD_STATE64 x86_THREAD_STATE64
#define thread_state64_t x86_thread_state64_t
#elif defined(_M_ARM64)
#define THREAD_STATE64_COUNT ARM_THREAD_STATE64_COUNT
#define THREAD_STATE64 ARM_THREAD_STATE64
#define thread_state64_t arm_thread_state64_t
#else
#error Unknown Darwin Platform
#endif
void PageFaultHandler::SignalHandler(mach_port_t port)
{
Threading::SetNameOfCurrentThread("Mach Exception Thread");
#pragma pack(4)
struct
{
mach_msg_header_t Head;
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
int64_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[THREAD_STATE64_COUNT];
mach_msg_trailer_t trailer;
} msg_in;
struct
{
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
int flavor;
mach_msg_type_number_t new_stateCnt;
natural_t new_state[THREAD_STATE64_COUNT];
} msg_out;
#pragma pack()
memset(&msg_in, 0xee, sizeof(msg_in));
memset(&msg_out, 0xee, sizeof(msg_out));
mach_msg_size_t send_size = 0;
mach_msg_option_t option = MACH_RCV_MSG;
while (true)
{
kern_return_t r;
if ((r = mach_msg_overwrite(&msg_out.Head, option, send_size, sizeof(msg_in), port,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL, &msg_in.Head, 0)))
{
pxFail(fmt::format("CRITICAL: mach_msg_overwrite: {:x}", r).c_str());
}
if (msg_in.Head.msgh_id == MACH_NOTIFY_NO_SENDERS)
{
// the other thread exited
mach_port_deallocate(mach_task_self(), port);
return;
}
if (msg_in.Head.msgh_id != 2406)
{
pxFailRel("unknown message received");
return;
}
if (msg_in.flavor != THREAD_STATE64)
{
pxFailRel(fmt::format("unknown flavour {}, expected {}", msg_in.flavor, THREAD_STATE64).c_str());
return;
}
s_exception_handler_mutex.lock();
thread_state64_t* state = (thread_state64_t*)msg_in.old_state;
HandlerResult result = HandlerResult::ExecuteNextHandler;
if (!s_in_exception_handler)
{
s_in_exception_handler = true;
#ifdef _M_ARM64
result = HandlePageFault(reinterpret_cast<void*>(state->__pc), reinterpret_cast<void*>(msg_in.code[1]), (msg_in.code[0] & 2) != 0);
#else
result = HandlePageFault(reinterpret_cast<void*>(state->__rip), reinterpret_cast<void*>(msg_in.code[1]), (msg_in.code[0] & 2) != 0);
#endif
s_in_exception_handler = false;
}
// Set up the reply.
msg_out.Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(msg_in.Head.msgh_bits), 0);
msg_out.Head.msgh_remote_port = msg_in.Head.msgh_remote_port;
msg_out.Head.msgh_local_port = MACH_PORT_NULL;
msg_out.Head.msgh_id = msg_in.Head.msgh_id + 100;
msg_out.NDR = msg_in.NDR;
if (result != HandlerResult::ContinueExecution) // cooked
{
msg_out.RetCode = KERN_FAILURE;
msg_out.flavor = 0;
msg_out.new_stateCnt = 0;
// The crash handler on macOS or Linux doesn't use context passed to it
// Stubbing it here is fine
CrashHandler::CrashSignalHandler(-1, nullptr, nullptr);
pxFailRel("CrashSignalHandler returned when it should have terminated us!");
}
else
{
// Resumes execution right where we left off (re-executes instruction that caused the SIGSEGV)
msg_out.RetCode = KERN_SUCCESS;
msg_out.flavor = THREAD_STATE64;
msg_out.new_stateCnt = THREAD_STATE64_COUNT;
memcpy(msg_out.new_state, msg_in.old_state, THREAD_STATE64_COUNT * sizeof(natural_t));
}
msg_out.Head.msgh_size =
offsetof(__typeof__(msg_out), new_state) + msg_out.new_stateCnt * sizeof(natural_t);
send_size = msg_out.Head.msgh_size;
option |= MACH_SEND_MSG;
s_exception_handler_mutex.unlock();
}
}
bool PageFaultHandler::Install(Error* error)
{
exception_mask_t masks[EXC_TYPES_COUNT];
mach_port_t ports[EXC_TYPES_COUNT];
exception_behavior_t behaviors[EXC_TYPES_COUNT];
thread_state_flavor_t flavors[EXC_TYPES_COUNT];
mach_msg_type_number_t count = EXC_TYPES_COUNT;
kern_return_t r = task_get_exception_ports(mach_task_self(), EXC_MASK_ALL,
masks, &count, ports, behaviors, flavors);
mach_port_t port;
if ((r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port)))
{
pxFailRel(fmt::format("mach_port_allocate: {:x}", r).c_str());
return false;
}
std::thread sig_thread(PageFaultHandler::SignalHandler, port);
sig_thread.detach();
if ((r = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND)))
{
mach_port_deallocate(mach_task_self(), port);
pxFailRel(fmt::format("mach_port_insert_right: {:x}", r).c_str());
return false;
}
task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, MACH_PORT_NULL, EXCEPTION_DEFAULT, THREAD_STATE_NONE);
if ((r = thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS, port, EXCEPTION_STATE | MACH_EXCEPTION_CODES, THREAD_STATE64)))
{
mach_port_deallocate(mach_task_self(), port);
pxFailRel(fmt::format("thread_set_exception_ports: {:x}", r).c_str());
return false;
}
if ((r = mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1)))
{
mach_port_deallocate(mach_task_self(), port);
pxFailRel(fmt::format("mach_port_mod_refs: {:x}", r).c_str());
return false;
}
mach_port_t previous;
if ((r = mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_NO_SENDERS, 0, port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous)))
{
mach_port_deallocate(mach_task_self(), port);
pxFailRel(fmt::format("mach_port_mod_refs: {:x}", r).c_str());
return false;
}
s_installed = true;
return true;
}
#else
void PageFaultHandler::SignalHandler(int sig, siginfo_t* info, void* ctx)
{
#if defined(_M_X86)
@@ -644,3 +839,4 @@ bool PageFaultHandler::Install(Error* error)
s_installed = true;
return true;
}
#endif
+1 -1
View File
@@ -184,7 +184,7 @@ if __name__ == "__main__":
args = parser.parse_args()
MAX_DIFF_FRAMES = args.maxframes
outfile = open(args.outfile, "w")
outfile = open(args.outfile, "w", encoding="utf-8")
write(FILE_HEADER)
if not check_regression_tests(os.path.realpath(args.baselinedir), os.path.realpath(args.testdir)):
@@ -123,6 +123,11 @@ void BreakpointDialog::accept()
bp->cond.expression = expr;
bp->cond.expressionString = m_ui.txtCondition->text().toStdString();
}
else
{
bp->hasCond = false;
bp->cond = {};
}
}
if (auto* mc = std::get_if<MemCheck>(&m_bp_mc))
{
@@ -159,6 +164,11 @@ void BreakpointDialog::accept()
mc->cond.expression = expr;
mc->cond.expressionString = m_ui.txtCondition->text().toStdString();
}
else
{
mc->hasCond = false;
mc->cond = {};
}
int condition = 0;
if (m_ui.chkRead->isChecked())
+1 -1
View File
@@ -135,7 +135,7 @@
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Options and Patches</string>
<string>Fast Boot Options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
@@ -40,7 +40,6 @@ MemoryCardSettingsWidget::MemoryCardSettingsWidget(SettingsWindow* settings_dial
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.directory, m_ui.browse, m_ui.open, m_ui.reset, "Folders",
"MemoryCards", Path::Combine(EmuFolders::DataRoot, "memcards"));
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.automaticManagement, "EmuCore", "McdFolderAutoManage", true);
setupAdditionalUi();
@@ -58,11 +57,6 @@ MemoryCardSettingsWidget::MemoryCardSettingsWidget(SettingsWindow* settings_dial
connect(m_ui.deleteCard, &QPushButton::clicked, this, &MemoryCardSettingsWidget::deleteCard);
refresh();
dialog()->registerWidgetHelp(m_ui.automaticManagement, tr("Automatically manage saves based on running game"),
tr("Checked"),
tr("(Folder type only / Card size: Auto) Loads only the relevant booted game saves, ignoring others. Avoids "
"running out of space for saves."));
}
MemoryCardSettingsWidget::~MemoryCardSettingsWidget() = default;
@@ -9,6 +9,7 @@
#include <QtWidgets/QTreeWidget>
#include <QtWidgets/QToolButton>
#include <QtWidgets/QListWidget>
#include <QtWidgets/QCheckBox>
#include <array>
#include <optional>
@@ -175,22 +175,6 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="settingsGroupBox">
<property name="title">
<string>Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QCheckBox" name="automaticManagement">
<property name="text">
<string>Automatically manage saves based on running game</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
@@ -211,7 +195,6 @@
<tabstop>renameCard</tabstop>
<tabstop>convertCard</tabstop>
<tabstop>deleteCard</tabstop>
<tabstop>automaticManagement</tabstop>
</tabstops>
<resources/>
<connections/>
+14 -7
View File
@@ -140,27 +140,34 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
return;
}
// Locate home directory
// Get path to Desktop or per-user Start Menu\Programs directory
// https://superuser.com/questions/1489874/how-can-i-get-the-real-path-of-desktop-in-windows-explorer/1789849#1789849
// https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath
// https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid
std::string link_file;
if (const char* home = std::getenv("USERPROFILE"))
if (PWSTR directory; SUCCEEDED(SHGetKnownFolderPath(is_desktop ? FOLDERID_Desktop : FOLDERID_Programs, 0, NULL, &directory)))
{
std::string directory_utf8 = StringUtil::WideStringToUTF8String(directory);
CoTaskMemFree(directory);
if (is_desktop)
link_file = Path::ToNativePath(fmt::format("{}/Desktop/{}.lnk", home, clean_name));
link_file = Path::ToNativePath(fmt::format("{}/{}.lnk", directory_utf8, clean_name));
else
{
const std::string start_menu_dir = Path::ToNativePath(fmt::format("{}/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/PCSX2", home));
if (!FileSystem::EnsureDirectoryExists(start_menu_dir.c_str(), false))
const std::string pcsx2_start_menu_dir = Path::ToNativePath(fmt::format("{}/PCSX2", directory_utf8));
if (!FileSystem::EnsureDirectoryExists(pcsx2_start_menu_dir.c_str(), false))
{
QMessageBox::critical(this, tr("Failed to create shortcut"), tr("Could not create start menu directory."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
return;
}
link_file = Path::ToNativePath(fmt::format("{}/{}.lnk", start_menu_dir, clean_name));
link_file = Path::ToNativePath(fmt::format("{}/{}.lnk", pcsx2_start_menu_dir, clean_name));
}
}
else
{
QMessageBox::critical(this, tr("Failed to create shortcut"), tr("Home path is empty."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
CoTaskMemFree(directory);
QMessageBox::critical(this, tr("Failed to create shortcut"), is_desktop ? tr("'Desktop' directory not found") : tr("User's 'Start Menu\\Programs' directory not found"), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
return;
}
+3
View File
@@ -295,6 +295,9 @@
<property name="text">
<string>Desktop</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
File diff suppressed because it is too large Load Diff
+2
View File
@@ -830,6 +830,7 @@ set(pcsx2HostHeaders
set(pcsx2ImGuiSources
ImGui/FullscreenUI.cpp
ImGui/FullscreenUI_Settings.cpp
ImGui/ImGuiFullscreen.cpp
ImGui/ImGuiManager.cpp
ImGui/ImGuiOverlays.cpp
@@ -837,6 +838,7 @@ set(pcsx2ImGuiSources
set(pcsx2ImGuiHeaders
ImGui/FullscreenUI.h
ImGui/FullscreenUI_Internal.h
ImGui/ImGuiAnimated.h
ImGui/ImGuiFullscreen.h
ImGui/ImGuiManager.h
-1
View File
@@ -1326,7 +1326,6 @@ struct Pcsx2Config
UseSavestateSelector : 1,
InhibitScreensaver : 1,
BackupSavestate : 1,
McdFolderAutoManage : 1,
ManuallySetRealTimeClock : 1, // passes user-set real-time clock information to cdvd at startup
UseSystemLocaleFormat : 1, // presents OS time format instead of yyyy-MM-dd HH:mm:ss for manual RTC
+11 -2
View File
@@ -139,12 +139,15 @@ The clamp modes are also numerically based.
### GS Hardware Mipmap Fixes
* mipmap [`0` or `1` or `2`] {Off, Basic, Full} Default: Automatic (No value, looks up GameDB)
* mipmap [`0` or `1`] {Off, On} Default: On (looks up GameDB)
* trilinearFiltering [`0` or `1` or `2`] {None, Trilinear, Trilinear Ultra} Default: None (`0`)
### GS Hardware General Fixes
* beforeDraw {`OI` with suffix } {None unless specific game GSC} Default: Automatic (No value, looks up GameDB) with valid variable name (ex. OI_BurnoutGames)
* moveHandler {`MV` with suffix } {None unless specific game GSC} Default: Automatic (No value, looks up GameDB) with valid variable name (ex. MV_Ico)
* afterDraw {`OO` with suffix } {None unless specific game GSC} Default: Automatic (No value, looks up GameDB) with valid variable name
* conservativeFramebuffer [`0` or `1`] {Off or On} Default: On (`1`)
* texturePreloading [`0` or `1` or `2`] {None, Partial or Full Hash Cache} Default: None (`0`)
@@ -153,11 +156,17 @@ The clamp modes are also numerically based.
### GS Hardware Renderer Fixes
* autoFlush [`0` or `1` or `2`] {Disabled, Enabled (Sprites Only), Enabled (All Primitives)} Default: Off (`0`)
* partialTargetInvalidation [`0` or `1`] {Off, On} Default: Off (`0`)
* PCRTCOffsets [`0` or `1`] {Off, On} Default: Off (`0`)
* PCRTCOverscan [`0` or `1`] {Off, On} Default: Off (`0`)
* disableDepthSupport [`0` or `1`] {Off, On} Default: Off (`0`)
* disablePartialInvalidation [`0` or `1`] {Off, On} Default: Off (`0`)
* cpuFramebufferConversion [`0` or `1`] {Off, On} Default: Off (`0`)
* preloadFrameData [`0` or `1`] {Off, On} Default: Off (`0`)
* textureInsideRT [`0` or `1`] {Disabled, Inside Targets, Merge Targets} Default: Off (`0`)
* textureInsideRT [`0` or `1`or `2`] {Disabled, Inside Targets, Merge Targets} Default: Off (`0`)
* PCRTCOverscan [`0` or `1`] {Off, On} Default: Off (`0`)
* PCRTCOverscan [`0` or `1`] {Off, On} Default: Off (`0`)
* cpuCLUTRender [`0` or `1` or `2`] {Disabled, Normal, Aggressive} Default: Disabled (`0`)
* cpuSpriteRenderBW [Value between `0` to `10`] {Disabled, 1 (64), 2 (128), 3 (192), 4 (256), 5 (320), 6 (384), 7 (448), 8 (512), 9 (576), 10 (640)} Default: Off (`0`)
-5
View File
@@ -229,11 +229,6 @@
"minimum": 0,
"maximum": 100000
},
"halfBottomOverride": {
"type": "integer",
"minimum": 0,
"maximum": 1
},
"halfPixelOffset": {
"type": "integer",
"minimum": 0,
+116 -14
View File
@@ -1899,6 +1899,30 @@ void GSState::FlushWrite()
r = m_tr.rect;
// If the end isn't where it said it would be, we need to calculate the end point.
// Star Wars - The Clone Wars just sets the rect to 16x4095 then YOLO's about half a page, then kills the transfer.
// If we just nuke the whole lot, even though nothing has been transferred, we risk killing data we don't mean to.
if (m_tr.end < m_tr.total && GSIsHardwareRenderer())
{
const GSLocalMemory::psm_t& psm_s = GSLocalMemory::m_psm[m_tr.m_blit.DPSM];
// Convert to nibbles then back to bytes after, in case trbpp is 4.
const u32 in_data_pixel_count = (((len * 2) + ((psm_s.trbpp / 4) - 1)) / (psm_s.trbpp / 4));
const u32 rect_pixel_count = r.width() * r.height();
if (rect_pixel_count > in_data_pixel_count)
{
const int calculated_height = ((in_data_pixel_count + (r.width() - 1)) / r.width());
// Just setting the height should be okay...
r.w = std::max(r.y + calculated_height, psm_s.bs.y);
if (m_draw_transfers.size() > 0 && m_tr.m_blit.DBP == m_draw_transfers.back().blit.DBP)
{
m_draw_transfers.back().rect = r;
}
}
}
InvalidateVideoMem(m_env.BITBLTBUF, r);
const GSLocalMemory::writeImage wi = GSLocalMemory::m_psm[m_env.BITBLTBUF.DPSM].wi;
@@ -3536,7 +3560,7 @@ __forceinline bool AreTrianglesQuad(const GSVertex* RESTRICT vin, const u16* RES
return are_quad;
}
__forceinline bool AreTrianglesQuadNonAA(const GSVertex* RESTRICT vin, const u16* RESTRICT index0, const u16* RESTRICT index1)
bool GSState::AreTrianglesQuadNonAA(const GSVertex* RESTRICT vin, const u16* RESTRICT index0, const u16* RESTRICT index1)
{
u32 v0[3] = {
vin[index0[0]].XYZ.U32[0],
@@ -3755,7 +3779,7 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
}
PRIM_OVERLAP overlap = PRIM_OVERLAP_NO;
bool check_quads = primclass == GS_TRIANGLE_CLASS;
bool check_quads = (primclass == GS_TRIANGLE_CLASS);
u32 i = 0;
u32 skip = 0; // Number of indices to skip if we have the bbox from the previous iteration.
@@ -3766,11 +3790,85 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
{
u32 j = i + skip;
skip = 0;
GSVector4i bbox(INT_MAX, INT_MAX, -INT_MAX, -INT_MAX);
while (j < count)
{
if (check_quads && j + 3 < count)
bool got_bbox = false;
// Assuming that indices 0-5 represent two triangles:
// Triangle strips: indices 1, 2 are identical to indices 3, 4. Indices 0, 5 are different.
// Triangles fans: indices 0, 2 are identical to indices 3, 4. Indices 1, 5 are different.
// Warning: this depends on how the vertices are arranged in the vertex kick.
// if that changes this detection will break.
constexpr std::array<std::array<std::array<int, 3>, 2>, 2> tri_order({
// Triangle strip expected indices.
std::array{ std::array<int, 3>{ 1, 2, 0 }, std::array<int, 3>{ 3, 4, 5 } },
// Triangle fan expected indices.
std::array{ std::array<int, 3>{ 0, 2, 1 }, std::array<int, 3>{ 3, 4, 5 } },
});
const auto CheckTriangleQuads = [&]<int type>() {
constexpr std::array<int, 3> tri0 = tri_order[type][0];
constexpr std::array<int, 3> tri1 = tri_order[type][1];
if (!got_bbox && primclass == GS_TRIANGLE_CLASS && j + 3 < count &&
index[j + tri0[0]] == index[j + tri1[0]] &&
index[j + tri0[1]] == index[j + tri1[1]])
{
// Get the initial triangle bbox.
bbox = GSVector4i(v[index[j + 0]].m[1]).upl16().xyxy();
bbox = bbox.runion(GSVector4i(v[index[j + 1]].m[1]).upl16().xyxy());
bbox = bbox.runion(GSVector4i(v[index[j + 2]].m[1]).upl16().xyxy());
while (true)
{
// There is a shared edge between this and next triangle.
// Check if the unshared point is on opposite sides of the shared edge.
const GSVector4i shared0 = GSVector4i(v[index[j + skip + tri0[0]]].m[1]).upl16();
const GSVector4i shared1 = GSVector4i(v[index[j + skip + tri0[1]]].m[1]).upl16() - shared0;
const GSVector4i unshared0 = GSVector4i(v[index[j + skip + tri0[2]]].m[1]).upl16() - shared0;
const GSVector4i unshared1 = GSVector4i(v[index[j + skip + tri1[2]]].m[1]).upl16() - shared0;
// Cross product signs comparison.
if ((unshared0.x * shared1.y - unshared0.y * shared1.x >= 0) ==
(unshared1.x * shared1.y - unshared1.y * shared1.x >= 0))
{
break; // Triangles are on same side of the shared edge so there's overlap.
}
// Corners are on opposite sides so we can assume a non-axis-aligned quad.
// Take union with the single unshared point.
bbox = bbox.runion(GSVector4i(v[index[j + skip + tri1[2]]].m[1]).upl16().xyxy());
skip += 3;
got_bbox = true;
if (!(j + skip + 3 < count &&
index[j + skip + tri0[0]] == index[j + skip + tri1[0]] &&
index[j + skip + tri0[1]] == index[j + skip + tri1[1]]))
{
// Cannot continue the strip/fan. Consume the last triangle and exit.
skip += 3;
break;
}
}
}
};
// First check: see if the triangles are part of triangle strip/fan.
if (primclass == GS_TRIANGLE_CLASS)
{
CheckTriangleQuads.template operator()<0>(); // Check triangle strips.
CheckTriangleQuads.template operator()<1>(); // Check triangle fans.
}
// Second check: see if triangles form an axis-aligned quad.
if (!got_bbox && primclass == GS_TRIANGLE_CLASS && check_quads && j + 3 < count)
{
const u16* RESTRICT idx0 = &index[j + 0];
const u16* RESTRICT idx1 = &index[j + 3];
@@ -3780,31 +3878,30 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
if (AreTrianglesQuad<0, 0>(v, idx0, idx1, &tri0, &tri1))
{
// tri.b is right angle corner
GSVector4i corner0 = GSVector4i(v[idx0[tri0.b]].m[1]).upl16().xyxy();
GSVector4i corner1 = GSVector4i(v[idx1[tri1.b]].m[1]).upl16().xyxy();
bbox = corner0.runion(corner1);
bbox = GSVector4i(v[idx0[tri0.b]].m[1]).upl16().xyxy();
bbox = bbox.runion(GSVector4i(v[idx1[tri1.b]].m[1]).upl16().xyxy());
skip = 6;
got_bbox = true;
}
else
{
bbox = GSVector4i(v[index[j + 0]].m[1]).upl16().xyxy();
bbox = bbox.runion(GSVector4i(v[index[j + 1]].m[1]).upl16().xyxy());
bbox = bbox.runion(GSVector4i(v[index[j + 2]].m[1]).upl16().xyxy());
skip = 3;
// If we fail a quad check assume the rest are not quads.
// If we fail a quad check assume the rest are not quads since the check is relatively expensive.
check_quads = false;
}
}
else
// Default case: just take the bbox of the prim vertices.
if (!got_bbox)
{
bbox = GSVector4i(v[GetIndex(j)].m[1]).upl16().xyxy();
for (int k = 1; k < n; k++) // Unroll
bbox = bbox.runion(GSVector4i(v[GetIndex(j + k)].m[1]).upl16().xyxy());
skip = n;
got_bbox = true;
}
// Avoid degenerate bbox.
@@ -5496,6 +5593,11 @@ bool GSState::IsOpaque()
return true;
const GSDrawingContext* context = m_context;
const u32 fmsk = GSLocalMemory::m_psm[context->FRAME.PSM].fmsk;
// If we aren't drawing color, it's equivilant to opaque.
if ((context->FRAME.FBMSK & fmsk) == (fmsk & 0x00FFFFFF))
return true;
int amin = 0;
int amax = 0xff;
+1
View File
@@ -478,6 +478,7 @@ public:
template<bool shuffle_check>
bool TrianglesAreQuadsImpl();
bool AreTrianglesQuadNonAA(const GSVertex* RESTRICT vin, const u16* RESTRICT index0, const u16* RESTRICT index1);
bool TrianglesAreQuads(bool shuffle_check = false);
template <u32 primclass>
PRIM_OVERLAP GetPrimitiveOverlapDrawlistImpl(bool save_drawlist = false, bool save_bbox = false, float bbox_scale = 1.0f);
+4 -5
View File
@@ -244,7 +244,7 @@ const char* GSDevice::RenderAPIToString(RenderAPI api)
bool GSDevice::GetRequestedExclusiveFullscreenMode(u32* width, u32* height, float* refresh_rate)
{
const std::string mode = Host::GetBaseStringSettingValue("EmuCore/GS", "FullscreenMode", "");
const std::string mode = Host::GetStringSettingValue("EmuCore/GS", "FullscreenMode", "");
if (!mode.empty())
{
const std::string_view mode_view = mode;
@@ -899,14 +899,13 @@ void GSDevice::ShadeBoost()
if (ResizeRenderTarget(&m_target_tmp, m_current->GetWidth(), m_current->GetHeight(), false, false))
{
// predivide to avoid the divide (multiply) in the shader
const float params[4] = {
const GSVector4 params(
static_cast<float>(GSConfig.ShadeBoost_Brightness) * (1.0f / 50.0f),
static_cast<float>(GSConfig.ShadeBoost_Contrast) * (1.0f / 50.0f),
static_cast<float>(GSConfig.ShadeBoost_Saturation) * (1.0f / 50.0f),
static_cast<float>(GSConfig.ShadeBoost_Gamma) * (1.0f / 50.0f),
};
static_cast<float>(GSConfig.ShadeBoost_Gamma) * (1.0f / 50.0f));
DoShadeBoost(m_current, m_target_tmp, params);
DoShadeBoost(m_current, m_target_tmp, params.v);
m_current = m_target_tmp;
}
+4 -2
View File
@@ -426,7 +426,8 @@ GSRendererType D3D::GetPreferredRenderer()
if (!feature_level.has_value())
return GSRendererType::DX11;
else if (feature_level == D3D_FEATURE_LEVEL_12_0)
return check_vulkan_supported() ? GSRendererType::VK : GSRendererType::OGL;
//return check_vulkan_supported() ? GSRendererType::VK : GSRendererType::OGL;
return GSRendererType::DX12;
else if (feature_level == D3D_FEATURE_LEVEL_11_0)
return GSRendererType::OGL;
else
@@ -439,7 +440,8 @@ GSRendererType D3D::GetPreferredRenderer()
if (!feature_level.has_value())
return GSRendererType::DX11;
else if (feature_level == D3D_FEATURE_LEVEL_12_0)
return check_vulkan_supported() ? GSRendererType::VK : GSRendererType::DX12;
//return check_vulkan_supported() ? GSRendererType::VK : GSRendererType::DX12;
return GSRendererType::DX12;
else if (feature_level == D3D_FEATURE_LEVEL_11_1)
return GSRendererType::DX12;
else
+99 -30
View File
@@ -38,6 +38,15 @@ static bool SupportsTextureFormat(ID3D11Device* dev, DXGI_FORMAT format)
return (support & D3D11_FORMAT_SUPPORT_TEXTURE2D) != 0;
}
static bool IsTextureFormatHWBlendable(ID3D11Device* dev, DXGI_FORMAT format)
{
UINT support;
if (FAILED(dev->CheckFormatSupport(format, &support)))
return false;
return (support & D3D11_FORMAT_SUPPORT_RENDER_TARGET) && (support & D3D11_FORMAT_SUPPORT_BLENDABLE);
}
GSDevice11::GSDevice11()
{
memset(&m_state, 0, sizeof(m_state));
@@ -599,6 +608,15 @@ void GSDevice11::SetFeatures(IDXGIAdapter1* adapter)
m_max_texture_size = (m_feature_level >= D3D_FEATURE_LEVEL_11_0) ?
D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION :
D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION;
m_conservative_depth = (m_feature_level >= D3D_FEATURE_LEVEL_11_0);
m_rgba16_unorm_hw_blend = IsTextureFormatHWBlendable(m_dev.get(), DXGI_FORMAT_R16G16B16A16_UNORM);
// Let the user know if said features are available.
Console.WriteLnFmt("D3D11: DXTn Texture Compression: {}", m_features.dxt_textures ? "Supported" : "Not Supported");
Console.WriteLnFmt("D3D11: BC6/7 Texture Compression: {}", m_features.bptc_textures ? "Supported" : "Not Supported");
Console.WriteLnFmt("D3D11: Conservative Depth: {}", m_conservative_depth ? "Supported" : "Not Supported");
Console.WriteLnFmt("D3D11: RGBA16 UNORM Hardware Blending: {}", m_rgba16_unorm_hw_blend ? "Supported" : "Not Supported");
}
bool GSDevice11::HasSurface() const
@@ -972,15 +990,15 @@ void GSDevice11::EndPresent()
if (m_vsync_mode != GSVSyncMode::FIFO && m_gpu_timing_enabled)
PopTimestampQuery();
// clear out the swap chain view, it might get resized..
OMSetRenderTargets(nullptr, nullptr, nullptr, nullptr);
const UINT sync_interval = static_cast<UINT>(m_vsync_mode == GSVSyncMode::FIFO);
const UINT flags = (m_vsync_mode == GSVSyncMode::Disabled && m_using_allow_tearing) ? DXGI_PRESENT_ALLOW_TEARING : 0;
m_swap_chain->Present(sync_interval, flags);
if (m_gpu_timing_enabled)
KickTimestampQuery();
// clear out the swap chain view, it might get resized..
OMSetRenderTargets(nullptr, nullptr, nullptr, nullptr);
}
bool GSDevice11::CreateTimestampQueries()
@@ -1296,6 +1314,8 @@ void GSDevice11::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTextur
void GSDevice11::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ID3D11PixelShader* ps, ID3D11Buffer* ps_cb, ID3D11BlendState* bs, bool linear)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
CommitClear(sTex);
const bool draw_in_depth = dTex && dTex->IsDepthStencil();
@@ -1303,6 +1323,10 @@ void GSDevice11::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTextur
GSVector2i ds;
if (dTex)
{
// preemptively bind svr if possible
if (m_state.cached_rt_view != sTex && m_state.cached_dsv != sTex)
PSSetShaderResource(0, sTex);
// ps unbind conflicting srvs
PSUnbindConflictingSRVs(dTex);
@@ -1363,11 +1387,17 @@ void GSDevice11::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTextur
void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
CommitClear(sTex);
GSVector2i ds;
if (dTex)
{
// preemptively bind svr if possible
if (m_state.cached_rt_view != sTex && m_state.cached_dsv != sTex)
PSSetShaderResource(0, sTex);
// ps unbind conflicting srvs
PSUnbindConflictingSRVs(dTex);
@@ -1397,7 +1427,7 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
const float right = dRect.z * 2 / ds.x - 1.0f;
const float bottom = 1.0f - dRect.w * 2 / ds.y;
GSVertexPT1 vertices[] =
const GSVertexPT1 vertices[] =
{
{GSVector4(left, top, 0.5f, 1.0f), GSVector2(sRect.x, sRect.y)},
{GSVector4(right, top, 0.5f, 1.0f), GSVector2(sRect.z, sRect.y)},
@@ -1427,13 +1457,14 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
void GSDevice11::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize)
{
// match merge cb
struct Uniforms
struct alignas(16) Uniforms
{
float scale;
float pad1[3];
u32 offsetX, offsetY, dOffset;
u32 pad2;
};
const Uniforms cb = {sScale, {}, offsetX, offsetY, dOffset};
const Uniforms cb = {sScale, {}, offsetX, offsetY, dOffset, 0};
m_ctx->UpdateSubresource(m_merge.cb.get(), 0, nullptr, &cb, 0, 0);
const GSVector4 dRect(0, 0, dSize, 1);
@@ -1444,14 +1475,15 @@ void GSDevice11::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u
void GSDevice11::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM)
{
// match merge cb
struct Uniforms
struct alignas(16) Uniforms
{
float scale;
float pad1[3];
u32 SBW, DBW, SPSM;
u32 pad2;
};
const Uniforms cb = {sScale, {}, SBW, DBW, SPSM};
const Uniforms cb = {sScale, {}, SBW, DBW, SPSM, 0};
m_ctx->UpdateSubresource(m_merge.cb.get(), 0, nullptr, &cb, 0, 0);
const GSVector4 dRect(0, 0, dTex->GetWidth(), dTex->GetHeight());
@@ -1461,7 +1493,7 @@ void GSDevice11::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offs
void GSDevice11::FilteredDownsampleTexture(GSTexture* sTex, GSTexture* dTex, u32 downsample_factor, const GSVector2i& clamp_min, const GSVector4& dRect)
{
struct Uniforms
struct alignas(16) Uniforms
{
float weight;
float step_multiplier;
@@ -1520,6 +1552,8 @@ void GSDevice11::DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_re
void GSDevice11::DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, const GSVector2& ds)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// Don't use primitive restart here, it ends up slower on some drivers.
const u32 vertex_reserve_size = num_rects * 4;
const u32 index_reserve_size = num_rects * 6;
@@ -1719,6 +1753,7 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
ShaderMacro sm;
sm.AddMacro("PIXEL_SHADER", 1);
sm.AddMacro("PS_HAS_CONSERVATIVE_DEPTH", m_conservative_depth);
sm.AddMacro("PS_FST", sel.fst);
sm.AddMacro("PS_WMS", sel.wms);
sm.AddMacro("PS_WMT", sel.wmt);
@@ -2063,13 +2098,13 @@ void GSDevice11::RenderImGui()
UpdateImGuiTextures();
const float L = 0.0f;
constexpr float L = 0.0f;
const float R = static_cast<float>(m_window_info.surface_width);
const float T = 0.0f;
constexpr float T = 0.0f;
const float B = static_cast<float>(m_window_info.surface_height);
// clang-format off
const float ortho_projection[4][4] =
const GSVector4 ortho_projection[4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
@@ -2160,6 +2195,8 @@ void GSDevice11::RenderImGui()
void GSDevice11::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSVector4i& bbox)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// sfex3 (after the capcom logo), vf4 (first menu fading in), ffxii shadows, rumble roses shadows, persona4 shadows
CommitClear(rt);
@@ -2167,6 +2204,11 @@ void GSDevice11::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSV
m_ctx->ClearDepthStencilView(*static_cast<GSTexture11*>(ds), D3D11_CLEAR_STENCIL, 0.0f, 0);
// preemptively bind svr if possible
if (m_state.cached_rt_view != rt && m_state.cached_dsv != rt)
PSSetShaderResource(0, rt);
// ps unbind conflicting srvs
PSUnbindConflictingSRVs(ds);
@@ -2380,6 +2422,7 @@ void GSDevice11::VSSetShader(ID3D11VertexShader* vs, ID3D11Buffer* vs_cb)
void GSDevice11::PSSetShaderResource(int i, GSTexture* sr)
{
// Update local state only, PSUpdateShaderState updates gpu state.
m_state.ps_sr_views[i] = *static_cast<GSTexture11*>(sr);
}
@@ -2416,7 +2459,7 @@ void GSDevice11::PSUpdateShaderState(const bool sr_update, const bool ss_update)
if (sr_update)
{
bool sr_changed = false;
for (size_t i = 0; i < m_state.ps_sr_views.size(); ++i)
for (size_t i = 0; i < MAX_TEXTURES; ++i)
{
if (m_state.ps_cached_sr_views[i] != m_state.ps_sr_views[i])
{
@@ -2428,14 +2471,14 @@ void GSDevice11::PSUpdateShaderState(const bool sr_update, const bool ss_update)
if (sr_changed)
{
m_state.ps_cached_sr_views = m_state.ps_sr_views;
m_ctx->PSSetShaderResources(0, m_state.ps_sr_views.size(), m_state.ps_sr_views.data());
m_ctx->PSSetShaderResources(0, MAX_TEXTURES, m_state.ps_sr_views.data());
}
}
if (ss_update)
{
bool ss_changed = false;
for (size_t i = 0; i < m_state.ps_ss.size(); ++i)
for (size_t i = 0; i < MAX_SAMPLERS; ++i)
{
if (m_state.ps_cached_ss[i] != m_state.ps_ss[i])
{
@@ -2447,7 +2490,7 @@ void GSDevice11::PSUpdateShaderState(const bool sr_update, const bool ss_update)
if (ss_changed)
{
m_state.ps_cached_ss = m_state.ps_ss;
m_ctx->PSSetSamplers(0, m_state.ps_ss.size(), m_state.ps_ss.data());
m_ctx->PSSetSamplers(0, MAX_SAMPLERS, m_state.ps_ss.data());
}
}
}
@@ -2456,9 +2499,10 @@ void GSDevice11::PSUnbindConflictingSRVs(GSTexture* tex1, GSTexture* tex2)
{
// Make sure no SRVs are bound using the same texture before binding it to a RTV.
bool changed = false;
for (size_t i = 0; i < m_state.ps_sr_views.size(); i++)
for (size_t i = 0; i < MAX_TEXTURES; i++)
{
if ((tex1 && m_state.ps_sr_views[i] == *static_cast<GSTexture11*>(tex1)) || (tex2 && m_state.ps_sr_views[i] == *static_cast<GSTexture11*>(tex2)))
// We chech against what's currently bound (cached_sr_views), then update local state (ps_sr_views) which calls PSUpdateShaderState to update gpu state.
if ((tex1 && m_state.ps_cached_sr_views[i] == *static_cast<GSTexture11*>(tex1)) || (tex2 && m_state.ps_cached_sr_views[i] == *static_cast<GSTexture11*>(tex2)))
{
m_state.ps_sr_views[i] = nullptr;
changed = true;
@@ -2602,7 +2646,6 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
const GSVector4 dRect(config.colclip_update_area);
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
StretchRect(colclip_rt, sRect, config.rt, dRect, ShaderConvert::COLCLIP_RESOLVE, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(colclip_rt);
g_gs_device->SetColorClipTexture(nullptr);
@@ -2619,7 +2662,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
{
config.colclip_update_area = config.drawarea;
colclip_rt = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::ColorClip, false);
colclip_rt = CreateRenderTarget(rtsize.x, rtsize.y, m_rgba16_unorm_hw_blend ? GSTexture::Format::ColorClip : GSTexture::Format::ColorHDR, false);
if (!colclip_rt)
{
Console.Warning("D3D11: Failed to allocate ColorClip render target, aborting draw.");
@@ -2631,7 +2674,6 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
const GSVector4 dRect = GSVector4((config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
StretchRect(config.rt, sRect, colclip_rt, dRect, ShaderConvert::COLCLIP_INIT, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
}
@@ -2702,20 +2744,29 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
if (config.tex && config.tex == config.ds)
read_only_dsv = static_cast<GSTexture11*>(config.ds)->ReadOnlyDepthStencilView();
// Should be called before changing local srv state.
PSUnbindConflictingSRVs(colclip_rt ? colclip_rt : config.rt, read_only_dsv ? nullptr : config.ds);
// Preemptively bind svr if possible.
// We update the local state, then if there are srv conflicts PSUnbindConflictingSRVs will update the gpu state.
if (config.tex)
{
CommitClear(config.tex);
PSSetShaderResource(0, config.tex);
if (m_state.cached_rt_view != config.tex && m_state.cached_dsv != config.tex)
PSSetShaderResource(0, config.tex);
}
if (config.pal)
{
CommitClear(config.pal);
PSSetShaderResource(1, config.pal);
if (m_state.cached_rt_view != config.pal && m_state.cached_dsv != config.pal)
PSSetShaderResource(1, config.pal);
}
// Should be called before changing current gpu state.
PSUnbindConflictingSRVs(colclip_rt ? colclip_rt : config.rt, read_only_dsv ? nullptr : config.ds);
if (config.tex)
PSSetShaderResource(0, config.tex);
if (config.pal)
PSSetShaderResource(1, config.pal);
SetupVS(config.vs, &config.cb_vs);
SetupPS(config.ps, &config.cb_ps, config.sampler);
@@ -2816,7 +2867,6 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
const GSVector4 dRect(config.colclip_update_area);
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
StretchRect(colclip_rt, sRect, config.rt, dRect, ShaderConvert::COLCLIP_RESOLVE, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(colclip_rt);
g_gs_device->SetColorClipTexture(nullptr);
@@ -2845,6 +2895,7 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config, GSTexture* draw_rt_clo
{
const u32 draw_list_size = static_cast<u32>(config.drawlist->size());
const u32 indices_per_prim = config.indices_per_prim;
const GSVector4i rtsize(0, 0, draw_rt->GetWidth(), draw_rt->GetHeight());
pxAssert(config.drawlist && !config.drawlist->empty());
pxAssert(config.drawlist_bbox && static_cast<u32>(config.drawlist_bbox->size()) == draw_list_size);
@@ -2853,10 +2904,28 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config, GSTexture* draw_rt_clo
{
const u32 count = (*config.drawlist)[n] * indices_per_prim;
GSVector4i bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea);
const GSVector4i original_bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea);
GSVector4i snapped_bbox = original_bbox;
// Copy only the part needed by the draw.
CopyAndBind(bbox);
// We don't want the snapped box adjustments when the rect is empty as it might make the copy to pass.
// The empty rect itself needs to be handled in renderer properly.
if (!snapped_bbox.rempty())
{
// Aligning bbox to 4 pixel boundaries so copies will be faster using Direct Memory Access,
// otherwise it may stall as more commands need to be issued.
snapped_bbox.left &= ~3;
snapped_bbox.top &= ~3;
snapped_bbox.right = (snapped_bbox.right + 3) & ~3;
snapped_bbox.bottom = (snapped_bbox.bottom + 3) & ~3;
// Ensure the new sizes are within bounds.
snapped_bbox.left = std::max(0, snapped_bbox.left);
snapped_bbox.top = std::max(0, snapped_bbox.top);
snapped_bbox.right = std::min(snapped_bbox.right, rtsize.right);
snapped_bbox.bottom = std::min(snapped_bbox.bottom, rtsize.bottom);
}
CopyAndBind(snapped_bbox);
DrawIndexedPrimitive(p, count);
p += count;
+2
View File
@@ -136,6 +136,8 @@ private:
bool m_using_flip_model_swap_chain = true;
bool m_using_allow_tearing = false;
bool m_is_exclusive_fullscreen = false;
bool m_conservative_depth = false;
bool m_rgba16_unorm_hw_blend = false;
struct
{
+42 -16
View File
@@ -193,11 +193,24 @@ void GSDevice12::LoadAgilitySDK()
if (agility_loaded)
return;
HRESULT hr;
// On older versions of Windows 10 (example 2019 LTSC) D3D12GetInterface may fail because it doesn't exist,
// in such case we can check if D3D12GetInterface exists first.
const HMODULE d3d12 = GetModuleHandleW(L"d3d12.dll");
if (!d3d12)
return;
using PFN_D3D12GetInterface = HRESULT(WINAPI*)(REFCLSID rclsid, REFIID riid, void** ppv);
auto pD3D12GetInterface = reinterpret_cast<PFN_D3D12GetInterface>(GetProcAddress(d3d12, "D3D12GetInterface"));
if (!pD3D12GetInterface)
{
Console.Error("D3D12: Agility SDK configuration is not available");
return;
}
// See https://microsoft.github.io/DirectX-Specs/d3d/IndependentDevices.html
ComPtr<ID3D12SDKConfiguration1> sdk_configuration;
hr = D3D12GetInterface(CLSID_D3D12SDKConfiguration, IID_PPV_ARGS(sdk_configuration.put()));
HRESULT hr;
hr = pD3D12GetInterface(CLSID_D3D12SDKConfiguration, IID_PPV_ARGS(sdk_configuration.put()));
if (FAILED(hr))
{
Console.Error("D3D12: Agility SDK configuration is not available");
@@ -925,6 +938,7 @@ void GSDevice12::SetVSyncMode(GSVSyncMode mode, bool allow_present_throttle)
if (GetSwapChainBufferCount() != old_buffer_count)
{
ExecuteCommandList(true);
DestroySwapChain();
if (!CreateSwapChain())
pxFailRel("Failed to recreate swap chain after vsync change.");
@@ -1393,6 +1407,11 @@ bool GSDevice12::CheckFeatures(const u32& vendor_id)
DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, sizeof(allow_tearing_supported));
m_allow_tearing_supported = (SUCCEEDED(hr) && allow_tearing_supported == TRUE);
D3D12_FEATURE_DATA_D3D12_OPTIONS3 device_options3 = {};
hr = m_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS3, &device_options3, sizeof(device_options3));
m_typed_casting_supported = SUCCEEDED(hr) && device_options3.CastingFullyTypedFormatSupported;
Console.WriteLnFmt("D3D12: Casting Fully Typed Formats: {}", m_typed_casting_supported ? "Supported" : "Not Supported");
D3D12_FEATURE_DATA_D3D12_OPTIONS12 device_options12 = {};
hr = m_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS12, &device_options12, sizeof(device_options12));
if (SUCCEEDED(hr))
@@ -1456,7 +1475,7 @@ void GSDevice12::LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_f
const auto& mapping = s_format_mapping[static_cast<int>(format)];
if (d3d_format)
*d3d_format = mapping[0];
*d3d_format = (format == GSTexture::Format::DepthStencil && !m_typed_casting_supported) ? DXGI_FORMAT_R32G8X24_TYPELESS : mapping[0];
if (srv_format)
*srv_format = mapping[1];
if (rtv_format)
@@ -1622,14 +1641,14 @@ void GSDevice12::UpdateCLUTTexture(
GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize)
{
// match merge cb
struct Uniforms
struct alignas(16) Uniforms
{
float scale;
float pad1[3];
u32 offsetX, offsetY, dOffset;
u32 pad2;
};
const Uniforms cb = {sScale, {}, offsetX, offsetY, dOffset};
const Uniforms cb = {sScale, {}, offsetX, offsetY, dOffset, 0};
SetUtilityRootSignature();
SetUtilityPushConstants(&cb, sizeof(cb));
@@ -1643,14 +1662,15 @@ void GSDevice12::ConvertToIndexedTexture(
GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM)
{
// match merge cb
struct Uniforms
struct alignas(16) Uniforms
{
float scale;
float pad1[3];
u32 SBW, DBW, SPSM;
u32 pad2;
};
const Uniforms cb = {sScale, {}, SBW, DBW, SPSM};
const Uniforms cb = {sScale, {}, SBW, DBW, SPSM, 0};
SetUtilityRootSignature();
SetUtilityPushConstants(&cb, sizeof(cb));
@@ -1662,7 +1682,7 @@ void GSDevice12::ConvertToIndexedTexture(
void GSDevice12::FilteredDownsampleTexture(GSTexture* sTex, GSTexture* dTex, u32 downsample_factor, const GSVector2i& clamp_min, const GSVector4& dRect)
{
struct Uniforms
struct alignas(16) Uniforms
{
float weight;
float step_multiplier;
@@ -1728,6 +1748,8 @@ void GSDevice12::DrawMultiStretchRects(
void GSDevice12::DoMultiStretchRects(
const MultiStretchRect* rects, u32 num_rects, GSTexture12* dTex, ShaderConvert shader)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// Set up vertices first.
const u32 vertex_reserve_size = num_rects * 4 * sizeof(GSVertexPT1);
const u32 index_reserve_size = num_rects * 6 * sizeof(u16);
@@ -1865,6 +1887,8 @@ void GSDevice12::DoStretchRect(GSTexture12* sTex, const GSVector4& sRect, GSText
void GSDevice12::DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// ia
const float left = dRect.x * 2 / ds.x - 1.0f;
const float top = 1.0f - dRect.y * 2 / ds.y;
@@ -2171,7 +2195,7 @@ void GSDevice12::RenderImGui()
const float B = static_cast<float>(m_window_info.surface_height);
// clang-format off
const float ortho_projection[4][4] =
const GSVector4 ortho_projection[4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
@@ -3033,6 +3057,7 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector&
ShaderMacro sm;
sm.AddMacro("PIXEL_SHADER", 1);
sm.AddMacro("PS_HAS_CONSERVATIVE_DEPTH", 1);
sm.AddMacro("PS_FST", sel.fst);
sm.AddMacro("PS_WMS", sel.wms);
sm.AddMacro("PS_WMT", sel.wmt);
@@ -3885,6 +3910,8 @@ void GSDevice12::SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb)
void GSDevice12::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSVector4i& bbox)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
GL_PUSH("SetupDATE {%d,%d} %dx%d", bbox.left, bbox.top, bbox.width(), bbox.height());
const GSVector2i size(ds->GetSize());
@@ -3917,6 +3944,8 @@ void GSDevice12::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSV
GSTexture12* GSDevice12::SetupPrimitiveTrackingDATE(GSHWDrawConfig& config, PipelineSelector& pipe)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// How this is done:
// - can't put a barrier for the image in the middle of the normal render pass, so that's out
// - so, instead of just filling the int texture with INT_MAX, we sample the RT and use -1 for failing values
@@ -3992,10 +4021,6 @@ GSTexture12* GSDevice12::SetupPrimitiveTrackingDATE(GSHWDrawConfig& config, Pipe
void GSDevice12::FeedbackBarrier(const GSTexture12* texture)
{
// The DX12 spec notes "You may not read from, or consume, a write that occurred within the same render pass".
// The only exception being the implicit reads for render target blending or depth testing.
// Thus, in addition to a barrier, we need to end the render pass.
EndRenderPass();
if (m_enhanced_barriers)
{
// Enhanced barriers allows for single resource feedback.
@@ -4008,6 +4033,10 @@ void GSDevice12::FeedbackBarrier(const GSTexture12* texture)
}
else
{
// The DX12 spec notes "You may not read from, or consume, a write that occurred within the same render pass".
// The only exception being the implicit reads for render target blending or depth testing.
// Thus, in addition to a barrier, we need to end the render pass.
EndRenderPass();
// Specify null for the after resource as both resources are used after the barrier.
// While this may also be true before the barrier, we only write using the main resource.
D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_ALIASING, D3D12_RESOURCE_BARRIER_FLAG_NONE};
@@ -4063,7 +4092,6 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
SetPipeline(m_colclip_finish_pipelines[pipe.ds].get());
SetUtilityTexture(colclip_rt, m_point_sampler_cpu);
DrawStretchRect(sRect, GSVector4(config.colclip_update_area), rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(colclip_rt);
g_gs_device->SetColorClipTexture(nullptr);
@@ -4265,7 +4293,6 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
const GSVector4 drawareaf = GSVector4((config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 sRect(drawareaf / GSVector4(rtsize.x, rtsize.y).xyxy());
DrawStretchRect(sRect, GSVector4(drawareaf), rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
GL_POP();
@@ -4343,7 +4370,6 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
SetPipeline(m_colclip_finish_pipelines[pipe.ds].get());
SetUtilityTexture(colclip_rt, m_point_sampler_cpu);
DrawStretchRect(sRect, GSVector4(config.colclip_update_area), rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(colclip_rt);
g_gs_device->SetColorClipTexture(nullptr);
+2 -1
View File
@@ -314,7 +314,8 @@ private:
bool m_allow_tearing_supported = false;
bool m_using_allow_tearing = false;
bool m_is_exclusive_fullscreen = false;
bool m_enhanced_barriers = true;
bool m_enhanced_barriers = false;
bool m_typed_casting_supported = false;
bool m_device_lost = false;
ComPtr<ID3D12RootSignature> m_tfx_root_signature;
+2 -1
View File
@@ -756,7 +756,7 @@ bool GSHwHack::GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip)
config.ps.channel = ChannelFetch_RGB;
config.colormask.wrgba = 1 | 2 | 4;
r.EndHLEHardwareDraw(false);
src->m_last_draw = r.s_n;
return true;
}
else
@@ -814,6 +814,7 @@ bool GSHwHack::GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip)
config.ps.channel = ChannelFetch_RED + channel;
config.colormask.wrgba = 8;
r.EndHLEHardwareDraw(false);
dst->m_last_draw = r.s_n;
}
return true;
+348 -55
View File
@@ -165,11 +165,12 @@ GSTexture* GSRendererHW::GetOutput(int i, float& scale, int& y_offset)
if (GSTextureCache::Target* rt = g_texture_cache->LookupDisplayTarget(TEX0, framebufferSize, GetTextureScaleFactor(), false))
{
const u32 bp_adj = (TEX0.TBP0 < rt->m_TEX0.TBP0 && rt->UnwrappedEndBlock() > GS_MAX_BLOCKS) ? (TEX0.TBP0 + GS_MAX_BLOCKS) : TEX0.TBP0;
rt->Update();
t = rt->m_texture;
scale = rt->m_scale;
const int delta = TEX0.TBP0 - rt->m_TEX0.TBP0;
const int delta = bp_adj - rt->m_TEX0.TBP0;
if (delta > 0 && curFramebuffer.FBW != 0)
{
const int pages = delta >> 5u;
@@ -2338,6 +2339,12 @@ void GSRendererHW::Draw()
return;
}
// Sometimes everything will get reset and it will draw a single black point in the top left corner,
// which can cause invalid targets to be created, so might as well skip it.
if (GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).eq(GSVector4i::zero()) && m_vt.m_eq.rgba == 0xffff &&
m_vt.m_max.c.rgba32() == 0 && m_draw_env->PRIM.PRIM == GS_POINTLIST && m_env.PRIM.PRIM != GS_POINTLIST)
return;
// Channel shuffles repeat lots of draws. Get out early if we can.
if (m_channel_shuffle)
{
@@ -2844,7 +2851,7 @@ void GSRendererHW::Draw()
const bool page_aligned = (m_r.w % pgs.y) == (pgs.y - 1) || (m_r.w % pgs.y) == 0;
const bool is_zero_color_clear = (GetConstantDirectWriteMemClearColor() == 0 && !preserve_rt_color && page_aligned);
const bool is_zero_depth_clear = (GetConstantDirectWriteMemClearDepth() == 0 && !preserve_depth && page_aligned);
bool gs_mem_cleared = false;
// If it's an invalid-sized draw, do the mem clear on the CPU, we don't want to create huge targets.
// If clearing to zero, don't bother creating the target. Games tend to clear more than they use, wasting VRAM/bandwidth.
if (is_zero_color_clear || is_zero_depth_clear || height_invalid)
@@ -2876,7 +2883,7 @@ void GSRendererHW::Draw()
{
g_texture_cache->InvalidateTemporaryZ();
}
gs_mem_cleared |= overwriting_whole_rt && overwriting_whole_ds && (!no_rt || !no_ds);
if (overwriting_whole_rt && overwriting_whole_ds &&
TryGSMemClear(no_rt, preserve_rt_color, is_zero_color_clear, rt_end_bp,
no_ds, preserve_depth, is_zero_depth_clear, ds_end_bp))
@@ -2906,6 +2913,27 @@ void GSRendererHW::Draw()
return;
}
}
// If not a zero clear or the RT's aren't fully overwritten, we need to see if this is clearing for a future operation.
// So if the FBP or Z being cleared isn't getting used next frame, clear the actual GS memory.
if (!gs_mem_cleared)
{
const int get_next_ctx = m_env.PRIM.CTXT;
const GSDrawingContext& next_ctx = m_env.CTXT[get_next_ctx];
if ((!no_rt && next_ctx.FRAME.FBP != m_cached_ctx.FRAME.FBP) || (!no_ds && next_ctx.ZBUF.ZBP != m_cached_ctx.ZBUF.ZBP))
{
bool frame_masked = no_rt || (m_cached_ctx.FRAME.FBMSK & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk) || !IsOpaque() || !IsRTWritten();
const bool z_masked = no_ds || m_cached_ctx.ZBUF.ZMSK;
if (frame_masked && m_cached_ctx.FRAME.PSM == PSMCT32 && m_cached_ctx.FRAME.FBMSK == 0xFF000000u)
{
frame_masked = no_rt || !IsOpaque() || !IsRTWritten();
}
// Force clear of memory but don't invalidate anything.
TryGSMemClear(frame_masked, false, false, 0, z_masked, false, false, 0);
}
}
}
GIFRegTEX0 TEX0 = {};
@@ -3215,6 +3243,7 @@ void GSRendererHW::Draw()
float target_scale = GetTextureScaleFactor();
bool scaled_copy = false;
int scale_draw = IsScalingDraw(src, m_primitive_covers_without_gaps != NoGapsType::GapsFound);
m_downscale_source = false;
if (GSConfig.UserHacks_NativeScaling != GSNativeScaling::Off)
{
@@ -3248,8 +3277,6 @@ void GSRendererHW::Draw()
scale_draw = 1;
scaled_copy = true;
}
m_downscale_source = false;
}
}
@@ -3819,7 +3846,6 @@ void GSRendererHW::Draw()
GL_CACHE("HW: RT in RT Z copy back draw %d z_vert_offset %d z_offset %d", s_n, z_vertical_offset, vertical_offset);
g_gs_device->StretchRect(g_texture_cache->GetTemporaryZ(), sRect, ds->m_texture, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
g_texture_cache->InvalidateTemporaryZ();
@@ -3836,7 +3862,6 @@ void GSRendererHW::Draw()
dRect = dRect.min_u32(GSVector4i(ds->m_unscaled_size.x * ds->m_scale, ds->m_unscaled_size.y * ds->m_scale).xyxy());
g_gs_device->StretchRect(ds->m_texture, sRect, g_texture_cache->GetTemporaryZ(), GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
z_address_info.rect_since = GSVector4i::zero();
g_texture_cache->SetTemporaryZInfo(z_address_info);
}
@@ -3884,7 +3909,6 @@ void GSRendererHW::Draw()
if (m_cached_ctx.TEST.ZTST > ZTST_ALWAYS || !dRect.rintersect(GSVector4i(GSVector4(m_r) * ds->m_scale)).eq(dRect))
{
g_gs_device->StretchRect(ds->m_texture, sRect, tex, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
g_texture_cache->SetTemporaryZ(tex);
g_texture_cache->SetTemporaryZInfo(ds->m_TEX0.TBP0, page_offset, rt_page_offset);
@@ -4496,7 +4520,6 @@ void GSRendererHW::Draw()
{
const GSVector4i dRect = GSVector4i(0, 0, g_texture_cache->GetTemporaryZ()->GetWidth(), g_texture_cache->GetTemporaryZ()->GetHeight());
g_gs_device->StretchRect(g_texture_cache->GetTemporaryZ(), GSVector4(0.0f, 0.0f, 1.0f, 1.0f), tex, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
g_texture_cache->InvalidateTemporaryZ();
g_texture_cache->SetTemporaryZ(tex);
}
@@ -4688,31 +4711,260 @@ void GSRendererHW::Draw()
// Note: second hack corrects only the texture coordinate
// Be careful to not correct downscaled targets, this can get messy and break post processing
// but it still needs to adjust native stuff from memory as it's not been compensated for upscaling (Dragon Quest 8 font for example).
if (CanUpscale() && (m_vt.m_primclass == GS_SPRITE_CLASS) && rt && rt->GetScale() > 1.0f)
if (CanUpscale() && ((m_vt.m_primclass == GS_SPRITE_CLASS) || ((m_index.tail % 6 == 0) && m_vt.m_primclass == GS_TRIANGLE_CLASS && m_vt.m_eq.z)) && rt && rt->GetScale() > 1.0f)
{
const u32 count = m_vertex.next;
GSVertex* v = &m_vertex.buff[0];
bool valid_format = true;
// Hack to avoid vertical black line in various games (ace combat/tekken)
if (GSConfig.UserHacks_AlignSpriteX)
if (m_vt.m_primclass == GS_TRIANGLE_CLASS)
{
// Note for performance reason I do the check only once on the first
// primitive
const int win_position = v[1].XYZ.X - context->XYOFFSET.OFX;
const bool unaligned_position = ((win_position & 0xF) == 8);
const bool unaligned_texture = ((v[1].U & 0xF) == 0) && PRIM->FST; // I'm not sure this check is useful
const bool hole_in_vertex = (count < 4) || (v[1].XYZ.X != v[2].XYZ.X);
if (hole_in_vertex && unaligned_position && (unaligned_texture || !PRIM->FST))
const GSVertex* RESTRICT v = m_vertex.buff;
const u16* RESTRICT index = m_index.buff;
const size_t count = m_index.tail;
for (int i = 0; i < count; i += 6)
{
// Normaly vertex are aligned on full pixels and texture in half
// pixels. Let's extend the coverage of an half-pixel to avoid
// hole after upscaling
for (u32 i = 0; i < count; i += 2)
// Non-axis aligned check when only two triangles
if (!AreTrianglesQuadNonAA(v, &index[i], &index[i + 3]))
{
v[i + 1].XYZ.X += 8;
// I really don't know if it is a good idea. Neither what to do for !PRIM->FST
if (unaligned_texture)
v[i + 1].U += 8;
valid_format = false;
break;
}
}
}
if (valid_format)
{
const u32 count = m_vertex.next;
GSVertex* v = &m_vertex.buff[0];
u16* idx = &m_index.buff[0];
const bool indexed_texture = src && src->m_scale == 1.0f && GSLocalMemory::m_psm[src->m_TEX0.PSM].pal > 0;
// Shortcut for when the sprites are right up against each other.
if (GSConfig.UserHacks_AlignSpriteX && m_vt.m_primclass == GS_SPRITE_CLASS && indexed_texture && PRIM->FST && m_index.tail > 2 &&
((v[idx[1]].U == v[idx[2]].U && (v[idx[1]].XYZ.X == v[idx[2]].XYZ.X) && v[idx[0]].V == v[idx[2]].V) ||
(v[idx[1]].V == v[idx[2]].V && (v[idx[1]].XYZ.Y == v[idx[2]].XYZ.Y) && v[idx[0]].U == v[idx[2]].U)))
{
for (int i = 0; i < m_index.tail; i += 2)
{
if (!((v[idx[i + 1]].U == v[idx[i + 2]].U && (v[idx[i + 1]].XYZ.X == v[idx[i + 2]].XYZ.X) && v[idx[i]].V == v[idx[i + 2]].V)))
{
if (v[idx[i + 1]].U & 0x8)
v[idx[i + 1]].U -= 8;
}
if (!((v[idx[i + 1]].V == v[idx[i + 2]].V && (v[idx[i + 1]].XYZ.Y == v[idx[i + 2]].XYZ.Y) && v[idx[i]].U == v[idx[i + 2]].U)))
{
if (v[idx[i + 1]].V & 0x8)
v[idx[i + 11]].V -= 8;
}
}
if (v[idx[m_index.tail - 1]].U & 0x8)
v[idx[m_index.tail - 1]].U -= 8;
if (v[idx[m_index.tail - 1]].V & 0x8)
v[idx[m_index.tail - 1]].V -= 8;
}
else
// Hack to avoid vertical black line in various games (ace combat/tekken)
if (GSConfig.UserHacks_AlignSpriteX)
{
// Note for performance reason I do the check only once on the first
// primitive
const int win_position0 = v[idx[0]].XYZ.X - context->XYOFFSET.OFX;
const int win_position1 = v[idx[1]].XYZ.X - context->XYOFFSET.OFX;
const bool unaligned_position = ((win_position0 & 0xf) != 0) || ((win_position1 & 0xF) != 0);
const int first_s = (v[idx[0]].ST.S / v[idx[0]].RGBAQ.Q) * static_cast<int>(1 << m_cached_ctx.TEX0.TW);
const bool unaligned_texture = (PRIM->FST && ((v[1].U & 0xF) == 0)) || (!PRIM->FST && (first_s & 0xF) == 0); // I'm not sure this check is useful
const int gap = std::min(static_cast<int>(v[2].XYZ.X), static_cast<int>(v[3].XYZ.X)) - std::max(static_cast<int>(v[0].XYZ.X), static_cast<int>(v[1].XYZ.X));
bool hole_in_vertex = (count < 4) || (v[1].XYZ.X != v[2].XYZ.X && ((v[1].XYZ.X ^ v[2].XYZ.X) & 0xF) != 0);
const bool is_tri = m_vt.m_primclass == GS_TRIANGLE_CLASS;
const int skip = is_tri ? 3 : 2;
GSVector2 gradient = GSVector2(1.0f, 1.0f);
if (m_lod.y == 0)
{
bool can_disable_linear = true;
for (u32 i = 0; i < m_index.tail; i += skip)
{
const int x_offset = (is_tri && v[idx[i]].XYZ.X == v[idx[i + 1]].XYZ.X) ? 2 : 1;
const int y_offset = (is_tri && v[idx[i]].XYZ.Y == v[idx[i + 1]].XYZ.Y) ? 2 : 1;
GSVector2 vert = GSVector2(std::abs(static_cast<float>(v[idx[i]].XYZ.X - v[idx[i + x_offset]].XYZ.X)), std::abs(static_cast<float>(v[idx[i]].XYZ.Y - v[idx[i + y_offset]].XYZ.Y)));
GSVector2 tex;
if (!PRIM->FST)
{
const int s_offset = (is_tri && (v[idx[i]].ST.S / v[idx[i]].RGBAQ.Q) == (v[idx[i + 1]].ST.S / v[idx[i + 1]].RGBAQ.Q)) ? 2 : 1;
const int t_offset = (is_tri && (v[idx[i]].ST.T / v[idx[i]].RGBAQ.Q) == (v[idx[i + 1]].ST.T / v[idx[i + 1]].RGBAQ.Q)) ? 2 : 1;
GSVector2 v0, v1;
float s = std::min((v[idx[i]].ST.S / v[idx[i]].RGBAQ.Q), 1.0f);
float t = std::min((v[idx[i]].ST.T / v[idx[i]].RGBAQ.Q), 1.0f);
v0.x = static_cast<int>((1 << m_cached_ctx.TEX0.TW) * s * 16.0f);
v0.y = static_cast<int>((1 << m_cached_ctx.TEX0.TH) * t * 16.0f);
s = std::min((v[idx[i + s_offset]].ST.S / v[idx[i + s_offset]].RGBAQ.Q), 1.0f);
t = std::min((v[idx[i + t_offset]].ST.T / v[idx[i + t_offset]].RGBAQ.Q), 1.0f);
v1.x = static_cast<int>((1 << m_cached_ctx.TEX0.TW) * s * 16.0f);
v1.y = static_cast<int>((1 << m_cached_ctx.TEX0.TH) * t * 16.0f);
tex = GSVector2(std::abs(v1.x - v0.x), std::abs(v1.y - v0.y));
}
else
{
const int u_offset = (is_tri && v[idx[i]].U == v[idx[i + 1]].U) ? 2 : 1;
const int v_offset = (is_tri && v[idx[i]].V == v[idx[i + 1]].V) ? 2 : 1;
tex = GSVector2(std::abs(static_cast<float>(v[idx[i]].U - v[idx[i + u_offset]].U)), std::abs(static_cast<float>(v[idx[i]].V - v[idx[i + v_offset]].V)));
}
GSVector2 grad = tex / vert;
if ((grad.x != gradient.x && grad.x != floor(grad.x)) || (grad.y != gradient.y && grad.y != floor(grad.y)))
{
can_disable_linear = false;
gradient = grad;
break;
}
}
if (can_disable_linear)
{
m_vt.m_filter.linear = 0;
m_vt.m_filter.opt_linear = 0;
}
}
/*if ((m_vt.m_primclass == GS_SPRITE_CLASS) && hole_in_vertex && unaligned_position && (unaligned_texture || !PRIM->FST))
{
// Normaly vertex are aligned on full pixels and texture in half
// pixels. Let's extend the coverage of an half-pixel to avoid
// hole after upscaling
for (u32 i = 0; i < count; i += 2)
{
v[i + 1].XYZ.X += 8;
// I really don't know if it is a good idea. Neither what to do for !PRIM->FST
if (unaligned_texture)
v[i + 1].U += 8;
}
}
else */if (indexed_texture && (gradient.x > (1.0f / 16.0f) || gradient.y > (1.0f / 16.0f)))
{
const int comparitor = unaligned_position ? 0 : 8;
if (!PRIM->FST)
{
int s_offset = (is_tri && (v[idx[0]].ST.S / v[idx[0]].RGBAQ.Q) == (v[idx[1]].ST.S / v[idx[1]].RGBAQ.Q)) ? 2 : 1;
int t_offset = (is_tri && (v[idx[0]].ST.T / v[idx[0]].RGBAQ.Q) == (v[idx[1]].ST.T / v[idx[1]].RGBAQ.Q)) ? 2 : 1;
GSVector2i v0, v1;
float q = v[idx[0]].RGBAQ.Q == 0 ? FLT_MIN : v[idx[0]].RGBAQ.Q;
float s = v[idx[0]].ST.S / q;
float t = v[idx[0]].ST.T / q;
v0.x = static_cast<int>((1 << m_cached_ctx.TEX0.TW) * s * 16.0f);
v0.y = static_cast<int>((1 << m_cached_ctx.TEX0.TH) * t * 16.0f);
q = v[idx[s_offset]].RGBAQ.Q == 0 ? FLT_MIN : v[idx[s_offset]].RGBAQ.Q;
s = v[idx[s_offset]].ST.S / q;
t = v[idx[t_offset]].ST.T / q;
v1.x = static_cast<int>((1 << m_cached_ctx.TEX0.TW) * s * 16.0f);
v1.y = static_cast<int>((1 << m_cached_ctx.TEX0.TH) * t * 16.0f);
bool small_texture = std::abs(v1.x - v0.x) <= (64 * 16) || std::abs(v1.y - v0.y) <= (64 * 16);
bool offset_texture_x = (m_vt.IsLinear() || ((v0.x & 0xF) && (v1.x & 0xF))) && small_texture; // Keep them relatively small to avoid full screen stuff.
bool offset_texture_y = (m_vt.IsLinear() || ((v0.y & 0xF) && (v1.y & 0xF))) && small_texture;
//if (offset_texture_x && offset_texture_y)
{
for (u32 i = m_index.buff[0]; i < m_index.tail; i += skip)
{
GSVector2 st;
if (gradient.x > (1.0f / 16.0f))
{
float largest_s = std::max(is_tri ? (v[i + 2].ST.S / v[i + 2].RGBAQ.Q) : static_cast<float>(0), std::max((v[i].ST.S / v[i].RGBAQ.Q), (v[i + 1].ST.S / v[i + 1].RGBAQ.Q)));
float smallest_s = std::min(is_tri ? (v[i + 2].ST.S / v[i + 2].RGBAQ.Q) : static_cast<float>(0), std::min((v[i].ST.S / v[i].RGBAQ.Q), (v[i + 1].ST.S / v[i + 1].RGBAQ.Q)));
for (int j = 0; j < skip; j++)
{
q = v[i + j].RGBAQ.Q == 0 ? FLT_MIN : v[i + j].RGBAQ.Q;
float s = v[i + j].ST.S / q;
st.x = static_cast<float>(1 << m_cached_ctx.TEX0.TW) * s;
if ((v[i + j].ST.S / q) == largest_s)
v[i + j].ST.S = ((st.x - 0.5f) / static_cast<float>(1 << m_cached_ctx.TEX0.TW)) * q;
// Check the minimap in Persona 3.
if ((static_cast<int>(st.x * 16) & 0x8) == comparitor && (v[i + j].ST.S / q) == smallest_s)
v[i + j].ST.S = (std::max(0.0f, (st.x - 0.5f)) / static_cast<float>(1 << m_cached_ctx.TEX0.TW)) * q;
}
}
if (gradient.y > (1.0f / 16.0f))
{
float largest_t = std::max(is_tri ? (v[i + 2].ST.T / v[i + 2].RGBAQ.Q) : static_cast<float>(0), std::max((v[i].ST.T / v[i].RGBAQ.Q), (v[i + 1].ST.T / v[i + 1].RGBAQ.Q)));
float smallest_t = std::min(is_tri ? (v[i + 2].ST.T / v[i + 2].RGBAQ.Q) : static_cast<float>(0), std::min((v[i].ST.T / v[i].RGBAQ.Q), (v[i + 1].ST.T / v[i + 1].RGBAQ.Q)));
for (int j = 0; j < skip; j++)
{
q = v[i + j].RGBAQ.Q == 0 ? FLT_MIN : v[i + j].RGBAQ.Q;
float t = v[i + j].ST.T / q;
st.y = static_cast<float>(1 << m_cached_ctx.TEX0.TH) * t;
if ((v[i + j].ST.T / q) == largest_t)
v[i + j].ST.T = ((st.y - 0.5f) / static_cast<float>(1 << m_cached_ctx.TEX0.TH)) * q;
// Check the minimap in Persona 3.
if ((static_cast<int>(st.y * 16) & 0x8) == comparitor && (v[i + j].ST.T / q) == smallest_t)
v[i + j].ST.T = (std::max(0.0f, (st.y - 0.5f)) / static_cast<float>(1 << m_cached_ctx.TEX0.TH)) * q;
}
}
}
}
}
else
{
int u_offset = (is_tri && (v[0].U == v[1].U)) ? 2 : 1;
int v_offset = (is_tri && (v[0].V == v[1].V)) ? 2 : 1;
bool small_texture = std::abs(static_cast<int>(v[idx[u_offset]].U) - static_cast<int>(v[idx[0]].U)) <= (64 * 16) || std::abs(static_cast<int>(v[idx[v_offset]].V) - static_cast<int>(v[idx[0]].V)) <= (64 * 16);
bool offset_texture_x = (m_vt.IsLinear() || ((v[0].U & 0xF) && (v[idx[u_offset]].U & 0xF))) && small_texture;
bool offset_texture_y = (m_vt.IsLinear() || ((v[0].V & 0xF) && (v[idx[v_offset]].V & 0xF))) && small_texture;
if (offset_texture_x && offset_texture_y)
{
for (u32 i = m_index.buff[0]; i < m_index.tail; i += skip)
{
if (gradient.x > (1.0f / 16.0f))
{
const u16 largest_u = std::max(is_tri ? v[idx[i + 2]].U : static_cast<u16>(0), std::max(v[idx[i]].U, v[idx[i + 1]].U));
if ((v[idx[i]].U & 0x8) == comparitor && v[idx[i]].U == largest_u)
v[idx[i]].U = std::max(static_cast<int>(v[idx[i]].U) - 8, 0);
if ((v[idx[i + 1]].U & 0x8) == comparitor && v[idx[i + 1]].U == largest_u)
v[idx[i + 1]].U = std::max(static_cast<int>(v[idx[i + 1]].U) - 8, 0);
if (is_tri && (v[idx[i + 2]].U & 0x8) == comparitor && v[idx[i + 2]].U == largest_u)
v[idx[i + 2]].U = std::max(static_cast<int>(v[idx[i + 2]].U) - 8, 0);
}
if (gradient.y > (1.0f / 16.0f))
{
const u16 largest_v = std::max(is_tri ? v[idx[i + 2]].V : static_cast<u16>(0), std::max(v[idx[i]].V, v[idx[i + 1]].V));
if ((v[idx[i]].V & 0x8) == comparitor && v[idx[i]].V == largest_v)
v[idx[i]].V = std::max(static_cast<int>(v[idx[i]].V) - 8, 0);
if ((v[idx[i + 1]].V & 0x8) == comparitor && v[idx[i + 1]].V == largest_v)
v[idx[i + 1]].V = std::max(static_cast<int>(v[idx[i + 1]].V) - 8, 0);
if (is_tri && (v[idx[i + 2]].V & 0x8) == comparitor && v[idx[i + 2]].V == largest_v)
v[idx[i + 2]].V = std::max(static_cast<int>(v[idx[i + 2]].V) - 8, 0);
}
}
}
}
}
else if ((m_vt.m_primclass == GS_SPRITE_CLASS) && hole_in_vertex && unaligned_position && (unaligned_texture || !PRIM->FST))
{
// Normaly vertex are aligned on full pixels and texture in half
// pixels. Let's extend the coverage of an half-pixel to avoid
// hole after upscaling
for (u32 i = 0; i < count; i += 2)
{
v[i + 1].XYZ.X += 8;
// I really don't know if it is a good idea. Neither what to do for !PRIM->FST
if (unaligned_texture)
v[i + 1].U += 8;
}
}
}
}
@@ -4803,7 +5055,6 @@ void GSRendererHW::Draw()
GL_CACHE("HW: RT in RT Z copy back draw %d z_vert_offset %d rt_vert_offset %d z_horz_offset %d rt_horz_offset %d", s_n, z_vertical_offset, vertical_offset, z_horizontal_offset, horizontal_offset);
g_gs_device->StretchRect(g_texture_cache->GetTemporaryZ(), sRect, ds->m_texture, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
else if (m_temp_z_full_copy)
{
@@ -4816,7 +5067,6 @@ void GSRendererHW::Draw()
GL_CACHE("HW: RT in RT Z copy back draw %d z_vert_offset %d z_offset %d", s_n, z_vertical_offset, vertical_offset);
g_gs_device->StretchRect(g_texture_cache->GetTemporaryZ(), sRect, ds->m_texture, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
m_temp_z_full_copy = false;
@@ -5168,7 +5418,9 @@ void GSRendererHW::EmulateZbuffer(const GSTextureCache::Target* ds)
m_conf.cb_vs.max_depth = GSVector2i(0xFFFFFFFF);
//ps_cb.MaxDepth = GSVector4(0.0f, 0.0f, 0.0f, 1.0f);
m_conf.ps.zclamp = 0;
m_conf.ps.zfloor = !m_cached_ctx.ZBUF.ZMSK;
m_conf.ps.zfloor = !m_vt.m_eq.z &&
(m_vt.m_primclass == GS_TRIANGLE_CLASS || m_vt.m_primclass == GS_LINE_CLASS) &&
(m_cached_ctx.DepthWrite() || (m_cached_ctx.DepthRead() && m_cached_ctx.TEST.ZTST == ZTST_GREATER));
if (clamp_z)
{
@@ -5881,6 +6133,7 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, const boo
{
case AccBlendLevel::Maximum:
sw_blending |= true;
accumulation_blend &= (GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 32);
[[fallthrough]];
case AccBlendLevel::Full:
sw_blending |= m_conf.ps.blend_a != m_conf.ps.blend_b && alpha_c0_high_max_one;
@@ -7062,8 +7315,6 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
if (m_downscale_source)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// Can't use box filtering on depth (yet), or fractional scales.
if (src_target->m_texture->IsDepthStencil() || std::floor(src_target->GetScale()) != src_target->GetScale())
{
@@ -7091,9 +7342,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
}
else
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
const GSVector4i offset = copy_range - GSVector4i(copy_dst_offset).xyxy();
// Adjust for bilinear, must be done after calculating offset.
copy_range.x -= 1;
copy_range.y -= 1;
@@ -8935,17 +9184,17 @@ bool GSRendererHW::TryGSMemClear(bool no_rt, bool preserve_rt, bool invalidate_r
if (m_r.width() < ((static_cast<int>(m_cached_ctx.FRAME.FBW) - 1) * 64))
return false;
if (!no_rt && !preserve_rt)
if (!no_rt && (!preserve_rt || (IsOpaque() && m_cached_ctx.FRAME.FBMSK)))
{
ClearGSLocalMemory(m_context->offset.fb, m_r, GetConstantDirectWriteMemClearColor());
if (invalidate_rt)
if (invalidate_rt && !preserve_rt)
{
g_texture_cache->InvalidateVideoMem(m_context->offset.fb, m_r, false);
g_texture_cache->InvalidateContainedTargets(
GSLocalMemory::GetStartBlockAddress(
m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, m_r),
rt_end_bp, m_cached_ctx.FRAME.PSM, m_cached_ctx.FRAME.FBW);
rt_end_bp, m_cached_ctx.FRAME.PSM, m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.FBMSK);
GSUploadQueue clear_queue;
clear_queue.draw = s_n;
@@ -8956,6 +9205,13 @@ bool GSRendererHW::TryGSMemClear(bool no_rt, bool preserve_rt, bool invalidate_r
clear_queue.zero_clear = true;
m_draw_transfers.push_back(clear_queue);
}
else
{
g_texture_cache->InvalidateContainedTargets(
GSLocalMemory::GetStartBlockAddress(
m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, m_r),
rt_end_bp, m_cached_ctx.FRAME.PSM, m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.FBMSK, true);
}
}
if (!no_ds && !preserve_z)
@@ -8969,6 +9225,15 @@ bool GSRendererHW::TryGSMemClear(bool no_rt, bool preserve_rt, bool invalidate_r
GSLocalMemory::GetStartBlockAddress(
m_cached_ctx.ZBUF.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.ZBUF.PSM, m_r),
ds_end_bp, m_cached_ctx.ZBUF.PSM, m_cached_ctx.FRAME.FBW);
GSUploadQueue clear_queue;
clear_queue.draw = s_n;
clear_queue.rect = m_r;
clear_queue.blit.DBP = m_cached_ctx.ZBUF.Block();
clear_queue.blit.DBW = m_cached_ctx.FRAME.FBW;
clear_queue.blit.DPSM = m_cached_ctx.ZBUF.PSM;
clear_queue.zero_clear = true;
m_draw_transfers.push_back(clear_queue);
}
}
@@ -8987,6 +9252,7 @@ void GSRendererHW::ClearGSLocalMemory(const GSOffset& off, const GSVector4i& r,
const int right = r.right;
const int bottom = r.bottom;
int top = r.top;
u32 drawing_mask = GSLocalMemory::m_psm[psm].depth ? 0x0 : m_cached_ctx.FRAME.FBMSK;
// Process the page aligned region first, then fall back to anything which is not.
// Since pages are linear in memory, we can do it basically with a vector memset.
@@ -9002,22 +9268,34 @@ void GSRendererHW::ClearGSLocalMemory(const GSOffset& off, const GSVector4i& r,
if (format == GSLocalMemory::PSM_FMT_32)
{
const GSVector4i vcolor = GSVector4i(vert_color);
const GSVector4i vcolor = GSVector4i(vert_color & ~drawing_mask);
const u32 iterations_per_page = (pages_wide * pixels_per_page) / 4;
const GSVector4i mask = GSVector4i(drawing_mask);
pxAssert((off.bp() & (GS_BLOCKS_PER_PAGE - 1)) == 0);
for (u32 current_page = off.bp() >> 5; top < page_aligned_bottom; top += pgs.y, current_page += fbw)
{
current_page &= (GS_MAX_PAGES - 1);
GSVector4i* ptr = reinterpret_cast<GSVector4i*>(m_mem.vm8() + current_page * GS_PAGE_SIZE);
GSVector4i* const ptr_end = ptr + iterations_per_page;
while (ptr != ptr_end)
*(ptr++) = vcolor;
if (drawing_mask)
{
while (ptr != ptr_end)
{
*ptr = (*ptr & mask) | vcolor;
ptr++;
}
}
else
{
while (ptr != ptr_end)
*(ptr++) = vcolor;
}
}
}
else if (format == GSLocalMemory::PSM_FMT_24)
{
const GSVector4i mask = GSVector4i::xff000000();
const GSVector4i vcolor = GSVector4i(vert_color & 0x00ffffffu);
const GSVector4i mask = GSVector4i::xff000000() | GSVector4i(drawing_mask);
const GSVector4i vcolor = GSVector4i((vert_color & 0x00ffffffu) & ~drawing_mask);
const u32 iterations_per_page = (pages_wide * pixels_per_page) / 4;
pxAssert((off.bp() & (GS_BLOCKS_PER_PAGE - 1)) == 0);
for (u32 current_page = off.bp() >> 5; top < page_aligned_bottom; top += pgs.y, current_page += fbw)
@@ -9036,7 +9314,10 @@ void GSRendererHW::ClearGSLocalMemory(const GSOffset& off, const GSVector4i& r,
{
const u16 converted_color = ((vert_color >> 16) & 0x8000) | ((vert_color >> 9) & 0x7C00) |
((vert_color >> 6) & 0x7E0) | ((vert_color >> 3) & 0x1F);
const u16 converted_mask = ((drawing_mask >> 16) & 0x8000) | ((drawing_mask >> 9) & 0x7C00) |
((drawing_mask >> 6) & 0x7E0) | ((drawing_mask >> 3) & 0x1F);
const GSVector4i vcolor = GSVector4i::broadcast16(converted_color);
const GSVector4i mask = GSVector4i::broadcast16(converted_mask);
const u32 iterations_per_page = (pages_wide * pixels_per_page) / 8;
pxAssert((off.bp() & (GS_BLOCKS_PER_PAGE - 1)) == 0);
for (u32 current_page = off.bp() >> 5; top < page_aligned_bottom; top += pgs.y, current_page += fbw)
@@ -9044,14 +9325,27 @@ void GSRendererHW::ClearGSLocalMemory(const GSOffset& off, const GSVector4i& r,
current_page &= (GS_MAX_PAGES - 1);
GSVector4i* ptr = reinterpret_cast<GSVector4i*>(m_mem.vm8() + current_page * GS_PAGE_SIZE);
GSVector4i* const ptr_end = ptr + iterations_per_page;
while (ptr != ptr_end)
*(ptr++) = vcolor;
if (converted_mask)
{
while (ptr != ptr_end)
{
*ptr = (*ptr & mask) | vcolor;
ptr++;
}
}
else
{
while (ptr != ptr_end)
*(ptr++) = vcolor;
}
}
}
}
if (format == GSLocalMemory::PSM_FMT_32)
{
const u32 mask = drawing_mask;
const u32 vcolor = vert_color & ~mask;
// Based on WritePixel32
u32* vm = m_mem.vm32();
for (int y = top; y < bottom; y++)
@@ -9059,25 +9353,28 @@ void GSRendererHW::ClearGSLocalMemory(const GSOffset& off, const GSVector4i& r,
GSOffset::PAHelper pa = off.assertSizesMatch(GSLocalMemory::swizzle32).paMulti(0, y);
for (int x = left; x < right; x++)
vm[pa.value(x)] = vert_color;
vm[pa.value(x)] = vcolor | (vm[pa.value(x)] & mask);
}
}
else if (format == GSLocalMemory::PSM_FMT_24)
{
// Based on WritePixel24
u32* vm = m_mem.vm32();
const u32 write_color = vert_color & 0xffffffu;
const u32 mask = drawing_mask | 0xff000000u;
const u32 write_color = (vert_color & 0xffffffu) & ~mask;
for (int y = top; y < bottom; y++)
{
GSOffset::PAHelper pa = off.assertSizesMatch(GSLocalMemory::swizzle32).paMulti(0, y);
for (int x = left; x < right; x++)
vm[pa.value(x)] = (vm[pa.value(x)] & 0xff000000u) | write_color;
vm[pa.value(x)] = (vm[pa.value(x)] & mask) | write_color;
}
}
else if (format == GSLocalMemory::PSM_FMT_16)
{
const u16 converted_color = ((vert_color >> 16) & 0x8000) | ((vert_color >> 9) & 0x7C00) | ((vert_color >> 6) & 0x7E0) | ((vert_color >> 3) & 0x1F);
const u16 converted_mask = ((drawing_mask >> 16) & 0x8000) | ((drawing_mask >> 9) & 0x7C00) |
((drawing_mask >> 6) & 0x7E0) | ((drawing_mask >> 3) & 0x1F);
const u16 converted_color = (((vert_color >> 16) & 0x8000) | ((vert_color >> 9) & 0x7C00) | ((vert_color >> 6) & 0x7E0) | ((vert_color >> 3) & 0x1F)) & ~converted_mask;
// Based on WritePixel16
u16* vm = m_mem.vm16();
@@ -9086,7 +9383,7 @@ void GSRendererHW::ClearGSLocalMemory(const GSOffset& off, const GSVector4i& r,
GSOffset::PAHelper pa = off.assertSizesMatch(GSLocalMemory::swizzle16).paMulti(0, y);
for (int x = left; x < right; x++)
vm[pa.value(x)] = converted_color;
vm[pa.value(x)] = converted_color | (vm[pa.value(x)] & converted_mask);
}
}
}
@@ -9152,11 +9449,7 @@ bool GSRendererHW::OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Sourc
const GSVector4 sRect(m_vt.m_min.t.x / tw, m_vt.m_min.t.y / th, m_vt.m_max.t.x / tw, m_vt.m_max.t.y / th);
const GSVector4i r_full_new(0, 0, tw, th);
g_gs_device->StretchRect(tex->m_texture, sRect, rt, dRect, ShaderConvert::COPY, m_vt.IsRealLinear());
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
g_gs_device->CopyRect(rt, tex->m_texture, r_full_new, 0, 0);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
g_gs_device->Recycle(rt);
}
File diff suppressed because it is too large Load Diff
+13 -3
View File
@@ -32,8 +32,18 @@ public:
constexpr static bool CheckOverlap(const u32 a_bp, const u32 a_bp_end, const u32 b_bp, const u32 b_bp_end) noexcept
{
const bool valid = a_bp <= a_bp_end && b_bp <= b_bp_end;
const bool overlap = a_bp <= b_bp_end && a_bp_end >= b_bp;
u32 b_bp_start_synced = b_bp;
u32 b_bp_end_synced = b_bp_end;
// Check for wrapping
if (a_bp_end > GS_MAX_BLOCKS && b_bp_end < a_bp)
{
b_bp_start_synced += GS_MAX_BLOCKS;
b_bp_end_synced += GS_MAX_BLOCKS;
}
const bool valid = a_bp <= a_bp_end && b_bp_start_synced <= b_bp_end_synced;
const bool overlap = a_bp <= b_bp_end_synced && a_bp_end >= b_bp_start_synced;
return valid && overlap;
}
@@ -522,7 +532,7 @@ public:
bool HasTargetInHeightCache(u32 bp, u32 fbw, u32 psm, u32 max_age = std::numeric_limits<u32>::max(), bool move_front = true);
bool Has32BitTarget(u32 bp);
void InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 write_psm = PSMCT32, u32 write_bw = 1);
void InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 write_psm = PSMCT32, u32 write_bw = 1, u32 fb_mask = 0x00000000, bool ignore_exact = false);
void InvalidateVideoMemType(int type, u32 bp, u32 write_psm = PSMCT32, u32 write_fbmsk = 0, bool dirty_only = false);
void InvalidateVideoMemSubTarget(GSTextureCache::Target* rt);
void InvalidateVideoMem(const GSOffset& off, const GSVector4i& r, bool target = true);
+2 -3
View File
@@ -1587,6 +1587,7 @@ void GSDeviceMTL::DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect
[m_current_render.encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip
vertexStart:0
vertexCount:4];
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
}
@@ -1597,6 +1598,7 @@ void GSDeviceMTL::RenderCopy(GSTexture* sTex, id<MTLRenderPipelineState> pipelin
MRESetPipeline(pipeline);
MRESetTexture(sTex, GSMTLTextureIndexNonHW);
[m_current_render.encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
}
@@ -2164,7 +2166,6 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
{
BeginRenderPass(@"ColorClip Resolve", config.rt, MTLLoadActionLoad, nullptr, MTLLoadActionDontCare);
RenderCopy(colclip_rt, m_colclip_resolve_pipeline, config.colclip_update_area);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(colclip_rt);
@@ -2194,7 +2195,6 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
case GSTexture::State::Dirty:
BeginRenderPass(@"ColorClip Init", colclip_rt, MTLLoadActionDontCare, nullptr, MTLLoadActionDontCare);
RenderCopy(config.rt, m_colclip_init_pipeline, copy_rect);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
break;
case GSTexture::State::Cleared:
@@ -2306,7 +2306,6 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
{
BeginRenderPass(@"ColorClip Resolve", config.rt, MTLLoadActionLoad, nullptr, MTLLoadActionDontCare);
RenderCopy(colclip_rt, m_colclip_resolve_pipeline, config.colclip_update_area);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(colclip_rt);
+23 -4
View File
@@ -770,6 +770,11 @@ bool GSDeviceOGL::CheckFeatures()
m_features.line_expand ? "hardware" : (m_features.vs_expand ? "vertex expanding" : "UNSUPPORTED"),
m_features.vs_expand ? "vertex expanding" : "CPU");
if (!GLAD_GL_ARB_conservative_depth)
{
Console.Warning("GLAD_GL_ARB_conservative_depth is not supported. This will reduce performance.");
}
return true;
}
@@ -1293,6 +1298,16 @@ std::string GSDeviceOGL::GenGlslHeader(const std::string_view entry, GLenum type
else
header += "#define HAS_FRAMEBUFFER_FETCH 0\n";
if (GLAD_GL_ARB_conservative_depth)
{
header += "#extension GL_ARB_conservative_depth : enable\n";
header += "#define PS_HAS_CONSERVATIVE_DEPTH 1\n";
}
else
{
header += "#define PS_HAS_CONSERVATIVE_DEPTH 0\n";
}
// Allow to puts several shader in 1 files
switch (type)
{
@@ -1408,10 +1423,11 @@ std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
// Copy a sub part of texture (same as below but force a conversion)
void GSDeviceOGL::BlitRect(GSTexture* sTex, const GSVector4i& r, const GSVector2i& dsize, bool at_origin, bool linear)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
CommitClear(sTex, true);
GL_PUSH(fmt::format("CopyRectConv from {}", static_cast<GSTextureOGL*>(sTex)->GetID()).c_str());
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// NOTE: This previously used glCopyTextureSubImage2D(), but this appears to leak memory in
// the loading screens of Evolution Snowboarding in Intel/NVIDIA drivers.
@@ -1642,6 +1658,8 @@ void GSDeviceOGL::FilteredDownsampleTexture(GSTexture* sTex, GSTexture* dTex, u3
void GSDeviceOGL::DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// Original code from DX
const float left = dRect.x * 2 / ds.x - 1.0f;
const float right = dRect.z * 2 / ds.x - 1.0f;
@@ -1712,6 +1730,8 @@ void GSDeviceOGL::DrawMultiStretchRects(
void GSDeviceOGL::DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, const GSVector2& ds)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
const u32 vertex_reserve_size = num_rects * 4 * sizeof(GSVertexPT1);
const u32 index_reserve_size = num_rects * 6 * sizeof(u16);
auto vertex_map = m_vertex_stream_buffer->Map(sizeof(GSVertexPT1), vertex_reserve_size);
@@ -1907,6 +1927,8 @@ void GSDeviceOGL::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float par
void GSDeviceOGL::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSVector4i& bbox)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
GL_PUSH("DATE First Pass");
// sfex3 (after the capcom logo), vf4 (first menu fading in), ffxii shadows, rumble roses shadows, persona4 shadows
@@ -2443,7 +2465,6 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
const GSVector4 dRect(config.colclip_update_area);
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
StretchRect(colclip_rt, sRect, config.rt, dRect, ShaderConvert::COLCLIP_RESOLVE, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(colclip_rt);
g_gs_device->SetColorClipTexture(nullptr);
@@ -2478,7 +2499,6 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
const GSVector4 dRect = GSVector4((config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
StretchRect(config.rt, sRect, colclip_rt, dRect, ShaderConvert::COLCLIP_INIT, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
}
@@ -2747,7 +2767,6 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
const GSVector4 dRect(config.colclip_update_area);
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
StretchRect(colclip_rt, sRect, config.rt, dRect, ShaderConvert::COLCLIP_RESOLVE, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(colclip_rt);
g_gs_device->SetColorClipTexture(nullptr);
+62 -135
View File
@@ -481,72 +481,26 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
vkGetPhysicalDeviceQueueFamilyProperties(m_physical_device, &queue_family_count, queue_family_properties.data());
DevCon.WriteLn("%u vulkan queue families", queue_family_count);
std::vector<uint32_t> queue_family_users(queue_family_count, 0);
// Find graphics and present queues.
m_graphics_queue_family_index = queue_family_count;
m_present_queue_family_index = queue_family_count;
u32 present_queue_index = 0;
m_spin_queue_family_index = queue_family_count;
u32 spin_queue_index = 0;
// Graphics Queue
for (uint32_t i = 0; i < queue_family_count; i++)
{
if (queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
VkBool32 graphics_supported = queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT;
if (graphics_supported)
{
m_graphics_queue_family_index = i;
queue_family_users[i]++;
break;
// Quit now, no need for a present queue.
if (!surface)
{
break;
}
}
}
// Spinwait Queue
for (uint32_t i = 0; i < queue_family_count; i++)
{
if (queue_family_properties[i].queueCount == queue_family_users[i])
continue;
if (!(queue_family_properties[i].queueFlags & VK_QUEUE_COMPUTE_BIT))
continue;
if (queue_family_properties[i].timestampValidBits == 0)
continue; // We need timing
if (!(queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT))
if (surface)
{
m_spin_queue_family_index = i;
break;
}
else if (m_spin_queue_family_index == queue_family_count)
m_spin_queue_family_index = i;
}
if (m_spin_queue_family_index != queue_family_count)
{
spin_queue_index = queue_family_users[m_spin_queue_family_index];
queue_family_users[m_spin_queue_family_index]++;
m_spin_queue_is_graphics_queue = false;
}
else
{
// No spare queue? Try the graphics queue.
if ((queue_family_properties[m_graphics_queue_family_index].queueFlags & VK_QUEUE_COMPUTE_BIT) &&
(queue_family_properties[m_graphics_queue_family_index].timestampValidBits != 0))
{
m_spin_queue_family_index = m_graphics_queue_family_index;
spin_queue_index = 0;
m_spin_queue_is_graphics_queue = true;
}
else
m_spin_queue_is_graphics_queue = false;
}
// Present Queue
if (surface)
{
for (uint32_t i = 0; i < queue_family_count; i++)
{
if (queue_family_properties[i].queueCount == queue_family_users[i])
continue;
VkBool32 present_supported;
VkResult res = vkGetPhysicalDeviceSurfaceSupportKHR(m_physical_device, i, surface, &present_supported);
if (res != VK_SUCCESS)
@@ -555,48 +509,35 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
return false;
}
if (!present_supported)
continue;
// Perfer aync compute queue
if ((queue_family_properties[i].queueFlags & VK_QUEUE_COMPUTE_BIT) &&
!(queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT))
{
m_present_queue_family_index = i;
break;
}
else if (m_present_queue_family_index == queue_family_count)
m_present_queue_family_index = i;
}
if (m_present_queue_family_index != queue_family_count)
{
present_queue_index = queue_family_users[m_present_queue_family_index];
queue_family_users[m_present_queue_family_index]++;
}
else
{
// No spare queue? Try the graphics queue.
VkBool32 present_supported;
VkResult res = vkGetPhysicalDeviceSurfaceSupportKHR(m_physical_device, m_graphics_queue_family_index, surface, &present_supported);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceSupportKHR failed: ");
return false;
}
if (present_supported)
{
m_present_queue_family_index = m_graphics_queue_family_index;
present_queue_index = 0;
m_present_queue_family_index = i;
}
// Prefer one queue family index that does both graphics and present.
if (graphics_supported && present_supported)
{
break;
}
}
}
// Swap spin and present to simplify queue priorities logic.
if (!m_spin_queue_is_graphics_queue && m_present_queue_family_index == m_spin_queue_family_index)
std::swap(spin_queue_index, present_queue_index);
for (uint32_t i = 0; i < queue_family_count; i++)
{
// Pick a queue for spinning
if (!(queue_family_properties[i].queueFlags & VK_QUEUE_COMPUTE_BIT))
continue; // We need compute
if (queue_family_properties[i].timestampValidBits == 0)
continue; // We need timing
const bool queue_is_used = i == m_graphics_queue_family_index || i == m_present_queue_family_index;
if (queue_is_used && m_spin_queue_family_index != queue_family_count)
continue; // Found a non-graphics queue to use
spin_queue_index = 0;
m_spin_queue_family_index = i;
if (queue_is_used && queue_family_properties[i].queueCount > 1)
spin_queue_index = 1;
if (!(queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT))
break; // Async compute queue, definitely pick this one
}
if (m_graphics_queue_family_index == queue_family_count)
{
Console.Error("VK: Failed to find an acceptable graphics queue.");
@@ -614,16 +555,14 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
device_info.flags = 0;
device_info.queueCreateInfoCount = 0;
// Low priority for the spin queue
static constexpr float queue_priorities[] = {1.0f, 1.0f, 0.0f};
static constexpr float queue_priorities[] = {1.0f, 0.0f}; // Low priority for the spin queue
std::array<VkDeviceQueueCreateInfo, 3> queue_infos;
VkDeviceQueueCreateInfo& graphics_queue_info = queue_infos[device_info.queueCreateInfoCount++];
graphics_queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
graphics_queue_info.pNext = nullptr;
graphics_queue_info.flags = 0;
graphics_queue_info.queueFamilyIndex = m_graphics_queue_family_index;
graphics_queue_info.queueCount = queue_family_users[m_graphics_queue_family_index];
graphics_queue_info.queueCount = 1;
graphics_queue_info.pQueuePriorities = queue_priorities;
if (surface != VK_NULL_HANDLE && m_graphics_queue_family_index != m_present_queue_family_index)
@@ -633,19 +572,19 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
present_queue_info.pNext = nullptr;
present_queue_info.flags = 0;
present_queue_info.queueFamilyIndex = m_present_queue_family_index;
present_queue_info.queueCount = queue_family_users[m_present_queue_family_index];
present_queue_info.queueCount = 1;
present_queue_info.pQueuePriorities = queue_priorities;
}
if (m_spin_queue_family_index == m_graphics_queue_family_index)
{
if (spin_queue_index == 1)
graphics_queue_info.pQueuePriorities = queue_priorities + 1;
if (spin_queue_index != 0)
graphics_queue_info.queueCount = 2;
}
else if (m_spin_queue_family_index == m_present_queue_family_index)
{
if (spin_queue_index == 1)
queue_infos[1].pQueuePriorities = queue_priorities + 1;
if (spin_queue_index != 0)
queue_infos[1].queueCount = 2; // present queue
}
else if (m_spin_queue_family_index != queue_family_count)
{
@@ -655,7 +594,7 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
spin_queue_info.flags = 0;
spin_queue_info.queueFamilyIndex = m_spin_queue_family_index;
spin_queue_info.queueCount = 1;
spin_queue_info.pQueuePriorities = queue_priorities + 2;
spin_queue_info.pQueuePriorities = queue_priorities + 1;
}
device_info.pQueueCreateInfos = queue_infos.data();
@@ -744,11 +683,13 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
vkGetDeviceQueue(m_device, m_graphics_queue_family_index, 0, &m_graphics_queue);
if (surface)
{
vkGetDeviceQueue(m_device, m_present_queue_family_index, present_queue_index, &m_present_queue);
vkGetDeviceQueue(m_device, m_present_queue_family_index, 0, &m_present_queue);
}
m_spinning_supported = m_spin_queue_family_index != queue_family_count &&
queue_family_properties[m_graphics_queue_family_index].timestampValidBits > 0 &&
m_device_properties.limits.timestampPeriod > 0;
m_spin_queue_is_graphics_queue =
m_spin_queue_family_index == m_graphics_queue_family_index && spin_queue_index == 0;
m_gpu_timing_supported = (m_device_properties.limits.timestampComputeAndGraphics != 0 &&
queue_family_properties[m_graphics_queue_family_index].timestampValidBits > 0 &&
@@ -1348,24 +1289,8 @@ void GSDeviceVK::SubmitCommandBuffer(VKSwapChain* present_swap_chain)
if (present_swap_chain)
{
// vkQueuePresentKHR on NVidia dosn't seem to properly wait on the passed semaphore, causing artifacts.
// OBS capture with BPM encouters issues, but can apparently occur on the presented image aswell.
// Instead, wait on the RenderingFinished semaphore with vkQueueSubmit.
const uint32_t present_wait_bits = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
const VkSubmitInfo submit_present_wait_info = {VK_STRUCTURE_TYPE_SUBMIT_INFO, nullptr, 1,
present_swap_chain->GetRenderingFinishedSemaphorePtr(), &present_wait_bits, 0,
nullptr, 1, present_swap_chain->GetPresentReadySemaphorePtr()};
res = vkQueueSubmit(m_present_queue, 1, &submit_present_wait_info, nullptr);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkQueueSubmit failed: ");
m_last_submit_failed = true;
return;
}
const VkPresentInfoKHR present_info = {VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, nullptr, 1,
present_swap_chain->GetPresentReadySemaphorePtr(), 1, present_swap_chain->GetSwapChainPtr(),
present_swap_chain->GetRenderingFinishedSemaphorePtr(), 1, present_swap_chain->GetSwapChainPtr(),
present_swap_chain->GetCurrentImageIndexPtr(), nullptr};
present_swap_chain->ResetImageAcquireResult();
@@ -3004,6 +2929,8 @@ void GSDeviceVK::DrawMultiStretchRects(
void GSDeviceVK::DoMultiStretchRects(
const MultiStretchRect* rects, u32 num_rects, GSTextureVK* dTex, ShaderConvert shader)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// Set up vertices first.
const u32 vertex_reserve_size = num_rects * 4 * sizeof(GSVertexPT1);
const u32 index_reserve_size = num_rects * 6 * sizeof(u16);
@@ -3157,6 +3084,8 @@ void GSDeviceVK::DoStretchRect(GSTextureVK* sTex, const GSVector4& sRect, GSText
void GSDeviceVK::DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// ia
const float left = dRect.x * 2 / ds.x - 1.0f;
const float top = 1.0f - dRect.y * 2 / ds.y;
@@ -3206,7 +3135,7 @@ void GSDeviceVK::UpdateCLUTTexture(
GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize)
{
// Super annoying, but apparently NVIDIA doesn't like floats/ints packed together in the same vec4?
struct Uniforms
struct alignas(16) Uniforms
{
u32 offsetX, offsetY, dOffset, pad1;
float scale;
@@ -3225,7 +3154,7 @@ void GSDeviceVK::UpdateCLUTTexture(
void GSDeviceVK::ConvertToIndexedTexture(
GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM)
{
struct Uniforms
struct alignas(16) Uniforms
{
u32 SBW;
u32 DBW;
@@ -3246,7 +3175,7 @@ void GSDeviceVK::ConvertToIndexedTexture(
void GSDeviceVK::FilteredDownsampleTexture(GSTexture* sTex, GSTexture* dTex, u32 downsample_factor, const GSVector2i& clamp_min, const GSVector4& dRect)
{
struct Uniforms
struct alignas(16) Uniforms
{
GSVector2i clamp_min;
int downsample_factor;
@@ -4509,16 +4438,13 @@ void GSDeviceVK::RenderImGui()
UpdateImGuiTextures();
const float uniforms[2][2] = {{
2.0f / static_cast<float>(m_window_info.surface_width),
2.0f / static_cast<float>(m_window_info.surface_height),
},
{
-1.0f,
-1.0f,
}};
const GSVector4 uniforms(
2.0f / static_cast<float>(m_window_info.surface_width),
2.0f / static_cast<float>(m_window_info.surface_height),
-1.0f,
-1.0f);
SetUtilityPushConstants(uniforms, sizeof(uniforms));
SetUtilityPushConstants(&uniforms, sizeof(uniforms));
SetPipeline(m_imgui_pipeline);
if (m_utility_sampler != m_linear_sampler)
@@ -5546,6 +5472,8 @@ void GSDeviceVK::SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb)
void GSDeviceVK::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSVector4i& bbox)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
GL_PUSH("SetupDATE {%d,%d} %dx%d", bbox.left, bbox.top, bbox.width(), bbox.height());
const GSVector2i size(ds->GetSize());
@@ -5573,6 +5501,8 @@ void GSDeviceVK::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSV
GSTextureVK* GSDeviceVK::SetupPrimitiveTrackingDATE(GSHWDrawConfig& config)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// How this is done:
// - can't put a barrier for the image in the middle of the normal render pass, so that's out
// - so, instead of just filling the int texture with INT_MAX, we sample the RT and use -1 for failing values
@@ -5754,7 +5684,6 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
SetPipeline(m_colclip_finish_pipelines[pipe.ds][pipe.IsRTFeedbackLoop()]);
SetUtilityTexture(colclip_rt, m_point_sampler);
DrawStretchRect(sRect, drawareaf, rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(colclip_rt);
g_gs_device->SetColorClipTexture(nullptr);
@@ -5971,7 +5900,6 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
const GSVector4 drawareaf = GSVector4((config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 sRect(drawareaf / GSVector4(rtsize).xyxy());
DrawStretchRect(sRect, drawareaf, rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
GL_POP();
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));
@@ -6073,7 +6001,6 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
SetPipeline(m_colclip_finish_pipelines[pipe.ds][pipe.IsRTFeedbackLoop()]);
SetUtilityTexture(colclip_rt, m_point_sampler);
DrawStretchRect(sRect, drawareaf, rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(colclip_rt);
g_gs_device->SetColorClipTexture(nullptr);
-14
View File
@@ -490,18 +490,6 @@ bool VKSwapChain::CreateSwapChain()
sema.available_semaphore = VK_NULL_HANDLE;
return false;
}
res = vkCreateSemaphore(
GSDeviceVK::GetInstance()->GetDevice(), &semaphore_info, nullptr, &sema.present_ready_semaphore);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateSemaphore failed: ");
vkDestroySemaphore(GSDeviceVK::GetInstance()->GetDevice(), sema.rendering_finished_semaphore, nullptr);
vkDestroySemaphore(GSDeviceVK::GetInstance()->GetDevice(), sema.available_semaphore, nullptr);
sema.rendering_finished_semaphore = VK_NULL_HANDLE;
sema.available_semaphore = VK_NULL_HANDLE;
return false;
}
}
return true;
@@ -517,8 +505,6 @@ void VKSwapChain::DestroySwapChainImages()
m_images.clear();
for (auto& it : m_semaphores)
{
if (it.present_ready_semaphore != VK_NULL_HANDLE)
vkDestroySemaphore(GSDeviceVK::GetInstance()->GetDevice(), it.present_ready_semaphore, nullptr);
if (it.rendering_finished_semaphore != VK_NULL_HANDLE)
vkDestroySemaphore(GSDeviceVK::GetInstance()->GetDevice(), it.rendering_finished_semaphore, nullptr);
if (it.available_semaphore != VK_NULL_HANDLE)
-9
View File
@@ -64,14 +64,6 @@ public:
{
return &m_semaphores[m_current_semaphore].rendering_finished_semaphore;
}
__fi VkSemaphore GetPresentReadySemaphore() const
{
return m_semaphores[m_current_semaphore].present_ready_semaphore;
}
__fi const VkSemaphore* GetPresentReadySemaphorePtr() const
{
return &m_semaphores[m_current_semaphore].present_ready_semaphore;
}
VkFormat GetTextureFormat() const;
VkResult AcquireNextImage();
@@ -100,7 +92,6 @@ private:
{
VkSemaphore available_semaphore;
VkSemaphore rendering_finished_semaphore;
VkSemaphore present_ready_semaphore;
};
WindowInfo m_window_info;
-1
View File
@@ -380,7 +380,6 @@ static const char* s_gs_hw_fix_names[] = {
"trilinearFiltering",
"skipDrawStart",
"skipDrawEnd",
"halfBottomOverride",
"halfPixelOffset",
"roundSprite",
"nativeScaling",
-1
View File
@@ -65,7 +65,6 @@ namespace GameDatabaseSchema
TrilinearFiltering,
SkipDrawStart,
SkipDrawEnd,
HalfBottomOverride,
HalfPixelOffset,
RoundSprite,
NativeScaling,
File diff suppressed because it is too large Load Diff
+496
View File
@@ -0,0 +1,496 @@
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
#include "FullscreenUI.h"
#include "ImGuiFullscreen.h"
#include "common/Timer.h"
#include "Input/InputManager.h"
#define TR_CONTEXT "FullscreenUI"
template <size_t L>
class IconStackString : public SmallStackString<L>
{
public:
__fi IconStackString(const char* icon, const char* str)
{
SmallStackString<L>::format("{} {}", icon, Host::TranslateToStringView(TR_CONTEXT, str));
}
__fi IconStackString(const char8_t* icon, const char* str)
{
SmallStackString<L>::format("{} {}", reinterpret_cast<const char*>(icon), Host::TranslateToStringView(TR_CONTEXT, str));
}
__fi IconStackString(const char* icon, const char* str, const char* suffix)
{
SmallStackString<L>::format("{} {}##{}", icon, Host::TranslateToStringView(TR_CONTEXT, str), suffix);
}
__fi IconStackString(const char8_t* icon, const char* str, const char* suffix)
{
SmallStackString<L>::format("{} {}##{}", reinterpret_cast<const char*>(icon), Host::TranslateToStringView(TR_CONTEXT, str), suffix);
}
};
#define FSUI_ICONSTR(icon, str) IconStackString<256>(icon, str).c_str()
#define FSUI_ICONSTR_S(icon, str, suffix) IconStackString<256>(icon, str, suffix).c_str()
#define FSUI_STR(str) Host::TranslateToString(TR_CONTEXT, str)
#define FSUI_CSTR(str) Host::TranslateToCString(TR_CONTEXT, str)
#define FSUI_VSTR(str) Host::TranslateToStringView(TR_CONTEXT, str)
#define FSUI_FSTR(str) fmt::runtime(Host::TranslateToStringView(TR_CONTEXT, str))
#define FSUI_NSTR(str) str
using ImGuiFullscreen::ActiveButton;
using ImGuiFullscreen::AddNotification;
using ImGuiFullscreen::BeginFullscreenColumns;
using ImGuiFullscreen::BeginFullscreenColumnWindow;
using ImGuiFullscreen::BeginFullscreenWindow;
using ImGuiFullscreen::BeginHorizontalMenu;
using ImGuiFullscreen::BeginMenuButtons;
using ImGuiFullscreen::BeginNavBar;
using ImGuiFullscreen::CenterImage;
using ImGuiFullscreen::CloseChoiceDialog;
using ImGuiFullscreen::CloseFileSelector;
using ImGuiFullscreen::EndFullscreenColumns;
using ImGuiFullscreen::EndFullscreenColumnWindow;
using ImGuiFullscreen::EndFullscreenWindow;
using ImGuiFullscreen::EndHorizontalMenu;
using ImGuiFullscreen::EndMenuButtons;
using ImGuiFullscreen::EndNavBar;
using ImGuiFullscreen::EnumChoiceButton;
using ImGuiFullscreen::FloatingButton;
using ImGuiFullscreen::FocusResetType;
using ImGuiFullscreen::ForceKeyNavEnabled;
using ImGuiFullscreen::g_large_font;
using ImGuiFullscreen::g_layout_padding_left;
using ImGuiFullscreen::g_layout_padding_top;
using ImGuiFullscreen::g_medium_font;
using ImGuiFullscreen::GetCachedSvgTexture;
using ImGuiFullscreen::GetCachedSvgTextureAsync;
using ImGuiFullscreen::GetCachedTexture;
using ImGuiFullscreen::GetCachedTextureAsync;
using ImGuiFullscreen::GetPlaceholderTexture;
using ImGuiFullscreen::GetQueuedFocusResetType;
using ImGuiFullscreen::HorizontalMenuItem;
using ImGuiFullscreen::HorizontalMenuSvgItem;
using ImGuiFullscreen::InputFilterType;
using ImGuiFullscreen::IsFocusResetQueued;
using ImGuiFullscreen::IsGamepadInputSource;
using ImGuiFullscreen::LAYOUT_FOOTER_HEIGHT;
using ImGuiFullscreen::LAYOUT_LARGE_FONT_SIZE;
using ImGuiFullscreen::LAYOUT_MEDIUM_FONT_SIZE;
using ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT;
using ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY;
using ImGuiFullscreen::LAYOUT_MENU_BUTTON_X_PADDING;
using ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING;
using ImGuiFullscreen::LAYOUT_SCREEN_HEIGHT;
using ImGuiFullscreen::LAYOUT_SCREEN_WIDTH;
using ImGuiFullscreen::LayoutScale;
using ImGuiFullscreen::LoadSvgTexture;
using ImGuiFullscreen::LoadTexture;
using ImGuiFullscreen::MenuButton;
using ImGuiFullscreen::MenuButtonFrame;
using ImGuiFullscreen::MenuButtonWithoutSummary;
using ImGuiFullscreen::MenuButtonWithValue;
using ImGuiFullscreen::MenuHeading;
using ImGuiFullscreen::MenuHeadingButton;
using ImGuiFullscreen::MenuImageButton;
using ImGuiFullscreen::ModAlpha;
using ImGuiFullscreen::MulAlpha;
using ImGuiFullscreen::NavButton;
using ImGuiFullscreen::NavTitle;
using ImGuiFullscreen::OpenChoiceDialog;
using ImGuiFullscreen::OpenConfirmMessageDialog;
using ImGuiFullscreen::OpenFileSelector;
using ImGuiFullscreen::OpenInfoMessageDialog;
using ImGuiFullscreen::OpenInputStringDialog;
using ImGuiFullscreen::PopPrimaryColor;
using ImGuiFullscreen::PushPrimaryColor;
using ImGuiFullscreen::QueueResetFocus;
using ImGuiFullscreen::ResetFocusHere;
using ImGuiFullscreen::RightAlignNavButtons;
using ImGuiFullscreen::SetFullscreenFooterText;
using ImGuiFullscreen::ShowToast;
using ImGuiFullscreen::SvgScaling;
using ImGuiFullscreen::ThreeWayToggleButton;
using ImGuiFullscreen::ToggleButton;
using ImGuiFullscreen::UIBackgroundColor;
using ImGuiFullscreen::UIBackgroundHighlightColor;
using ImGuiFullscreen::UIBackgroundLineColor;
using ImGuiFullscreen::UIBackgroundTextColor;
using ImGuiFullscreen::UIDisabledColor;
using ImGuiFullscreen::UIPopupBackgroundColor;
using ImGuiFullscreen::UIPrimaryColor;
using ImGuiFullscreen::UIPrimaryDarkColor;
using ImGuiFullscreen::UIPrimaryLightColor;
using ImGuiFullscreen::UIPrimaryLineColor;
using ImGuiFullscreen::UIPrimaryTextColor;
using ImGuiFullscreen::UISecondaryColor;
using ImGuiFullscreen::UISecondaryStrongColor;
using ImGuiFullscreen::UISecondaryTextColor;
using ImGuiFullscreen::UISecondaryWeakColor;
using ImGuiFullscreen::UITextHighlightColor;
using ImGuiFullscreen::WantsToCloseMenu;
namespace FullscreenUI
{
enum class MainWindowType
{
None,
Landing,
StartGame,
Exit,
GameList,
GameListSettings,
Settings,
PauseMenu,
Achievements,
Leaderboards,
};
enum class PauseSubMenu
{
None,
Exit,
Achievements,
};
enum class SettingsPage
{
Summary,
Interface,
BIOS,
Emulation,
Graphics,
Audio,
MemoryCard,
NetworkHDD,
Folders,
Achievements,
Controller,
Hotkey,
Advanced,
Patches,
Cheats,
GameFixes,
Count
};
enum class GameListView
{
Grid,
List,
Count
};
enum class IPAddressType
{
PS2IP,
SubnetMask,
Gateway,
DNS1,
DNS2,
Other
};
//////////////////////////////////////////////////////////////////////////
// Main
//////////////////////////////////////////////////////////////////////////
void UpdateGameDetails(std::string path, std::string serial, std::string title, u32 disc_crc, u32 crc);
bool AreAnyDialogsOpen();
void PauseForMenuOpen(bool set_pause_menu_open);
void ClosePauseMenu();
void OpenPauseSubMenu(PauseSubMenu submenu);
void DrawLandingTemplate(ImVec2* menu_pos, ImVec2* menu_size);
void DrawLandingWindow();
void DrawStartGameWindow();
void DrawExitWindow();
void DrawPauseMenu(MainWindowType type);
void ExitFullscreenAndOpenURL(const std::string_view url);
void CopyTextToClipboard(std::string title, const std::string_view text);
void DrawAboutWindow();
void OpenAboutWindow();
void GetStandardSelectionFooterText(SmallStringBase& dest, bool back_instead_of_cancel);
void ApplyLayoutSettings(const SettingsInterface* bsi = nullptr);
void DrawSvgTexture(GSTexture* padded_texture, ImVec2 unpadded_size);
void DrawCachedSvgTexture(const std::string& path, ImVec2 size, SvgScaling mode);
void DrawCachedSvgTextureAsync(const std::string& path, ImVec2 size, SvgScaling mode);
void DrawListSvgTexture(ImDrawList* drawList, GSTexture* padded_texture, const ImVec2& p_min, const ImVec2& p_unpadded_max);
inline MainWindowType s_current_main_window = MainWindowType::None;
inline PauseSubMenu s_current_pause_submenu = PauseSubMenu::None;
inline bool s_initialized = false;
inline bool s_tried_to_initialize = false;
inline bool s_pause_menu_was_open = false;
inline bool s_was_paused_on_quick_menu_open = false;
inline bool s_about_window_open = false;
// achievements login dialog state
inline bool s_achievements_login_open = false;
inline bool s_achievements_login_logging_in = false;
inline char s_achievements_login_username[256] = {};
inline char s_achievements_login_password[256] = {};
inline Achievements::LoginRequestReason s_achievements_login_reason = Achievements::LoginRequestReason::UserInitiated;
// local copies of the currently-running game
inline std::string s_current_game_title;
inline std::string s_current_game_subtitle;
inline std::string s_current_disc_serial;
inline std::string s_current_disc_path;
inline u32 s_current_disc_crc;
//////////////////////////////////////////////////////////////////////////
// Resources
//////////////////////////////////////////////////////////////////////////
bool LoadResources();
bool LoadSvgResources();
void DestroyResources();
inline std::array<std::shared_ptr<GSTexture>, static_cast<u32>(GameDatabaseSchema::Compatibility::Perfect)>
s_game_compatibility_textures;
inline std::shared_ptr<GSTexture> s_banner_texture;
inline std::vector<std::unique_ptr<GSTexture>> s_cleanup_textures;
//////////////////////////////////////////////////////////////////////////
// Landing
//////////////////////////////////////////////////////////////////////////
void SwitchToLanding();
ImGuiFullscreen::FileSelectorFilters GetOpenFileFilters();
ImGuiFullscreen::FileSelectorFilters GetDiscImageFilters();
ImGuiFullscreen::FileSelectorFilters GetAudioFileFilters();
ImGuiFullscreen::FileSelectorFilters GetImageFileFilters();
void DoVMInitialize(const VMBootParameters& boot_params, bool switch_to_landing_on_failure);
void DoStartPath(
const std::string& path, std::optional<s32> state_index = std::nullopt, std::optional<bool> fast_boot = std::nullopt);
void DoStartFile();
void DoStartBIOS();
void DoStartDisc(const std::string& drive);
void DoStartDisc();
void DoToggleFrameLimit();
void DoToggleSoftwareRenderer();
void RequestShutdown(bool save_state);
void DoShutdown(bool save_state);
void RequestReset();
void DoReset();
void DoChangeDiscFromFile();
void RequestChangeDisc();
void DoRequestExit();
void DoDesktopMode();
void DoToggleFullscreen();
void ConfirmShutdownIfMemcardBusy(std::function<void(bool)> callback);
bool ShouldDefaultToGameList();
//////////////////////////////////////////////////////////////////////////
// Save State List
//////////////////////////////////////////////////////////////////////////
struct SaveStateListEntry
{
std::string title;
std::string summary;
std::string path;
std::unique_ptr<GSTexture> preview_texture;
time_t timestamp;
s32 slot;
};
void InitializePlaceholderSaveStateListEntry(SaveStateListEntry* li, s32 slot);
bool InitializeSaveStateListEntry(
SaveStateListEntry* li, const std::string& title, const std::string& serial, u32 crc, s32 slot, bool backup = false);
void ClearSaveStateEntryList();
u32 PopulateSaveStateListEntries(const std::string& title, const std::string& serial, u32 crc);
bool OpenLoadStateSelectorForGame(const std::string& game_path);
bool OpenSaveStateSelector(bool is_loading);
void CloseSaveStateSelector();
void DrawSaveStateSelector(bool is_loading);
bool OpenLoadStateSelectorForGameResume(const GameList::Entry* entry);
void DrawResumeStateSelector();
void DoLoadState(std::string path, std::optional<s32> slot, bool backup);
void DoSaveState(s32 slot);
inline std::vector<SaveStateListEntry> s_save_state_selector_slots;
inline std::string s_save_state_selector_game_path;
inline s32 s_save_state_selector_submenu_index = -1;
inline bool s_save_state_selector_open = false;
inline bool s_save_state_selector_loading = true;
inline bool s_save_state_selector_resuming = false;
//////////////////////////////////////////////////////////////////////////
// Game List
//////////////////////////////////////////////////////////////////////////
void DrawGameListWindow();
void DrawGameList(const ImVec2& heading_size);
void DrawGameGrid(const ImVec2& heading_size);
void HandleGameListActivate(const GameList::Entry* entry);
void HandleGameListOptions(const GameList::Entry* entry);
void DrawGameListSettingsWindow();
void SwitchToGameList();
void PopulateGameListEntryList();
GSTexture* GetTextureForGameListEntryType(GameList::EntryType type, const ImVec2& size, SvgScaling mode = SvgScaling::Stretch);
GSTexture* GetGameListCover(const GameList::Entry* entry);
void DrawGameCover(const GameList::Entry* entry, const ImVec2& size);
void DrawGameCover(const GameList::Entry* entry, ImDrawList* draw_list, const ImVec2& min, const ImVec2& max);
// For when we have no GameList entry
void DrawFallbackCover(const ImVec2& size);
void DrawFallbackCover(ImDrawList* draw_list, const ImVec2& min, const ImVec2& max);
// Lazily populated cover images.
inline std::unordered_map<std::string, std::string> s_cover_image_map;
inline std::vector<const GameList::Entry*> s_game_list_sorted_entries;
inline GameListView s_game_list_view = GameListView::Grid;
//////////////////////////////////////////////////////////////////////////
// Background
//////////////////////////////////////////////////////////////////////////
void LoadCustomBackground();
void DrawCustomBackground();
inline std::shared_ptr<GSTexture> s_custom_background_texture;
inline std::string s_custom_background_path;
inline bool s_custom_background_enabled = false;
//////////////////////////////////////////////////////////////////////////
// Achievements
//////////////////////////////////////////////////////////////////////////
void SwitchToAchievementsWindow();
void SwitchToLeaderboardsWindow();
void DrawAchievementsLoginWindow();
//////////////////////////////////////////////////////////////////////////
// Settings
//////////////////////////////////////////////////////////////////////////
static constexpr double INPUT_BINDING_TIMEOUT_SECONDS = 5.0;
static constexpr u32 NUM_MEMORY_CARD_PORTS = 2;
void SwitchToSettings();
void SwitchToGameSettings();
void SwitchToGameSettings(const std::string& path);
void SwitchToGameSettings(const GameList::Entry* entry);
void SwitchToGameSettings(const std::string_view serial, u32 crc);
void DrawSettingsWindow();
void DrawSummarySettingsPage();
void DrawInterfaceSettingsPage();
void DrawBIOSSettingsPage();
void DrawEmulationSettingsPage();
void DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_advanced_settings);
void DrawAudioSettingsPage();
void DrawMemoryCardSettingsPage();
void DrawNetworkHDDSettingsPage();
void DrawFoldersSettingsPage();
void DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& settings_lock);
void DrawControllerSettingsPage();
void DrawHotkeySettingsPage();
void DrawAdvancedSettingsPage();
void DrawPatchesOrCheatsSettingsPage(bool cheats);
void DrawGameFixesSettingsPage();
bool IsEditingGameSettings(SettingsInterface* bsi);
SettingsInterface* GetEditingSettingsInterface();
SettingsInterface* GetEditingSettingsInterface(bool game_settings);
bool ShouldShowAdvancedSettings(SettingsInterface* bsi);
void SetSettingsChanged(SettingsInterface* bsi);
bool GetEffectiveBoolSetting(SettingsInterface* bsi, const char* section, const char* key, bool default_value);
s32 GetEffectiveIntSetting(SettingsInterface* bsi, const char* section, const char* key, s32 default_value);
void DoCopyGameSettings();
void DoClearGameSettings();
void ResetControllerSettings();
void DoLoadInputProfile();
void DoSaveInputProfile();
void DoSaveInputProfile(const std::string& name);
void DoResetSettings();
bool DrawToggleSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
bool default_value, bool enabled = true, bool allow_tristate = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT,
std::pair<ImFont*, float> font = g_large_font, std::pair<ImFont*, float> summary_font = g_medium_font);
void DrawIntListSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
int default_value, const char* const* options, size_t option_count, bool translate_options, int option_offset = 0,
bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font,
std::pair<ImFont*, float> summary_font = g_medium_font);
void DrawIntRangeSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
int default_value, int min_value, int max_value, const char* format = "%d", bool enabled = true,
float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font, std::pair<ImFont*, float> summary_font = g_medium_font);
void DrawIntSpinBoxSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
int default_value, int min_value, int max_value, int step_value, const char* format = "%d", bool enabled = true,
float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font, std::pair<ImFont*, float> summary_font = g_medium_font);
void DrawFloatRangeSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
float default_value, float min_value, float max_value, const char* format = "%f", float multiplier = 1.0f, bool enabled = true,
float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font, std::pair<ImFont*, float> summary_font = g_medium_font);
void DrawFloatSpinBoxSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section,
const char* key, float default_value, float min_value, float max_value, float step_value, float multiplier,
const char* format = "%f", bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT,
std::pair<ImFont*, float> font = g_large_font, std::pair<ImFont*, float> summary_font = g_medium_font);
void DrawIntRectSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section,
const char* left_key, int default_left, const char* top_key, int default_top, const char* right_key, int default_right,
const char* bottom_key, int default_bottom, int min_value, int max_value, int step_value, const char* format = "%d",
bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font,
std::pair<ImFont*, float> summary_font = g_medium_font);
void DrawStringListSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
const char* default_value, const char* const* options, const char* const* option_values, size_t option_count,
bool translate_options, bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font,
std::pair<ImFont*, float> summary_font = g_medium_font, const char* translation_ctx = "FullscreenUI");
void DrawStringListSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
const char* default_value, SettingInfo::GetOptionsCallback options_callback, bool enabled = true,
float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font, std::pair<ImFont*, float> summary_font = g_medium_font);
void DrawIPAddressSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
const char* default_value, bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT,
std::pair<ImFont*, float> font = g_large_font, std::pair<ImFont*, float> summary_font = g_medium_font,
IPAddressType ip_type = IPAddressType::Other);
void DrawFloatListSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
float default_value, const char* const* options, const float* option_values, size_t option_count, bool translate_options,
bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font,
std::pair<ImFont*, float> summary_font = g_medium_font);
template <typename DataType, typename SizeType>
void DrawEnumSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section,
const char* key, DataType default_value,
std::optional<DataType> (*from_string_function)(const char* str),
const char* (*to_string_function)(DataType value),
const char* (*to_display_string_function)(DataType value), SizeType option_count,
bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT,
std::pair<ImFont*, float> font = g_large_font, std::pair<ImFont*, float> summary_font = g_medium_font);
void DrawFolderSetting(SettingsInterface* bsi, const char* title, const char* section, const char* key,
const std::string& runtime_var, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font,
std::pair<ImFont*, float> summary_font = g_medium_font);
void DrawPathSetting(SettingsInterface* bsi, const char* title, const char* section, const char* key, const char* default_value,
bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font,
std::pair<ImFont*, float> summary_font = g_medium_font);
void DrawClampingModeSetting(SettingsInterface* bsi, const char* title, const char* summary, int vunum);
void PopulateGraphicsAdapterList();
void PopulateGameListDirectoryCache(SettingsInterface* si);
void PopulatePatchesAndCheatsList(const std::string_view serial, u32 crc);
void BeginInputBinding(SettingsInterface* bsi, InputBindingInfo::Type type, const std::string_view section,
const std::string_view key, const std::string_view display_name);
void DrawInputBindingWindow();
void DrawInputBindingButton(SettingsInterface* bsi, InputBindingInfo::Type type, const char* section, const char* name, const char* display_name, const char* icon_name, bool show_type = true);
void ClearInputBindingVariables();
void StartAutomaticBinding(u32 port);
void DrawSettingInfoSetting(SettingsInterface* bsi, const char* section, const char* key, const SettingInfo& si,
const char* translation_ctx);
void OpenMemoryCardCreateDialog();
void DoCreateMemoryCard(std::string name, MemoryCardType type, MemoryCardFileType file_type, bool use_ntfs_compression = false);
inline SettingsPage s_settings_page = SettingsPage::Interface;
inline std::unique_ptr<INISettingsInterface> s_game_settings_interface;
inline std::unique_ptr<GameList::Entry> s_game_settings_entry;
inline std::vector<std::pair<std::string, bool>> s_game_list_directories_cache;
inline std::vector<GSAdapterInfo> s_graphics_adapter_list_cache;
inline std::vector<Patch::PatchInfo> s_game_patch_list;
inline std::vector<std::string> s_enabled_game_patch_cache;
inline std::vector<Patch::PatchInfo> s_game_cheats_list;
inline std::vector<std::string> s_enabled_game_cheat_cache;
inline u32 s_game_cheat_unlabelled_count = 0;
inline std::vector<const HotkeyInfo*> s_hotkey_list_cache;
inline std::atomic_bool s_settings_changed{false};
inline std::atomic_bool s_game_settings_changed{false};
inline InputBindingInfo::Type s_input_binding_type = InputBindingInfo::Type::Unknown;
inline std::string s_input_binding_section;
inline std::string s_input_binding_key;
inline std::string s_input_binding_display_name;
inline std::vector<InputBindingKey> s_input_binding_new_bindings;
inline std::vector<std::pair<InputBindingKey, std::pair<float, float>>> s_input_binding_value_ranges;
inline Common::Timer s_input_binding_timer;
} // namespace FullscreenUI
File diff suppressed because it is too large Load Diff
-2
View File
@@ -1923,7 +1923,6 @@ Pcsx2Config::Pcsx2Config()
{
bitset = 0;
// Set defaults for fresh installs / reset settings
McdFolderAutoManage = true;
EnablePatches = true;
EnableFastBoot = true;
EnableRecordingTools = true;
@@ -1979,7 +1978,6 @@ void Pcsx2Config::LoadSaveCore(SettingsWrapper& wrap)
SettingsWrapBitBool(HostFs);
SettingsWrapBitBool(BackupSavestate);
SettingsWrapBitBool(McdFolderAutoManage);
SettingsWrapBitBool(WarnAboutUnsafeSettings);
+3 -3
View File
@@ -611,7 +611,7 @@ void FileMcd_EmuOpen()
Mcd::impl.Open();
Mcd::implFolder.SetFiltering(EmuConfig.McdFolderAutoManage);
Mcd::implFolder.SetFiltering(true);
Mcd::implFolder.Open();
}
@@ -823,7 +823,7 @@ int FileMcd_ReIndex(uint port, uint slot, const std::string& filter)
// return Mcd::impl.ReIndex( combinedSlot, filter );
// break;
case MemoryCardType::Folder:
if (!Mcd::implFolder.ReIndex(combinedSlot, EmuConfig.McdFolderAutoManage, filter))
if (!Mcd::implFolder.ReIndex(combinedSlot, true, filter))
return -1;
break;
default:
@@ -930,7 +930,7 @@ std::vector<AvailableMcdInfo> FileMcd_GetAvailableCards(bool include_in_use_card
Pcsx2Config::McdOptions config;
config.Enabled = true;
config.Type = MemoryCardType::Folder;
sourceFolderMemoryCard.Open(fd.FileName, config, (8 * 1024 * 1024) / FolderMemoryCard::ClusterSize, EmuConfig.McdFolderAutoManage, "");
sourceFolderMemoryCard.Open(fd.FileName, config, (8 * 1024 * 1024) / FolderMemoryCard::ClusterSize, true, "");
mcds.push_back({std::move(basename), std::move(fd.FileName), fd.ModificationTime,
MemoryCardType::Folder, MemoryCardFileType::Unknown, 0u, sourceFolderMemoryCard.IsFormatted()});
-1
View File
@@ -200,7 +200,6 @@ void SPU2::DoFullDump()
fprintf(dump, " - Sound Start Address: %x\n", Cores[c].Voices[v].StartA);
fprintf(dump, " - Next Data Address: %x\n", Cores[c].Voices[v].NextA);
fprintf(dump, " - Play Status: %s\n", (Cores[c].Voices[v].ADSR.Phase > 0) ? "Playing" : "Not Playing");
fprintf(dump, " - Block Sample: %d\n", Cores[c].Voices[v].SCurrent);
}
fprintf(dump, "#### END OF DUMP.\n\n");
}
+9 -9
View File
@@ -163,7 +163,7 @@ void V_Core::StartADMAWrite(u16* pMem, u32 sz)
if ((AutoDMACtrl & (Index + 1)) == 0)
{
ActiveTSA = 0x2000 + (Index << 10);
DMAICounter = size * 4;
DMAICounter = size * 48;
LastClock = psxRegs.cycle;
}
else if (size >= 256)
@@ -191,7 +191,7 @@ void V_Core::StartADMAWrite(u16* pMem, u32 sz)
if (SPU2::MsgToConsole())
SPU2::ConLog("ADMA%c Error Size of %x too small\n", GetDmaIndexChar(), size);
InputDataLeft = 0;
DMAICounter = size * 4;
DMAICounter = size * 48;
LastClock = psxRegs.cycle;
}
}
@@ -248,7 +248,7 @@ void V_Core::FinishDMAwrite()
DMA7LogWrite(DMAPtr, ReadSize << 1);
#endif
u32 buff1end = ActiveTSA + std::min(ReadSize, (u32)0x100 + std::abs(DMAICounter / 4));
u32 buff1end = ActiveTSA + std::min(ReadSize, (u32)0x100 + std::abs(DMAICounter / 48));
u32 buff2end = 0;
if (buff1end > 0x100000)
{
@@ -343,7 +343,7 @@ void V_Core::FinishDMAwrite()
DMAPtr += TDA - ActiveTSA;
ReadSize -= TDA - ActiveTSA;
DMAICounter = (DMAICounter - ReadSize) * 4;
DMAICounter = (DMAICounter - ReadSize) * 48;
CounterUpdate(DMAICounter);
@@ -354,7 +354,7 @@ void V_Core::FinishDMAwrite()
void V_Core::FinishDMAread()
{
u32 buff1end = ActiveTSA + std::min(ReadSize, (u32)0x100 + std::abs(DMAICounter / 4));
u32 buff1end = ActiveTSA + std::min(ReadSize, (u32)0x100 + std::abs(DMAICounter / 48));
u32 buff2end = 0;
if (buff1end > 0x100000)
@@ -426,9 +426,9 @@ void V_Core::FinishDMAread()
// DMA Reads are done AFTER the delay, so to get the timing right we need to scheule one last DMA to catch IRQ's
if (ReadSize)
DMAICounter = std::min(ReadSize, (u32)0x100) * 4;
DMAICounter = std::min(ReadSize, (u32)0x100) * 48;
else
DMAICounter = 4;
DMAICounter = 48;
CounterUpdate(DMAICounter);
@@ -446,7 +446,7 @@ void V_Core::DoDMAread(u16* pMem, u32 size)
ReadSize = size;
IsDMARead = true;
LastClock = psxRegs.cycle;
DMAICounter = std::min(ReadSize, (u32)0x100) * 4;
DMAICounter = (std::min(ReadSize, (u32)0x100) * 48);
Regs.STATX &= ~0x80;
Regs.STATX |= 0x400;
//Regs.ATTR |= 0x30;
@@ -470,7 +470,7 @@ void V_Core::DoDMAwrite(u16* pMem, u32 size)
{
Regs.STATX &= ~0x80;
//Regs.ATTR |= 0x30;
DMAICounter = 1 * 4;
DMAICounter = 1 * 48;
LastClock = psxRegs.cycle;
return;
}
+98 -115
View File
@@ -89,55 +89,13 @@ int g_counter_cache_ignores = 0;
#define XAFLAG_LOOP (1ul << 1)
#define XAFLAG_LOOP_START (1ul << 2)
static __forceinline s32 GetNextDataBuffered(V_Core& thiscore, uint voiceidx)
static __forceinline void GetNextDataBuffered(V_Core& thiscore, uint voiceidx)
{
V_Voice& vc(thiscore.Voices[voiceidx]);
if ((vc.SCurrent & 3) == 0)
if (vc.SBuffer == nullptr)
{
IncrementNextA(thiscore, voiceidx);
if ((vc.NextA & 7) == 0) // vc.SCurrent == 24 equivalent
{
if (vc.LoopFlags & XAFLAG_LOOP_END)
{
thiscore.Regs.ENDX |= (1 << voiceidx);
vc.NextA = vc.LoopStartA | 1;
if (!(vc.LoopFlags & XAFLAG_LOOP))
{
vc.Stop();
if (IsDevBuild)
{
if (SPU2::MsgVoiceOff())
SPU2::ConLog("* SPU2: Voice Off by EndPoint: %d \n", voiceidx);
}
}
}
else
vc.NextA++; // no, don't IncrementNextA here. We haven't read the header yet.
}
}
if (vc.SCurrent == 28)
{
vc.SCurrent = 0;
// We'll need the loop flags and buffer pointers regardless of cache status:
for (int i = 0; i < 2; i++)
if (Cores[i].IRQEnable && Cores[i].IRQA == (vc.NextA & 0xFFFF8))
SetIrqCall(i);
s16* memptr = GetMemPtr(vc.NextA & 0xFFFF8);
vc.LoopFlags = *memptr >> 8; // grab loop flags from the upper byte.
if ((vc.LoopFlags & XAFLAG_LOOP_START) && !vc.LoopMode)
{
vc.LoopStartA = vc.NextA & 0xFFFF8;
}
const int cacheIdx = vc.NextA / pcm_WordsPerBlock;
const int cacheIdx = (vc.NextA & 0xFFFF8) / pcm_WordsPerBlock;
PcmCacheEntry& cacheLine = pcm_cache_data[cacheIdx];
vc.SBuffer = cacheLine.Sampledata;
@@ -172,46 +130,18 @@ static __forceinline s32 GetNextDataBuffered(V_Core& thiscore, uint voiceidx)
g_counter_cache_misses++;
}
s16* memptr = GetMemPtr(vc.NextA & 0xFFFF8);
XA_decode_block(vc.SBuffer, memptr, vc.Prev1, vc.Prev2);
}
}
return vc.SBuffer[vc.SCurrent++];
}
static __forceinline void GetNextDataDummy(V_Core& thiscore, uint voiceidx)
{
V_Voice& vc(thiscore.Voices[voiceidx]);
IncrementNextA(thiscore, voiceidx);
if ((vc.NextA & 7) == 0) // vc.SCurrent == 24 equivalent
// Get the sample index for NextA, we have to subtract 1 to ignore the loop header
int sampleIdx = ((vc.NextA % pcm_WordsPerBlock) - 1) * 4;
for (int i = 0; i < 4; i++)
{
if (vc.LoopFlags & XAFLAG_LOOP_END)
{
thiscore.Regs.ENDX |= (1 << voiceidx);
vc.NextA = vc.LoopStartA | 1;
}
else
vc.NextA++; // no, don't IncrementNextA here. We haven't read the header yet.
vc.DecodeFifo[(vc.DecPosWrite + i) % 32] = vc.SBuffer[sampleIdx + i];
}
if (vc.SCurrent == 28)
{
for (int i = 0; i < 2; i++)
if (Cores[i].IRQEnable && Cores[i].IRQA == (vc.NextA & 0xFFFF8))
SetIrqCall(i);
vc.LoopFlags = *GetMemPtr(vc.NextA & 0xFFFF8) >> 8; // grab loop flags from the upper byte.
if ((vc.LoopFlags & XAFLAG_LOOP_START) && !vc.LoopMode)
vc.LoopStartA = vc.NextA & 0xFFFF8;
vc.SCurrent = 0;
}
vc.SP -= 0x1000 * (4 - (vc.SCurrent & 3));
vc.SCurrent += 4 - (vc.SCurrent & 3);
}
/////////////////////////////////////////////////////////////////////////////////////////
@@ -237,6 +167,69 @@ static __forceinline StereoOut32 ApplyVolume(const StereoOut32& data, const V_Vo
ApplyVolume(data.Right, volume.Right.Value));
}
static __forceinline void UpdateBlockHeader(V_Core& thiscore, uint voiceidx)
{
V_Voice& vc(thiscore.Voices[voiceidx]);
for (int i = 0; i < 2; i++)
if (Cores[i].IRQEnable && Cores[i].IRQA == (vc.NextA & 0xFFFF8))
SetIrqCall(i);
s16* memptr = GetMemPtr(vc.NextA & 0xFFFF8);
vc.LoopFlags = *memptr >> 8; // grab loop flags from the upper byte.
if ((vc.LoopFlags & XAFLAG_LOOP_START) && !vc.LoopMode)
{
vc.LoopStartA = vc.NextA & 0xFFFF8;
}
}
static __forceinline void DecodeSamples(uint coreidx, uint voiceidx)
{
V_Core& thiscore(Cores[coreidx]);
V_Voice& vc(thiscore.Voices[voiceidx]);
// Update the block header on every audio frame
UpdateBlockHeader(thiscore, voiceidx);
// When a voice is started at 0 pitch, NAX quickly advances to SSA + 5
// So that would mean the decode buffer holds around 12 samples
if (((int)(vc.DecPosWrite - vc.DecPosRead)) > 12) {
// Sufficient data buffered
return;
}
if (vc.ADSR.Phase > V_ADSR::PHASE_STOPPED)
{
GetNextDataBuffered(thiscore, voiceidx);
}
vc.DecPosWrite += 4;
IncrementNextA(thiscore, voiceidx);
if ((vc.NextA & 7) == 0)
{
if (vc.LoopFlags & XAFLAG_LOOP_END)
{
thiscore.Regs.ENDX |= (1 << voiceidx);
vc.NextA = vc.LoopStartA;
if (!(vc.LoopFlags & XAFLAG_LOOP))
{
vc.Stop();
if (IsDevBuild)
{
if (SPU2::MsgVoiceOff())
SPU2::ConLog("* SPU2: Voice Off by EndPoint: %d \n", voiceidx);
}
}
}
IncrementNextA(thiscore, voiceidx);
vc.SBuffer = nullptr;
}
}
static void __forceinline UpdatePitch(uint coreidx, uint voiceidx)
{
V_Voice& vc(Cores[coreidx].Voices[voiceidx]);
@@ -278,33 +271,27 @@ static __forceinline void CalculateADSR(V_Core& thiscore, uint voiceidx)
pxAssume(vc.ADSR.Value >= 0); // ADSR should never be negative...
}
__forceinline static s32 GaussianInterpolate(s32 pv4, s32 pv3, s32 pv2, s32 pv1, s32 i)
static __forceinline void ConsumeSamples(V_Core& thiscore, uint voiceidx)
{
s32 out = 0;
out = (interpTable[i][0] * pv4) >> 15;
out += (interpTable[i][1] * pv3) >> 15;
out += (interpTable[i][2] * pv2) >> 15;
out += (interpTable[i][3] * pv1) >> 15;
V_Voice& vc(thiscore.Voices[voiceidx]);
return out;
int consumed = vc.SP >> 12;
vc.SP &= 0xfff;
vc.DecPosRead += consumed;
}
static __forceinline s32 GetVoiceValues(V_Core& thiscore, uint voiceidx)
{
V_Voice& vc(thiscore.Voices[voiceidx]);
while (vc.SP >= 0)
{
vc.PV4 = vc.PV3;
vc.PV3 = vc.PV2;
vc.PV2 = vc.PV1;
vc.PV1 = GetNextDataBuffered(thiscore, voiceidx);
vc.SP -= 0x1000;
}
int phase = (vc.SP & 0x0ff0) >> 4;
s32 out = 0;
out += (interpTable[phase][0] * vc.DecodeFifo[(vc.DecPosRead + 0) % 32]) >> 15;
out += (interpTable[phase][1] * vc.DecodeFifo[(vc.DecPosRead + 1) % 32]) >> 15;
out += (interpTable[phase][2] * vc.DecodeFifo[(vc.DecPosRead + 2) % 32]) >> 15;
out += (interpTable[phase][3] * vc.DecodeFifo[(vc.DecPosRead + 3) % 32]) >> 15;
const s32 mu = vc.SP + 0x1000;
return GaussianInterpolate(vc.PV4, vc.PV3, vc.PV2, vc.PV1, (mu & 0x0ff0) >> 4);
return out;
}
// This is Dr. Hell's noise algorithm as implemented in pcsxr
@@ -382,21 +369,13 @@ static __forceinline StereoOut32 MixVoice(uint coreidx, uint voiceidx)
V_Core& thiscore(Cores[coreidx]);
V_Voice& vc(thiscore.Voices[voiceidx]);
// If this assertion fails, it mans SCurrent is being corrupted somewhere, or is not initialized
// properly. Invalid values in SCurrent will cause errant IRQs and corrupted audio.
pxAssertMsg((vc.SCurrent <= 28) && (vc.SCurrent != 0), "Current sample should always range from 1->28");
// Most games don't use much volume slide effects. So only call the UpdateVolume
// methods when needed by checking the flag outside the method here...
// (Note: Ys 6 : Ark of Nephistm uses these effects)
vc.Volume.Update();
// SPU2 Note: The spu2 continues to process voices for eternity, always, so we
// have to run through all the motions of updating the voice regardless of it's
// audible status. Otherwise IRQs might not trigger and emulation might fail.
UpdatePitch(coreidx, voiceidx);
DecodeSamples(coreidx, voiceidx);
StereoOut32 voiceOut(0, 0);
s32 Value = 0;
@@ -419,11 +398,14 @@ static __forceinline StereoOut32 MixVoice(uint coreidx, uint voiceidx)
voiceOut = ApplyVolume(StereoOut32(Value, Value), vc.Volume);
}
else
{
while (vc.SP >= 0)
GetNextDataDummy(thiscore, voiceidx); // Dummy is enough
}
// SPU2 Note: The spu2 continues to process voices for eternity, always, so we
// have to run through all the motions of updating the voice regardless of it's
// audible status. Otherwise IRQs might not trigger and emulation might fail.
UpdatePitch(coreidx, voiceidx);
ConsumeSamples(thiscore, voiceidx);
// Write-back of raw voice data (post ADSR applied)
if (voiceidx == 1)
@@ -533,7 +515,8 @@ StereoOut32 V_Core::Mix(const VoiceMixSet& inVoices, const StereoOut32& Input, c
return TD + ApplyVolume(RV, FxVol);
}
static StereoOut32 DCFilter(StereoOut32 input) {
static StereoOut32 DCFilter(StereoOut32 input)
{
// A simple DC blocking high-pass filter
// Implementation from http://peabody.sapp.org/class/dmp2/lab/dcblock/
// The magic number 0x7f5c is ceil(INT16_MAX * 0.995)
@@ -634,9 +617,9 @@ __forceinline void spu2Mix()
if (SPU2::MsgCache())
{
SPU2::ConLog(" * SPU2 > CacheStats > Hits: %d Misses: %d Ignores: %d\n",
g_counter_cache_hits,
g_counter_cache_misses,
g_counter_cache_ignores);
g_counter_cache_hits,
g_counter_cache_misses,
g_counter_cache_ignores);
}
g_counter_cache_hits =
+4 -17
View File
@@ -256,29 +256,16 @@ struct V_Voice
// Sample pointer (19:12 bit fixed point)
s32 SP;
// Sample pointer for Cubic Interpolation
// Cubic interpolation mixes a sample behind Linear, so that it
// can have sample data to either side of the end points from which
// to extrapolate. This SP represents that late sample position.
s32 SPc;
// Previous sample values - used for interpolation
// Inverted order of these members to match the access order in the
// code (might improve cache hits).
s32 PV4;
s32 PV3;
s32 PV2;
s32 PV1;
// Last outputted audio value, used for voice modulation.
s32 OutX;
s32 NextCrest; // temp value for Crest calculation
// SBuffer now points directly to an ADPCM cache entry.
s16* SBuffer;
// sample position within the current decoded packet.
s32 SCurrent;
// Each voice has a buffer of decoded samples
s32 DecodeFifo[32];
u32 DecPosWrite;
u32 DecPosRead;
// it takes a few ticks for voices to start on the real SPU2?
void Start();
+8 -13
View File
@@ -181,7 +181,6 @@ void V_Core::Init(int index)
VoiceGates[v].WetR = -1;
Voices[v].Volume = V_VolumeSlideLR(0, 0); // V_VolumeSlideLR::Max;
Voices[v].SCurrent = 28;
Voices[v].ADSR.Counter = 0;
Voices[v].ADSR.Value = 0;
@@ -190,6 +189,10 @@ void V_Core::Init(int index)
Voices[v].NextA = 0x2801;
Voices[v].StartA = 0x2800;
Voices[v].LoopStartA = 0x2800;
memset(Voices[v].DecodeFifo, 0, sizeof(Voices[v].DecodeFifo));
Voices[v].DecPosRead = 0;
Voices[v].DecPosWrite = 0;
}
DMAICounter = 0;
@@ -212,23 +215,18 @@ void V_Voice::Start()
}
ADSR.Attack();
SCurrent = 28;
LoopMode = 0;
// When SP >= 0 the next sample will be grabbed, we don't want this to happen
// instantly because in the case of pitch being 0 we want to delay getting
// the next block header. This is a hack to work around the fact that unlike
// the HW we don't update the block header on every cycle.
SP = -1;
SP = 0;
LoopFlags = 0;
NextA = StartA | 1;
Prev1 = 0;
Prev2 = 0;
PV1 = PV2 = 0;
PV3 = PV4 = 0;
NextCrest = -0x8000;
SBuffer = nullptr;
DecPosRead = 0;
DecPosWrite = 0;
}
void V_Voice::Stop()
@@ -989,12 +987,10 @@ static void RegWrite_VoiceAddr(u16 value)
// Wallace And Gromit: Curse Of The Were-Rabbit.
thisvoice.NextA = ((u32)(value & 0x0F) << 16) | (thisvoice.NextA & 0xFFF8) | 1;
thisvoice.SCurrent = 28;
break;
case 5:
thisvoice.NextA = (thisvoice.NextA & 0x0F0000) | (value & 0xFFF8) | 1;
thisvoice.SCurrent = 28;
break;
}
}
@@ -1212,7 +1208,6 @@ static void RegWrite_Core(u16 value)
for (uint v = 0; v < 24; ++v)
{
Cores[1].Voices[v].Volume = V_VolumeSlideLR(0, 0); // V_VolumeSlideLR::Max;
Cores[1].Voices[v].SCurrent = 28;
Cores[1].Voices[v].ADSR.Value = 0;
Cores[1].Voices[v].ADSR.Phase = 0;
+1 -1
View File
@@ -26,7 +26,7 @@ enum class FreezeAction
// [SAVEVERSION+]
// This informs the auto updater that the users savestates will be invalidated.
static const u32 g_SaveVersion = (0x9A55 << 16) | 0x0000;
static const u32 g_SaveVersion = (0x9A57 << 16) | 0x0000;
// the freezing data between submodules and core
+1 -1
View File
@@ -3,4 +3,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 = 80;
static constexpr u32 SHADER_CACHE_VERSION = 82;
-2
View File
@@ -2993,8 +2993,6 @@ void VMManager::CheckForMemoryCardConfigChanges(const Pcsx2Config& old_config)
}
}
changed |= (EmuConfig.McdFolderAutoManage != old_config.McdFolderAutoManage);
if (!changed)
return;
+2
View File
@@ -254,6 +254,7 @@
<ClCompile Include="Host\SDLAudioStream.cpp" />
<ClCompile Include="Hotkeys.cpp" />
<ClCompile Include="ImGui\FullscreenUI.cpp" />
<ClCompile Include="ImGui\FullscreenUI_Settings.cpp" />
<ClCompile Include="ImGui\ImGuiFullscreen.cpp" />
<ClCompile Include="ImGui\ImGuiManager.cpp" />
<ClCompile Include="ImGui\ImGuiOverlays.cpp" />
@@ -700,6 +701,7 @@
<ClInclude Include="Host\AudioStream.h" />
<ClInclude Include="Host\AudioStreamTypes.h" />
<ClInclude Include="ImGui\FullscreenUI.h" />
<ClInclude Include="ImGui\FullscreenUI_Internal.h" />
<ClInclude Include="ImGui\ImGuiAnimated.h" />
<ClInclude Include="ImGui\ImGuiFullscreen.h" />
<ClInclude Include="ImGui\ImGuiManager.h" />
+6
View File
@@ -1352,6 +1352,9 @@
<ClCompile Include="ImGui\FullscreenUI.cpp">
<Filter>Misc\ImGui</Filter>
</ClCompile>
<ClCompile Include="ImGui\FullscreenUI_Settings.cpp">
<Filter>Misc\ImGui</Filter>
</ClCompile>
<ClCompile Include="ImGui\ImGuiFullscreen.cpp">
<Filter>Misc\ImGui</Filter>
</ClCompile>
@@ -2304,6 +2307,9 @@
<ClInclude Include="ImGui\FullscreenUI.h">
<Filter>Misc\ImGui</Filter>
</ClInclude>
<ClInclude Include="ImGui\FullscreenUI_Internal.h">
<Filter>Misc\ImGui</Filter>
</ClInclude>
<ClInclude Include="ImGui\ImGuiFullscreen.h">
<Filter>Misc\ImGui</Filter>
</ClInclude>
@@ -5,90 +5,110 @@ import os
START_IDENT = "// TRANSLATION-STRING-AREA-BEGIN"
END_IDENT = "// TRANSLATION-STRING-AREA-END"
src_file = os.path.join(os.path.dirname(__file__), "..", "pcsx2", "ImGui", "FullscreenUI.cpp")
src_files = [
os.path.join(os.path.dirname(__file__), "..", "pcsx2", "ImGui", "FullscreenUI.cpp"),
os.path.join(os.path.dirname(__file__), "..", "pcsx2", "ImGui", "FullscreenUI_Settings.cpp"),
]
with open(src_file, "r") as f:
full_source = f.read()
def extract_strings_from_source(source_content):
"""Extract FSUI translation strings from source content."""
strings = []
for token in ["FSUI_STR", "FSUI_CSTR", "FSUI_FSTR", "FSUI_NSTR", "FSUI_VSTR", "FSUI_ICONSTR", "FSUI_ICONSTR_S"]:
token_len = len(token)
last_pos = 0
while True:
last_pos = source_content.find(token, last_pos)
if last_pos < 0:
break
strings = []
for token in ["FSUI_STR", "FSUI_CSTR", "FSUI_FSTR", "FSUI_NSTR", "FSUI_VSTR", "FSUI_ICONSTR", "FSUI_ICONSTR_S"]:
token_len = len(token)
last_pos = 0
while True:
last_pos = full_source.find(token, last_pos)
if last_pos < 0:
break
if last_pos >= 8 and source_content[last_pos - 8:last_pos] == "#define ":
last_pos += len(token)
continue
if last_pos >= 8 and full_source[last_pos - 8:last_pos] == "#define ":
last_pos += len(token)
continue
if source_content[last_pos + token_len] == '(':
start_pos = last_pos + token_len + 1
end_pos = source_content.find(")", start_pos)
s = source_content[start_pos:end_pos]
if full_source[last_pos + token_len] == '(':
start_pos = last_pos + token_len + 1
end_pos = full_source.find(")", start_pos)
s = full_source[start_pos:end_pos]
# Split into string arguments, removing "
string_args = [""]
arg = 0;
cpos = s.find(',')
pos = s.find('"')
while pos >= 0 or cpos >= 0:
assert pos == 0 or s[pos - 1] != '\\'
if cpos == -1 or pos < cpos:
epos = pos
while True:
epos = s.find('"', epos + 1)
# found ')' in string, extend s to next ')'
if epos == -1:
end_pos = source_content.find(")", end_pos + 1)
s = source_content[start_pos:end_pos]
epos = pos
continue
# Split into sting arguments, removing "
string_args = [""]
arg = 0;
cpos = s.find(',')
pos = s.find('"')
while pos >= 0 or cpos >= 0:
assert pos == 0 or s[pos - 1] != '\\'
if cpos == -1 or pos < cpos:
epos = pos
while True:
epos = s.find('"', epos + 1)
# found ')' in string, extend s to next ')'
if epos == -1:
end_pos = full_source.find(")", end_pos + 1)
s = full_source[start_pos:end_pos]
epos = pos
continue
if s[epos - 1] == '\\':
continue
else:
break
if s[epos - 1] == '\\':
continue
else:
break
assert epos > pos
string_args[arg] += s[pos+1:epos]
cpos = s.find(',', epos + 1)
pos = s.find('"', epos + 1)
else:
arg += 1
string_args.append("")
cpos = s.find(',', cpos + 1)
assert epos > pos
string_args[arg] += s[pos+1:epos]
cpos = s.find(',', epos + 1)
pos = s.find('"', epos + 1)
print(string_args)
# FSUI_ICONSTR and FSUI_ICONSTR_S need to translate the only the second argument
# other defines take only a single argument
if len(string_args) >= 2:
new_s = string_args[1]
else:
arg += 1
string_args.append("")
cpos = s.find(',', cpos + 1)
new_s = string_args[0]
print(string_args)
assert len(new_s) > 0
# FSUI_ICONSTR and FSUI_ICONSTR_S need to translate the only the second argument
# other defines take only a single argument
if len(string_args) >= 2:
new_s = string_args[1]
else:
new_s = string_args[0]
if new_s not in strings:
strings.append(new_s)
last_pos += len(token)
return strings
assert len(new_s) > 0
def process_file(src_file):
"""Process a single source file extract strings and update its translation area."""
print(f"\nProcessing: {src_file}")
with open(src_file, "r") as f:
source = f.read()
#assert (end_pos - start_pos) < 300
#if (end_pos - start_pos) >= 300:
# print("WARNING: Long string")
# print(new_s)
if new_s not in strings:
strings.append(new_s)
last_pos += len(token)
start = source.find(START_IDENT)
end = source.find(END_IDENT)
if start < 0 or end <= start:
print(f" Warning: No translation string area found in {src_file}")
return 0
source_without_area = source[:start] + source[end + len(END_IDENT):]
strings = extract_strings_from_source(source_without_area)
print(f" Found {len(strings)} unique strings.")
new_area = ""
for string in strings:
new_area += f"TRANSLATE_NOOP(\"FullscreenUI\", \"{string}\");\n"
new_source = source[:start + len(START_IDENT) + 1] + new_area + source[end:]
with open(src_file, "w") as f:
f.write(new_source)
return len(strings)
print(f"Found {len(strings)} unique strings.")
total_strings = 0
for src_file in src_files:
total_strings += process_file(src_file)
start = full_source.find(START_IDENT)
end = full_source.find(END_IDENT)
assert start >= 0 and end > start
new_area = ""
for string in list(strings):
new_area += f"TRANSLATE_NOOP(\"FullscreenUI\", \"{string}\");\n"
full_source = full_source[:start+len(START_IDENT)+1] + new_area + full_source[end:]
with open(src_file, "w") as f:
f.write(full_source)
print(f"\nTotal: {total_strings} unique strings across all files.")