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