Compare commits

..

21 Commits

Author SHA1 Message Date
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
30 changed files with 968 additions and 1463 deletions

View File

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

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.
@@ -25578,6 +25582,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 +26234,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 +35861,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 +35934,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.
@@ -59824,6 +59836,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 +61242,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 +71376,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 +74919,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 +75699,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.

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,

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
@@ -140,7 +140,7 @@ struct PS_OUTPUT
#endif
#endif
#if PS_ZCLAMP || PS_ZFLOOR
float depth : SV_Depth;
float depth : SV_DepthLessEqual;
#endif
};

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) && HAS_CONSERVATIVE_DEPTH
layout(depth_less) out float gl_FragDepth;
#endif
vec4 sample_from_rt()
{
#if !NEEDS_RT

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)

View File

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

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>

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;
}

View File

@@ -295,6 +295,9 @@
<property name="text">
<string>Desktop</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>

View File

@@ -2223,7 +2223,7 @@ Leaderboard Position: {1} of {2}</source>
</message>
<message>
<location filename="../Settings/BIOSSettingsWidget.ui" line="138"/>
<source>Options and Patches</source>
<source>Fast Boot Options</source>
<translation type="unfinished"></translation>
</message>
<message>
@@ -2339,18 +2339,18 @@ Leaderboard Position: {1} of {2}</source>
</message>
<message>
<location filename="../Debugger/Breakpoints/BreakpointDialog.cpp" line="103"/>
<location filename="../Debugger/Breakpoints/BreakpointDialog.cpp" line="132"/>
<location filename="../Debugger/Breakpoints/BreakpointDialog.cpp" line="137"/>
<source>Invalid Address</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Debugger/Breakpoints/BreakpointDialog.cpp" line="119"/>
<location filename="../Debugger/Breakpoints/BreakpointDialog.cpp" line="155"/>
<location filename="../Debugger/Breakpoints/BreakpointDialog.cpp" line="160"/>
<source>Invalid Condition</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Debugger/Breakpoints/BreakpointDialog.cpp" line="139"/>
<location filename="../Debugger/Breakpoints/BreakpointDialog.cpp" line="144"/>
<source>Invalid Size</source>
<translation type="unfinished"></translation>
</message>
@@ -6804,11 +6804,6 @@ The URL was: %1</source>
<source>BIOS Selection</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/ImGui/FullscreenUI.cpp" line="9512"/>
<source>Options and Patches</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/ImGui/FullscreenUI.cpp" line="9513"/>
<source>Skips the intro screen, and bypasses region checks.</source>
@@ -9942,6 +9937,11 @@ Do you want to shutdown anyway and IRREVERSIBLY CORRUPT YOUR MEMORY CARD?</sourc
<source>Shows the number of dumped and loaded texture replacements on the OSD.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/ImGui/FullscreenUI.cpp" line="9512"/>
<source>Fast Boot Options</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/ImGui/FullscreenUI.cpp" line="9604"/>
<source>Bilinear Dirty Upscale</source>
@@ -11893,7 +11893,7 @@ This action cannot be undone.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp" line="5055"/>
<location filename="../../pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp" line="5052"/>
<source>Spin GPU During Readbacks is enabled, but calibrated timestamps are unavailable. This might be really slow.</source>
<translation type="unfinished"></translation>
</message>
@@ -19742,12 +19742,12 @@ Savestates should not be used in place of in-game saves.</source>
<context>
<name>MemoryCardListWidget</name>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="456"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="450"/>
<source>Yes</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="456"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="450"/>
<source>No</source>
<translation type="unfinished"></translation>
</message>
@@ -19781,7 +19781,7 @@ Savestates should not be used in place of in-game saves.</source>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.ui" line="90"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="113"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="107"/>
<source>Reset</source>
<translation type="unfinished"></translation>
</message>
@@ -19812,170 +19812,149 @@ Savestates should not be used in place of in-game saves.</source>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.ui" line="148"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="305"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="299"/>
<source>Create</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.ui" line="155"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="299"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="293"/>
<source>Rename</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.ui" line="162"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="300"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="294"/>
<source>Convert</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.ui" line="169"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="301"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="295"/>
<source>Delete</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.ui" line="181"/>
<source>Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.ui" line="187"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="62"/>
<source>Automatically manage saves based on running game</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="63"/>
<source>Checked</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="64"/>
<source>(Folder type only / Card size: Auto) Loads only the relevant booted game saves, ignoring others. Avoids running out of space for saves.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="93"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="87"/>
<source>Swap Memory Cards</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="113"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="107"/>
<source>Eject Memory Card</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="152"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="275"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="340"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="146"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="269"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="334"/>
<source>Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="212"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="222"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="206"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="216"/>
<source>Delete Memory Card</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="237"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="244"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="251"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="258"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="231"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="238"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="245"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="252"/>
<source>Rename Memory Card</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="237"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="231"/>
<source>New Card Name</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="244"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="238"/>
<source>New name is invalid, it must end with .ps2</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="252"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="246"/>
<source>New name is invalid, a card with this name already exists.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="105"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="99"/>
<source>Slot %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="152"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="146"/>
<source>This Memory Card cannot be recognized or is not a valid file type.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="213"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="207"/>
<source>Are you sure you wish to delete the Memory Card &apos;%1&apos;?
This action cannot be reversed, and you will lose any saves on the card.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="223"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="217"/>
<source>Failed to delete the Memory Card. The log may have more information.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="259"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="253"/>
<source>Failed to rename Memory Card. The log may contain more information.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="275"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="269"/>
<source>Cannot convert an unformatted memory card.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="294"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="288"/>
<source>Use for Slot %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="340"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="334"/>
<source>Both slots must have a card selected to swap.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="356"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="350"/>
<source>PS2 (8MB)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="359"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="353"/>
<source>PS2 (16MB)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="362"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="356"/>
<source>PS2 (32MB)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="365"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="359"/>
<source>PS2 (64MB)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="368"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="362"/>
<source>PS1 (128KB)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="372"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="381"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="366"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="375"/>
<source>Unknown</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="377"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="371"/>
<source>PS2 (Folder)</source>
<translation type="unfinished"></translation>
</message>
@@ -19983,12 +19962,12 @@ This action cannot be reversed, and you will lose any saves on the card.</source
<context>
<name>MemoryCardSlotWidget</name>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="513"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="507"/>
<source>%1 [%2]</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="519"/>
<location filename="../Settings/MemoryCardSettingsWidget.cpp" line="513"/>
<source>%1 [Missing]</source>
<extracomment>Ignore Crowdin&apos;s warning for [Missing], the text should be translated.</extracomment>
<translation type="unfinished"></translation>
@@ -22483,115 +22462,120 @@ Scanning recursively takes more time, but will identify files in subdirectories.
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="139"/>
<location filename="../ShortcutCreationDialog.cpp" line="154"/>
<location filename="../ShortcutCreationDialog.cpp" line="163"/>
<location filename="../ShortcutCreationDialog.cpp" line="160"/>
<location filename="../ShortcutCreationDialog.cpp" line="170"/>
<location filename="../ShortcutCreationDialog.cpp" line="181"/>
<location filename="../ShortcutCreationDialog.cpp" line="203"/>
<location filename="../ShortcutCreationDialog.cpp" line="214"/>
<location filename="../ShortcutCreationDialog.cpp" line="290"/>
<location filename="../ShortcutCreationDialog.cpp" line="301"/>
<location filename="../ShortcutCreationDialog.cpp" line="309"/>
<location filename="../ShortcutCreationDialog.cpp" line="337"/>
<location filename="../ShortcutCreationDialog.cpp" line="370"/>
<location filename="../ShortcutCreationDialog.cpp" line="409"/>
<location filename="../ShortcutCreationDialog.cpp" line="177"/>
<location filename="../ShortcutCreationDialog.cpp" line="188"/>
<location filename="../ShortcutCreationDialog.cpp" line="210"/>
<location filename="../ShortcutCreationDialog.cpp" line="221"/>
<location filename="../ShortcutCreationDialog.cpp" line="297"/>
<location filename="../ShortcutCreationDialog.cpp" line="308"/>
<location filename="../ShortcutCreationDialog.cpp" line="316"/>
<location filename="../ShortcutCreationDialog.cpp" line="344"/>
<location filename="../ShortcutCreationDialog.cpp" line="377"/>
<location filename="../ShortcutCreationDialog.cpp" line="416"/>
<source>Failed to create shortcut</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="139"/>
<location filename="../ShortcutCreationDialog.cpp" line="301"/>
<location filename="../ShortcutCreationDialog.cpp" line="308"/>
<source>Filename contains illegal character.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="154"/>
<location filename="../ShortcutCreationDialog.cpp" line="160"/>
<source>Could not create start menu directory.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="163"/>
<source>Home path is empty.</source>
<location filename="../ShortcutCreationDialog.cpp" line="170"/>
<source>&apos;Desktop&apos; directory not found</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="170"/>
<source>User&apos;s &apos;Start Menu\Programs&apos; directory not found</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="177"/>
<source>A shortcut with the same name already exists.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="181"/>
<location filename="../ShortcutCreationDialog.cpp" line="370"/>
<location filename="../ShortcutCreationDialog.cpp" line="188"/>
<location filename="../ShortcutCreationDialog.cpp" line="377"/>
<source>File path contains invalid character(s).</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="203"/>
<location filename="../ShortcutCreationDialog.cpp" line="210"/>
<source>CoInitialize failed (%1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="223"/>
<location filename="../ShortcutCreationDialog.cpp" line="230"/>
<source>CoCreateInstance failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="232"/>
<location filename="../ShortcutCreationDialog.cpp" line="239"/>
<source>SetPath failed (%1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="241"/>
<location filename="../ShortcutCreationDialog.cpp" line="248"/>
<source>SetWorkingDirectory failed (%1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="252"/>
<location filename="../ShortcutCreationDialog.cpp" line="259"/>
<source>SetArguments failed (%1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="263"/>
<location filename="../ShortcutCreationDialog.cpp" line="270"/>
<source>SetIconLocation failed (%1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="271"/>
<location filename="../ShortcutCreationDialog.cpp" line="278"/>
<source>QueryInterface failed (%1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="280"/>
<location filename="../ShortcutCreationDialog.cpp" line="287"/>
<source>Failed to save the shortcut (%1)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="290"/>
<location filename="../ShortcutCreationDialog.cpp" line="297"/>
<source>Cannot create a shortcut without a title.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="309"/>
<location filename="../ShortcutCreationDialog.cpp" line="316"/>
<source>Executable path is empty.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="337"/>
<location filename="../ShortcutCreationDialog.cpp" line="344"/>
<source>Path to the Home directory is empty.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="399"/>
<location filename="../ShortcutCreationDialog.cpp" line="406"/>
<source>Desktop Shortcut Files (*.desktop)</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="401"/>
<location filename="../ShortcutCreationDialog.cpp" line="408"/>
<source>Select Shortcut Save Destination</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ShortcutCreationDialog.cpp" line="409"/>
<location filename="../ShortcutCreationDialog.cpp" line="416"/>
<source>Failed to create .desktop file</source>
<translation type="unfinished"></translation>
</message>
@@ -25524,42 +25508,42 @@ Error was: {}</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3171"/>
<location filename="../../pcsx2/VMManager.cpp" line="3169"/>
<source>Fast CDVD is enabled, this may break games.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3175"/>
<location filename="../../pcsx2/VMManager.cpp" line="3173"/>
<source>Cycle rate/skip is not at default, this may crash or make games run too slow.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3185"/>
<location filename="../../pcsx2/VMManager.cpp" line="3183"/>
<source>Upscale multiplier is below native, this will break rendering.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3225"/>
<location filename="../../pcsx2/VMManager.cpp" line="3223"/>
<source>Mipmapping is disabled. This may break rendering in some games.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3230"/>
<location filename="../../pcsx2/VMManager.cpp" line="3228"/>
<source>Debug device is enabled. This will massively reduce performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3260"/>
<location filename="../../pcsx2/VMManager.cpp" line="3258"/>
<source>Texture filtering is not set to Bilinear (PS2). This will break rendering in some games.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3723"/>
<location filename="../../pcsx2/VMManager.cpp" line="3721"/>
<source>No Game Running</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3190"/>
<location filename="../../pcsx2/VMManager.cpp" line="3188"/>
<source>Trilinear filtering is not set to automatic. This may break rendering in some games.</source>
<translation type="unfinished"></translation>
</message>
@@ -25643,142 +25627,142 @@ PCSX2 will be able to run once you&apos;ve placed your BIOS image inside the fol
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3118"/>
<location filename="../../pcsx2/VMManager.cpp" line="3116"/>
<source>Cheats have been disabled due to RetroAchievements Hardcore Mode.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3195"/>
<location filename="../../pcsx2/VMManager.cpp" line="3193"/>
<source>Blending Accuracy is below Basic, this may break effects in some games.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3200"/>
<location filename="../../pcsx2/VMManager.cpp" line="3198"/>
<source>Hardware Download Mode is not set to Accurate, this may break rendering in some games.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3235"/>
<location filename="../../pcsx2/VMManager.cpp" line="3233"/>
<source>Dithering is set to Force 32 bit. This will break rendering in some games.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3240"/>
<location filename="../../pcsx2/VMManager.cpp" line="3238"/>
<source>Dithering is disabled. This will cause color banding in some games.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3245"/>
<location filename="../../pcsx2/VMManager.cpp" line="3243"/>
<source>Integer scaling is enabled. This may shrink the image.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3254"/>
<location filename="../../pcsx2/VMManager.cpp" line="3252"/>
<source>Graphics API is not set to Automatic. This may cause performance problems and graphical issues.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3266"/>
<location filename="../../pcsx2/VMManager.cpp" line="3264"/>
<source>EE FPU Round Mode is not set to default, this may break some games.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3272"/>
<location filename="../../pcsx2/VMManager.cpp" line="3270"/>
<source>EE FPU Clamp Mode is not set to default, this may break some games.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3277"/>
<location filename="../../pcsx2/VMManager.cpp" line="3275"/>
<source>VU0 Round Mode is not set to default, this may break some games.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3282"/>
<location filename="../../pcsx2/VMManager.cpp" line="3280"/>
<source>VU1 Round Mode is not set to default, this may break some games.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3289"/>
<location filename="../../pcsx2/VMManager.cpp" line="3287"/>
<source>VU Clamp Mode is not set to default, this may break some games.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3294"/>
<location filename="../../pcsx2/VMManager.cpp" line="3292"/>
<source>128MB RAM is enabled. Compatibility with some games may be affected.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3299"/>
<location filename="../../pcsx2/VMManager.cpp" line="3297"/>
<source>Game Fixes are not enabled. Compatibility with some games may be affected.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3304"/>
<location filename="../../pcsx2/VMManager.cpp" line="3302"/>
<source>Compatibility Patches are not enabled. Compatibility with some games may be affected.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3308"/>
<location filename="../../pcsx2/VMManager.cpp" line="3306"/>
<source>Frame rate for NTSC is not default. This may break some games.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3310"/>
<location filename="../../pcsx2/VMManager.cpp" line="3308"/>
<source>Frame rate for PAL is not default. This may break some games.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3329"/>
<location filename="../../pcsx2/VMManager.cpp" line="3327"/>
<source>EE Recompiler is not enabled, this will significantly reduce performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3334"/>
<location filename="../../pcsx2/VMManager.cpp" line="3332"/>
<source>VU0 Recompiler is not enabled, this will significantly reduce performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3339"/>
<location filename="../../pcsx2/VMManager.cpp" line="3337"/>
<source>VU1 Recompiler is not enabled, this will significantly reduce performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3344"/>
<location filename="../../pcsx2/VMManager.cpp" line="3342"/>
<source>IOP Recompiler is not enabled, this will significantly reduce performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3349"/>
<location filename="../../pcsx2/VMManager.cpp" line="3347"/>
<source>EE Cache is enabled, this will significantly reduce performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3354"/>
<location filename="../../pcsx2/VMManager.cpp" line="3352"/>
<source>EE Wait Loop Detection is not enabled, this may reduce performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3359"/>
<location filename="../../pcsx2/VMManager.cpp" line="3357"/>
<source>INTC Spin Detection is not enabled, this may reduce performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3362"/>
<location filename="../../pcsx2/VMManager.cpp" line="3360"/>
<source>Fastmem is not enabled, this will reduce performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3366"/>
<location filename="../../pcsx2/VMManager.cpp" line="3364"/>
<source>Instant VU1 is disabled, this may reduce performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3371"/>
<location filename="../../pcsx2/VMManager.cpp" line="3369"/>
<source>mVU Flag Hack is not enabled, this may reduce performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3205"/>
<location filename="../../pcsx2/VMManager.cpp" line="3203"/>
<source>GPU Palette Conversion is enabled, this may reduce performance.</source>
<translation type="unfinished"></translation>
</message>
@@ -25788,17 +25772,17 @@ PCSX2 will be able to run once you&apos;ve placed your BIOS image inside the fol
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3210"/>
<location filename="../../pcsx2/VMManager.cpp" line="3208"/>
<source>Texture Preloading is not Full, this may reduce performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3215"/>
<location filename="../../pcsx2/VMManager.cpp" line="3213"/>
<source>Estimate texture region is enabled, this may reduce performance.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../pcsx2/VMManager.cpp" line="3220"/>
<location filename="../../pcsx2/VMManager.cpp" line="3218"/>
<source>Texture dumping is enabled, this will continually dump textures to disk.</source>
<translation type="unfinished"></translation>
</message>

File diff suppressed because it is too large Load Diff

View File

@@ -127,41 +127,37 @@ private:
protected:
static constexpr int INVALID_ALPHA_MINMAX = 500;
static constexpr int MAX_DRAW_BUFFERS = 3;
GSVertex m_v = {};
float m_q = 1.0f;
GSVector4i m_scissor_cull_min = {};
GSVector4i m_scissor_cull_max = {};
GSVector4i m_xyof = {};
u32 m_used_buffers_idx = 0;
u32 m_current_buffer_idx = 0;
bool m_recent_buffer_switch = false;
struct GSVertexBuff
struct
{
GSVertex* buff;
GSVertex* buff_copy; // same size buffer to copy/modify the original buffer
GSVertex* buff_copy; // same size buffer to copy/modify the original buffer
u32 head, tail, next, maxcount; // head: first vertex, tail: last vertex + 1, next: last indexed + 1
u32 xy_tail;
GSVector4i xy[4];
GSVector4i xyhead;
};
} m_vertex = {};
GSVertexBuff m_vertex_buffers[MAX_DRAW_BUFFERS];
GSVertexBuff* m_vertex;
struct GSIndexBuff
struct
{
u16* buff;
u32 tail;
};
} m_index = {};
GSIndexBuff m_index_buffers[MAX_DRAW_BUFFERS];
GSIndexBuff* m_index;
GSVertexBuff m_draw_vertex = {};
struct
{
GSVertex* buff;
u32 head, tail, next, maxcount; // head: first vertex, tail: last vertex + 1, next: last indexed + 1
u32 xy_tail;
GSVector4i xy[4];
GSVector4i xyhead;
} m_draw_vertex = {};
struct
{
@@ -169,16 +165,6 @@ protected:
u32 tail;
} m_draw_index = {};
struct GSDrawBufferEnv
{
GSDrawingEnvironment m_env;
int m_backed_up_ctx = 0;
u32 m_dirty_regs = 0;
GSVector4i draw_rect = GSVector4i::zero();
};
GSDrawBufferEnv m_env_buffers[MAX_DRAW_BUFFERS] = {};
void UpdateContext();
void UpdateScissor();
@@ -189,7 +175,6 @@ protected:
template<u32 prim> void HandleAutoFlush();
bool EarlyDetectShuffle(u32 prim);
void CheckCLUTValidity(u32 prim);
bool CheckOverlapVerts(u32 n);
template <u32 prim, bool auto_flush> void VertexKick(u32 skip);
@@ -256,10 +241,9 @@ public:
GSLocalMemory m_mem;
GSDrawingEnvironment m_env = {};
GSDrawingEnvironment m_prev_env = {};
GSDrawingEnvironment m_temp_env = {};
const GSDrawingEnvironment* m_draw_env = &m_env;
GSDrawingContext* m_context = nullptr;
GSVector4i temp_draw_rect;
GSVector4i temp_draw_rect = {};
std::unique_ptr<GSDumpBase> m_dump;
bool m_scissor_invalid = false;
bool m_quad_check_valid = false;
@@ -457,15 +441,7 @@ public:
virtual void Reset(bool hardware_reset);
virtual void UpdateSettings(const Pcsx2Config::GSOptions& old_config);
void ResetDrawBuffers();
void ResetDrawBufferIdx();
void FlushBuffers(bool use_flush_reason = false, GSFlushReason flush_reason = GSFlushReason::CONTEXTCHANGE);
void PushBuffer();
void SetDrawBufferEnv();
void SetDrawBuffDirty();
bool CanBufferNewDraw();
void Flush(GSFlushReason reason);
void FlushDraw(GSFlushReason reason);
u32 CalcMask(int exp, int max_exp);
void FlushPrim();
bool TestDrawChanged();
@@ -478,7 +454,7 @@ public:
virtual void Move();
GSVector4i GetTEX0Rect(GSDrawingContext prev_ctx);
GSVector4i GetTEX0Rect();
void CheckWriteOverlap(bool req_write, bool req_read);
void Write(const u8* mem, int len);
void Read(u8* mem, int len);

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;
}

View File

@@ -1397,7 +1397,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 +1427,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 +1445,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 +1463,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;
@@ -2063,13 +2065,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 },

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.");
@@ -1622,14 +1636,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 +1657,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 +1677,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;
@@ -2171,7 +2186,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 },

View File

@@ -115,7 +115,7 @@ bool GSHwHack::GSC_IRem(GSRendererHW& r, int& skip)
// Detect the deswizzling shuffle from depth, copying the RG and BA separately on each half of the page (ignore the split).
if (RTME && RFBP != RTBP0 && RFPSM == PSMCT16S && RTPSM == PSMCT16S)
{
if (r.m_vt.m_max.p.x == 64 && r.m_vt.m_max.p.y == 64 && r.m_index->tail == 128)
if (r.m_vt.m_max.p.x == 64 && r.m_vt.m_max.p.y == 64 && r.m_index.tail == 128)
{
const GSVector4i draw_size(r.m_vt.m_min.p.x, r.m_vt.m_min.p.y/2, r.m_vt.m_max.p.x, r.m_vt.m_max.p.y/2);
const GSVector4i read_size(r.m_vt.m_min.t.x, r.m_vt.m_min.t.y/2, r.m_vt.m_max.t.x, r.m_vt.m_max.t.y/2);
@@ -126,7 +126,7 @@ bool GSHwHack::GSC_IRem(GSRendererHW& r, int& skip)
}
// Following the previous draw, it tries to copy everything read from depth and offset it by 2, for the alternate line channel shuffle (skipped above).
if (RTBP0 == (RFBP - 0x20) && r.m_vt.m_max.p.x == 64 && r.m_vt.m_max.p.y == 34 && r.m_index->tail == 2)
if (RTBP0 == (RFBP - 0x20) && r.m_vt.m_max.p.x == 64 && r.m_vt.m_max.p.y == 34 && r.m_index.tail == 2)
{
GSVector4i draw_size(r.m_vt.m_min.p.x, r.m_vt.m_min.p.y - 2.0f, r.m_vt.m_max.p.x, r.m_vt.m_max.p.y - 2.0f);
GSVector4i read_size(r.m_vt.m_min.t.x, r.m_vt.m_min.t.y, r.m_vt.m_max.t.x, r.m_vt.m_max.t.y);
@@ -264,10 +264,10 @@ bool GSHwHack::GSC_SFEX3(GSRendererHW& r, int& skip)
// Skipping is no good as the copy is used again later, and it causes a weird shimmer/echo effect every other frame.
// Add on the height from the second part of the draw to the first, to make it one big rect.
r.m_vertex->buff[1].XYZ.Y += r.m_vertex->buff[r.m_vertex->tail - 1].XYZ.Y - r.m_context->XYOFFSET.OFY;
r.m_vertex->buff[1].V = r.m_vertex->buff[r.m_vertex->tail - 1].V;
r.m_vertex->tail = 2;
r.m_index->tail = 2;
r.m_vertex.buff[1].XYZ.Y += r.m_vertex.buff[r.m_vertex.tail - 1].XYZ.Y - r.m_context->XYOFFSET.OFY;
r.m_vertex.buff[1].V = r.m_vertex.buff[r.m_vertex.tail - 1].V;
r.m_vertex.tail = 2;
r.m_index.tail = 2;
}
}
@@ -332,9 +332,9 @@ bool GSHwHack::GSC_NamcoGames(GSRendererHW& r, int& skip)
{
if (skip == 0)
{
if (!s_nativeres && r.PRIM->PRIM == GS_SPRITE && RTME && RTEX0.TFX == 1 && RFPSM == RTPSM && RTPSM == PSMCT32 && RFBMSK == 0xFF000000 && r.m_index->tail > 2)
if (!s_nativeres && r.PRIM->PRIM == GS_SPRITE && RTME && RTEX0.TFX == 1 && RFPSM == RTPSM && RTPSM == PSMCT32 && RFBMSK == 0xFF000000 && r.m_index.tail > 2)
{
GSVertex* v = &r.m_vertex->buff[0];
GSVertex* v = &r.m_vertex.buff[0];
// Don't enable hack on native res.
// Fixes ghosting/blur effect and white lines appearing in stages: Moonfit Wilderness, Acid Rain - caused by upscaling.
// Game copies the framebuffer as individual page rects with slight offsets (like 1/16 of a pixel etc) which doesn't wokr well with upscaling.
@@ -348,7 +348,7 @@ bool GSHwHack::GSC_NamcoGames(GSRendererHW& r, int& skip)
else
{
// Fixes the alignment of the two halves for the heat haze on the temple stage.
for (u32 i = 0; i < r.m_index->tail; i+=2)
for (u32 i = 0; i < r.m_index.tail; i+=2)
{
v[i].XYZ.Y -= 0x8;
}
@@ -676,7 +676,7 @@ bool GSHwHack::GSC_NFSUndercover(GSRendererHW& r, int& skip)
if (RPRIM->TME && Frame.PSM == PSMCT16S && Frame.FBMSK != 0 && Frame.FBW == 10 && Texture.TBW == 1 && Texture.TBP0 == 0x02800 && Texture.PSM == PSMZ16S)
{
GSVertex* v = &r.m_vertex->buff[1];
GSVertex* v = &r.m_vertex.buff[1];
v[0].XYZ.X = static_cast<u16>(RCONTEXT->XYOFFSET.OFX + ((r.m_r.z * 2) << 4));
v[0].XYZ.Y = static_cast<u16>(RCONTEXT->XYOFFSET.OFY + (r.m_r.w << 4));
v[0].U = r.m_r.z << 4;
@@ -687,8 +687,8 @@ bool GSHwHack::GSC_NFSUndercover(GSRendererHW& r, int& skip)
r.m_vt.m_max.p.y = r.m_r.w;
r.m_vt.m_max.t.x = r.m_r.z;
r.m_vt.m_max.t.y = r.m_r.w;
r.m_vertex->head = r.m_vertex->tail = r.m_vertex->next = 2;
r.m_index->tail = 2;
r.m_vertex.head = r.m_vertex.tail = r.m_vertex.next = 2;
r.m_index.tail = 2;
skip = 79;
}
else
@@ -840,7 +840,7 @@ bool GSHwHack::GSC_Battlefield2(GSRendererHW& r, int& skip)
if (dst)
{
float dc = r.m_vertex->buff[1].XYZ.Z;
float dc = r.m_vertex.buff[1].XYZ.Z;
g_gs_device->ClearDepth(dst->m_texture, dc * std::exp2(-32.0f));
}
}
@@ -858,9 +858,9 @@ bool GSHwHack::GSC_BlueTongueGames(GSRendererHW& r, int& skip)
if (RPRIM->TME && RTEX0.TW == 3 && RTEX0.TH == 3 && RTEX0.PSM == 0 && RFRAME.FBMSK == 0x00FFFFFF && RFRAME.FBW == 8 && r.PCRTCDisplays.GetResolution().x > 512)
{
// Check we are drawing stripes
for (u32 i = 1; i < r.m_vertex->tail; i+=2)
for (u32 i = 1; i < r.m_vertex.tail; i+=2)
{
int value = (((r.m_vertex->buff[i].XYZ.X - r.m_vertex->buff[i - 1].XYZ.X) + 8) >> 4);
int value = (((r.m_vertex.buff[i].XYZ.X - r.m_vertex.buff[i - 1].XYZ.X) + 8) >> 4);
if (value != 32)
return false;
}
@@ -872,18 +872,18 @@ bool GSHwHack::GSC_BlueTongueGames(GSRendererHW& r, int& skip)
for (int vert = 32; vert < 40; vert+=2)
{
r.m_vertex->buff[vert].XYZ.X = context->XYOFFSET.OFX + (((vert * 16) << 4) - 8);
r.m_vertex->buff[vert].XYZ.Y = context->XYOFFSET.OFY;
r.m_vertex->buff[vert].U = (vert * 16) << 4;
r.m_vertex->buff[vert].V = 0;
r.m_vertex->buff[vert+1].XYZ.X = context->XYOFFSET.OFX + ((((vert * 16) + 32) << 4) - 8);
r.m_vertex->buff[vert+1].XYZ.Y = context->XYOFFSET.OFY + (r.PCRTCDisplays.GetResolution().y << 4) + 8;
r.m_vertex->buff[vert+1].U = ((vert * 16) + 32) << 4;
r.m_vertex->buff[vert+1].V = r.PCRTCDisplays.GetResolution().y << 4;
r.m_vertex.buff[vert].XYZ.X = context->XYOFFSET.OFX + (((vert * 16) << 4) - 8);
r.m_vertex.buff[vert].XYZ.Y = context->XYOFFSET.OFY;
r.m_vertex.buff[vert].U = (vert * 16) << 4;
r.m_vertex.buff[vert].V = 0;
r.m_vertex.buff[vert+1].XYZ.X = context->XYOFFSET.OFX + ((((vert * 16) + 32) << 4) - 8);
r.m_vertex.buff[vert+1].XYZ.Y = context->XYOFFSET.OFY + (r.PCRTCDisplays.GetResolution().y << 4) + 8;
r.m_vertex.buff[vert+1].U = ((vert * 16) + 32) << 4;
r.m_vertex.buff[vert+1].V = r.PCRTCDisplays.GetResolution().y << 4;
}
/*r.m_vertex->head = r.m_vertex->tail = r.m_vertex->next = 2;
r.m_index->tail = 2;*/
/*r.m_vertex.head = r.m_vertex.tail = r.m_vertex.next = 2;
r.m_index.tail = 2;*/
r.m_vt.m_max.p.x = r.m_r.z;
r.m_vt.m_max.p.y = r.m_r.w;
@@ -917,7 +917,7 @@ bool GSHwHack::GSC_BlueTongueGames(GSRendererHW& r, int& skip)
// This is the giant dither-like depth buffer. We need this on the CPU *and* the GPU for textures which are
// rendered on both.
if (context->FRAME.FBW == 8 && r.m_index->tail == 32 && r.PRIM->TME && context->TEX0.TBW == 1)
if (context->FRAME.FBW == 8 && r.m_index.tail == 32 && r.PRIM->TME && context->TEX0.TBW == 1)
{
r.SwPrimRender(r, false, false);
return false;
@@ -957,8 +957,8 @@ bool GSHwHack::GSC_MetalGearSolid3(GSRendererHW& r, int& skip)
GL_INS("OI_MetalGearSolid3(): %x -> %x, %dx%d, subtract %d", RFBP, RFBP + (RFBW / 2), r.m_r.width(), r.m_r.height(),
w_sub);
for (u32 i = 0; i < r.m_vertex->next; i++)
r.m_vertex->buff[i].XYZ.X -= w_sub_fp;
for (u32 i = 0; i < r.m_vertex.next; i++)
r.m_vertex.buff[i].XYZ.X -= w_sub_fp;
// No point adjusting the scissor, it just ends up expanding out anyway.. but we do have to fix up the draw rect.
r.m_r -= GSVector4i(w_sub);
@@ -971,7 +971,7 @@ bool GSHwHack::GSC_Turok(GSRendererHW& r, int& skip)
// Since we can't look in to the future to check this, the options are either rearrange all the pages in a target when the width changes
// (very slow, could break a ton of stuff which stores different things in the alpha channel), or this. I choose this.
if (r.m_index->tail == 6 && RPRIM->PRIM == 4 && !RTME && RFBMSK == 0x00FFFFFF && floor(r.m_vt.m_max.p.x) == 512 && r.m_env.CTXT[r.m_backed_up_ctx].FRAME.FBW == 10 && RFRAME.FBW == 8 && RFPSM == PSMCT32 && RTEST.ATE && RTEST.ATST == ATST_GEQUAL)
if (r.m_index.tail == 6 && RPRIM->PRIM == 4 && !RTME && RFBMSK == 0x00FFFFFF && floor(r.m_vt.m_max.p.x) == 512 && r.m_env.CTXT[r.m_backed_up_ctx].FRAME.FBW == 10 && RFRAME.FBW == 8 && RFPSM == PSMCT32 && RTEST.ATE && RTEST.ATST == ATST_GEQUAL)
{
int num_pages = r.m_cached_ctx.FRAME.FBW * ((floor(r.m_vt.m_max.p.y) + 31) / 32);
r.m_cached_ctx.FRAME.FBW = 10;
@@ -988,7 +988,7 @@ bool GSHwHack::GSC_Turok(GSRendererHW& r, int& skip)
bool GSHwHack::OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
const u32 n_vertices = r.m_vertex->next;
const u32 n_vertices = r.m_vertex.next;
const int w = r.m_r.width();
const int h = r.m_r.height();
const bool is_copy = !r.PRIM->ABE || (
@@ -1021,7 +1021,7 @@ bool GSHwHack::OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds
const u32 FBP = r.m_cached_ctx.FRAME.Block();
const u32 FBW = r.m_cached_ctx.FRAME.FBW;
GL_INS("PointListPalette - m_r = <%d, %d => %d, %d>, n_vertices = %u, FBP = 0x%x, FBW = %u", r.m_r.x, r.m_r.y, r.m_r.z, r.m_r.w, n_vertices, FBP, FBW);
const GSVertex* RESTRICT v = r.m_vertex->buff;
const GSVertex* RESTRICT v = r.m_vertex.buff;
const int ox(r.m_context->XYOFFSET.OFX);
const int oy(r.m_context->XYOFFSET.OFY);
for (size_t i = 0; i < n_vertices; ++i)
@@ -1219,9 +1219,9 @@ bool GSHwHack::OI_ArTonelico2(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GST
buffer to adapt the page width properly.
*/
const GSVertex* v = &r.m_vertex->buff[0];
const GSVertex* v = &r.m_vertex.buff[0];
if (ds && r.m_vertex->next == 2 && !RPRIM->TME && RFRAME.FBW == 10 && v->XYZ.Z == 0 && RTEST.ZTST == ZTST_ALWAYS)
if (ds && r.m_vertex.next == 2 && !RPRIM->TME && RFRAME.FBW == 10 && v->XYZ.Z == 0 && RTEST.ZTST == ZTST_ALWAYS)
{
GL_INS("OI_ArTonelico2");
g_gs_device->ClearDepth(ds, 0.0f);

View File

@@ -218,7 +218,7 @@ void GSRendererHW::Lines2Sprites()
// each sprite converted to quad needs twice the space
while (m_vertex->tail * 2 > m_vertex->maxcount)
while (m_vertex.tail * 2 > m_vertex.maxcount)
{
GrowVertexBuffer();
}
@@ -226,14 +226,14 @@ void GSRendererHW::Lines2Sprites()
// assume vertices are tightly packed and sequentially indexed (it should be the case)
const bool predivide_q = PRIM->TME && !PRIM->FST && m_vt.m_accurate_stq;
if (m_vertex->next >= 2)
if (m_vertex.next >= 2)
{
const u32 count = m_vertex->next;
const u32 count = m_vertex.next;
int i = static_cast<int>(count) * 2 - 4;
GSVertex* s = &m_vertex->buff[count - 2];
GSVertex* q = &m_vertex->buff[count * 2 - 4];
u16* RESTRICT index = &m_index->buff[count * 3 - 6];
GSVertex* s = &m_vertex.buff[count - 2];
GSVertex* q = &m_vertex.buff[count * 2 - 4];
u16* RESTRICT index = &m_index.buff[count * 3 - 6];
// Sprites are flat shaded, so the provoking vertex doesn't matter here.
constexpr GSVector4i indices = GSVector4i::cxpr16(0, 1, 2, 1, 2, 3, 0, 0);
@@ -287,19 +287,19 @@ void GSRendererHW::Lines2Sprites()
std::memcpy(&index[4], &high, sizeof(high));
}
m_vertex->head = m_vertex->tail = m_vertex->next = count * 2;
m_index->tail = count * 3;
m_vertex.head = m_vertex.tail = m_vertex.next = count * 2;
m_index.tail = count * 3;
}
}
void GSRendererHW::ExpandLineIndices()
{
const u32 process_count = (m_index->tail + 7) / 8 * 8;
const u32 process_count = (m_index.tail + 7) / 8 * 8;
constexpr u32 expansion_factor = 3;
m_index->tail *= expansion_factor;
GSVector4i* end = reinterpret_cast<GSVector4i*>(m_index->buff);
GSVector4i* read = reinterpret_cast<GSVector4i*>(m_index->buff + process_count);
GSVector4i* write = reinterpret_cast<GSVector4i*>(m_index->buff + process_count * expansion_factor);
m_index.tail *= expansion_factor;
GSVector4i* end = reinterpret_cast<GSVector4i*>(m_index.buff);
GSVector4i* read = reinterpret_cast<GSVector4i*>(m_index.buff + process_count);
GSVector4i* write = reinterpret_cast<GSVector4i*>(m_index.buff + process_count * expansion_factor);
constexpr GSVector4i mask0 = GSVector4i::cxpr8(0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 2, 3, 4, 5, 4, 5);
constexpr GSVector4i mask1 = GSVector4i::cxpr8(6, 7, 4, 5, 6, 7, 6, 7, 8, 9, 8, 9, 10, 11, 8, 9);
@@ -353,13 +353,13 @@ __fi bool GSRendererHW::Is8PixelReverseSprite(const GSVertex& v0, const GSVertex
// Fix the vertex position/tex_coordinate from 16 bits color to 32 bits color
void GSRendererHW::ConvertSpriteTextureShuffle(u32& process_rg, u32& process_ba, bool& shuffle_across, GSTextureCache::Target* rt, GSTextureCache::Source* tex)
{
pxAssert(m_vertex->next % 2 == 0); // Either sprites or an even number of triangles.
pxAssert(m_vertex.next % 2 == 0); // Either sprites or an even number of triangles.
const bool recursive_draw = m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0;
const bool sprites = m_vt.m_primclass == GS_SPRITE_CLASS;
u32 count = m_vertex->next;
GSVertex* v = &m_vertex->buff[0];
u32 count = m_vertex.next;
GSVertex* v = &m_vertex.buff[0];
const GIFRegXYOFFSET& o = m_context->XYOFFSET;
// Could be drawing upside down or just back to front on the actual verts.
// Iterate through the sprites in order and find one to infer which channels are being shuffled.
@@ -500,12 +500,12 @@ void GSRendererHW::ConvertSpriteTextureShuffle(u32& process_rg, u32& process_ba,
// no need to adjust v[0] because it should already be correct.
if (PRIM->FST)
{
v[1].U = v[m_index->buff[m_index->tail - 1]].U;
v[1].V = v[m_index->buff[m_index->tail - 1]].V;
v[1].U = v[m_index.buff[m_index.tail - 1]].U;
v[1].V = v[m_index.buff[m_index.tail - 1]].V;
}
else
{
v[1].ST = v[m_index->buff[m_index->tail - 1]].ST;
v[1].ST = v[m_index.buff[m_index.tail - 1]].ST;
}
}
else
@@ -526,8 +526,8 @@ void GSRendererHW::ConvertSpriteTextureShuffle(u32& process_rg, u32& process_ba,
}
}
m_r = r;
m_vertex->head = m_vertex->tail = m_vertex->next = 2;
m_index->tail = 2;
m_vertex.head = m_vertex.tail = m_vertex.next = 2;
m_index.tail = 2;
return;
}
@@ -640,8 +640,8 @@ void GSRendererHW::ConvertSpriteTextureShuffle(u32& process_rg, u32& process_ba,
}
if (wi != count)
{
count = m_vertex->head = m_vertex->tail = m_vertex->next = wi;
m_index->tail = wi;
count = m_vertex.head = m_vertex.tail = m_vertex.next = wi;
m_index.tail = wi;
}
}
@@ -805,7 +805,7 @@ void GSRendererHW::ConvertSpriteTextureShuffle(u32& process_rg, u32& process_ba,
}
}
if (m_index->tail == 0)
if (m_index.tail == 0)
{
GL_INS("HW: ConvertSpriteTextureShuffle: Culled all vertices; exiting.");
return;
@@ -896,17 +896,17 @@ void GSRendererHW::ConvertSpriteTextureShuffle(u32& process_rg, u32& process_ba,
m_context->scissor.in.w /= 2;
m_context->scissor.in.z *= 2;
v[1].XYZ.X = ((v[m_index->buff[m_index->tail - 1]].XYZ.X - m_context->XYOFFSET.OFX) * 2) + m_context->XYOFFSET.OFX;
v[1].XYZ.Y = ((v[m_index->buff[m_index->tail - 1]].XYZ.Y - m_context->XYOFFSET.OFY) / 2) + m_context->XYOFFSET.OFY;
v[1].XYZ.X = ((v[m_index.buff[m_index.tail - 1]].XYZ.X - m_context->XYOFFSET.OFX) * 2) + m_context->XYOFFSET.OFX;
v[1].XYZ.Y = ((v[m_index.buff[m_index.tail - 1]].XYZ.Y - m_context->XYOFFSET.OFY) / 2) + m_context->XYOFFSET.OFY;
v[1].U = v[m_index->buff[m_index->tail - 1]].U * 2;
v[1].V = v[m_index->buff[m_index->tail - 1]].V / 2;
v[1].U = v[m_index.buff[m_index.tail - 1]].U * 2;
v[1].V = v[m_index.buff[m_index.tail - 1]].V / 2;
v[1].ST.S = v[m_index->buff[m_index->tail - 1]].ST.S * 2;
v[1].ST.T = v[m_index->buff[m_index->tail - 1]].ST.T / 2;
v[1].ST.S = v[m_index.buff[m_index.tail - 1]].ST.S * 2;
v[1].ST.T = v[m_index.buff[m_index.tail - 1]].ST.T / 2;
m_vertex->head = m_vertex->tail = m_vertex->next = 2;
m_index->tail = 2;
m_vertex.head = m_vertex.tail = m_vertex.next = 2;
m_index.tail = 2;
m_cached_ctx.TEX0.TBW *= 2;
m_cached_ctx.FRAME.FBW *= 2;
@@ -923,7 +923,7 @@ GSVector4 GSRendererHW::RealignTargetTextureCoordinate(const GSTextureCache::Sou
return GSVector4(0.0f);
}
const GSVertex* v = &m_vertex->buff[0];
const GSVertex* v = &m_vertex.buff[0];
const float scale = tex->GetScale();
const bool linear = m_vt.IsRealLinear();
const int t_position = v[0].U;
@@ -1009,7 +1009,7 @@ void GSRendererHW::MergeSprite(GSTextureCache::Source* tex)
// neither in a fast way. So instead let's just take the hypothesis that all sprites must have the same
// size.
// Tested on Tekken 5.
const GSVertex* v = &m_vertex->buff[0];
const GSVertex* v = &m_vertex.buff[0];
bool is_paving = true;
bool is_paving_h = true;
bool is_paving_v = true;
@@ -1018,7 +1018,7 @@ void GSRendererHW::MergeSprite(GSTextureCache::Source* tex)
const int first_dpU = v[1].U - v[0].U;
const int first_dpY = v[1].XYZ.Y - v[0].XYZ.Y;
const int first_dpV = v[1].V - v[0].V;
for (u32 i = 0; i < m_vertex->next; i += 2)
for (u32 i = 0; i < m_vertex.next; i += 2)
{
const int dpX = v[i + 1].XYZ.X - v[i].XYZ.X;
const int dpU = v[i + 1].U - v[i].U;
@@ -1043,14 +1043,14 @@ void GSRendererHW::MergeSprite(GSTextureCache::Source* tex)
const GSVector4 delta_p = m_vt.m_max.p - m_vt.m_min.p;
const GSVector4 delta_t = m_vt.m_max.t - m_vt.m_min.t;
const bool is_blit = PrimitiveOverlap() == PRIM_OVERLAP_NO;
GL_INS("HW: PP SAMPLER: Dp %f %f Dt %f %f. Is blit %d, is paving %d, count %d", delta_p.x, delta_p.y, delta_t.x, delta_t.y, is_blit, is_paving, m_vertex->tail);
GL_INS("HW: PP SAMPLER: Dp %f %f Dt %f %f. Is blit %d, is paving %d, count %d", delta_p.x, delta_p.y, delta_t.x, delta_t.y, is_blit, is_paving, m_vertex.tail);
#endif
if (is_paving)
{
// Replace all sprite with a single fullscreen sprite.
u32 unique_verts = 2;
GSVertex* s = &m_vertex->buff[0];
GSVertex* s = &m_vertex.buff[0];
if (is_paving_h)
{
s[0].XYZ.X = static_cast<u16>((16.0f * m_vt.m_min.p.x) + m_context->XYOFFSET.OFX);
@@ -1061,7 +1061,7 @@ void GSRendererHW::MergeSprite(GSTextureCache::Source* tex)
}
else
{
for (u32 i = 2; i < (m_vertex->tail & ~1); i++)
for (u32 i = 2; i < (m_vertex.tail & ~1); i++)
{
bool unique_found = false;
@@ -1102,7 +1102,7 @@ void GSRendererHW::MergeSprite(GSTextureCache::Source* tex)
}
else
{
for (u32 i = 2; i < (m_vertex->tail & ~1); i++)
for (u32 i = 2; i < (m_vertex.tail & ~1); i++)
{
bool unique_found = false;
@@ -1133,8 +1133,8 @@ void GSRendererHW::MergeSprite(GSTextureCache::Source* tex)
}
}
m_vertex->head = m_vertex->tail = m_vertex->next = unique_verts;
m_index->tail = unique_verts;
m_vertex.head = m_vertex.tail = m_vertex.next = unique_verts;
m_index.tail = unique_verts;
}
}
}
@@ -1261,7 +1261,7 @@ bool GSRendererHW::IsPossibleChannelShuffle() const
{
if (!PRIM->TME || m_cached_ctx.TEX0.PSM != PSMT8 || // 8-bit texture draw
m_vt.m_primclass != GS_SPRITE_CLASS || // draw_sprite_tex
(m_vertex->tail <= 2 && (((m_vt.m_max.p - m_vt.m_min.p) <= GSVector4(8.0f)).mask() & 0x3) == 0x3)) // Powerdrome does a tiny shuffle on a couple of pixels, can't reliably translate this.
(m_vertex.tail <= 2 && (((m_vt.m_max.p - m_vt.m_min.p) <= GSVector4(8.0f)).mask() & 0x3) == 0x3)) // Powerdrome does a tiny shuffle on a couple of pixels, can't reliably translate this.
{
return false;
}
@@ -1269,7 +1269,7 @@ bool GSRendererHW::IsPossibleChannelShuffle() const
const int mask = (((m_vt.m_max.p - m_vt.m_min.p) <= GSVector4(64.0f)).mask() & 0x3);
if (mask == 0x3) // single_page
{
const GSVertex* v = &m_vertex->buff[0];
const GSVertex* v = &m_vertex.buff[0];
const int draw_width = std::abs(v[1].XYZ.X - v[0].XYZ.X) >> 4;
const int draw_height = std::abs(v[1].XYZ.Y - v[0].XYZ.Y) >> 4;
@@ -1291,7 +1291,7 @@ bool GSRendererHW::IsPossibleChannelShuffle() const
if (m_cached_ctx.TEX0.TBW == (m_cached_ctx.FRAME.FBW * 2) &&
GSLocalMemory::IsPageAligned(m_cached_ctx.FRAME.PSM, GSVector4i(m_vt.m_min.p.upld(m_vt.m_max.p))))
{
const GSVertex* v = &m_vertex->buff[0];
const GSVertex* v = &m_vertex.buff[0];
const int draw_width = std::abs(v[1].XYZ.X - v[0].XYZ.X) >> 4;
const int draw_height = std::abs(v[1].XYZ.Y - v[0].XYZ.Y) >> 4;
@@ -1357,7 +1357,7 @@ bool GSRendererHW::IsSplitTextureShuffle(GIFRegTEX0& rt_TEX0, GSVector4i& valid_
return false;
// Different channel being shuffled, so needs to be handled separately (misdetection in 50 Cent)
if (m_vertex->buff[m_index->buff[0]].U != m_v.U)
if (m_vertex.buff[m_index.buff[0]].U != m_v.U)
return false;
// Check that both the position and texture coordinates are page aligned, so we can work in pages instead of coordinates.
@@ -1745,7 +1745,7 @@ bool GSRendererHW::IsDepthAlwaysPassing()
// Depth is always pass/fail (no read) and write are discarded.
return (!m_cached_ctx.TEST.ZTE || m_cached_ctx.TEST.ZTST <= ZTST_ALWAYS) ||
// Depth test will always pass
(m_cached_ctx.TEST.ZTST == ZTST_GEQUAL && m_vt.m_eq.z && std::min(m_vertex->buff[check_index].XYZ.Z, max_z) == max_z);
(m_cached_ctx.TEST.ZTST == ZTST_GEQUAL && m_vt.m_eq.z && std::min(m_vertex.buff[check_index].XYZ.Z, max_z) == max_z);
}
bool GSRendererHW::IsUsingCsInBlend()
@@ -1784,7 +1784,7 @@ bool GSRendererHW::IsTBPFrameOrZ(u32 tbp, bool frame_only)
// Depth is always pass/fail (no read) and write are discarded.
(zm != 0 && m_cached_ctx.TEST.ZTST <= ZTST_ALWAYS) ||
// Depth test will always pass
(zm != 0 && m_cached_ctx.TEST.ZTST == ZTST_GEQUAL && m_vt.m_eq.z && std::min(m_vertex->buff[0].XYZ.Z, max_z) == max_z) ||
(zm != 0 && m_cached_ctx.TEST.ZTST == ZTST_GEQUAL && m_vt.m_eq.z && std::min(m_vertex.buff[0].XYZ.Z, max_z) == max_z) ||
// Depth will be written through the RT
(!no_rt && m_cached_ctx.FRAME.FBP == m_cached_ctx.ZBUF.ZBP && !PRIM->TME && zm == 0 && (fm & fm_mask) == 0 && m_cached_ctx.TEST.ZTE)) ||
// No color or Z being written.
@@ -1801,17 +1801,17 @@ void GSRendererHW::HandleManualDeswizzle()
// Check if it's doing manual deswizzling first (draws are 32x16), if they are, check if the Z is flat, if not,
// we're gonna have to get creative and swap around the quandrants, but that's a TODO.
GSVertex* v = &m_vertex->buff[0];
GSVertex* v = &m_vertex.buff[0];
// Check for page quadrant and compare it to the quadrant from the verts, if it does match then we need to do correction.
const GSVector2i page_quadrant = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].pgs / 2;
if (PRIM->FST)
{
for (u32 i = 0; i < m_index->tail; i += 2)
for (u32 i = 0; i < m_index.tail; i += 2)
{
const u32 index_first = m_index->buff[i];
const u32 index_last = m_index->buff[i + 1];
const u32 index_first = m_index.buff[i];
const u32 index_last = m_index.buff[i + 1];
if ((abs((v[index_last].U) - (v[index_first].U)) >> 4) != page_quadrant.x || (abs((v[index_last].V) - (v[index_first].V)) >> 4) != page_quadrant.y)
return;
@@ -1819,10 +1819,10 @@ void GSRendererHW::HandleManualDeswizzle()
}
else
{
for (u32 i = 0; i < m_index->tail; i += 2)
for (u32 i = 0; i < m_index.tail; i += 2)
{
const u32 index_first = m_index->buff[i];
const u32 index_last = m_index->buff[i + 1];
const u32 index_first = m_index.buff[i];
const u32 index_last = m_index.buff[i + 1];
const u32 x = abs(((v[index_last].ST.S / v[index_last].RGBAQ.Q) * (1 << m_context->TEX0.TW)) - ((v[index_first].ST.S / v[index_first].RGBAQ.Q) * (1 << m_context->TEX0.TW)));
const u32 y = abs(((v[index_last].ST.T / v[index_last].RGBAQ.Q) * (1 << m_context->TEX0.TH)) - ((v[index_first].ST.T / v[index_first].RGBAQ.Q) * (1 << m_context->TEX0.TH)));
@@ -2027,7 +2027,7 @@ void GSRendererHW::SwSpriteRender()
const bool alpha_blending_enabled = NeedsBlending();
const GSVertex& v = m_index->tail > 0 ? m_vertex->buff[m_index->buff[m_index->tail - 1]] : GSVertex(); // Last vertex if any.
const GSVertex& v = m_index.tail > 0 ? m_vertex.buff[m_index.buff[m_index.tail - 1]] : GSVertex(); // Last vertex if any.
const GSVector4i vc = GSVector4i(v.RGBAQ.R, v.RGBAQ.G, v.RGBAQ.B, v.RGBAQ.A) // 0x000000AA000000BB000000GG000000RR
.ps32(); // 0x00AA00BB00GG00RR00AA00BB00GG00RR
@@ -2156,10 +2156,10 @@ bool GSRendererHW::CanUseSwSpriteRender()
return false;
if (PRIM->PRIM != GS_TRIANGLESTRIP && PRIM->PRIM != GS_SPRITE) // Triangle strip or sprite draw
return false;
if (m_vt.m_primclass == GS_TRIANGLE_CLASS && (PRIM->PRIM != GS_TRIANGLESTRIP || m_vertex->tail != 4)) // If triangle class, strip draw with 4 vertices (two prims, emulating single sprite prim)
if (m_vt.m_primclass == GS_TRIANGLE_CLASS && (PRIM->PRIM != GS_TRIANGLESTRIP || m_vertex.tail != 4)) // If triangle class, strip draw with 4 vertices (two prims, emulating single sprite prim)
return false;
// TODO If GS_TRIANGLESTRIP draw, check that the draw is axis aligned
if (m_vt.m_primclass == GS_SPRITE_CLASS && (PRIM->PRIM != GS_SPRITE || m_vertex->tail != 2)) // If sprite class, sprite draw with 2 vertices (one prim)
if (m_vt.m_primclass == GS_SPRITE_CLASS && (PRIM->PRIM != GS_SPRITE || m_vertex.tail != 2)) // If sprite class, sprite draw with 2 vertices (one prim)
return false;
if (m_cached_ctx.DepthRead() || m_cached_ctx.DepthWrite()) // No depth handling
return false;
@@ -2203,8 +2203,8 @@ void GSRendererHW::RoundSpriteOffset()
#if defined(DEBUG_V) || defined(DEBUG_U)
bool debug = linear;
#endif
const u32 count = m_vertex->next;
GSVertex* v = &m_vertex->buff[0];
const u32 count = m_vertex.next;
GSVertex* v = &m_vertex.buff[0];
for (u32 i = 0; i < count; i += 2)
{
@@ -2333,7 +2333,7 @@ void GSRendererHW::Draw()
m_cached_ctx.FRAME = context->FRAME;
m_cached_ctx.ZBUF = context->ZBUF;
if (IsBadFrame())
if (IsBadFrame())
{
GL_INS("HW: Warning skipping a draw call (%d)", s_n);
return;
@@ -2807,7 +2807,7 @@ void GSRendererHW::Draw()
}
const u32 vert_index = (m_vt.m_primclass == GS_TRIANGLE_CLASS) ? 2 : 1;
u32 const_color = m_vertex->buff[m_index->buff[vert_index]].RGBAQ.U32[0];
u32 const_color = m_vertex.buff[m_index.buff[vert_index]].RGBAQ.U32[0];
u32 fb_mask = m_cached_ctx.FRAME.FBMSK;
// If we could just check the colour, it would be great, but Echo Night decided it's going to set the alpha and green to 128, for some reason, and actually be 32bit, so it ruined my day.
@@ -2843,7 +2843,7 @@ void GSRendererHW::Draw()
m_cached_ctx.TEXA.TA0 = 0;
m_cached_ctx.TEXA.TA1 = 128;
m_cached_ctx.FRAME.PSM = (m_cached_ctx.FRAME.PSM & 2) ? m_cached_ctx.FRAME.PSM : PSMCT16;
m_vertex->buff[m_index->buff[1]].RGBAQ.U32[0] = const_color;
m_vertex.buff[m_index.buff[1]].RGBAQ.U32[0] = const_color;
ReplaceVerticesWithSprite(m_r, GSVector2i(m_r.width(), m_r.height()));
}
@@ -3060,13 +3060,13 @@ void GSRendererHW::Draw()
const u32 page_alignment = GSLocalMemory::IsPageAlignedMasked(m_cached_ctx.TEX0.PSM, m_r);
const bool page_aligned = (page_alignment & 0xF0F0) != 0; // Make sure Y is page aligned.
if (!no_rt && page_aligned && m_cached_ctx.ZBUF.ZMSK && GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp >= 16 &&
(m_vt.m_primclass == GS_SPRITE_CLASS || (m_vt.m_primclass == GS_TRIANGLE_CLASS && (m_index->tail % 6) == 0 && TrianglesAreQuads(true) && m_index->tail > 6)))
(m_vt.m_primclass == GS_SPRITE_CLASS || (m_vt.m_primclass == GS_TRIANGLE_CLASS && (m_index.tail % 6) == 0 && TrianglesAreQuads(true) && m_index.tail > 6)))
{
// Tail check is to make sure we have enough strips to go all the way across the page, or if it's using a region clamp could be used to draw strips.
if (GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp == 16 &&
(m_index->tail >= (m_cached_ctx.TEX0.TBW * 2) || m_cached_ctx.TEX0.TBP0 == m_cached_ctx.FRAME.Block() || m_cached_ctx.CLAMP.WMS > CLAMP_CLAMP || m_cached_ctx.CLAMP.WMT > CLAMP_CLAMP))
(m_index.tail >= (m_cached_ctx.TEX0.TBW * 2) || m_cached_ctx.TEX0.TBP0 == m_cached_ctx.FRAME.Block() || m_cached_ctx.CLAMP.WMS > CLAMP_CLAMP || m_cached_ctx.CLAMP.WMT > CLAMP_CLAMP))
{
const GSVertex* v = &m_vertex->buff[0];
const GSVertex* v = &m_vertex.buff[0];
const int first_x = std::clamp((static_cast<int>(((v[0].XYZ.X - m_context->XYOFFSET.OFX) + 8))) >> 4, 0, 2048);
const bool offset_last = PRIM->FST ? (v[1].U > v[0].U) : ((v[1].ST.S / v[1].RGBAQ.Q) > (v[0].ST.S / v[1].RGBAQ.Q));
@@ -3092,11 +3092,11 @@ void GSRendererHW::Draw()
{
bool shuffle_channel_reads = !m_cached_ctx.FRAME.FBMSK;
const u32 increment = (m_vt.m_primclass == GS_TRIANGLE_CLASS) ? 3 : 2;
const GSVertex* v = &m_vertex->buff[0];
const GSVertex* v = &m_vertex.buff[0];
if (shuffle_channel_reads)
{
for (u32 i = 0; i < m_index->tail; i += increment)
for (u32 i = 0; i < m_index.tail; i += increment)
{
const int first_u = (PRIM->FST ? v[i].U : static_cast<int>(v[i].ST.S / v[(increment == 2) ? i + 1 : i].RGBAQ.Q)) >> 4;
const int second_u = (PRIM->FST ? v[i + 1].U : static_cast<int>(v[i + 1].ST.S / v[i + 1].RGBAQ.Q)) >> 4;
@@ -3243,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)
{
@@ -3276,8 +3277,6 @@ void GSRendererHW::Draw()
scale_draw = 1;
scaled_copy = true;
}
m_downscale_source = false;
}
}
@@ -3446,9 +3445,9 @@ void GSRendererHW::Draw()
if (vertical_offset || horizontal_offset)
{
GSVertex* v = &m_vertex->buff[0];
GSVertex* v = &m_vertex.buff[0];
for (u32 i = 0; i < m_vertex->tail; i++)
for (u32 i = 0; i < m_vertex.tail; i++)
{
v[i].XYZ.X += horizontal_offset << 4;
v[i].XYZ.Y += vertical_offset << 4;
@@ -3743,9 +3742,9 @@ void GSRendererHW::Draw()
if (vertical_offset || horizontal_offset)
{
GSVertex* v = &m_vertex->buff[0];
GSVertex* v = &m_vertex.buff[0];
for (u32 i = 0; i < m_vertex->tail; i++)
for (u32 i = 0; i < m_vertex.tail; i++)
{
v[i].XYZ.X += horizontal_offset << 4;
v[i].XYZ.Y += vertical_offset << 4;
@@ -4032,7 +4031,7 @@ void GSRendererHW::Draw()
if (m_process_texture)
{
GIFRegCLAMP MIP_CLAMP = m_cached_ctx.CLAMP;
const GSVertex* v = &m_vertex->buff[0];
const GSVertex* v = &m_vertex.buff[0];
if (rt)
{
@@ -4054,7 +4053,7 @@ void GSRendererHW::Draw()
// Both input and output are 16 bits and texture was initially 32 bits! Same for the target, Sonic Unleash makes a new target which really is 16bit.
m_texture_shuffle = ((m_same_group_texture_shuffle || (tex_psm.bpp == 16)) && (GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16) && (shuffle_coords || rt->m_32_bits_fmt)) &&
(src->m_32_bits_fmt || m_copy_16bit_to_target_shuffle) &&
(draw_sprite_tex || (m_vt.m_primclass == GS_TRIANGLE_CLASS && (m_index->tail % 6) == 0 && TrianglesAreQuads(true)));
(draw_sprite_tex || (m_vt.m_primclass == GS_TRIANGLE_CLASS && (m_index.tail % 6) == 0 && TrianglesAreQuads(true)));
if (m_texture_shuffle && IsSplitTextureShuffle(rt->m_TEX0, rt->m_valid))
{
@@ -4222,13 +4221,13 @@ void GSRendererHW::Draw()
if (!m_texture_shuffle && !m_channel_shuffle)
{
// Try to turn blits in to single sprites, saves upscaling problems when striped clears/blits.
if (m_vt.m_primclass == GS_SPRITE_CLASS && m_primitive_covers_without_gaps == NoGapsType::FullCover && m_index->tail > 2 && (!PRIM->TME || TextureCoversWithoutGapsNotEqual()) && m_vt.m_eq.rgba == 0xFFFF)
if (m_vt.m_primclass == GS_SPRITE_CLASS && m_primitive_covers_without_gaps == NoGapsType::FullCover && m_index.tail > 2 && (!PRIM->TME || TextureCoversWithoutGapsNotEqual()) && m_vt.m_eq.rgba == 0xFFFF)
{
// Full final framebuffer only.
const GSVector2i fb_size = PCRTCDisplays.GetFramebufferSize(-1);
if (std::abs(fb_size.x - m_r.width()) <= 1 && std::abs(fb_size.y - m_r.height()) <= 1)
{
GSVertex* v = m_vertex->buff;
GSVertex* v = m_vertex.buff;
v[0].XYZ.Z = v[1].XYZ.Z;
v[0].RGBAQ = v[1].RGBAQ;
@@ -4237,23 +4236,23 @@ void GSRendererHW::Draw()
m_vt.m_eq.z = true;
m_vt.m_eq.f = true;
v[1].XYZ.X = v[m_index->tail - 1].XYZ.X;
v[1].XYZ.Y = v[m_index->tail - 1].XYZ.Y;
v[1].XYZ.X = v[m_index.tail - 1].XYZ.X;
v[1].XYZ.Y = v[m_index.tail - 1].XYZ.Y;
if (PRIM->FST)
{
v[1].U = v[m_index->tail - 1].U;
v[1].V = v[m_index->tail - 1].V;
v[1].U = v[m_index.tail - 1].U;
v[1].V = v[m_index.tail - 1].V;
}
else
{
v[1].ST.S = v[m_index->tail - 1].ST.S;
v[1].ST.T = v[m_index->tail - 1].ST.T;
v[1].RGBAQ.Q = v[m_index->tail - 1].RGBAQ.Q;
v[1].ST.S = v[m_index.tail - 1].ST.S;
v[1].ST.T = v[m_index.tail - 1].ST.T;
v[1].RGBAQ.Q = v[m_index.tail - 1].RGBAQ.Q;
}
m_vertex->head = m_vertex->tail = m_vertex->next = 2;
m_index->tail = 2;
m_vertex.head = m_vertex.tail = m_vertex.next = 2;
m_index.tail = 2;
}
}
@@ -4718,8 +4717,8 @@ void GSRendererHW::Draw()
// 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)
{
const u32 count = m_vertex->next;
GSVertex* v = &m_vertex->buff[0];
const u32 count = m_vertex.next;
GSVertex* v = &m_vertex.buff[0];
// Hack to avoid vertical black line in various games (ace combat/tekken)
if (GSConfig.UserHacks_AlignSpriteX)
@@ -4945,30 +4944,30 @@ bool GSRendererHW::VerifyIndices()
switch (m_vt.m_primclass)
{
case GS_SPRITE_CLASS:
if (m_index->tail % 2 != 0)
if (m_index.tail % 2 != 0)
return false;
[[fallthrough]];
case GS_POINT_CLASS:
// Expect indices to be flat increasing
for (u32 i = 0; i < m_index->tail; i++)
for (u32 i = 0; i < m_index.tail; i++)
{
if (m_index->buff[i] != i)
if (m_index.buff[i] != i)
return false;
}
break;
case GS_LINE_CLASS:
if (m_index->tail % 2 != 0)
if (m_index.tail % 2 != 0)
return false;
// Expect each line to be a pair next to each other
// VS expand relies on this!
for (u32 i = 0; i < m_index->tail; i += 2)
for (u32 i = 0; i < m_index.tail; i += 2)
{
if (m_index->buff[i] + 1 != m_index->buff[i + 1])
if (m_index.buff[i] + 1 != m_index.buff[i + 1])
return false;
}
break;
case GS_TRIANGLE_CLASS:
if (m_index->tail % 3 != 0)
if (m_index.tail % 3 != 0)
return false;
break;
case GS_INVALID_CLASS:
@@ -4993,9 +4992,9 @@ void GSRendererHW::HandleProvokingVertexFirst()
// If all first/last vertices have the same color there is nothing to do.
bool first_eq_last = true;
for (u32 i = 0; i < m_index->tail; i += n)
for (u32 i = 0; i < m_index.tail; i += n)
{
if (m_vertex->buff[m_index->buff[i]].RGBAQ.U32[0] != m_vertex->buff[m_index->buff[i + n - 1]].RGBAQ.U32[0])
if (m_vertex.buff[m_index.buff[i]].RGBAQ.U32[0] != m_vertex.buff[m_index.buff[i + n - 1]].RGBAQ.U32[0])
{
first_eq_last = false;
break;
@@ -5005,21 +5004,21 @@ void GSRendererHW::HandleProvokingVertexFirst()
return;
// De-index the vertices using the copy buffer
while (m_vertex->maxcount < m_index->tail)
while (m_vertex.maxcount < m_index.tail)
GrowVertexBuffer();
for (int i = static_cast<int>(m_index->tail) - 1; i >= 0; i--)
for (int i = static_cast<int>(m_index.tail) - 1; i >= 0; i--)
{
m_vertex->buff_copy[i] = m_vertex->buff[m_index->buff[i]];
m_index->buff[i] = static_cast<u16>(i);
m_vertex.buff_copy[i] = m_vertex.buff[m_index.buff[i]];
m_index.buff[i] = static_cast<u16>(i);
}
std::swap(m_vertex->buff, m_vertex->buff_copy);
m_vertex->head = m_vertex->next = m_vertex->tail = m_index->tail;
std::swap(m_vertex.buff, m_vertex.buff_copy);
m_vertex.head = m_vertex.next = m_vertex.tail = m_index.tail;
// Put correct color in the first vertex
for (u32 i = 0; i < m_index->tail; i += n)
for (u32 i = 0; i < m_index.tail; i += n)
{
m_vertex->buff[i].RGBAQ.U32[0] = m_vertex->buff[i + n - 1].RGBAQ.U32[0];
m_vertex->buff[i + n - 1].RGBAQ.U32[0] = 0xff; // Make last vertex red for debugging if used improperly
m_vertex.buff[i].RGBAQ.U32[0] = m_vertex.buff[i + n - 1].RGBAQ.U32[0];
m_vertex.buff[i + n - 1].RGBAQ.U32[0] = 0xff; // Make last vertex red for debugging if used improperly
}
}
@@ -5029,8 +5028,8 @@ void GSRendererHW::SetupIA(float target_scale, float sx, float sy, bool req_vert
if (GSConfig.UserHacks_ForceEvenSpritePosition && !m_isPackedUV_HackFlag && m_process_texture && PRIM->FST)
{
for (u32 i = 0; i < m_vertex->next; i++)
m_vertex->buff[i].UV &= 0x3FEF3FEF;
for (u32 i = 0; i < m_vertex.next; i++)
m_vertex.buff[i].UV &= 0x3FEF3FEF;
}
const bool unscale_pt_ln = !GSConfig.UserHacks_DisableSafeFeatures && (target_scale != 1.0f);
@@ -5056,9 +5055,9 @@ void GSRendererHW::SetupIA(float target_scale, float sx, float sy, bool req_vert
m_conf.vs.expand = GSHWDrawConfig::VSExpand::Point;
m_conf.cb_vs.point_size = GSVector2(16.0f * sx, 16.0f * sy);
m_conf.topology = GSHWDrawConfig::Topology::Triangle;
m_conf.verts = m_vertex->buff;
m_conf.nverts = m_vertex->next;
m_conf.nindices = m_index->tail * 6;
m_conf.verts = m_vertex.buff;
m_conf.nverts = m_vertex.next;
m_conf.nindices = m_index.tail * 6;
m_conf.indices_per_prim = 6;
return;
}
@@ -5107,19 +5106,19 @@ void GSRendererHW::SetupIA(float target_scale, float sx, float sy, bool req_vert
if (req_vert_backup)
{
memcpy(m_draw_vertex.buff, m_vertex->buff, sizeof(GSVertex) * m_vertex->next);
memcpy(m_draw_index.buff, m_index->buff, sizeof(u16) * m_index->tail);
memcpy(m_draw_vertex.buff, m_vertex.buff, sizeof(GSVertex) * m_vertex.next);
memcpy(m_draw_index.buff, m_index.buff, sizeof(u16) * m_index.tail);
m_conf.verts = m_draw_vertex.buff;
m_conf.indices = m_draw_index.buff;
}
else
{
m_conf.verts = m_vertex->buff;
m_conf.indices = m_index->buff;
m_conf.verts = m_vertex.buff;
m_conf.indices = m_index.buff;
}
m_conf.nverts = m_vertex->next;
m_conf.nindices = m_index->tail * 3;
m_conf.nverts = m_vertex.next;
m_conf.nindices = m_index.tail * 3;
m_conf.indices_per_prim = 6;
return;
}
@@ -5141,9 +5140,9 @@ void GSRendererHW::SetupIA(float target_scale, float sx, float sy, bool req_vert
// See note above in GS_SPRITE_CLASS.
if (m_vt.m_accurate_stq && m_vt.m_eq.stq) [[unlikely]]
{
GSVertex* const v = m_vertex->buff;
GSVertex* const v = m_vertex.buff;
const GSVector4 v_q = GSVector4(v[0].RGBAQ.Q);
for (u32 i = 0; i < m_vertex->next; i++)
for (u32 i = 0; i < m_vertex.next; i++)
{
// v[i].ST.ST /= v[i].RGBAQ.Q; v[i].RGBAQ.Q = 1.0f; (Q / Q = 1)
GSVector4 v_st = GSVector4::load<true>(&v[i].ST);
@@ -5160,19 +5159,19 @@ void GSRendererHW::SetupIA(float target_scale, float sx, float sy, bool req_vert
if (req_vert_backup)
{
memcpy(m_draw_vertex.buff, m_vertex->buff, sizeof(GSVertex) * m_vertex->next);
memcpy(m_draw_index.buff, m_index->buff, sizeof(u16) * m_index->tail);
memcpy(m_draw_vertex.buff, m_vertex.buff, sizeof(GSVertex) * m_vertex.next);
memcpy(m_draw_index.buff, m_index.buff, sizeof(u16) * m_index.tail);
m_conf.verts = m_draw_vertex.buff;
m_conf.indices = m_draw_index.buff;
}
else
{
m_conf.verts = m_vertex->buff;
m_conf.indices = m_index->buff;
m_conf.verts = m_vertex.buff;
m_conf.indices = m_index.buff;
}
m_conf.nverts = m_vertex->next;
m_conf.nindices = m_index->tail;
m_conf.nverts = m_vertex.next;
m_conf.nindices = m_index.tail;
}
void GSRendererHW::EmulateZbuffer(const GSTextureCache::Target* ds)
@@ -5196,7 +5195,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)
{
@@ -5231,7 +5232,7 @@ void GSRendererHW::EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GS
ConvertSpriteTextureShuffle(process_rg, process_ba, shuffle_across, rt, tex);
if (m_index->tail == 0)
if (m_index.tail == 0)
return; // Rewriting sprites can result in an empty draw.
// If date is enabled you need to test the green channel instead of the alpha channel.
@@ -5419,7 +5420,7 @@ __ri u32 GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool t
m_conf.ps.urban_chaos_hle = 1;
}
}
else if (m_index->tail <= 64 && !IsPageCopy() && m_cached_ctx.CLAMP.WMT == 3)
else if (m_index.tail <= 64 && !IsPageCopy() && m_cached_ctx.CLAMP.WMT == 3)
{
// Blood will tell. I think it is channel effect too but again
// implemented in a different way. I don't want to add more CRC stuff. So
@@ -5436,7 +5437,7 @@ __ri u32 GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool t
}
else if (m_cached_ctx.CLAMP.WMS == 3 && ((m_cached_ctx.CLAMP.MAXU & 0x8) == 8))
{
const ChannelFetch channel_select = ((m_cached_ctx.CLAMP.WMT != 3 && (m_vertex->buff[m_index->buff[0]].V & 0x20) == 0) || (m_cached_ctx.CLAMP.WMT == 3 && ((m_cached_ctx.CLAMP.MAXV & 0x2) == 0))) ? ChannelFetch_BLUE : ChannelFetch_ALPHA;
const ChannelFetch channel_select = ((m_cached_ctx.CLAMP.WMT != 3 && (m_vertex.buff[m_index.buff[0]].V & 0x20) == 0) || (m_cached_ctx.CLAMP.WMT == 3 && ((m_cached_ctx.CLAMP.MAXV & 0x2) == 0))) ? ChannelFetch_BLUE : ChannelFetch_ALPHA;
// MGS3/Kill Zone
if (test_only)
@@ -5450,7 +5451,7 @@ __ri u32 GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool t
{
// Read either Red or Green. Let's check the V coordinate. 0-1 is likely top so
// red. 2-3 is likely bottom so green (actually depends on texture base pointer offset)
const bool green = (m_cached_ctx.CLAMP.WMT == 3 && ((m_cached_ctx.CLAMP.MAXV & 0x2) == 2)) || (PRIM->FST && (m_vertex->buff[0].V & 32));
const bool green = (m_cached_ctx.CLAMP.WMT == 3 && ((m_cached_ctx.CLAMP.MAXV & 0x2) == 2)) || (PRIM->FST && (m_vertex.buff[0].V & 32));
if (green && (m_cached_ctx.FRAME.FBMSK & 0x00FFFFFF) == 0x00FFFFFF)
{
// Typically used in Terminator 3
@@ -5583,7 +5584,7 @@ __ri u32 GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool t
m_channel_shuffle_src_valid = src->m_valid;
if (GSConfig.UserHacks_TextureInsideRt == GSTextureInRtMode::Disabled || ((src->m_TEX0.TBW == rt->m_TEX0.TBW) && (!m_in_target_draw && IsPageCopy())) || m_conf.ps.urban_chaos_hle || m_conf.ps.tales_of_abyss_hle)
{
GSVertex* s = &m_vertex->buff[0];
GSVertex* s = &m_vertex.buff[0];
s[0].XYZ.X = static_cast<u16>(m_context->XYOFFSET.OFX + 0);
s[1].XYZ.X = static_cast<u16>(m_context->XYOFFSET.OFX + 16384);
s[0].XYZ.Y = static_cast<u16>(m_context->XYOFFSET.OFY + 0);
@@ -5625,7 +5626,7 @@ __ri u32 GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool t
}
m_in_target_draw |= frame_page_offset > 0;
GSVertex* s = &m_vertex->buff[0];
GSVertex* s = &m_vertex.buff[0];
s[0].XYZ.X = static_cast<u16>(m_context->XYOFFSET.OFX + (m_r.x << 4));
s[1].XYZ.X = static_cast<u16>(m_context->XYOFFSET.OFX + (m_r.z << 4));
s[0].XYZ.Y = static_cast<u16>(m_context->XYOFFSET.OFY + (m_r.y << 4));
@@ -5657,8 +5658,8 @@ __ri u32 GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool t
m_channel_shuffle_finish = true;
}
m_vertex->head = m_vertex->tail = m_vertex->next = 2;
m_index->tail = 2;
m_vertex.head = m_vertex.tail = m_vertex.next = 2;
m_index.tail = 2;
m_primitive_covers_without_gaps = NoGapsType::FullCover;
m_conf.cb_ps.ChannelShuffleOffset = GSVector2(0, 0);
@@ -6602,7 +6603,7 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
if (can_offset && tex->m_scale > 1.0f)
{
const GSVertex* v = &m_vertex->buff[0];
const GSVertex* v = &m_vertex.buff[0];
if (PRIM->FST)
{
const int x1_frac = ((v[1].XYZ.X - m_context->XYOFFSET.OFX) & 0xf);
@@ -6673,7 +6674,7 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
if (can_offset && tex->m_scale > 1.0f)
{
const GSVertex* v = &m_vertex->buff[0];
const GSVertex* v = &m_vertex.buff[0];
if (PRIM->FST)
{
const int x1_frac = ((v[1].XYZ.X - m_context->XYOFFSET.OFX) & 0xf);
@@ -6696,7 +6697,7 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
}
}
if (m_vt.m_primclass == GS_SPRITE_CLASS && m_index->tail >= 4 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp >= 16 &&
if (m_vt.m_primclass == GS_SPRITE_CLASS && m_index.tail >= 4 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp >= 16 &&
((tex->m_from_target_TEX0.PSM & 0x30) == 0x30 || GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pal > 0))
{
HandleManualDeswizzle();
@@ -7382,7 +7383,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
if (rt)
{
EmulateTextureShuffleAndFbmask(rt, tex);
if (m_index->tail == 0)
if (m_index.tail == 0)
{
GL_INS("HW: DrawPrims: Texture shuffle emulation culled all vertices; exiting.");
return;
@@ -7580,7 +7581,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
GL_PERF("DATE: Fast with alpha %d-%d", GetAlphaMinMax().min, GetAlphaMinMax().max);
DATE_one = true;
}
else if (features.texture_barrier && ((m_vt.m_primclass == GS_SPRITE_CLASS && ComputeDrawlistGetSize(rt->m_scale) < 10) || (m_index->tail < 30)))
else if (features.texture_barrier && ((m_vt.m_primclass == GS_SPRITE_CLASS && ComputeDrawlistGetSize(rt->m_scale) < 10) || (m_index.tail < 30)))
{
// texture barrier will split the draw call into n draw call. It is very efficient for
// few primitive draws. Otherwise it sucks.
@@ -7995,7 +7996,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
// This kinda screws things up when using ST, so let's not.
if (m_vt.m_primclass == GS_SPRITE_CLASS && rtscale > 1.0f && (tex && PRIM->FST))
{
const GSVertex* v = &m_vertex->buff[0];
const GSVertex* v = &m_vertex.buff[0];
const int x1_frac = ((v[1].XYZ.X - m_context->XYOFFSET.OFX) & 0xf);
const int y1_frac = ((v[1].XYZ.Y - m_context->XYOFFSET.OFY) & 0xf);
if (x1_frac & 8)
@@ -8549,7 +8550,7 @@ bool GSRendererHW::DetectStripedDoubleClear(bool& no_rt, bool& no_ds)
!m_cached_ctx.ZBUF.ZMSK &&
(m_cached_ctx.FRAME.PSM & 0x30) != (m_cached_ctx.ZBUF.PSM & 0x30) &&
(m_cached_ctx.FRAME.PSM & 0xF) == (m_cached_ctx.ZBUF.PSM & 0xF) && m_vt.m_eq.z == 1 &&
m_vertex->buff[1].XYZ.Z == m_vertex->buff[1].RGBAQ.U32[0];
m_vertex.buff[1].XYZ.Z == m_vertex.buff[1].RGBAQ.U32[0];
// Z and color must be constant and the same and must be drawing strips.
if (!z_is_frame || m_vt.m_eq.rgba != 0xFFFF)
@@ -8564,12 +8565,12 @@ bool GSRendererHW::DetectStripedDoubleClear(bool& no_rt, bool& no_ds)
// LOTR has 4096 verts, so this isn't going to be super fast on that game, most games will be just 16 verts so they should be ok,
// and I could cheat and stop when we get a size that matches, but that might be a lucky misdetection, I don't wanna risk it.
int vertex_offset = 0;
int last_vertex = m_vertex->buff[0].XYZ.X;
int last_vertex = m_vertex.buff[0].XYZ.X;
for (u32 i = 1; i < m_vertex->tail; i++)
for (u32 i = 1; i < m_vertex.tail; i++)
{
vertex_offset = std::max(static_cast<int>((m_vertex->buff[i].XYZ.X - last_vertex) >> 4), vertex_offset);
last_vertex = m_vertex->buff[i].XYZ.X;
vertex_offset = std::max(static_cast<int>((m_vertex.buff[i].XYZ.X - last_vertex) >> 4), vertex_offset);
last_vertex = m_vertex.buff[i].XYZ.X;
// Found a gap which is much bigger, no point continuing to scan.
if (vertex_offset > strip_size)
@@ -8929,7 +8930,7 @@ bool GSRendererHW::TryTargetClear(GSTextureCache::Target* rt, GSTextureCache::Ta
if (ds && !preserve_depth && m_r.rintersect(ds->m_valid).eq(ds->m_valid))
{
const u32 max_z = 0xFFFFFFFF >> (GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].fmt * 8);
const u32 z = std::min(max_z, m_vertex->buff[1].XYZ.Z);
const u32 z = std::min(max_z, m_vertex.buff[1].XYZ.Z);
const float d = static_cast<float>(z) * 0x1p-32f;
GL_INS("HW: TryTargetClear(): DS at %x <= %f", ds->m_TEX0.TBP0, d);
g_gs_device->ClearDepth(ds->m_texture, d);
@@ -8995,7 +8996,7 @@ bool GSRendererHW::TryGSMemClear(bool no_rt, bool preserve_rt, bool invalidate_r
if (!no_ds && !preserve_z)
{
ClearGSLocalMemory(m_context->offset.zb, m_r, m_vertex->buff[1].XYZ.Z);
ClearGSLocalMemory(m_context->offset.zb, m_r, m_vertex.buff[1].XYZ.Z);
if (invalidate_z)
{
@@ -9170,7 +9171,7 @@ void GSRendererHW::ClearGSLocalMemory(const GSOffset& off, const GSVector4i& r,
bool GSRendererHW::OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Source* tex, const GSVector4i& r_draw)
{
// Not required when using Tex in RT
if (r_draw.w > 1024 && (m_vt.m_primclass == GS_SPRITE_CLASS) && (m_vertex->next == 2) && m_process_texture && !PRIM->ABE &&
if (r_draw.w > 1024 && (m_vt.m_primclass == GS_SPRITE_CLASS) && (m_vertex.next == 2) && m_process_texture && !PRIM->ABE &&
tex && !tex->m_target && m_cached_ctx.TEX0.TBW > 0 && GSConfig.UserHacks_TextureInsideRt == GSTextureInRtMode::Disabled)
{
GL_PUSH("HW: OI_BlitFMV");
@@ -9285,12 +9286,12 @@ bool GSRendererHW::TextureCoversWithoutGapsNotEqual()
}
// Simple case: one sprite.
if (m_index->tail == 2)
if (m_index.tail == 2)
{
return true;
}
const GSVertex* v = &m_vertex->buff[0];
const GSVertex* v = &m_vertex.buff[0];
const int first_dpY = v[1].XYZ.Y - v[0].XYZ.Y;
const int first_dpX = v[1].XYZ.X - v[0].XYZ.X;
const int first_dtV = v[1].V - v[0].V;
@@ -9300,7 +9301,7 @@ bool GSRendererHW::TextureCoversWithoutGapsNotEqual()
if ((first_dpX >> 4) == m_r.z)
{
// Borrowed from MergeSprite() modified to calculate heights.
for (u32 i = 2; i < m_vertex->next; i += 2)
for (u32 i = 2; i < m_vertex.next; i += 2)
{
const int last_tV = v[i - 1].V;
const int dtV = v[i + 1].V - v[i].V;
@@ -9318,7 +9319,7 @@ bool GSRendererHW::TextureCoversWithoutGapsNotEqual()
if ((first_dpY >> 4) == m_r.w)
{
// Borrowed from MergeSprite().
for (u32 i = 2; i < m_vertex->next; i += 2)
for (u32 i = 2; i < m_vertex.next; i += 2)
{
const int last_tU = v[i - 1].U;
const int this_start_U = v[i].U;
@@ -9368,7 +9369,7 @@ int GSRendererHW::IsScalingDraw(GSTextureCache::Source* src, bool no_gaps)
return 0;
const bool no_resize = (std::abs(draw_size.x - tex_size.x) <= 1 && std::abs(draw_size.y - tex_size.y) <= 1);
const bool can_maintain = no_resize || (!is_target_src && m_index->tail == 2);
const bool can_maintain = no_resize || (!is_target_src && m_index.tail == 2);
if (!src || ((!is_target_src || (src->m_from_target->m_downscaled || GSConfig.UserHacks_NativeScaling > GSNativeScaling::Aggressive)) && can_maintain))
return -1;
@@ -9404,9 +9405,9 @@ int GSRendererHW::IsScalingDraw(GSTextureCache::Source* src, bool no_gaps)
}
// Last ditched check if it's doing a lot of small draws exactly the same which could be recursive lighting bloom.
if (m_vt.m_primclass == GS_SPRITE_CLASS && m_index->tail > 2 && !no_gaps_or_single_sprite && m_context->TEX1.MMAG == 1 && !m_context->ALPHA.IsOpaque())
if (m_vt.m_primclass == GS_SPRITE_CLASS && m_index.tail > 2 && !no_gaps_or_single_sprite && m_context->TEX1.MMAG == 1 && !m_context->ALPHA.IsOpaque())
{
GSVertex* v = &m_vertex->buff[0];
GSVertex* v = &m_vertex.buff[0];
float tw = 1 << src->m_TEX0.TW;
float th = 1 << src->m_TEX0.TH;
@@ -9417,7 +9418,7 @@ int GSRendererHW::IsScalingDraw(GSTextureCache::Source* src, bool no_gaps)
if (first_x > first_u && first_y > first_v && !no_resize && std::abs(draw_size.x - first_x) <= 4 && std::abs(draw_size.y - first_y) <= 4)
{
for (u32 i = 2; i < m_index->tail; i += 2)
for (u32 i = 2; i < m_index.tail; i += 2)
{
const int next_u = (PRIM->FST) ? (v[i + 1].U - v[i].U) >> 4 : std::floor(static_cast<int>(tw * v[i + 1].ST.S) - static_cast<int>(tw * v[i].ST.S));
const int next_v = (PRIM->FST) ? (v[i + 1].V - v[i].V) >> 4 : std::floor(static_cast<int>(th * v[i + 1].ST.T) - static_cast<int>(th * v[i].ST.T));
@@ -9430,7 +9431,7 @@ int GSRendererHW::IsScalingDraw(GSTextureCache::Source* src, bool no_gaps)
if (next_u != first_u || next_v != first_v || next_x != first_x || next_y != first_y)
break;
if (i + 2 >= m_index->tail)
if (i + 2 >= m_index.tail)
return 2;
}
}
@@ -9441,13 +9442,13 @@ int GSRendererHW::IsScalingDraw(GSTextureCache::Source* src, bool no_gaps)
ClearType GSRendererHW::IsConstantDirectWriteMemClear()
{
const bool direct_draw = (m_vt.m_primclass == GS_SPRITE_CLASS) || (m_vt.m_primclass == GS_TRIANGLE_CLASS && (m_index->tail % 6) == 0 && TrianglesAreQuads());
const bool direct_draw = (m_vt.m_primclass == GS_SPRITE_CLASS) || (m_vt.m_primclass == GS_TRIANGLE_CLASS && (m_index.tail % 6) == 0 && TrianglesAreQuads());
// Constant Direct Write without texture/test/blending (aka a GS mem clear)
if (direct_draw && !PRIM->TME // Direct write
&& !(m_draw_env->SCANMSK.MSK & 2) && !m_cached_ctx.TEST.ATE // no alpha test
&& !m_cached_ctx.TEST.DATE // no destination alpha test
&& (!m_cached_ctx.TEST.ZTE || m_cached_ctx.TEST.ZTST == ZTST_ALWAYS) // no depth test
&& (m_vt.m_eq.rgba == 0xFFFF || m_vertex->next == 2) // constant color write
&& (m_vt.m_eq.rgba == 0xFFFF || m_vertex.next == 2) // constant color write
&& (!PRIM->FGE || m_vt.m_min.p.w == 255.0f)) // No fog effect
{
if ((PRIM->ABE && !m_context->ALPHA.IsOpaque()) || (m_cached_ctx.FRAME.FBMSK & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk))
@@ -9462,7 +9463,7 @@ u32 GSRendererHW::GetConstantDirectWriteMemClearColor() const
{
// Take the vertex colour, but check if the blending would make it black.
const u32 vert_index = (m_vt.m_primclass == GS_TRIANGLE_CLASS) ? 2 : 1;
u32 vert_color = m_vertex->buff[m_index->buff[vert_index]].RGBAQ.U32[0];
u32 vert_color = m_vertex.buff[m_index.buff[vert_index]].RGBAQ.U32[0];
if (PRIM->ABE && m_context->ALPHA.IsBlack())
vert_color &= 0xFF000000u;
@@ -9483,7 +9484,7 @@ u32 GSRendererHW::GetConstantDirectWriteMemClearColor() const
u32 GSRendererHW::GetConstantDirectWriteMemClearDepth() const
{
const u32 max_z = (0xFFFFFFFF >> (GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].fmt * 8));
return std::min(m_vertex->buff[1].XYZ.Z, max_z);
return std::min(m_vertex.buff[1].XYZ.Z, max_z);
}
bool GSRendererHW::IsReallyDithered() const
@@ -9505,7 +9506,7 @@ void GSRendererHW::ReplaceVerticesWithSprite(const GSVector4i& unscaled_rect, co
{
const GSVector4i fpr = unscaled_rect.sll32<4>();
const GSVector4i fpuv = unscaled_uv_rect.sll32<4>();
GSVertex* v = m_vertex->buff;
GSVertex* v = m_vertex.buff;
v[0].XYZ.X = static_cast<u16>(m_context->XYOFFSET.OFX + fpr.x);
v[0].XYZ.Y = static_cast<u16>(m_context->XYOFFSET.OFY + fpr.y);
@@ -9547,8 +9548,8 @@ void GSRendererHW::ReplaceVerticesWithSprite(const GSVector4i& unscaled_rect, co
m_vt.m_eq.z = true;
m_vt.m_eq.f = true;
m_vertex->head = m_vertex->tail = m_vertex->next = 2;
m_index->tail = 2;
m_vertex.head = m_vertex.tail = m_vertex.next = 2;
m_index.tail = 2;
m_r = unscaled_rect;
m_context->scissor.in = scissor;
@@ -9574,10 +9575,10 @@ void GSRendererHW::OffsetDraw(s32 fbp_offset, s32 zbp_offset, s32 xoffset, s32 y
const s32 fp_xoffset = xoffset << 4;
const s32 fp_yoffset = yoffset << 4;
for (u32 i = 0; i < m_vertex->next; i++)
for (u32 i = 0; i < m_vertex.next; i++)
{
m_vertex->buff[i].XYZ.X += fp_xoffset;
m_vertex->buff[i].XYZ.Y += fp_yoffset;
m_vertex.buff[i].XYZ.X += fp_xoffset;
m_vertex.buff[i].XYZ.Y += fp_yoffset;
}
}

View File

@@ -39,21 +39,21 @@ bool GSRendererHWFunctions::SwPrimRender(GSRendererHW& hw, bool invalidate_tc, b
GSRasterizerData data;
GSScanlineGlobalData& gd = data.global;
hw.m_sw_vertex_buffer.resize(((hw.m_vertex->next + 1) & ~1));
hw.m_sw_vertex_buffer.resize(((hw.m_vertex.next + 1) & ~1));
data.primclass = vt.m_primclass;
data.buff = nullptr;
data.vertex = hw.m_sw_vertex_buffer.data();
data.vertex_count = hw.m_vertex->next;
data.index = hw.m_index->buff;
data.index_count = hw.m_index->tail;
data.vertex_count = hw.m_vertex.next;
data.index = hw.m_index.buff;
data.index_count = hw.m_index.tail;
data.scanmsk_value = env.SCANMSK.MSK;
// Skip per pixel division if q is constant.
// Optimize the division by 1 with a nop. It also means that GS_SPRITE_CLASS must be processed when !vt.m_eq.q.
// If you have both GS_SPRITE_CLASS && vt.m_eq.q, it will depends on the first part of the 'OR'.
const u32 q_div = !hw.IsMipMapActive() && ((vt.m_eq.q && vt.m_min.t.z != 1.0f) || (!vt.m_eq.q && vt.m_primclass == GS_SPRITE_CLASS));
GSVertexSW::s_cvb[vt.m_primclass][PRIM->TME][PRIM->FST][q_div](context, data.vertex, hw.m_vertex->buff, hw.m_vertex->next);
GSVertexSW::s_cvb[vt.m_primclass][PRIM->TME][PRIM->FST][q_div](context, data.vertex, hw.m_vertex.buff, hw.m_vertex.next);
GSVector4i scissor = context->scissor.in;
GSVector4i bbox = GSVector4i(vt.m_min.p.floor().xyxy(vt.m_max.p.ceil())).rintersect(scissor);
@@ -524,12 +524,12 @@ bool GSRendererHWFunctions::SwPrimRender(GSRendererHW& hw, bool invalidate_tc, b
const u32 ofx = context->XYOFFSET.OFX;
for (int i = 0, j = hw.m_vertex->tail; i < j; i++)
for (int i = 0, j = hw.m_vertex.tail; i < j; i++)
{
#if _M_SSE >= 0x501
if ((((hw.m_vertex->buff[i].XYZ.X - ofx) + 15) >> 4) & 7) // aligned to 8
if ((((hw.m_vertex.buff[i].XYZ.X - ofx) + 15) >> 4) & 7) // aligned to 8
#else
if ((((hw.m_vertex->buff[i].XYZ.X - ofx) + 15) >> 4) & 3) // aligned to 4
if ((((hw.m_vertex.buff[i].XYZ.X - ofx) + 15) >> 4) & 3) // aligned to 4
#endif
{
gd.sel.notest = 0;

View File

@@ -5091,14 +5091,14 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
// We use dx/dy == 0 and the TBW check as a safeguard to make sure these go through to local memory.
// We can also recreate the target if it's previously been created in the height cache with a valid size.
// Good test case for this is the Xenosaga I cutscene transitions, or Gradius V.
if (src && !dst && ((dx == 0 && dy == 0 && ((static_cast<u32>(w) + 63) / 64) <= DBW) || HasTargetInHeightCache(DBP, DBW, DPSM, 10)))
if (src && !dst && ((((dx == 0 && dy == 0) || (dx == sx && dy == sy && DBW == src->m_TEX0.TBW)) && ((static_cast<u32>(w) + 63) / 64) <= DBW) || HasTargetInHeightCache(DBP, DBW, DPSM, 10)))
{
GIFRegTEX0 new_TEX0 = {};
new_TEX0.TBP0 = DBP;
new_TEX0.TBW = DBW;
new_TEX0.PSM = DPSM;
const GSVector2i target_size = GetTargetSize(DBP, DBW, DPSM, Common::AlignUpPow2(w, 64), h);
const GSVector2i target_size = (dx == 0 && dy == 0) ? GetTargetSize(DBP, DBW, DPSM, Common::AlignUpPow2(w, 64), h) : GSVector2i(src->m_valid.z, src->m_valid.w);
dst = LookupTarget(new_TEX0, target_size, src->m_scale, src->m_type);
if (!dst)
{

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 HAS_CONSERVATIVE_DEPTH 1\n";
}
else
{
header += "#define HAS_CONSERVATIVE_DEPTH 0\n";
}
// Allow to puts several shader in 1 files
switch (type)
{

View File

@@ -320,14 +320,14 @@ void GSRendererSW::RewriteVerticesIfSTOverflow()
constexpr int n = GSUtil::GetClassVertexCount(primclass);
// Make sure the copy buffer is large enough.
while (m_vertex->maxcount < m_index->tail)
while (m_vertex.maxcount < m_index.tail)
GrowVertexBuffer();
GSVertex* RESTRICT vertex = m_vertex->buff;
GSVertex* RESTRICT vertex_copy = m_vertex->buff_copy;
u16* RESTRICT index = m_index->buff;
GSVertex* RESTRICT vertex = m_vertex.buff;
GSVertex* RESTRICT vertex_copy = m_vertex.buff_copy;
u16* RESTRICT index = m_index.buff;
for (int i = 0; i < static_cast<int>(m_index->tail); i += n)
for (int i = 0; i < static_cast<int>(m_index.tail); i += n)
{
GSVector4 stcq[n];
@@ -381,18 +381,18 @@ void GSRendererSW::RewriteVerticesIfSTOverflow()
}
// Swap the buffers and fix the counts.
std::swap(m_vertex->buff, m_vertex->buff_copy);
m_vertex->head = m_vertex->next = m_vertex->tail = m_index->tail;
std::swap(m_vertex.buff, m_vertex.buff_copy);
m_vertex.head = m_vertex.next = m_vertex.tail = m_index.tail;
// Recalculate ST min/max/eq in the vertex trace.
GSVector4 tmin = GSVector4::cxpr(FLT_MAX);
GSVector4 tmax = GSVector4::cxpr(-FLT_MAX);
for (int i = 0; i < static_cast<int>(m_index->tail); i += n)
for (int i = 0; i < static_cast<int>(m_index.tail); i += n)
{
for (int j = 0; j < n; j++)
{
GSVector4 stcq = GSVector4::cast(GSVector4i(m_vertex->buff[i + j].m[0]));
const float Q = (primclass == GS_SPRITE_CLASS) ? stcq.w : m_vertex->buff[i + 1].RGBAQ.Q;
GSVector4 stcq = GSVector4::cast(GSVector4i(m_vertex.buff[i + j].m[0]));
const float Q = (primclass == GS_SPRITE_CLASS) ? stcq.w : m_vertex.buff[i + 1].RGBAQ.Q;
stcq = (stcq / Q).xyzw(stcq);
tmin = tmin.min(stcq);
@@ -451,11 +451,11 @@ void GSRendererSW::Draw()
SharedData* sd = static_cast<SharedData*>(data.get());
sd->primclass = m_vt.m_primclass;
sd->buff = (u8*)m_vertex_heap.alloc(sizeof(GSVertexSW) * ((m_vertex->next + 1) & ~1) + sizeof(u32) * m_index->tail, 64);
sd->buff = (u8*)m_vertex_heap.alloc(sizeof(GSVertexSW) * ((m_vertex.next + 1) & ~1) + sizeof(u32) * m_index.tail, 64);
sd->vertex = (GSVertexSW*)sd->buff;
sd->vertex_count = m_vertex->next;
sd->index = (u16*)(sd->buff + sizeof(GSVertexSW) * ((m_vertex->next + 1) & ~1));
sd->index_count = m_index->tail;
sd->vertex_count = m_vertex.next;
sd->index = (u16*)(sd->buff + sizeof(GSVertexSW) * ((m_vertex.next + 1) & ~1));
sd->index_count = m_index.tail;
sd->scanmsk_value = m_draw_env->SCANMSK.MSK;
// skip per pixel division if q is constant.
@@ -463,9 +463,9 @@ void GSRendererSW::Draw()
// If you have both GS_SPRITE_CLASS && m_vt.m_eq.q, it will depends on the first part of the 'OR'
u32 q_div = !IsMipMapActive() && ((m_vt.m_eq.q && m_vt.m_min.t.z != 1.0f) || (!m_vt.m_eq.q && m_vt.m_primclass == GS_SPRITE_CLASS));
GSVertexSW::s_cvb[m_vt.m_primclass][PRIM->TME][PRIM->FST][q_div](m_context, sd->vertex, m_vertex->buff, m_vertex->next);
GSVertexSW::s_cvb[m_vt.m_primclass][PRIM->TME][PRIM->FST][q_div](m_context, sd->vertex, m_vertex.buff, m_vertex.next);
std::memcpy(sd->index, m_index->buff, sizeof(u16) * m_index->tail);
std::memcpy(sd->index, m_index.buff, sizeof(u16) * m_index.tail);
GSVector4i scissor = context->scissor.in;
GSVector4i bbox = GSVector4i(m_vt.m_min.p.floor().upld(m_vt.m_max.p.floor())) + GSVector4i(0, 0, 1, 1); // right/bottom should be exclusive so +1
@@ -485,12 +485,12 @@ void GSRendererSW::Draw()
{
int n = GSUtil::GetVertexCount(PRIM->PRIM);
for (u32 i = 0, j = 0; i < m_index->tail; i += n, j++)
for (u32 i = 0, j = 0; i < m_index.tail; i += n, j++)
{
for (int k = 0; k < n; k++)
{
GSVertex* v = &m_vertex->buff[m_index->buff[i + k]];
GSVertex* vn = &m_vertex->buff[m_index->buff[i + n - 1]];
GSVertex* v = &m_vertex.buff[m_index.buff[i + k]];
GSVertex* vn = &m_vertex.buff[m_index.buff[i + n - 1]];
fprintf(s_fp, "%d:%d %f %f %f %f\n",
j, k,
@@ -1505,12 +1505,12 @@ bool GSRendererSW::GetScanlineGlobalData(SharedData* data)
u32 ofx = context->XYOFFSET.OFX;
for (int i = 0, j = m_vertex->tail; i < j; i++)
for (int i = 0, j = m_vertex.tail; i < j; i++)
{
#if _M_SSE >= 0x501
if ((((m_vertex->buff[i].XYZ.X - ofx) + 15) >> 4) & 7) // aligned to 8
if ((((m_vertex.buff[i].XYZ.X - ofx) + 15) >> 4) & 7) // aligned to 8
#else
if ((((m_vertex->buff[i].XYZ.X - ofx) + 15) >> 4) & 3) // aligned to 4
if ((((m_vertex.buff[i].XYZ.X - ofx) + 15) >> 4) & 3) // aligned to 4
#endif
{
gd.sel.notest = 0;

View File

@@ -3131,7 +3131,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;
@@ -3150,7 +3150,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;
@@ -3171,7 +3171,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;
@@ -4434,16 +4434,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)

View File

@@ -4461,7 +4461,7 @@ void FullscreenUI::DrawBIOSSettingsPage()
});
}
MenuHeading(FSUI_CSTR("Options and Patches"));
MenuHeading(FSUI_CSTR("Fast Boot Options"));
DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_FORWARD_FAST, "Fast Boot"), FSUI_CSTR("Skips the intro screen, and bypasses region checks."),
"EmuCore", "EnableFastBoot", true);
@@ -9509,7 +9509,7 @@ TRANSLATE_NOOP("FullscreenUI", "Resets configuration to defaults (excluding cont
TRANSLATE_NOOP("FullscreenUI", "BIOS Configuration");
TRANSLATE_NOOP("FullscreenUI", "Changes the BIOS image used to start future sessions.");
TRANSLATE_NOOP("FullscreenUI", "BIOS Selection");
TRANSLATE_NOOP("FullscreenUI", "Options and Patches");
TRANSLATE_NOOP("FullscreenUI", "Fast Boot Options");
TRANSLATE_NOOP("FullscreenUI", "Skips the intro screen, and bypasses region checks.");
TRANSLATE_NOOP("FullscreenUI", "Speed Control");
TRANSLATE_NOOP("FullscreenUI", "Sets the speed when running without fast forwarding.");

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");
}

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 =

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();

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;

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

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 = 81;