Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9310861cc2 | ||
|
|
53f0db99ca | ||
|
|
2eb125f6e2 | ||
|
|
f805d9432e | ||
|
|
054cb9e9fa | ||
|
|
1f77e7d90a | ||
|
|
7ed33832b8 | ||
|
|
d19e9cc662 | ||
|
|
846c9cec6a | ||
|
|
460e4b8bb3 | ||
|
|
2e6bd23f22 | ||
|
|
7763948fe4 | ||
|
|
2a0f001902 | ||
|
|
14df69e180 | ||
|
|
28b6389b36 | ||
|
|
905c0b8c7a | ||
|
|
b674cc95ac | ||
|
|
bccb2509b5 | ||
|
|
cd980c6604 | ||
|
|
ddff4ec083 | ||
|
|
5bf97be8ef | ||
|
|
7fadb6ed05 | ||
|
|
89688d67dd | ||
|
|
279007544a | ||
|
|
1c0f0e1423 | ||
|
|
1ab909addc | ||
|
|
99dfd15469 | ||
|
|
be18ecf74e | ||
|
|
713bd132dc | ||
|
|
20f03e237e | ||
|
|
7d3c3911ea | ||
|
|
ccfee2be83 | ||
|
|
1193b0b990 | ||
|
|
5a3ba4e563 | ||
|
|
e9c342ef74 | ||
|
|
8927ffa035 | ||
|
|
fdb0312326 | ||
|
|
2f21caa41c | ||
|
|
51a35d40e5 | ||
|
|
6ec524adec | ||
|
|
7d4a7ec8c7 | ||
|
|
0256c4521d | ||
|
|
be3ed181c1 | ||
|
|
38cf7884fe | ||
|
|
25d426e8b2 | ||
|
|
edff9ae684 | ||
|
|
a622e3bda6 | ||
|
|
4d54e28e63 | ||
|
|
c89d023f91 | ||
|
|
c7cfea1daa | ||
|
|
beb07365a0 | ||
|
|
0787c65e51 | ||
|
|
d48dea7273 | ||
|
|
2432cc6ed2 | ||
|
|
cf523d4215 | ||
|
|
906d87c4b8 | ||
|
|
9c4a92c015 | ||
|
|
0c056ba691 | ||
|
|
424158b28d | ||
|
|
57f118a5b5 | ||
|
|
65e75f3da2 | ||
|
|
41174cde45 | ||
|
|
cb7d01fb36 | ||
|
|
3e78f8e87d | ||
|
|
f2c032ba07 | ||
|
|
2ef1589e76 | ||
|
|
5c394557e3 | ||
|
|
5c4a5b0a61 | ||
|
|
be22b7349d | ||
|
|
023e4774a1 |
@@ -105,6 +105,7 @@ jobs:
|
||||
cmake -DCMAKE_PREFIX_PATH="$HOME/deps" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DUSE_OPENGL=OFF \
|
||||
-DUSE_VULKAN=OFF \
|
||||
-DDISABLE_ADVANCE_SIMD=ON \
|
||||
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON \
|
||||
-DUSE_SYSTEM_LIBS=OFF \
|
||||
|
||||
@@ -44,6 +44,7 @@ Thumbs.db
|
||||
Debug.txt
|
||||
install_log.txt
|
||||
bad_shader_*
|
||||
crash-*.txt
|
||||
|
||||
Debug
|
||||
Release
|
||||
|
||||
@@ -315,6 +315,7 @@
|
||||
03000000d81d00001000000000000000,iBuffalo BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
|
||||
030000005c0a00000285000000000000,iDroidCon,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b6,platform:Windows,
|
||||
03000000696400006964000000000000,iDroidCon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000511d00000230000000000000,iGUGU Gamecore,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b1,leftstick:b4,lefttrigger:b3,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b2,platform:Windows,
|
||||
03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,
|
||||
030000006f0e00002401000000000000,Injustice Fightstick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000830500005130000000000000,InterAct ActionPad,a:b0,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
|
||||
@@ -1065,10 +1066,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
05000000c82d00002038000000010000,8BitDo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000751000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
05000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00000660000011010000,8BitDo Pro 2,a:b1,b:b0,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:b4,y:b3,platform:Linux,
|
||||
05000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,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:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000631000014010000,8BitDo Pro 2,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,
|
||||
03000000c82d00000660000011010000,8BitDo Pro 2,a:b1,b:b0,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:b4,y:b3,platform:Linux,
|
||||
03000000c82d00001030000011010000,8BitDo Pro 2,a:b1,b:b0,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:b4,y:b3,platform:Linux,
|
||||
05000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,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:b4,y:b3,platform:Linux,
|
||||
03000000c82d00000020000000000000,8BitDo Pro 2 for Xbox,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,
|
||||
06000000c82d00000020000006010000,8BitDo Pro 2 for Xbox,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,
|
||||
03000000c82d00000131000011010000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
@@ -1094,9 +1095,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
05000000c82d00000261000000010000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000202800000900000000010000,8BitDo SNES30,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001730000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001530000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001630000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001730000011010000,8BitDo Ultimate C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001130000011010000,8BitDo Ultimate Wired,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:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00000760000011010000,8BitDo Ultimate Wireless,a:b1,b:b0,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:b4,y:b3,platform:Linux,
|
||||
03000000c82d00001230000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
@@ -1462,6 +1463,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
|
||||
050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
|
||||
0300000032150000030a000001010000,Razer Wildcat,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,
|
||||
03000000321500000b10000011010000,Razer Wolverine PS5 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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
|
||||
03000000790000001100000010010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
190000004b4800000111000000010000,RetroGame Joypad,a:b1,b:b0,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,platform:Linux,
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#define PS_POINT_SAMPLER 0
|
||||
#define PS_REGION_RECT 0
|
||||
#define PS_SHUFFLE 0
|
||||
#define PS_SHUFFLE_SAME 0
|
||||
#define PS_READ_BA 0
|
||||
#define PS_READ16_SRC 0
|
||||
#define PS_DFMT 0
|
||||
@@ -940,8 +941,17 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
{
|
||||
uint4 denorm_c = uint4(C);
|
||||
uint2 denorm_TA = uint2(float2(TA.xy) * 255.0f + 0.5f);
|
||||
|
||||
if (PS_READ16_SRC)
|
||||
|
||||
// Special case for 32bit input and 16bit output, shuffle used by The Godfather
|
||||
if (PS_SHUFFLE_SAME)
|
||||
{
|
||||
if (PS_READ_BA)
|
||||
C = (float4)(float((denorm_c.b & 0x7Fu) | (denorm_c.a & 0x80u)));
|
||||
else
|
||||
C.ga = C.rg;
|
||||
}
|
||||
// Copy of a 16bit source in to this target
|
||||
else if (PS_READ16_SRC)
|
||||
{
|
||||
C.rb = (float2)float((denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7u) << 5));
|
||||
if (denorm_c.a & 0x80u)
|
||||
@@ -949,28 +959,23 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
else
|
||||
C.ga = (float2)float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u));
|
||||
}
|
||||
// Write RB part. Mask will take care of the correct destination
|
||||
else if (PS_READ_BA)
|
||||
{
|
||||
C.rb = C.bb;
|
||||
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
|
||||
{
|
||||
// Mask will take care of the correct destination
|
||||
if (PS_READ_BA)
|
||||
C.rb = C.bb;
|
||||
else
|
||||
C.rb = C.rr;
|
||||
C.rb = C.rr;
|
||||
if (denorm_c.g & 0x80u)
|
||||
C.ga = (float2)(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
|
||||
|
||||
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)));
|
||||
}
|
||||
C.ga = (float2)(float((denorm_c.g & 0x7Fu) | (denorm_TA.x & 0x80u)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -779,17 +779,19 @@ float As = As_rgba.a;
|
||||
return;
|
||||
#endif
|
||||
|
||||
vec3 Cs = Color.rgb;
|
||||
|
||||
#if SW_BLEND_NEEDS_RT
|
||||
vec4 RT = trunc(fetch_rt() * 255.0f + 0.1f);
|
||||
#else
|
||||
// Not used, but we define it to make the selection below simpler.
|
||||
vec4 RT = vec4(0.0f);
|
||||
#endif
|
||||
// FIXME FMT_16 case
|
||||
// FIXME Ad or Ad * 2?
|
||||
float Ad = RT.a / 128.0f;
|
||||
|
||||
// Let the compiler do its jobs !
|
||||
vec3 Cd = RT.rgb;
|
||||
#endif
|
||||
vec3 Cs = Color.rgb;
|
||||
|
||||
#if PS_BLEND_A == 0
|
||||
vec3 A = Cs;
|
||||
@@ -950,26 +952,30 @@ 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
|
||||
|
||||
// Special case for 32bit input and 16bit output, shuffle used by The Godfather
|
||||
#if PS_SHUFFLE_SAME
|
||||
#if (PS_READ_BA)
|
||||
C = vec4(float((denorm_c.b & 0x7Fu) | (denorm_c.a & 0x80u)));
|
||||
#else
|
||||
C.ga = C.rg;
|
||||
#endif
|
||||
// Copy of a 16bit source in to this target
|
||||
#elif 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
|
||||
// Write RB part. Mask will take care of the correct destination
|
||||
#elif PS_READ_BA
|
||||
C.rb = C.bb;
|
||||
#else
|
||||
C.rb = C.rr;
|
||||
#endif
|
||||
|
||||
// FIXME precompute my_TA & 0x80
|
||||
|
||||
// Write GA part. Mask will take care of the correct destination
|
||||
// Note: GLSL 4.50/GL_EXT_shader_integer_mix support a mix instruction to select a component\n"
|
||||
// However Nvidia emulate it with an if (at least on kepler arch) ...\n"
|
||||
#if PS_READ_BA
|
||||
|
||||
// bit field operation requires GL4 HW. Could be nice to merge it with step/mix below
|
||||
// uint my_ta = (bool(bitfieldExtract(denorm_c.a, 7, 1))) ? denorm_TA.y : denorm_TA.x;
|
||||
// denorm_c.a = bitfieldInsert(denorm_c.a, bitfieldExtract(my_ta, 7, 1), 7, 1);
|
||||
@@ -981,6 +987,7 @@ void ps_main()
|
||||
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
|
||||
|
||||
#else
|
||||
C.rb = C.rr;
|
||||
if (bool(denorm_c.g & 0x80u))
|
||||
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
|
||||
else
|
||||
@@ -992,9 +999,8 @@ 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 // PS_READ_BA
|
||||
|
||||
#endif // READ16_SRC
|
||||
#endif // PS_SHUFFLE_SAME
|
||||
#endif // PS_SHUFFLE
|
||||
|
||||
// Must be done before alpha correction
|
||||
|
||||
@@ -277,7 +277,9 @@ void main()
|
||||
#define PS_TCOFFSETHACK 0
|
||||
#define PS_POINT_SAMPLER 0
|
||||
#define PS_SHUFFLE 0
|
||||
#define PS_SHUFFLE_SAME 0
|
||||
#define PS_READ_BA 0
|
||||
#define PS_WRITE_RG 0
|
||||
#define PS_READ16_SRC 0
|
||||
#define PS_DFMT 0
|
||||
#define PS_DEPTH_FMT 0
|
||||
@@ -300,7 +302,7 @@ void main()
|
||||
#endif
|
||||
|
||||
#define SW_BLEND (PS_BLEND_A || PS_BLEND_B || PS_BLEND_D)
|
||||
#define SW_BLEND_NEEDS_RT (PS_BLEND_A == 1 || PS_BLEND_B == 1 || PS_BLEND_C == 1 || PS_BLEND_D == 1)
|
||||
#define SW_BLEND_NEEDS_RT (SW_BLEND && (PS_BLEND_A == 1 || PS_BLEND_B == 1 || PS_BLEND_C == 1 || PS_BLEND_D == 1))
|
||||
#define SW_AD_TO_HW (PS_BLEND_C == 1 && PS_A_MASKED)
|
||||
|
||||
#define PS_FEEDBACK_LOOP_IS_NEEDED (PS_TEX_IS_FB == 1 || PS_FBMASK || SW_BLEND_NEEDS_RT || (PS_DATE >= 5))
|
||||
@@ -352,16 +354,16 @@ layout(set = 1, binding = 1) uniform texture2D Palette;
|
||||
|
||||
#if PS_FEEDBACK_LOOP_IS_NEEDED
|
||||
#if defined(DISABLE_TEXTURE_BARRIER) || defined(HAS_FEEDBACK_LOOP_LAYOUT)
|
||||
layout(set = 2, binding = 0) uniform texture2D RtSampler;
|
||||
layout(set = 1, binding = 2) uniform texture2D RtSampler;
|
||||
vec4 sample_from_rt() { return texelFetch(RtSampler, ivec2(gl_FragCoord.xy), 0); }
|
||||
#else
|
||||
layout(input_attachment_index = 0, set = 2, binding = 0) uniform subpassInput RtSampler;
|
||||
layout(input_attachment_index = 0, set = 1, binding = 2) uniform subpassInput RtSampler;
|
||||
vec4 sample_from_rt() { return subpassLoad(RtSampler); }
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if PS_DATE > 0
|
||||
layout(set = 2, binding = 1) uniform texture2D PrimMinTexture;
|
||||
layout(set = 1, binding = 3) uniform texture2D PrimMinTexture;
|
||||
#endif
|
||||
|
||||
#if NEEDS_TEX
|
||||
@@ -1197,33 +1199,36 @@ void main()
|
||||
#if PS_SHUFFLE
|
||||
uvec4 denorm_c = uvec4(C);
|
||||
uvec2 denorm_TA = uvec2(vec2(TA.xy) * 255.0f + 0.5f);
|
||||
#if PS_READ16_SRC
|
||||
|
||||
// Special case for 32bit input and 16bit output, shuffle used by The Godfather
|
||||
#if PS_SHUFFLE_SAME
|
||||
#if (PS_READ_BA)
|
||||
C = vec4(float((denorm_c.b & 0x7Fu) | (denorm_c.a & 0x80u)));
|
||||
#else
|
||||
C.ga = C.rg;
|
||||
#endif
|
||||
// Copy of a 16bit source in to this target
|
||||
#elif 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.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)));
|
||||
// Write RB part. Mask will take care of the correct destination
|
||||
#elif PS_READ_BA
|
||||
C.rb = C.bb;
|
||||
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
|
||||
// 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
|
||||
C.rb = C.rr;
|
||||
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 // PS_SHUFFLE_SAME
|
||||
#endif // PS_SHUFFLE
|
||||
|
||||
// Must be done before alpha correction
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ struct crash_info_t
|
||||
u64 reserved;
|
||||
u64 reserved2;
|
||||
};
|
||||
#define CRASH_ANNOTATION __attribute__((section("__DATA,__crash_info")))
|
||||
#define CRASH_ANNOTATION __attribute__((used, section("__DATA,__crash_info")))
|
||||
#define CRASH_VERSION 4
|
||||
extern "C" crash_info_t gCRAnnotations CRASH_ANNOTATION = { CRASH_VERSION };
|
||||
#endif
|
||||
|
||||
@@ -92,31 +92,31 @@ public:
|
||||
return value;
|
||||
}
|
||||
|
||||
__fi std::optional<int> GetOptionalIntValue(const char* section, const char* key, std::optional<int> default_value = std::nullopt)
|
||||
__fi std::optional<int> GetOptionalIntValue(const char* section, const char* key, std::optional<int> default_value = std::nullopt) const
|
||||
{
|
||||
int ret;
|
||||
return GetIntValue(section, key, &ret) ? std::optional<int>(ret) : default_value;
|
||||
}
|
||||
|
||||
__fi std::optional<uint> GetOptionalUIntValue(const char* section, const char* key, std::optional<uint> default_value = std::nullopt)
|
||||
__fi std::optional<uint> GetOptionalUIntValue(const char* section, const char* key, std::optional<uint> default_value = std::nullopt) const
|
||||
{
|
||||
uint ret;
|
||||
return GetUIntValue(section, key, &ret) ? std::optional<uint>(ret) : default_value;
|
||||
}
|
||||
|
||||
__fi std::optional<float> GetOptionalFloatValue(const char* section, const char* key, std::optional<float> default_value = std::nullopt)
|
||||
__fi std::optional<float> GetOptionalFloatValue(const char* section, const char* key, std::optional<float> default_value = std::nullopt) const
|
||||
{
|
||||
float ret;
|
||||
return GetFloatValue(section, key, &ret) ? std::optional<float>(ret) : default_value;
|
||||
}
|
||||
|
||||
__fi std::optional<double> GetOptionalDoubleValue(const char* section, const char* key, std::optional<double> default_value = std::nullopt)
|
||||
__fi std::optional<double> GetOptionalDoubleValue(const char* section, const char* key, std::optional<double> default_value = std::nullopt) const
|
||||
{
|
||||
double ret;
|
||||
return GetDoubleValue(section, key, &ret) ? std::optional<double>(ret) : default_value;
|
||||
}
|
||||
|
||||
__fi std::optional<bool> GetOptionalBoolValue(const char* section, const char* key, std::optional<bool> default_value = std::nullopt)
|
||||
__fi std::optional<bool> GetOptionalBoolValue(const char* section, const char* key, std::optional<bool> default_value = std::nullopt) const
|
||||
{
|
||||
bool ret;
|
||||
return GetBoolValue(section, key, &ret) ? std::optional<bool>(ret) : default_value;
|
||||
|
||||
@@ -513,5 +513,22 @@ std::optional<std::string> InputManager::ConvertHostKeyboardCodeToString(u32 cod
|
||||
|
||||
u32 QtUtils::KeyEventToCode(const QKeyEvent* ev)
|
||||
{
|
||||
return static_cast<u32>(ev->key()) | (static_cast<u32>(ev->modifiers()) & static_cast<u32>(Qt::KeypadModifier));
|
||||
int key = ev->key();
|
||||
Qt::KeyboardModifiers modifiers = ev->modifiers();
|
||||
|
||||
#ifdef __APPLE__
|
||||
// On macOS, Qt applies the Keypad modifier regardless of whether the arrow keys, or numpad was pressed.
|
||||
// The only way to differentiate between the keypad and the arrow keys is by the text.
|
||||
// Hopefully some keyboard layouts don't change the numpad positioning...
|
||||
if (modifiers & Qt::KeypadModifier && key >= Qt::Key_Insert && key <= Qt::Key_PageDown)
|
||||
{
|
||||
if (ev->text().isEmpty())
|
||||
{
|
||||
// Drop the modifier, because it's probably not actually a numpad push.
|
||||
modifiers &= ~Qt::KeypadModifier;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return static_cast<u32>(key) | (static_cast<u32>(modifiers) & static_cast<u32>(Qt::KeypadModifier));
|
||||
}
|
||||
|
||||
@@ -1078,7 +1078,10 @@ namespace SettingWidgetBinder
|
||||
static inline void BindSliderToIntSetting(SettingsInterface* sif, QSlider* slider, QLabel* label, const QString& label_suffix,
|
||||
std::string section, std::string key, s32 default_value)
|
||||
{
|
||||
const s32 global_value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value);
|
||||
s32 global_value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value);
|
||||
|
||||
//Clamp in case setting was updated manually using INI
|
||||
global_value = std::clamp(global_value, slider->minimum(), slider->maximum());
|
||||
|
||||
if (sif)
|
||||
{
|
||||
@@ -1086,7 +1089,9 @@ namespace SettingWidgetBinder
|
||||
QFont bold_font(orig_font);
|
||||
bold_font.setBold(true);
|
||||
|
||||
const s32 current_value = sif->GetOptionalIntValue(section.c_str(), key.c_str()).value_or(global_value);
|
||||
s32 current_value = sif->GetOptionalIntValue(section.c_str(), key.c_str()).value_or(global_value);
|
||||
current_value = std::clamp(current_value, slider->minimum(), slider->maximum());
|
||||
|
||||
slider->setValue(current_value);
|
||||
|
||||
label->setText(QStringLiteral("%1%2").arg(current_value).arg(label_suffix));
|
||||
@@ -1100,8 +1105,10 @@ namespace SettingWidgetBinder
|
||||
[sif, slider, label, label_suffix, orig_font = std::move(orig_font), section, key, default_value](const QPoint& pt) {
|
||||
QMenu menu(slider);
|
||||
slider->connect(menu.addAction(qApp->translate("SettingWidgetBinder", "Reset")), &QAction::triggered, slider,
|
||||
[sif, label, label_suffix, orig_font, section, key, default_value]() {
|
||||
const s32 global_value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value);
|
||||
[sif, slider, label, label_suffix, orig_font, section, key, default_value]() {
|
||||
s32 global_value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value);
|
||||
global_value = std::clamp(global_value, slider->minimum(), slider->maximum());
|
||||
|
||||
label->setText(QStringLiteral("%1%2").arg(global_value).arg(label_suffix));
|
||||
label->setFont(orig_font);
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
static constexpr s32 DEFAULT_NOTIFICATIONS_DURATION = 5;
|
||||
|
||||
AchievementSettingsWidget::AchievementSettingsWidget(SettingsDialog* dialog, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_dialog(dialog)
|
||||
@@ -48,6 +50,9 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsDialog* dialog, QWi
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.soundEffects, "Achievements", "SoundEffects", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.primedIndicators, "Achievements", "PrimedIndicators", true);
|
||||
|
||||
SettingWidgetBinder::BindSliderToIntSetting(sif, m_ui.notifications_duration, m_ui.notifications_duration_seconds,
|
||||
tr(" seconds"), "Achievements", "NotificationsDuration", DEFAULT_NOTIFICATIONS_DURATION);
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.enable, tr("Enable Achievements"), tr("Unchecked"),
|
||||
tr("When enabled and logged in, PCSX2 will scan for achievements on game load."));
|
||||
dialog->registerWidgetHelp(m_ui.testMode, tr("Enable Test Mode"), tr("Unchecked"),
|
||||
@@ -70,10 +75,18 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsDialog* dialog, QWi
|
||||
dialog->registerWidgetHelp(m_ui.primedIndicators, tr("Show Challenge Indicators"), tr("Checked"),
|
||||
tr("Shows icons in the lower-right corner of the screen when a challenge/primed achievement is active."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.notifications_duration, tr("Notification Duration"),
|
||||
tr("5 seconds"), tr("The duration, in seconds, an achievement popup notification will remain on screen."));
|
||||
dialog->registerWidgetHelp(m_ui.notifications_duration_label, tr("Notification Duration"),
|
||||
tr("5 seconds"), tr("The duration, in seconds, an achievement popup notification will remain on screen."));
|
||||
dialog->registerWidgetHelp(m_ui.notifications_duration_seconds, tr("Notification Duration"),
|
||||
tr("5 seconds"), tr("The duration, in seconds, an achievement popup notification will remain on screen."));
|
||||
|
||||
connect(m_ui.enable, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
|
||||
connect(m_ui.notifications, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
|
||||
connect(m_ui.challengeMode, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::updateEnableState);
|
||||
connect(m_ui.challengeMode, &QCheckBox::stateChanged, this, &AchievementSettingsWidget::onChallengeModeStateChanged);
|
||||
connect(m_ui.notifications_duration, &QSlider::valueChanged, this, &AchievementSettingsWidget::onNotificationsDurationChanged);
|
||||
|
||||
if (!m_dialog->isPerGameSettings())
|
||||
{
|
||||
@@ -105,6 +118,8 @@ void AchievementSettingsWidget::updateEnableState()
|
||||
{
|
||||
const bool enabled = m_dialog->getEffectiveBoolValue("Achievements", "Enabled", false);
|
||||
const bool challenge = m_dialog->getEffectiveBoolValue("Achievements", "ChallengeMode", false);
|
||||
const bool notifications = m_dialog->getEffectiveBoolValue("Achievements", "Notifications", true);
|
||||
|
||||
m_ui.testMode->setEnabled(enabled);
|
||||
m_ui.unofficialTestMode->setEnabled(enabled);
|
||||
m_ui.richPresence->setEnabled(enabled);
|
||||
@@ -113,6 +128,10 @@ void AchievementSettingsWidget::updateEnableState()
|
||||
m_ui.notifications->setEnabled(enabled);
|
||||
m_ui.soundEffects->setEnabled(enabled);
|
||||
m_ui.primedIndicators->setEnabled(enabled);
|
||||
|
||||
m_ui.notifications_duration->setEnabled(enabled && notifications);
|
||||
m_ui.notifications_duration_label->setEnabled(enabled && notifications);
|
||||
m_ui.notifications_duration_seconds->setEnabled(enabled && notifications);
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::onChallengeModeStateChanged()
|
||||
@@ -196,3 +215,9 @@ void AchievementSettingsWidget::onAchievementsRefreshed(quint32 id, const QStrin
|
||||
{
|
||||
m_ui.gameInfo->setText(game_info_string);
|
||||
}
|
||||
|
||||
void AchievementSettingsWidget::onNotificationsDurationChanged()
|
||||
{
|
||||
m_ui.notifications_duration_seconds->setText(tr("%1 seconds")
|
||||
.arg(m_ui.notifications_duration->value()));
|
||||
}
|
||||
@@ -33,6 +33,7 @@ private Q_SLOTS:
|
||||
void onLoginLogoutPressed();
|
||||
void onViewProfilePressed();
|
||||
void onAchievementsRefreshed(quint32 id, const QString& game_info_string, quint32 total, quint32 points);
|
||||
void onNotificationsDurationChanged();
|
||||
|
||||
private:
|
||||
void updateLoginState();
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>658</width>
|
||||
<height>496</height>
|
||||
<width>829</width>
|
||||
<height>641</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -39,13 +39,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="primedIndicators">
|
||||
<property name="text">
|
||||
<string>Show Challenge Indicators</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="richPresence">
|
||||
<property name="text">
|
||||
@@ -74,13 +67,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="testMode">
|
||||
<property name="text">
|
||||
<string>Enable Test Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="soundEffects">
|
||||
<property name="text">
|
||||
@@ -89,12 +75,93 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="primedIndicators">
|
||||
<property name="text">
|
||||
<string>Show Challenge Indicators</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="testMode">
|
||||
<property name="text">
|
||||
<string>Enable Test Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="notificationBox">
|
||||
<property name="title">
|
||||
<string>Notifications</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="notifications_box_layout" stretch="0,0">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="notifications">
|
||||
<property name="text">
|
||||
<string>Show Notifications</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="notifications_duration_layout" stretch="0,0,0">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="notifications_duration_label">
|
||||
<property name="text">
|
||||
<string>Duration</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="notifications_duration">
|
||||
<property name="minimum">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="invertedAppearance">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tickPosition">
|
||||
<enum>QSlider::TicksBelow</enum>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="notifications_duration_seconds">
|
||||
<property name="text">
|
||||
<string>5 seconds</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -51,6 +51,7 @@ GameListSettingsWidget::GameListSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
&GameListSettingsWidget::onAddSearchDirectoryButtonClicked);
|
||||
connect(m_ui.removeSearchDirectoryButton, &QPushButton::clicked, this,
|
||||
&GameListSettingsWidget::onRemoveSearchDirectoryButtonClicked);
|
||||
connect(m_ui.addExcludedFile, &QPushButton::clicked, this, &GameListSettingsWidget::onAddExcludedFileButtonClicked);
|
||||
connect(m_ui.addExcludedPath, &QPushButton::clicked, this, &GameListSettingsWidget::onAddExcludedPathButtonClicked);
|
||||
connect(m_ui.removeExcludedPath, &QPushButton::clicked, this,
|
||||
&GameListSettingsWidget::onRemoveExcludedPathButtonClicked);
|
||||
@@ -215,10 +216,21 @@ void GameListSettingsWidget::onRemoveSearchDirectoryButtonClicked()
|
||||
delete item;
|
||||
}
|
||||
|
||||
void GameListSettingsWidget::onAddExcludedFileButtonClicked()
|
||||
{
|
||||
QString path =
|
||||
QDir::toNativeSeparators(QFileDialog::getOpenFileName(QtUtils::GetRootWidget(this), tr("Select File")));
|
||||
if (path.isEmpty())
|
||||
return;
|
||||
|
||||
addExcludedPath(path.toStdString());
|
||||
}
|
||||
|
||||
void GameListSettingsWidget::onAddExcludedPathButtonClicked()
|
||||
{
|
||||
QString path =
|
||||
QDir::toNativeSeparators(QFileDialog::getOpenFileName(QtUtils::GetRootWidget(this), tr("Select Path")));
|
||||
QDir::toNativeSeparators(QFileDialog::getExistingDirectory(QtUtils::GetRootWidget(this), tr("Select Directory")));
|
||||
|
||||
if (path.isEmpty())
|
||||
return;
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ private Q_SLOTS:
|
||||
void onDirectoryListContextMenuRequested(const QPoint& point);
|
||||
void onAddSearchDirectoryButtonClicked();
|
||||
void onRemoveSearchDirectoryButtonClicked();
|
||||
void onAddExcludedFileButtonClicked();
|
||||
void onAddExcludedPathButtonClicked();
|
||||
void onRemoveExcludedPathButtonClicked();
|
||||
void onScanForNewGamesClicked();
|
||||
|
||||
@@ -57,12 +57,15 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
<string>Add...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="folder-add-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@@ -80,6 +83,9 @@
|
||||
<iconset theme="folder-reduce-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -129,12 +135,35 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
<string>Directory...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="folder-add-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="addExcludedFile">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>File...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="file-add-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@@ -152,6 +181,9 @@
|
||||
<iconset theme="file-reduce-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -63,6 +63,11 @@ GameSummaryWidget::GameSummaryWidget(const GameList::Entry* entry, SettingsDialo
|
||||
connect(m_ui.inputProfile, &QComboBox::currentIndexChanged, this, &GameSummaryWidget::onInputProfileChanged);
|
||||
connect(m_ui.verify, &QAbstractButton::clicked, this, &GameSummaryWidget::onVerifyClicked);
|
||||
connect(m_ui.searchHash, &QAbstractButton::clicked, this, &GameSummaryWidget::onSearchHashClicked);
|
||||
|
||||
bool has_custom_title = false, has_custom_region = false;
|
||||
GameList::CheckCustomAttributesForPath(m_entry_path, has_custom_title, has_custom_region);
|
||||
m_ui.restoreTitle->setEnabled(has_custom_title);
|
||||
m_ui.restoreRegion->setEnabled(has_custom_region);
|
||||
}
|
||||
|
||||
GameSummaryWidget::~GameSummaryWidget() = default;
|
||||
@@ -88,6 +93,24 @@ void GameSummaryWidget::populateDetails(const GameList::Entry* entry)
|
||||
m_ui.inputProfile->setCurrentIndex(m_ui.inputProfile->findText(QString::fromStdString(profile.value())));
|
||||
else
|
||||
m_ui.inputProfile->setCurrentIndex(0);
|
||||
|
||||
connect(m_ui.title, &QLineEdit::editingFinished, this, [this]() {
|
||||
if (m_ui.title->isModified())
|
||||
{
|
||||
setCustomTitle(m_ui.title->text().toStdString());
|
||||
m_ui.title->setModified(false);
|
||||
}
|
||||
});
|
||||
connect(m_ui.restoreTitle, &QAbstractButton::clicked, this, [this]() {
|
||||
setCustomTitle("");
|
||||
});
|
||||
|
||||
connect(m_ui.region, &QComboBox::currentIndexChanged, this, [this](int index) {
|
||||
setCustomRegion(index);
|
||||
});
|
||||
connect(m_ui.restoreRegion, &QAbstractButton::clicked, this, [this]() {
|
||||
setCustomRegion(-1);
|
||||
});
|
||||
}
|
||||
|
||||
void GameSummaryWidget::populateDiscPath(const GameList::Entry* entry)
|
||||
@@ -129,12 +152,7 @@ void GameSummaryWidget::onDiscPathChanged(const QString& value)
|
||||
|
||||
// force rescan of elf to update the serial
|
||||
g_main_window->rescanFile(m_entry_path);
|
||||
|
||||
// and re-fill our details (mainly the serial)
|
||||
auto lock = GameList::GetLock();
|
||||
const GameList::Entry* entry = GameList::GetEntryForPath(m_entry_path.c_str());
|
||||
if (entry)
|
||||
populateDetails(entry);
|
||||
repopulateCurrentDetails();
|
||||
}
|
||||
|
||||
void GameSummaryWidget::onDiscPathBrowseClicked()
|
||||
@@ -352,4 +370,31 @@ void GameSummaryWidget::setVerifyResult(QString error)
|
||||
m_ui.verifyResult->setPlainText(error);
|
||||
m_ui.verifyResult->setVisible(true);
|
||||
m_ui.searchHash->setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
void GameSummaryWidget::repopulateCurrentDetails()
|
||||
{
|
||||
auto lock = GameList::GetLock();
|
||||
const GameList::Entry* entry = GameList::GetEntryForPath(m_entry_path.c_str());
|
||||
if (entry)
|
||||
{
|
||||
populateDetails(entry);
|
||||
m_dialog->setWindowTitle(QString::fromStdString(entry->title));
|
||||
}
|
||||
}
|
||||
|
||||
void GameSummaryWidget::setCustomTitle(const std::string& text)
|
||||
{
|
||||
m_ui.restoreTitle->setEnabled(!text.empty());
|
||||
|
||||
GameList::SaveCustomTitleForPath(m_entry_path, text);
|
||||
repopulateCurrentDetails();
|
||||
}
|
||||
|
||||
void GameSummaryWidget::setCustomRegion(int region)
|
||||
{
|
||||
m_ui.restoreRegion->setEnabled(region >= 0);
|
||||
|
||||
GameList::SaveCustomRegionForPath(m_entry_path, region);
|
||||
repopulateCurrentDetails();
|
||||
}
|
||||
|
||||
@@ -47,6 +47,10 @@ private:
|
||||
void populateDiscPath(const GameList::Entry* entry);
|
||||
void populateTrackList(const GameList::Entry* entry);
|
||||
void setVerifyResult(QString error);
|
||||
void repopulateCurrentDetails();
|
||||
|
||||
void setCustomTitle(const std::string& text);
|
||||
void setCustomRegion(int region);
|
||||
|
||||
Ui::GameSummaryWidget m_ui;
|
||||
SettingsDialog* m_dialog;
|
||||
|
||||
@@ -34,11 +34,25 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="title">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="title">
|
||||
<property name="placeholderText">
|
||||
<string>Clear the line to restore the original title...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="restoreTitle">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Restore</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
@@ -140,170 +154,178 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QComboBox" name="region">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-B (Brazil)</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QComboBox" name="region">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-B (Brazil)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-C (China)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-HK (Hong Kong)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-J (Japan)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-K (Korea)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-T (Taiwan)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-U (US)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Other</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-A (Australia)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-AF (South Africa)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-AU (Austria)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-BE (Belgium)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-E (Europe/Australia)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-F (France)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-FI (Finland)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-G (Germany)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-GR (Greece)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-I (Italy)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-IN (India)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-M (Europe/Australia)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-NL (Netherlands)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-NO (Norway)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-P (Portugal)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-PL (Poland)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-R (Russia)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-S (Spain)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-SC (Scandinavia)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-SW (Sweden)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-SWI (Switzerland)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-UK (United Kingdom)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="restoreRegion">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Restore</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-C (China)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-HK (Hong Kong)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-J (Japan)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-K (Korea)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-T (Taiwan)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">NTSC-U (US)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Other</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-A (Australia)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-AF (South Africa)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-AU (Austria)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-BE (Belgium)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-E (Europe/Australia)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-F (France)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-FI (Finland)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-G (Germany)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-GR (Greece)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-I (Italy)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-IN (India)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-M (Europe/Australia)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-NL (Netherlands)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-NO (Norway)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-P (Portugal)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-PL (Poland)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-R (Russia)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-S (Spain)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-SC (Scandinavia)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-SW (Sweden)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-SWI (Switzerland)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string extracomment="Leave the code as-is, translate the country's name.">PAL-UK (United Kingdom)</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
@@ -488,22 +510,22 @@
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="searchHash">
|
||||
<property name="text">
|
||||
<string>Search on Redump.org...</string>
|
||||
</property>
|
||||
<property name="visible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Search on Redump.org...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -181,7 +181,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// HW Renderer Fixes
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.halfScreenFix, "EmuCore/GS", "UserHacks_Half_Bottom_Override", -1, -1);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cpuSpriteRenderBW, "EmuCore/GS", "UserHacks_CPUSpriteRenderBW", 0);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cpuSpriteRenderLevel, "EmuCore/GS", "UserHacks_CPUSpriteRenderLevel", 0);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.cpuCLUTRender, "EmuCore/GS", "UserHacks_CPUCLUTRender", 0);
|
||||
@@ -515,9 +514,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
|
||||
// Hardware Fixes tab
|
||||
{
|
||||
dialog->registerWidgetHelp(m_ui.halfScreenFix, tr("Half Screen Fix"), tr("Automatic (Default)"),
|
||||
tr("Control the half-screen fix detection on texture shuffling."));
|
||||
|
||||
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(""));
|
||||
@@ -1050,7 +1046,6 @@ void GraphicsSettingsWidget::resetManualHardwareFixes()
|
||||
|
||||
check_bool("EmuCore/GS", "UserHacks", false);
|
||||
|
||||
check_int("EmuCore/GS", "UserHacks_Half_Bottom_Override", -1);
|
||||
check_int("EmuCore/GS", "UserHacks_CPUSpriteRenderBW", 0);
|
||||
check_int("EmuCore/GS", "UserHacks_CPUCLUTRender", 0);
|
||||
check_int("EmuCore/GS", "UserHacks_GPUTargetCLUTMode", 0);
|
||||
|
||||
@@ -780,32 +780,6 @@
|
||||
<string>Hardware Fixes</string>
|
||||
</attribute>
|
||||
<layout class="QFormLayout" name="hardwareFixesLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Half Screen Fix:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="halfScreenFix">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Automatic (Default)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Force Disabled</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Force Enabled</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_36">
|
||||
<property name="text">
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "MainWindow.h"
|
||||
@@ -59,9 +59,10 @@ SettingsDialog::SettingsDialog(QWidget* parent)
|
||||
}
|
||||
|
||||
SettingsDialog::SettingsDialog(QWidget* parent, std::unique_ptr<SettingsInterface> sif, const GameList::Entry* game,
|
||||
std::string serial, u32 disc_crc)
|
||||
std::string serial, u32 disc_crc, QString filename)
|
||||
: QDialog(parent)
|
||||
, m_sif(std::move(sif))
|
||||
, m_filename(std::move(filename))
|
||||
, m_serial(std::move(serial))
|
||||
, m_disc_crc(disc_crc)
|
||||
{
|
||||
@@ -80,9 +81,9 @@ void SettingsDialog::setupUi(const GameList::Entry* game)
|
||||
if (isPerGameSettings())
|
||||
{
|
||||
QString summary = tr("<strong>Summary</strong><hr>This page shows details about the selected game. Changing the Input "
|
||||
"Profile will set the controller binding scheme for this game to whichever profile is chosen, instead "
|
||||
"of the default (Shared) configuration. The track list and dump verification can be used to determine "
|
||||
"if your disc image matches a known good dump. If it does not match, the game may be broken.");
|
||||
"Profile will set the controller binding scheme for this game to whichever profile is chosen, instead "
|
||||
"of the default (Shared) configuration. The track list and dump verification can be used to determine "
|
||||
"if your disc image matches a known good dump. If it does not match, the game may be broken.");
|
||||
if (game)
|
||||
{
|
||||
addWidget(new GameSummaryWidget(game, this, m_ui.settingsContainer), tr("Summary"),
|
||||
@@ -153,7 +154,7 @@ void SettingsDialog::setupUi(const GameList::Entry* game)
|
||||
addWidget(m_memory_card_settings = new MemoryCardSettingsWidget(this, m_ui.settingsContainer), tr("Memory Cards"),
|
||||
QStringLiteral("memcard-line"),
|
||||
tr("<strong>Memory Card Settings</strong><hr>Create and configure Memory Cards here.<br><br>Mouse over an option for "
|
||||
"additional information."));
|
||||
"additional information."));
|
||||
|
||||
addWidget(m_dev9_settings = new DEV9SettingsWidget(this, m_ui.settingsContainer), tr("Network & HDD"), QStringLiteral("global-line"),
|
||||
tr("<strong>Network & HDD Settings</strong><hr>These options control the network connectivity and internal HDD storage of the "
|
||||
@@ -330,6 +331,18 @@ bool SettingsDialog::eventFilter(QObject* object, QEvent* event)
|
||||
return QDialog::eventFilter(object, event);
|
||||
}
|
||||
|
||||
void SettingsDialog::setWindowTitle(const QString& title)
|
||||
{
|
||||
if (m_filename.isEmpty())
|
||||
{
|
||||
QDialog::setWindowTitle(title);
|
||||
}
|
||||
else
|
||||
{
|
||||
QDialog::setWindowTitle(QStringLiteral("%1 [%2]").arg(title, m_filename));
|
||||
}
|
||||
}
|
||||
|
||||
bool SettingsDialog::getEffectiveBoolValue(const char* section, const char* key, bool default_value) const
|
||||
{
|
||||
bool value;
|
||||
@@ -544,16 +557,12 @@ void SettingsDialog::openGamePropertiesDialog(const GameList::Entry* game, const
|
||||
}
|
||||
|
||||
std::string filename(VMManager::GetGameSettingsPath(serial, disc_crc));
|
||||
std::unique_ptr<INISettingsInterface> sif = std::make_unique<INISettingsInterface>(std::move(filename));
|
||||
std::unique_ptr<INISettingsInterface> sif = std::make_unique<INISettingsInterface>(filename);
|
||||
if (FileSystem::FileExists(sif->GetFileName().c_str()))
|
||||
sif->Load();
|
||||
|
||||
const QString window_title(tr("%1 [%2]")
|
||||
.arg(QtUtils::StringViewToQString(title))
|
||||
.arg(QtUtils::StringViewToQString(Path::GetFileName(sif->GetFileName()))));
|
||||
|
||||
SettingsDialog* dialog = new SettingsDialog(g_main_window, std::move(sif), game, std::move(serial), disc_crc);
|
||||
dialog->setWindowTitle(window_title);
|
||||
SettingsDialog* dialog = new SettingsDialog(g_main_window, std::move(sif), game, std::move(serial), disc_crc, QtUtils::StringViewToQString(Path::GetFileName(filename)));
|
||||
dialog->setWindowTitle(QtUtils::StringViewToQString(title));
|
||||
dialog->setModal(false);
|
||||
dialog->show();
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class SettingsDialog final : public QDialog
|
||||
|
||||
public:
|
||||
explicit SettingsDialog(QWidget* parent);
|
||||
SettingsDialog(QWidget* parent, std::unique_ptr<SettingsInterface> sif, const GameList::Entry* game, std::string serial, u32 disc_crc);
|
||||
SettingsDialog(QWidget* parent, std::unique_ptr<SettingsInterface> sif, const GameList::Entry* game, std::string serial, u32 disc_crc, QString filename = QString());
|
||||
~SettingsDialog();
|
||||
|
||||
static void openGamePropertiesDialog(const GameList::Entry* game, const std::string_view& title, std::string serial, u32 disc_crc);
|
||||
@@ -79,6 +79,8 @@ public:
|
||||
void registerWidgetHelp(QObject* object, QString title, QString recommended_value, QString text);
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
|
||||
void setWindowTitle(const QString& title);
|
||||
|
||||
QString getCategory() const;
|
||||
void setCategory(const char* category);
|
||||
|
||||
@@ -145,6 +147,8 @@ private:
|
||||
QObject* m_current_help_widget = nullptr;
|
||||
QMap<QObject*, QString> m_widget_help_text_map;
|
||||
|
||||
QString m_filename;
|
||||
|
||||
std::string m_serial;
|
||||
u32 m_disc_crc;
|
||||
};
|
||||
|
||||
@@ -371,12 +371,15 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
<string>Add...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="folder-add-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@@ -394,6 +397,9 @@
|
||||
<iconset theme="folder-reduce-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -1,68 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 512 512"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:export-filename="bitmap.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
sodipodi:docname="emulation-line-black.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:document-units="px"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.6074562"
|
||||
inkscape:cx="129.05298"
|
||||
inkscape:cy="350.53321"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="956"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="42"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:35;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
|
||||
id="rect1182"
|
||||
width="123.23695"
|
||||
height="369.71085"
|
||||
x="150.59978"
|
||||
y="71.144577" />
|
||||
<rect
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:35;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
|
||||
id="rect1184"
|
||||
width="78.14328"
|
||||
height="307.17816"
|
||||
x="273.83676"
|
||||
y="133.67725" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:25;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
|
||||
d="m 357.42017,445.85541 h 84.05005 l -84.05007,-84.05007"
|
||||
id="path1190" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:25;stroke-linecap:square;stroke-linejoin:miter;stroke-dasharray:none"
|
||||
d="M 142.36951,445.85542 H 67.083026 l 68.461054,-67.55757"
|
||||
id="path1192" />
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="emulation_Pcsx2_b" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 513 512"><defs><style>.cls-1{fill:none;}</style></defs><rect class="cls-1" width="513" height="512"/><path id="Ps2_-_By_Kam" d="M451.22,440.88l-76.33-77.28V116.18h-88.14V53.64H138.51V362.17l-77.72,78.69c-2.92,2.96-3.78,7.35-2.18,11.19,1.6,3.84,5.33,6.32,9.49,6.32h81.81c.07,0,.13-.02,.2-.02h211.39c.19,.01,.39,.03,.59,.03h81.81c4.16,0,7.89-2.48,9.49-6.32,1.6-3.84,.75-8.23-2.17-11.19Zm-199.48-17.52h-78.24V88.64h78.24V423.36Zm88.15,0h-53.14V151.18h53.14V423.36Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 584 B |
@@ -1 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="PCSX2_-_load" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 384"><defs><style>.cls-1{stroke-width:28px;}.cls-1,.cls-2{fill:none;stroke:#000;}.cls-2{stroke-width:29px;}</style></defs><g id="By_maxihplay"><path id="Forma_5" class="cls-1" d="M217.39,192.85c-.45,.33-52.68,43.98-52.68,43.98l52.04,47.7"/><path id="Forma_6" class="cls-1" d="M173.41,238.01l161.32-.73"/><path id="Forma_2" class="cls-1" d="M114.53,59.5l1.11,89.07s-1.77,2.71,5.57,3.34c7.33,.63,122.47,1.11,122.47,1.11,2.21-.93,3.56-3.18,3.34-5.57-.09-4.41,0-89.07,0-89.07"/><path id="Forma_1" class="cls-2" d="M322.31,290.47s-.12,21.8-.1,22.87c.31,13.33-11.31,12.25-11.31,12.25,0,0-244.42-1.35-252.73-1.11s-8.91-10.02-8.91-10.02V68.41s1.5-7.5,8.91-8.91,215.99,0,215.99,0l48.05,46.76,.27,72.95"/></g></svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="PCSX2_-_Save_b" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 384 384" style="enable-background:new 0 0 384 384;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;stroke:#000000;stroke-width:45;}
|
||||
.st1{stroke:#000000;stroke-width:15;}
|
||||
.st2{fill:none;stroke:#000000;stroke-width:30;stroke-linecap:round;stroke-linejoin:round;}
|
||||
.st3{fill:none;stroke:#000000;stroke-width:28;}
|
||||
</style>
|
||||
<g id="By_maxihplay">
|
||||
<g id="seta">
|
||||
<line class="st0" x1="192" y1="331.9" x2="192" y2="243.1"/>
|
||||
<path class="st1" d="M140.5,258l48-48c1.9-1.9,5-1.9,6.9,0l48,48c3.1,3.1,0.9,8.3-3.4,8.3h-96.1C139.6,266.3,137.4,261,140.5,258
|
||||
L140.5,258z"/>
|
||||
</g>
|
||||
<g id="save">
|
||||
<path class="st2" d="M112.5,318.2c-22.9-0.1-46.2-0.1-48.2-0.1c-8.3,0.2-8.9-10-8.9-10v-246c0,0,1.5-7.5,8.9-8.9
|
||||
c7.4-1.4,216,0,216,0l48,46.8l0.3,72.9c-0.1,72.4-0.3,124.1-0.3,133.1c0,1.6,0.1,6.3-3,9.4c-2.7,2.6-8.3,2.9-8.3,2.9l-47.6-0.4"/>
|
||||
<path id="Forma_2" class="st3" d="M120.7,53.4l1.1,89.1c0,0-1.8,2.7,5.6,3.3c7.3,0.6,122.5,1.1,122.5,1.1c2.2-0.9,3.6-3.2,3.3-5.6
|
||||
c-0.1-4.4,0-89.1,0-89.1"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 818 B After Width: | Height: | Size: 1.3 KiB |
@@ -1 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="PCSX2_-_load" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 384"><defs><style>.cls-1{stroke-width:28px;}.cls-1,.cls-2{fill:none;stroke:#000;}.cls-2{stroke-width:29px;}</style></defs><g id="By_maxihplay"><path id="Forma_6" class="cls-1" d="M309.03,236.01l-132.24-.6"/><path id="Forma_5" class="cls-1" d="M261.32,188.75c.45,.33,52.68,43.98,52.68,43.98l-52.04,47.7"/><path id="Forma_2" class="cls-1" d="M114.53,59.5l1.11,89.07s-1.77,2.71,5.57,3.34c7.33,.63,122.47,1.11,122.47,1.11,2.21-.93,3.56-3.18,3.34-5.57-.09-4.41,0-89.07,0-89.07"/><path id="Forma_1" class="cls-2" d="M322.31,290.47s-.12,21.8-.1,22.87c.31,13.33-11.31,12.25-11.31,12.25,0,0-244.42-1.35-252.73-1.11s-8.91-10.02-8.91-10.02V68.41s1.5-7.5,8.91-8.91,215.99,0,215.99,0l48.05,46.76,.27,72.95"/></g></svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="PCSX2_-_load_b" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 384 384" style="enable-background:new 0 0 384 384;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{stroke:#000000;stroke-width:15;}
|
||||
.st1{fill:none;stroke:#000000;stroke-width:45;}
|
||||
.st2{fill:none;stroke:#000000;stroke-width:30;stroke-linecap:round;stroke-linejoin:round;}
|
||||
.st3{fill:none;stroke:#000000;stroke-width:28;}
|
||||
</style>
|
||||
<g id="By_maxihplay">
|
||||
<g id="seta">
|
||||
<path class="st0" d="M242.4,279.2l-48,48c-1.9,1.9-5,1.9-6.9,0l-48-48c-3.1-3.1-0.9-8.3,3.4-8.3H239
|
||||
C243.3,270.9,245.5,276.2,242.4,279.2L242.4,279.2z"/>
|
||||
<line class="st1" x1="190.9" y1="205.3" x2="190.9" y2="294.1"/>
|
||||
</g>
|
||||
<g id="load">
|
||||
<path class="st2" d="M112.5,321.7c-22.9-0.1-46.2-0.1-48.2-0.1c-8.3,0.2-8.9-10-8.9-10V65.5c0,0,1.5-7.5,8.9-8.9
|
||||
c7.4-1.4,216,0,216,0l48,46.8l0.3,72.9c-0.1,72.4-0.3,124.1-0.3,133.1c0,1.6,0.1,6.3-3,9.4c-2.7,2.6-8.3,2.9-8.3,2.9l-47.6-0.4"/>
|
||||
<path id="Forma_2" class="st3" d="M120.7,56.6l1.1,89.1c0,0-1.8,2.7,5.6,3.3c7.3,0.6,122.5,1.1,122.5,1.1c2.2-0.9,3.6-3.2,3.3-5.6
|
||||
c-0.1-4.4,0-89.1,0-89.1"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 818 B After Width: | Height: | Size: 1.3 KiB |
@@ -1 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="guitar_PCSX2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 493.66 474.14"><defs><style>.cls-1,.cls-2{stroke:#000;stroke-miterlimit:10;}.cls-2{stroke-width:20px;}</style></defs><g id="By_maxihplay"><path id="corpo" class="cls-1" d="M188.41,192.55c-.75,14.56,6.75,47.85,20.03,67.76,19.32,28.97,42.12,37.78,54.54,43.83-17.76,8.99-43.47,27.14-53.18,55-1.18,3.26-1.97,6.71-2.27,10.24-3.93,46.09-40.35,63.32-70.26,64.13-14.57,.4-29.14-2.93-42.13-9.52-14.19-7.25-26.02-17.98-35.01-32.02-.41-.58-.81-1.15-1.22-1.73-10.27-14.2-16.49-30.76-17.93-47.84-1.42-16.1,1.39-31.83,7.86-45.71,2.65-5.68,10.86-21.28,28.05-31.02,12.09-6.86,22.94-7.51,28.71-8.28,17.19-2.32,61.18-15.77,82.82-64.84Zm19.53-71.29c-38.91,1.06-45.85,41.06-64.11,71.01-14.61,23.88-38.65,24.92-40.31,24.97h-.1C10.15,223.2-29.95,335.62,26.37,413.63c26.97,41.92,70.63,61.09,111.99,59.96,53.67-1.46,103.78-36.96,109.18-100.8h0c19.93-58.65,105.53-15.58,93.41-112.5-23.14,8.75-40.07,12.44-52.19,12.77-26.98,.73-31.85-18.13-27.99-30.95l-25.37-31.05c-20.51-52.62-2.68-69.18,10.16-86.16-6.4-1.49-32.54-3.87-37.62-3.63h0Z"/><path id="cordas" class="cls-2" d="M103.87,365.16l-2.97-3.25c-3.52-3.71-3.25-9.64,.56-13.17l65.75-61.02c3.8-3.52,9.64-3.25,13.17,.46l2.97,3.25c3.52,3.71,3.25,9.64-.56,13.17l-65.75,61.02c-3.71,3.52-9.64,3.25-13.17-.46h0Z"/><polygon id="braco" class="cls-2" points="239.3 255.5 223.65 236.65 365.87 104.18 382.89 121.88 239.3 255.5"/><path class="cls-1" d="M485.96,27.45l-16.94-18.24c-10.07-10.97-27.21-11.69-38.25-1.56l-68.38,63.39c-10.94,10.08-11.64,27.19-1.51,38.22l16.94,18.24c5.31,5.78,12.58,8.71,19.88,8.71,6.56,0,13.14-2.37,18.37-7.16l68.35-63.35c5.33-4.89,8.42-11.56,8.72-18.79,.29-7.2-2.23-14.09-7.17-19.47Z"/></g></svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="guitar_PCSX2_b" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 494.7 469.9" style="enable-background:new 0 0 494.7 469.9;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;}
|
||||
.st1{stroke:#000000;stroke-width:16;stroke-miterlimit:10;}
|
||||
.st2{stroke:#000000;stroke-width:20;stroke-miterlimit:10;}
|
||||
.st3{stroke:#000000;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<g id="By_Maxihplay">
|
||||
<rect class="st0" width="494.7" height="469.9"/>
|
||||
<path id="cordas" class="st1" d="M134.6,352.6l-2.7-2.5c-3.2-2.9-3.3-7.9-0.4-11l51.8-55.2c3-3.1,7.9-3.3,11.1-0.5l2.7,2.5
|
||||
c3.2,2.9,3.3,7.9,0.4,11l-51.8,55.2C142.8,355.3,137.8,355.4,134.6,352.6L134.6,352.6z"/>
|
||||
<polygon id="braco" class="st2" points="252.2,244.7 232.4,225.7 383.6,69.1 405,86.5 "/>
|
||||
<path id="cabeca" class="st3" d="M488.2,39.1l-72.5,75.3l-50.7-13.3l-11-38.9l80.6-52.8c2.3-1.4,5-1.8,7.7-1.4l41.6,13.3
|
||||
c0.5,0.2,5.2,2,6.8,7C492,32.1,491,36.2,488.2,39.1L488.2,39.1z"/>
|
||||
<g id="corpo">
|
||||
<path d="M202.9,140.8L202.9,140.8 M190.2,169.1l24,70.8c2.6,7.6,7.4,14.1,13.8,18.9l55.7,41c-1.4,0.6-2.8,1.2-4.2,1.8
|
||||
c-11.7,5-25,10.6-38.2,18.9c-0.6,0.4-1.1,0.8-1.7,1.1c-3.7,2.6-15.1,10.7-25,36.4c-6.4,16.5-9.5,26.1-15.1,54.2
|
||||
c-1,5-1.9,8.4-2.7,10.7c-14.2,1.9-30.3,3.7-46.5,3.7c-30.9,0-52.4-6.5-65.9-20l-0.3-0.3c-4.5-4.5-18.2-18.2-28.7-36.1
|
||||
c-10.8-18.4-14.2-34.2-10.3-47.2c1.5-5.1,3.4-10.8,5.5-15.2c4.7-9.8,12.9-17.5,23-21.5c7.1-2.8,15.1-5.4,23.7-8.1
|
||||
c13-4.2,27.7-8.9,42.2-15.6c9.3-4.3,16.5-12.1,20.1-21.8C165.3,226.3,179.7,193.2,190.2,169.1 M193.2,105.1
|
||||
c-10.1,0-20.9,7.4-27.6,22.9c-9.4,21.8-34.2,77.5-42.4,99.3c-21.1,9.8-43.9,14.9-63.8,22.9c-19.2,7.7-34.8,22.2-43.7,40.9
|
||||
c-3.2,6.6-5.5,13.8-7.6,20.6c-17.4,56.8,39,112.6,49,122.5c24.9,24.9,60.3,31.4,93.5,31.4c20.2,0,39.6-2.4,55.3-4.5
|
||||
c13.5-1.8,25.2-6.1,32.1-41.4c5.2-26.5,7.8-33.8,13.2-47.6c3.2-8.2,7.4-15.9,11.1-18.6c21-13.3,44.1-18.7,65.1-32
|
||||
c0.9-0.7,10-8.5,9.1-20.9c-0.8-10.6-8.4-16.7-9.6-17.6l-75.6-55.7l-34.7-102.1C212.2,112,203,105.1,193.2,105.1L193.2,105.1
|
||||
L193.2,105.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.2 KiB |
@@ -1,68 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 512 512"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:export-filename="bitmap.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
sodipodi:docname="emulation-line.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:document-units="px"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.6074562"
|
||||
inkscape:cx="128.66947"
|
||||
inkscape:cy="411.51219"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="956"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="42"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:35;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
|
||||
id="rect1182"
|
||||
width="123.23695"
|
||||
height="369.71085"
|
||||
x="150.59978"
|
||||
y="71.144577" />
|
||||
<rect
|
||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:35;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
|
||||
id="rect1184"
|
||||
width="78.14328"
|
||||
height="307.17816"
|
||||
x="273.83676"
|
||||
y="133.67725" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:25;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none"
|
||||
d="m 357.42017,445.85541 h 84.05005 l -84.05007,-84.05007"
|
||||
id="path1190" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:25;stroke-linecap:square;stroke-linejoin:miter;stroke-dasharray:none"
|
||||
d="M 142.36951,445.85542 H 67.083026 l 68.461054,-67.55757"
|
||||
id="path1192" />
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="emulation_Pcsx2_w" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 513 512"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:none;}</style></defs><rect class="cls-2" width="513" height="512"/><path id="Ps2_-_By_Kam" class="cls-1" d="M451.22,440.88l-76.33-77.28V116.18h-88.14V53.64H138.51V362.17l-77.72,78.69c-2.92,2.96-3.78,7.35-2.18,11.19,1.6,3.84,5.33,6.32,9.49,6.32h81.81c.07,0,.13-.02,.2-.02h211.39c.19,.01,.39,.03,.59,.03h81.81c4.16,0,7.89-2.48,9.49-6.32,1.6-3.84,.75-8.23-2.17-11.19Zm-199.48-17.52h-78.24V88.64h78.24V423.36Zm88.15,0h-53.14V151.18h53.14V423.36Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 616 B |
@@ -1 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="PCSX2_-_Save" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 384"><defs><style>.cls-1{stroke-width:29px;}.cls-1,.cls-2{fill:none;stroke:#fff;}.cls-2{stroke-width:28px;}</style></defs><g id="By_maxihplay"><path id="Forma_5" class="cls-2" d="M217.39,192.85c-.45,.33-52.68,43.98-52.68,43.98l52.04,47.7"/><path id="Forma_6" class="cls-2" d="M173.41,238.01l161.32-.73"/><path id="Forma_2" class="cls-2" d="M114.53,59.5l1.11,89.07s-1.77,2.71,5.57,3.34c7.33,.63,122.47,1.11,122.47,1.11,2.21-.93,3.56-3.18,3.34-5.57-.09-4.41,0-89.07,0-89.07"/><path id="Forma_1" class="cls-1" d="M322.31,290.47s-.12,21.8-.1,22.87c.31,13.33-11.31,12.25-11.31,12.25,0,0-244.42-1.35-252.73-1.11s-8.91-10.02-8.91-10.02V68.41s1.5-7.5,8.91-8.91,215.99,0,215.99,0l48.05,46.76,.27,72.95"/></g></svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="PCSX2_-_Save_w" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 384 384" style="enable-background:new 0 0 384 384;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;stroke:#FFFFFF;stroke-width:45;}
|
||||
.st1{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:15;}
|
||||
.st2{fill:none;stroke:#FFFFFF;stroke-width:30;stroke-linecap:round;stroke-linejoin:round;}
|
||||
.st3{fill:none;stroke:#FFFFFF;stroke-width:28;}
|
||||
</style>
|
||||
<g id="By_maxihplay">
|
||||
<g id="seta">
|
||||
<line class="st0" x1="192" y1="331.9" x2="192" y2="243.1"/>
|
||||
<path class="st1" d="M140.5,258l48-48c1.9-1.9,5-1.9,6.9,0l48,48c3.1,3.1,0.9,8.3-3.4,8.3h-96.1C139.6,266.3,137.4,261,140.5,258
|
||||
L140.5,258z"/>
|
||||
</g>
|
||||
<g id="save">
|
||||
<path class="st2" d="M112.5,318.2c-22.9-0.1-46.2-0.1-48.2-0.1c-8.3,0.2-8.9-10-8.9-10v-246c0,0,1.5-7.5,8.9-8.9
|
||||
c7.4-1.4,216,0,216,0l48,46.8l0.3,72.9c-0.1,72.4-0.3,124.1-0.3,133.1c0,1.6,0.1,6.3-3,9.4c-2.7,2.6-8.3,2.9-8.3,2.9l-47.6-0.4"/>
|
||||
<path id="Forma_2" class="st3" d="M120.7,53.4l1.1,89.1c0,0-1.8,2.7,5.6,3.3c7.3,0.6,122.5,1.1,122.5,1.1c2.2-0.9,3.6-3.2,3.3-5.6
|
||||
c-0.1-4.4,0-89.1,0-89.1"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 818 B After Width: | Height: | Size: 1.3 KiB |
@@ -1 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="PCSX2_-_load" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 384"><defs><style>.cls-1{stroke-width:29px;}.cls-1,.cls-2{fill:none;stroke:#fff;}.cls-2{stroke-width:28px;}</style></defs><g id="By_maxihplay"><path id="Forma_5" class="cls-2" d="M265.05,192.85c.45,.33,52.68,43.98,52.68,43.98l-52.04,47.7"/><path id="Forma_6" class="cls-2" d="M309.03,238.01l-132.24-.6"/><path id="Forma_2" class="cls-2" d="M114.53,59.5l1.11,89.07s-1.77,2.71,5.57,3.34c7.33,.63,122.47,1.11,122.47,1.11,2.21-.93,3.56-3.18,3.34-5.57-.09-4.41,0-89.07,0-89.07"/><path id="Forma_1" class="cls-1" d="M322.31,290.47s-.12,21.8-.1,22.87c.31,13.33-11.31,12.25-11.31,12.25,0,0-244.42-1.35-252.73-1.11s-8.91-10.02-8.91-10.02V68.41s1.5-7.5,8.91-8.91,215.99,0,215.99,0l48.05,46.76,.27,72.95"/></g></svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="PCSX2_-_Load_w" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 384 384" style="enable-background:new 0 0 384 384;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:15;}
|
||||
.st1{fill:none;stroke:#FFFFFF;stroke-width:45;}
|
||||
.st2{fill:none;stroke:#FFFFFF;stroke-width:30;stroke-linecap:round;stroke-linejoin:round;}
|
||||
.st3{fill:none;stroke:#FFFFFF;stroke-width:28;}
|
||||
</style>
|
||||
<g id="By_maxihplay">
|
||||
<g id="seta">
|
||||
<path class="st0" d="M242.4,279.2l-48,48c-1.9,1.9-5,1.9-6.9,0l-48-48c-3.1-3.1-0.9-8.3,3.4-8.3H239
|
||||
C243.3,270.9,245.5,276.2,242.4,279.2L242.4,279.2z"/>
|
||||
<line class="st1" x1="190.9" y1="205.3" x2="190.9" y2="294.1"/>
|
||||
</g>
|
||||
<g id="load">
|
||||
<path class="st2" d="M112.5,321.7c-22.9-0.1-46.2-0.1-48.2-0.1c-8.3,0.2-8.9-10-8.9-10V65.5c0,0,1.5-7.5,8.9-8.9
|
||||
c7.4-1.4,216,0,216,0l48,46.8l0.3,72.9c-0.1,72.4-0.3,124.1-0.3,133.1c0,1.6,0.1,6.3-3,9.4c-2.7,2.6-8.3,2.9-8.3,2.9l-47.6-0.4"/>
|
||||
<path id="Forma_2" class="st3" d="M120.7,56.6l1.1,89.1c0,0-1.8,2.7,5.6,3.3c7.3,0.6,122.5,1.1,122.5,1.1c2.2-0.9,3.6-3.2,3.3-5.6
|
||||
c-0.1-4.4,0-89.1,0-89.1"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 818 B After Width: | Height: | Size: 1.3 KiB |
@@ -1 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg id="guitar_PCSX2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 492.66 473.14"><defs><style>.cls-1,.cls-2{fill:#fff;}.cls-2{stroke:#fff;stroke-miterlimit:10;stroke-width:20px;}</style></defs><g id="By_maxihplay"><path id="corpo" class="cls-1" d="M187.91,192.05c-.75,14.56,6.75,47.85,20.03,67.76,19.32,28.97,42.12,37.78,54.54,43.83-17.76,8.99-43.47,27.14-53.18,55-1.18,3.26-1.97,6.71-2.27,10.24-3.93,46.09-40.35,63.32-70.26,64.13-14.57,.4-29.14-2.93-42.13-9.52-14.19-7.25-26.02-17.98-35.01-32.02-.41-.58-.81-1.15-1.22-1.73-10.27-14.2-16.49-30.76-17.93-47.84-1.42-16.1,1.39-31.83,7.86-45.71,2.65-5.68,10.86-21.28,28.05-31.02,12.09-6.86,22.94-7.51,28.71-8.28,17.19-2.32,61.18-15.77,82.82-64.84Zm19.53-71.29c-38.91,1.06-45.85,41.06-64.11,71.01-14.61,23.88-38.65,24.92-40.31,24.97h-.1C9.65,222.7-30.45,335.12,25.87,413.13c26.97,41.92,70.63,61.09,111.99,59.96,53.67-1.46,103.78-36.96,109.18-100.8h0c19.93-58.65,105.53-15.58,93.41-112.5-23.14,8.75-40.07,12.44-52.19,12.77-26.98,.73-31.85-18.13-27.99-30.95l-25.37-31.05c-20.51-52.62-2.68-69.18,10.16-86.16-6.4-1.49-32.54-3.87-37.62-3.63h0Z"/><path id="cordas" class="cls-2" d="M103.37,364.66l-2.97-3.25c-3.52-3.71-3.25-9.64,.56-13.17l65.75-61.02c3.8-3.52,9.64-3.25,13.17,.46l2.97,3.25c3.52,3.71,3.25,9.64-.56,13.17l-65.75,61.02c-3.71,3.52-9.64,3.25-13.17-.46h0Z"/><polygon id="braco" class="cls-2" points="238.8 255 223.15 236.15 365.37 103.68 382.39 121.38 238.8 255"/><path class="cls-1" d="M485.46,26.95l-16.94-18.24c-10.07-10.97-27.21-11.69-38.25-1.56l-68.38,63.39c-10.94,10.08-11.64,27.19-1.51,38.22l16.94,18.24c5.31,5.78,12.58,8.71,19.88,8.71,6.56,0,13.14-2.37,18.37-7.16l68.35-63.35c5.33-4.89,8.42-11.56,8.72-18.79,.29-7.2-2.23-14.09-7.17-19.47Z"/></g></svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="guitar_PCSX2_w" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 494.7 469.9" style="enable-background:new 0 0 494.7 469.9;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;}
|
||||
.st1{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:16;stroke-miterlimit:10;}
|
||||
.st2{fill:#FFFFFF;stroke:#FFFFFF;stroke-width:20;stroke-miterlimit:10;}
|
||||
.st3{fill:#FFFFFF;stroke:#FFFFFF;stroke-miterlimit:10;}
|
||||
.st4{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g id="By_Maxihplay">
|
||||
<rect class="st0" width="494.7" height="469.9"/>
|
||||
<path id="cordas" class="st1" d="M134.6,352.6l-2.7-2.5c-3.2-2.9-3.3-7.9-0.4-11l51.8-55.2c3-3.1,7.9-3.3,11.1-0.5l2.7,2.5
|
||||
c3.2,2.9,3.3,7.9,0.4,11l-51.8,55.2C142.8,355.3,137.8,355.4,134.6,352.6L134.6,352.6z"/>
|
||||
<polygon id="braco" class="st2" points="252.2,244.7 232.4,225.7 383.6,69.1 405,86.5 "/>
|
||||
<path id="cabeca" class="st3" d="M488.2,39.1l-72.5,75.3l-50.7-13.3l-11-38.9l80.6-52.8c2.3-1.4,5-1.8,7.7-1.4l41.6,13.3
|
||||
c0.5,0.2,5.2,2,6.8,7C492,32.1,491,36.2,488.2,39.1L488.2,39.1z"/>
|
||||
<g id="corpo">
|
||||
<path class="st4" d="M202.9,140.8L202.9,140.8 M190.2,169.1l24,70.8c2.6,7.6,7.4,14.1,13.8,18.9l55.7,41c-1.4,0.6-2.8,1.2-4.2,1.8
|
||||
c-11.7,5-25,10.6-38.2,18.9c-0.6,0.4-1.1,0.8-1.7,1.1c-3.7,2.6-15.1,10.7-25,36.4c-6.4,16.5-9.5,26.1-15.1,54.2
|
||||
c-1,5-1.9,8.4-2.7,10.7c-14.2,1.9-30.3,3.7-46.5,3.7c-30.9,0-52.4-6.5-65.9-20l-0.3-0.3c-4.5-4.5-18.2-18.2-28.7-36.1
|
||||
c-10.8-18.4-14.2-34.2-10.3-47.2c1.5-5.1,3.4-10.8,5.5-15.2c4.7-9.8,12.9-17.5,23-21.5c7.1-2.8,15.1-5.4,23.7-8.1
|
||||
c13-4.2,27.7-8.9,42.2-15.6c9.3-4.3,16.5-12.1,20.1-21.8C165.3,226.3,179.7,193.2,190.2,169.1 M193.2,105.1
|
||||
c-10.1,0-20.9,7.4-27.6,22.9c-9.4,21.8-34.2,77.5-42.4,99.3c-21.1,9.8-43.9,14.9-63.8,22.9c-19.2,7.7-34.8,22.2-43.7,40.9
|
||||
c-3.2,6.6-5.5,13.8-7.6,20.6c-17.4,56.8,39,112.6,49,122.5c24.9,24.9,60.3,31.4,93.5,31.4c20.2,0,39.6-2.4,55.3-4.5
|
||||
c13.5-1.8,25.2-6.1,32.1-41.4c5.2-26.5,7.8-33.8,13.2-47.6c3.2-8.2,7.4-15.9,11.1-18.6c21-13.3,44.1-18.7,65.1-32
|
||||
c0.9-0.7,10-8.5,9.1-20.9c-0.8-10.6-8.4-16.7-9.6-17.6l-75.6-55.7l-34.7-102.1C212.2,112,203,105.1,193.2,105.1L193.2,105.1
|
||||
L193.2,105.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.2 KiB |
@@ -132,6 +132,8 @@ namespace Achievements
|
||||
static void UnlockAchievementCallback(s32 status_code, const std::string& content_type, Common::HTTPDownloader::Request::Data data);
|
||||
static void SubmitLeaderboardCallback(s32 status_code, const std::string& content_type, Common::HTTPDownloader::Request::Data data, u32 lboard_id);
|
||||
|
||||
static s32 GetNotificationsDuration();
|
||||
|
||||
static bool s_active = false;
|
||||
static bool s_logged_in = false;
|
||||
static bool s_challenge_mode = false;
|
||||
@@ -1117,9 +1119,9 @@ void Achievements::DisplayAchievementSummary()
|
||||
summary.append(TRANSLATE_SV("Achievements", "Leaderboard submission is enabled."));
|
||||
}
|
||||
|
||||
MTGS::RunOnGSThread([title = std::move(title), summary = std::move(summary), icon = s_game_icon]() {
|
||||
MTGS::RunOnGSThread([duration = GetNotificationsDuration(), title = std::move(title), summary = std::move(summary), icon = s_game_icon]() {
|
||||
if (FullscreenUI::IsInitialized())
|
||||
ImGuiFullscreen::AddNotification(10.0f, std::move(title), std::move(summary), std::move(icon));
|
||||
ImGuiFullscreen::AddNotification(duration, std::move(title), std::move(summary), std::move(icon));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1137,9 +1139,9 @@ void Achievements::DisplayMasteredNotification()
|
||||
std::string message(fmt::format(
|
||||
"{} achievements, {} points{}", GetAchievementCount(), GetCurrentPointsForGame(), s_challenge_mode ? " (Hardcore Mode)" : ""));
|
||||
|
||||
MTGS::RunOnGSThread([title = std::move(title), message = std::move(message), icon = s_game_icon]() {
|
||||
MTGS::RunOnGSThread([duration = GetNotificationsDuration(), title = std::move(title), message = std::move(message), icon = s_game_icon]() {
|
||||
if (FullscreenUI::IsInitialized())
|
||||
ImGuiFullscreen::AddNotification(20.0f, std::move(title), std::move(message), std::move(icon));
|
||||
ImGuiFullscreen::AddNotification(duration, std::move(title), std::move(message), std::move(icon));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1850,9 +1852,9 @@ void Achievements::SubmitLeaderboardCallback(s32 status_code, const std::string&
|
||||
std::string summary = fmt::format(
|
||||
"Your Score: {} (Best: {})\nLeaderboard Position: {} of {}", submitted_score, best_score, response.new_rank, response.num_entries);
|
||||
|
||||
MTGS::RunOnGSThread([title = lb->title, summary = std::move(summary), icon = s_game_icon]() {
|
||||
MTGS::RunOnGSThread([duration = GetNotificationsDuration(), title = lb->title, summary = std::move(summary), icon = s_game_icon]() {
|
||||
if (FullscreenUI::IsInitialized())
|
||||
ImGuiFullscreen::AddNotification(10.0f, std::move(title), std::move(summary), std::move(icon));
|
||||
ImGuiFullscreen::AddNotification(duration, std::move(title), std::move(summary), std::move(icon));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1894,8 +1896,8 @@ void Achievements::UnlockAchievement(u32 achievement_id, bool add_notification /
|
||||
}
|
||||
|
||||
MTGS::RunOnGSThread(
|
||||
[title = std::move(title), description = achievement->description, icon = GetAchievementBadgePath(*achievement)]() {
|
||||
ImGuiFullscreen::AddNotification(15.0f, std::move(title), std::move(description), std::move(icon));
|
||||
[duration = GetNotificationsDuration(), title = std::move(title), description = achievement->description, icon = GetAchievementBadgePath(*achievement)]() {
|
||||
ImGuiFullscreen::AddNotification(duration, std::move(title), std::move(description), std::move(icon));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2153,6 +2155,12 @@ void Achievements::PokeMemory(unsigned address, unsigned num_bytes, void* ud, un
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
s32 Achievements::GetNotificationsDuration()
|
||||
{
|
||||
return EmuConfig.Achievements.NotificationsDuration;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_RAINTEGRATION
|
||||
|
||||
#include "RA_Consoles.h"
|
||||
|
||||
@@ -765,7 +765,6 @@ struct Pcsx2Config
|
||||
int SkipDrawEnd = 0;
|
||||
|
||||
GSHWAutoFlushLevel UserHacks_AutoFlush = GSHWAutoFlushLevel::Disabled;
|
||||
s8 UserHacks_HalfBottomOverride = -1;
|
||||
s8 UserHacks_HalfPixelOffset = 0;
|
||||
s8 UserHacks_RoundSprite = 0;
|
||||
s32 UserHacks_TCOffsetX = 0;
|
||||
@@ -1262,12 +1261,14 @@ struct Pcsx2Config
|
||||
PrimedIndicators : 1;
|
||||
BITFIELD_END
|
||||
|
||||
s32 NotificationsDuration = 5;
|
||||
|
||||
AchievementsOptions();
|
||||
void LoadSave(SettingsWrapper& wrap);
|
||||
|
||||
bool operator==(const AchievementsOptions& right) const
|
||||
{
|
||||
return OpEqu(bitset);
|
||||
return OpEqu(bitset) && OpEqu(NotificationsDuration);
|
||||
}
|
||||
|
||||
bool operator!=(const AchievementsOptions& right) const
|
||||
|
||||
@@ -286,5 +286,5 @@ private:
|
||||
static void WriteUInt16(u8* data, int* index, u16 value);
|
||||
static void WriteUInt32(u8* data, int* index, u32 value);
|
||||
static void WriteUInt64(u8* data, int* index, u64 value);
|
||||
static void WritePaddedString(u8* data, int* index, std::string value, u32 len);
|
||||
static void WritePaddedString(u8* data, int* index, const std::string& value, u32 len);
|
||||
};
|
||||
|
||||
@@ -37,7 +37,7 @@ void ATA::WriteUInt64(u8* data, int* index, u64 value)
|
||||
}
|
||||
|
||||
//No null char
|
||||
void ATA::WritePaddedString(u8* data, int* index, std::string value, u32 len)
|
||||
void ATA::WritePaddedString(u8* data, int* index, const std::string& value, u32 len)
|
||||
{
|
||||
memset(&data[*index], (u8)' ', len);
|
||||
memcpy(&data[*index], value.c_str(), value.length() < len ? value.length() : len);
|
||||
|
||||
@@ -39,7 +39,7 @@ void HddCreate::Start()
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
void HddCreate::WriteImage(std::string hddPath, u64 fileBytes, u64 zeroSizeBytes)
|
||||
void HddCreate::WriteImage(const std::string& hddPath, u64 fileBytes, u64 zeroSizeBytes)
|
||||
{
|
||||
constexpr int buffsize = 4 * 1024;
|
||||
const u8 buff[buffsize] = {0}; // 4kb.
|
||||
|
||||
@@ -49,5 +49,5 @@ protected:
|
||||
void SetCanceled();
|
||||
|
||||
private:
|
||||
void WriteImage(std::string hddPath, u64 fileBytes, u64 zeroSizeBytes);
|
||||
void WriteImage(const std::string& hddPath, u64 fileBytes, u64 zeroSizeBytes);
|
||||
};
|
||||
|
||||
@@ -277,7 +277,7 @@ namespace InternalServers
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void DNS_Server::GetHost(std::string url, DNS_State* state)
|
||||
void DNS_Server::GetHost(const std::string& url, DNS_State* state)
|
||||
{
|
||||
//Need to convert to UTF16
|
||||
const int size = MultiByteToWideChar(CP_UTF8, 0, url.c_str(), -1, nullptr, 0);
|
||||
@@ -347,7 +347,7 @@ namespace InternalServers
|
||||
delete data;
|
||||
}
|
||||
#elif defined(__POSIX__)
|
||||
void DNS_Server::GetHost(std::string url, DNS_State* state)
|
||||
void DNS_Server::GetHost(const std::string& url, DNS_State* state)
|
||||
{
|
||||
//Need to spin up thread, pass the parms to it
|
||||
|
||||
@@ -357,7 +357,7 @@ namespace InternalServers
|
||||
GetHostThread.detach();
|
||||
}
|
||||
|
||||
void DNS_Server::GetAddrInfoThread(std::string url, DNS_State* state)
|
||||
void DNS_Server::GetAddrInfoThread(const std::string& url, DNS_State* state)
|
||||
{
|
||||
addrinfo hints{0};
|
||||
hints.ai_family = AF_INET;
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace InternalServers
|
||||
private:
|
||||
void LoadHostList();
|
||||
bool CheckHostList(std::string url, DNS_State* state);
|
||||
void GetHost(std::string url, DNS_State* state);
|
||||
void GetHost(const std::string& url, DNS_State* state);
|
||||
void FinaliseDNS(DNS_State* state);
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -111,7 +111,7 @@ namespace InternalServers
|
||||
|
||||
static void __stdcall GetAddrInfoExCallback(DWORD dwError, DWORD dwBytes, OVERLAPPED* lpOverlapped);
|
||||
#elif defined(__POSIX__)
|
||||
void GetAddrInfoThread(std::string url, DNS_State* state);
|
||||
void GetAddrInfoThread(const std::string& url, DNS_State* state);
|
||||
#endif
|
||||
};
|
||||
} // namespace InternalServers
|
||||
|
||||
@@ -1088,7 +1088,7 @@ void MTSAH( std::string& output ) { _sap("mtsah\t%s, 0x%04X") GPR_REG[DECODE_R
|
||||
|
||||
|
||||
//***************************SPECIAL 2 CPU OPCODES*******************
|
||||
const char* pmfhl_sub[] = { "lw", "uw", "slw", "lh", "sh" };
|
||||
const char* pmfhl_sub[] = {"lw", "uw", "slw", "lh", "sh", "??", "??"};
|
||||
|
||||
void MADD( std::string& output ) { _sap("madd\t%s, %s %s") GPR_REG[DECODE_RD],GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); }
|
||||
void MADDU( std::string& output ) { _sap("maddu\t%s, %s %s") GPR_REG[DECODE_RD],GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]);}
|
||||
@@ -1104,8 +1104,8 @@ void MULTU1( std::string& output ) { _sap("multu1\t%s, %s, %s") GPR_REG[
|
||||
void DIV1( std::string& output ) { _sap("div1\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); }
|
||||
void DIVU1( std::string& output ) { _sap("divu1\t%s, %s") GPR_REG[DECODE_RS], GPR_REG[DECODE_RT]); }
|
||||
//that have parametres that i haven't figure out how to display...
|
||||
void PMFHL( std::string& output ) { _sap("pmfhl.%s \t%s") pmfhl_sub[DECODE_SA], GPR_REG[DECODE_RD]); }
|
||||
void PMTHL( std::string& output ) { _sap("pmthl.%s \t%s") pmfhl_sub[DECODE_SA], GPR_REG[DECODE_RS]); }
|
||||
void PMFHL( std::string& output ) { _sap("pmfhl.%s \t%s") pmfhl_sub[DECODE_SA & 0x7], GPR_REG[DECODE_RD]); }
|
||||
void PMTHL( std::string& output ) { _sap("pmthl.%s \t%s") pmfhl_sub[DECODE_SA & 0x7], GPR_REG[DECODE_RS]); }
|
||||
void PSLLH( std::string& output ) { _sap("psllh \t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA); }
|
||||
void PSRLH( std::string& output ) { _sap("psrlh \t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA);}
|
||||
void PSRAH( std::string& output ) { _sap("psrah \t%s, %s, 0x%02X") GPR_REG[DECODE_RD], GPR_REG[DECODE_RT], DECODE_SA);}
|
||||
|
||||
@@ -502,6 +502,63 @@ GSVector4i GSLocalMemory::GetRectForPageOffset(u32 base_bp, u32 offset_bp, u32 b
|
||||
return GSVector4i(pgs * page_offset_xy).xyxy() + GSVector4i::loadh(pgs);
|
||||
}
|
||||
|
||||
bool GSLocalMemory::HasOverlap(const u32 src_bp, const u32 src_bw, const u32 src_psm, const GSVector4i src_rect
|
||||
, const u32 dst_bp, const u32 dst_bw, const u32 dst_psm, const GSVector4i dst_rect)
|
||||
{
|
||||
const u32 src_start_bp = GSLocalMemory::GetStartBlockAddress(src_bp, src_bw, src_psm, src_rect) & ~(BLOCKS_PER_PAGE - 1);
|
||||
const u32 dst_start_bp = GSLocalMemory::GetStartBlockAddress(dst_bp, dst_bw, dst_psm, dst_rect) & ~(BLOCKS_PER_PAGE - 1);
|
||||
|
||||
u32 src_end_bp = ((GSLocalMemory::GetEndBlockAddress(src_bp, src_bw, src_psm, src_rect) + 1) + (BLOCKS_PER_PAGE - 1)) & ~(BLOCKS_PER_PAGE - 1);
|
||||
u32 dst_end_bp = ((GSLocalMemory::GetEndBlockAddress(dst_bp, dst_bw, dst_psm, dst_rect) + 1) + (BLOCKS_PER_PAGE - 1)) & ~(BLOCKS_PER_PAGE - 1);
|
||||
|
||||
if (src_start_bp == src_end_bp)
|
||||
{
|
||||
src_end_bp = (src_end_bp + BLOCKS_PER_PAGE) & ~(MAX_BLOCKS - 1);
|
||||
}
|
||||
|
||||
if (dst_start_bp == dst_end_bp)
|
||||
{
|
||||
dst_end_bp = (dst_end_bp + BLOCKS_PER_PAGE) & ~(MAX_BLOCKS - 1);
|
||||
}
|
||||
|
||||
// Source has wrapped, 2 separate checks.
|
||||
if (src_end_bp <= src_start_bp)
|
||||
{
|
||||
// Destination has also wrapped, so they *have* to overlap.
|
||||
if (dst_end_bp <= dst_start_bp)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dst_end_bp > src_start_bp)
|
||||
return true;
|
||||
|
||||
if (dst_start_bp < src_end_bp)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else // No wrapping on source.
|
||||
{
|
||||
// Destination wraps.
|
||||
if (dst_end_bp <= dst_start_bp)
|
||||
{
|
||||
if (src_end_bp > dst_start_bp)
|
||||
return true;
|
||||
|
||||
if (src_start_bp < dst_end_bp)
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dst_start_bp < src_end_bp && dst_end_bp > src_start_bp)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
///////////////////
|
||||
|
||||
void GSLocalMemory::ReadTexture(const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA)
|
||||
|
||||
@@ -546,6 +546,7 @@ public:
|
||||
GSPixelOffset* GetPixelOffset(const GIFRegFRAME& FRAME, const GIFRegZBUF& ZBUF);
|
||||
GSPixelOffset4* GetPixelOffset4(const GIFRegFRAME& FRAME, const GIFRegZBUF& ZBUF);
|
||||
std::vector<GSVector2i>* GetPage2TileMap(const GIFRegTEX0& TEX0);
|
||||
static bool HasOverlap(u32 src_bp, u32 src_bw, u32 src_psm, GSVector4i src_rect, u32 dst_bp, u32 dst_bw, u32 dst_psm, GSVector4i dst_rect);
|
||||
static bool IsPageAligned(u32 psm, const GSVector4i& rc);
|
||||
static u32 GetStartBlockAddress(u32 bp, u32 bw, u32 psm, GSVector4i rect);
|
||||
static u32 GetEndBlockAddress(u32 bp, u32 bw, u32 psm, GSVector4i rect);
|
||||
|
||||
@@ -1416,7 +1416,7 @@ void GSState::GIFRegHandlerTRXDIR(const GIFReg* RESTRICT r)
|
||||
{
|
||||
GL_REG("TRXDIR = 0x%x_%x", r->U32[1], r->U32[0]);
|
||||
|
||||
Flush(GSFlushReason::GSTRANSFER);
|
||||
FlushWrite();
|
||||
|
||||
m_env.TRXDIR = r->TRXDIR;
|
||||
|
||||
@@ -1429,6 +1429,7 @@ void GSState::GIFRegHandlerTRXDIR(const GIFReg* RESTRICT r)
|
||||
m_tr.Init(m_env.TRXPOS.SSAX, m_env.TRXPOS.SSAY, m_env.BITBLTBUF, false);
|
||||
break;
|
||||
case 2: // local -> local
|
||||
CheckWriteOverlap(true, true);
|
||||
Move();
|
||||
break;
|
||||
default: // 3 deactivated as stated by manual. Tested on hardware and no transfers happen.
|
||||
@@ -1476,6 +1477,7 @@ void GSState::Flush(GSFlushReason reason)
|
||||
}
|
||||
|
||||
m_dirty_gs_regs = 0;
|
||||
temp_draw_rect = GSVector4i::zero();
|
||||
}
|
||||
|
||||
m_state_flush_reason = GSFlushReason::UNKNOWN;
|
||||
@@ -1508,6 +1510,8 @@ void GSState::FlushWrite()
|
||||
|
||||
g_perfmon.Put(GSPerfMon::Swizzle, len);
|
||||
s_transfer_n++;
|
||||
if (m_tr.start >= m_tr.total)
|
||||
m_env.TRXDIR.XDIR = 3;
|
||||
}
|
||||
|
||||
// This function decides if the context has changed in a way which warrants flushing the draw.
|
||||
@@ -1687,59 +1691,148 @@ void GSState::FlushPrim()
|
||||
}
|
||||
}
|
||||
}
|
||||
GSVector4i GSState::GetTEX0Rect()
|
||||
{
|
||||
GSVector4i ret = GSVector4i::zero();
|
||||
const GSDrawingContext& prev_ctx = m_prev_env.CTXT[m_prev_env.PRIM.CTXT];
|
||||
|
||||
if (prev_ctx.CLAMP.WMS <= 1) // CLAMP/REPEAT
|
||||
{
|
||||
ret.x = 0;
|
||||
ret.z = 1 << prev_ctx.TEX0.TW;
|
||||
}
|
||||
else if (prev_ctx.CLAMP.WMS == 3) // REGION_REPEAT
|
||||
{
|
||||
ret.x = prev_ctx.CLAMP.MAXU;
|
||||
ret.z = prev_ctx.CLAMP.MAXU | prev_ctx.CLAMP.MINU;
|
||||
}
|
||||
else // 2 REGION_CLAMP
|
||||
{
|
||||
ret.x = prev_ctx.CLAMP.MINU;
|
||||
ret.z = prev_ctx.CLAMP.MAXU;
|
||||
}
|
||||
|
||||
if (prev_ctx.CLAMP.WMT <= 1) // CLAMP/REPEAT
|
||||
{
|
||||
ret.y = 0;
|
||||
ret.w = 1 << prev_ctx.TEX0.TH;
|
||||
}
|
||||
else if (prev_ctx.CLAMP.WMT == 3) // REGION_REPEAT
|
||||
{
|
||||
ret.y = prev_ctx.CLAMP.MAXV;
|
||||
ret.w = prev_ctx.CLAMP.MAXV | prev_ctx.CLAMP.MINV;
|
||||
}
|
||||
else // 2 REGION_CLAMP
|
||||
{
|
||||
ret.y = prev_ctx.CLAMP.MINV;
|
||||
ret.w = prev_ctx.CLAMP.MAXV;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GSState::CheckWriteOverlap(bool req_write, bool req_read)
|
||||
{
|
||||
const int w = m_env.TRXREG.RRW;
|
||||
const int h = m_env.TRXREG.RRH;
|
||||
const GIFRegBITBLTBUF& blit = m_env.BITBLTBUF;
|
||||
|
||||
const GSDrawingContext& prev_ctx = m_prev_env.CTXT[m_prev_env.PRIM.CTXT];
|
||||
const GSVector4i write_rect = GSVector4i(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, m_env.TRXPOS.DSAX + w, m_env.TRXPOS.DSAY + h);
|
||||
const u32 write_start_bp = GSLocalMemory::GetStartBlockAddress(blit.DBP, blit.DBW, blit.DPSM, write_rect);
|
||||
const u32 write_end_bp = ((GSLocalMemory::GetEndBlockAddress(blit.DBP, blit.DBW, blit.DPSM, write_rect) + 1) + (BLOCKS_PER_PAGE - 1)) & ~(BLOCKS_PER_PAGE - 1);
|
||||
const GSVector4i tex_rect = m_prev_env.PRIM.TME ? GetTEX0Rect() : GSVector4i::zero();
|
||||
|
||||
if (m_index.tail > 0)
|
||||
{
|
||||
// Only flush on a NEW transfer if a pending one is using the same address or overlap.
|
||||
// Check Fast & Furious (Hardare mode) and Assault Suits Valken (either renderer) and Tomb Raider - Angel of Darkness menu (TBP != DBP but overlaps).
|
||||
// Cartoon Network overwrites its own Z buffer in the middle of a draw.
|
||||
// Alias wraps its transfers, so be careful
|
||||
const GSVector4i read_rect = GSVector4i(m_env.TRXPOS.SSAX, m_env.TRXPOS.SSAY, m_env.TRXPOS.SSAX + w, m_env.TRXPOS.SSAY + h);
|
||||
|
||||
if (req_write && m_prev_env.PRIM.TME)
|
||||
{
|
||||
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.TEX0.TBP0, prev_ctx.TEX0.TBW, prev_ctx.TEX0.PSM, tex_rect))
|
||||
{
|
||||
|
||||
Flush(GSFlushReason::UPLOADDIRTYTEX);
|
||||
}
|
||||
if (prev_ctx.TEX1.MXL > 0 && prev_ctx.TEX1.MMIN >= 2 && prev_ctx.TEX1.MMIN <= 5)
|
||||
{
|
||||
switch (prev_ctx.TEX1.MXL)
|
||||
{
|
||||
case 6:
|
||||
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.MIPTBP2.TBP6, prev_ctx.MIPTBP2.TBW6, prev_ctx.TEX0.PSM, GSVector4i(tex_rect.x >> 6, tex_rect.y >> 6, tex_rect.z >> 6, tex_rect.w >> 6)))
|
||||
Flush(GSFlushReason::UPLOADDIRTYTEX);
|
||||
[[fallthrough]];
|
||||
case 5:
|
||||
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.MIPTBP2.TBP5, prev_ctx.MIPTBP2.TBW5, prev_ctx.TEX0.PSM, GSVector4i(tex_rect.x >> 5, tex_rect.y >> 5, tex_rect.z >> 5, tex_rect.w >> 5)))
|
||||
Flush(GSFlushReason::UPLOADDIRTYTEX);
|
||||
[[fallthrough]];
|
||||
case 4:
|
||||
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.MIPTBP2.TBP4, prev_ctx.MIPTBP2.TBW4, prev_ctx.TEX0.PSM, GSVector4i(tex_rect.x >> 4, tex_rect.y >> 4, tex_rect.z >> 4, tex_rect.w >> 4)))
|
||||
Flush(GSFlushReason::UPLOADDIRTYTEX);
|
||||
[[fallthrough]];
|
||||
case 3:
|
||||
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.MIPTBP1.TBP3, prev_ctx.MIPTBP1.TBW3, prev_ctx.TEX0.PSM, GSVector4i(tex_rect.x >> 3, tex_rect.y >> 3, tex_rect.z >> 3, tex_rect.w >> 3)))
|
||||
Flush(GSFlushReason::UPLOADDIRTYTEX);
|
||||
[[fallthrough]];
|
||||
case 2:
|
||||
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.MIPTBP1.TBP2, prev_ctx.MIPTBP1.TBW2, prev_ctx.TEX0.PSM, GSVector4i(tex_rect.x >> 2, tex_rect.y >> 2, tex_rect.z >> 2, tex_rect.w >> 2)))
|
||||
Flush(GSFlushReason::UPLOADDIRTYTEX);
|
||||
[[fallthrough]];
|
||||
case 1:
|
||||
if (GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.MIPTBP1.TBP1, prev_ctx.MIPTBP1.TBW1, prev_ctx.TEX0.PSM, GSVector4i(tex_rect.x >> 1, tex_rect.y >> 1, tex_rect.z >> 1, tex_rect.w >> 1)))
|
||||
Flush(GSFlushReason::UPLOADDIRTYTEX);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const u32 frame_mask = GSLocalMemory::m_psm[prev_ctx.FRAME.PSM].fmsk;
|
||||
const bool frame_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && (prev_ctx.TEST.AFAIL == 2 || prev_ctx.TEST.AFAIL == 0)) && ((prev_ctx.FRAME.FBMSK & frame_mask) != frame_mask)) || prev_ctx.TEST.DATE;
|
||||
if (frame_required)
|
||||
{
|
||||
if ((req_write && GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.FRAME.Block(), prev_ctx.FRAME.FBW, prev_ctx.FRAME.PSM, temp_draw_rect)) ||
|
||||
(req_read && GSLocalMemory::HasOverlap(blit.SBP, blit.SBW, blit.SPSM, read_rect, prev_ctx.FRAME.Block(), prev_ctx.FRAME.FBW, prev_ctx.FRAME.PSM, temp_draw_rect)))
|
||||
Flush(GSFlushReason::UPLOADDIRTYFRAME);
|
||||
}
|
||||
|
||||
const bool zbuf_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && prev_ctx.TEST.AFAIL != 2) && !prev_ctx.ZBUF.ZMSK) || (prev_ctx.TEST.ZTE && prev_ctx.TEST.ZTST > ZTST_ALWAYS);
|
||||
if (zbuf_required)
|
||||
{
|
||||
if ((req_write && GSLocalMemory::HasOverlap(blit.DBP, blit.DBW, blit.DPSM, write_rect, prev_ctx.ZBUF.Block(), prev_ctx.FRAME.FBW, prev_ctx.ZBUF.PSM, temp_draw_rect)) ||
|
||||
(req_read && GSLocalMemory::HasOverlap(blit.SBP, blit.SBW, blit.SPSM, read_rect, prev_ctx.ZBUF.Block(), prev_ctx.FRAME.FBW, prev_ctx.ZBUF.PSM, temp_draw_rect)))
|
||||
Flush(GSFlushReason::UPLOADDIRTYZBUF);
|
||||
}
|
||||
}
|
||||
|
||||
// Invalid the CLUT if it crosses paths.
|
||||
m_mem.m_clut.InvalidateRange(write_start_bp, write_end_bp);
|
||||
}
|
||||
|
||||
void GSState::Write(const u8* mem, int len)
|
||||
{
|
||||
if (m_env.TRXDIR.XDIR == 3)
|
||||
return;
|
||||
|
||||
const int w = m_env.TRXREG.RRW;
|
||||
int h = m_env.TRXREG.RRH;
|
||||
const int h = m_env.TRXREG.RRH;
|
||||
|
||||
CheckWriteOverlap(true, false);
|
||||
|
||||
if (!m_tr.Update(w, h, GSLocalMemory::m_psm[m_env.BITBLTBUF.DPSM].trbpp, len))
|
||||
{
|
||||
m_env.TRXDIR.XDIR = 3;
|
||||
return;
|
||||
}
|
||||
|
||||
GIFRegBITBLTBUF& blit = m_tr.m_blit;
|
||||
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[blit.DPSM];
|
||||
|
||||
if (!m_tr.Update(w, h, psm.trbpp, len))
|
||||
return;
|
||||
|
||||
if (m_tr.end == 0)
|
||||
{
|
||||
const GSDrawingContext& prev_ctx = m_prev_env.CTXT[m_prev_env.PRIM.CTXT];
|
||||
const u32 write_start_bp = m_mem.m_psm[blit.DPSM].info.bn(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, blit.DBP, blit.DBW);
|
||||
const u32 write_end_bp = m_mem.m_psm[blit.DPSM].info.bn(m_env.TRXPOS.DSAX + w - 1, m_env.TRXPOS.DSAY + h - 1, blit.DBP, blit.DBW);
|
||||
|
||||
// Only flush on a NEW transfer if a pending one is using the same address or overlap.
|
||||
// Check Fast & Furious (Hardare mode) and Assault Suits Valken (either renderer) and Tomb Raider - Angel of Darkness menu (TBP != DBP but overlaps).
|
||||
if (m_index.tail > 0)
|
||||
{
|
||||
const u32 tex_end_bp = m_mem.m_psm[prev_ctx.TEX0.PSM].info.bn((1 << prev_ctx.TEX0.TW) - 1, (1 << prev_ctx.TEX0.TH) - 1, prev_ctx.TEX0.TBP0, prev_ctx.TEX0.TBW);
|
||||
|
||||
if (m_prev_env.PRIM.TME && write_end_bp > prev_ctx.TEX0.TBP0 && write_start_bp <= tex_end_bp)
|
||||
Flush(GSFlushReason::UPLOADDIRTYTEX);
|
||||
else
|
||||
{
|
||||
const u32 frame_mask = GSLocalMemory::m_psm[prev_ctx.FRAME.PSM].fmsk;
|
||||
const bool frame_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && prev_ctx.TEST.AFAIL == 2) && ((prev_ctx.FRAME.FBMSK & frame_mask) != frame_mask)) || (prev_ctx.TEST.ATE && prev_ctx.TEST.ATST > ATST_ALWAYS);
|
||||
if (frame_required)
|
||||
{
|
||||
const u32 draw_start_bp = m_mem.m_psm[prev_ctx.FRAME.PSM].info.bn(temp_draw_rect.x, temp_draw_rect.y, prev_ctx.FRAME.Block(), prev_ctx.FRAME.FBW);
|
||||
const u32 draw_end_bp = m_mem.m_psm[prev_ctx.FRAME.PSM].info.bn(temp_draw_rect.z - 1, temp_draw_rect.w - 1, prev_ctx.FRAME.Block(), prev_ctx.FRAME.FBW);
|
||||
|
||||
if (write_end_bp > draw_start_bp && write_start_bp <= draw_end_bp)
|
||||
Flush(GSFlushReason::UPLOADDIRTYFRAME);
|
||||
}
|
||||
|
||||
const bool zbuf_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && prev_ctx.TEST.AFAIL != 2) && !prev_ctx.ZBUF.ZMSK) || (prev_ctx.TEST.ZTE && prev_ctx.TEST.ZTST > ZTST_ALWAYS);
|
||||
if (zbuf_required)
|
||||
{
|
||||
const u32 draw_start_bp = m_mem.m_psm[prev_ctx.ZBUF.PSM].info.bn(temp_draw_rect.x, temp_draw_rect.y, prev_ctx.ZBUF.Block(), prev_ctx.FRAME.FBW);
|
||||
const u32 draw_end_bp = m_mem.m_psm[prev_ctx.ZBUF.PSM].info.bn(temp_draw_rect.z - 1, temp_draw_rect.w - 1, prev_ctx.ZBUF.Block(), prev_ctx.FRAME.FBW);
|
||||
|
||||
if (write_end_bp > draw_start_bp && write_start_bp <= draw_end_bp)
|
||||
Flush(GSFlushReason::UPLOADDIRTYZBUF);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Invalid the CLUT if it crosses paths.
|
||||
m_mem.m_clut.InvalidateRange(write_start_bp, write_end_bp);
|
||||
|
||||
GSVector4i r;
|
||||
|
||||
r.left = m_env.TRXPOS.DSAX;
|
||||
@@ -1780,7 +1873,7 @@ void GSState::Write(const u8* mem, int len)
|
||||
|
||||
g_perfmon.Put(GSPerfMon::Swizzle, len);
|
||||
s_transfer_n++;
|
||||
|
||||
m_env.TRXDIR.XDIR = 3;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1799,11 +1892,16 @@ void GSState::InitReadFIFO(u8* mem, int len)
|
||||
if (len <= 0 || m_tr.total != 0)
|
||||
return;
|
||||
|
||||
if (m_env.TRXDIR.XDIR == 3)
|
||||
return;
|
||||
|
||||
const int w = m_env.TRXREG.RRW;
|
||||
const int h = m_env.TRXREG.RRH;
|
||||
|
||||
const u16 bpp = GSLocalMemory::m_psm[m_env.BITBLTBUF.SPSM].trbpp;
|
||||
|
||||
CheckWriteOverlap(false, true);
|
||||
|
||||
if (!m_tr.Update(w, h, bpp, len))
|
||||
return;
|
||||
|
||||
@@ -1834,13 +1932,24 @@ void GSState::Read(u8* mem, int len)
|
||||
if (len <= 0 || m_tr.total == 0)
|
||||
return;
|
||||
|
||||
if (m_env.TRXDIR.XDIR == 3)
|
||||
return;
|
||||
|
||||
const int w = m_env.TRXREG.RRW;
|
||||
const int h = m_env.TRXREG.RRH;
|
||||
const u16 bpp = GSLocalMemory::m_psm[m_env.BITBLTBUF.SPSM].trbpp;
|
||||
|
||||
CheckWriteOverlap(false, true);
|
||||
|
||||
if (!m_tr.Update(w, h, bpp, len))
|
||||
return;
|
||||
|
||||
const int draw = s_n;
|
||||
|
||||
|
||||
if (draw != s_n)
|
||||
DevCon.Warning("Warning! Possible incorrect data download");
|
||||
|
||||
// If it wraps memory, we need to break it up so we don't read out of bounds.
|
||||
if ((m_tr.end + len) > m_mem.m_vmsize)
|
||||
{
|
||||
@@ -1856,6 +1965,9 @@ void GSState::Read(u8* mem, int len)
|
||||
memcpy(mem, &m_tr.buff[m_tr.end], len);
|
||||
m_tr.end += len;
|
||||
}
|
||||
|
||||
if(m_tr.end >= m_tr.total)
|
||||
m_env.TRXDIR.XDIR = 3;
|
||||
}
|
||||
|
||||
void GSState::Move()
|
||||
@@ -1864,6 +1976,9 @@ void GSState::Move()
|
||||
// guitar hero copies the far end of the board to do a similar blend too
|
||||
s_transfer_n++;
|
||||
|
||||
if (m_env.TRXDIR.XDIR == 3)
|
||||
return;
|
||||
|
||||
int sx = m_env.TRXPOS.SSAX;
|
||||
int sy = m_env.TRXPOS.SSAY;
|
||||
int dx = m_env.TRXPOS.DSAX;
|
||||
@@ -1909,43 +2024,6 @@ void GSState::Move()
|
||||
const GSOffset spo = m_mem.GetOffset(sbp, sbw, m_env.BITBLTBUF.SPSM);
|
||||
const GSOffset dpo = m_mem.GetOffset(dbp, dbw, m_env.BITBLTBUF.DPSM);
|
||||
|
||||
const GSDrawingContext& prev_ctx = m_prev_env.CTXT[m_prev_env.PRIM.CTXT];
|
||||
const u32 write_start_bp = m_mem.m_psm[m_env.BITBLTBUF.DPSM].info.bn(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, dbp, dbw);
|
||||
const u32 write_end_bp = m_mem.m_psm[m_env.BITBLTBUF.DPSM].info.bn(m_env.TRXPOS.DSAX + w - 1, m_env.TRXPOS.DSAY + h - 1, dbp, dbw);
|
||||
|
||||
// Only flush on a NEW transfer if a pending one is using the same address or overlap.
|
||||
// Unknown if games use this one, but best to be safe.
|
||||
if (m_index.tail > 0)
|
||||
{
|
||||
const u32 tex_end_bp = m_mem.m_psm[prev_ctx.TEX0.PSM].info.bn((1 << prev_ctx.TEX0.TW) - 1, (1 << prev_ctx.TEX0.TH) - 1, prev_ctx.TEX0.TBP0, prev_ctx.TEX0.TBW);
|
||||
|
||||
if (m_prev_env.PRIM.TME && write_end_bp >= prev_ctx.TEX0.TBP0 && write_start_bp <= tex_end_bp)
|
||||
Flush(GSFlushReason::LOCALTOLOCALMOVE);
|
||||
else
|
||||
{
|
||||
const u32 frame_mask = GSLocalMemory::m_psm[prev_ctx.FRAME.PSM].fmsk;
|
||||
const bool frame_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && prev_ctx.TEST.AFAIL == 2) && ((prev_ctx.FRAME.FBMSK & frame_mask) != frame_mask)) || (prev_ctx.TEST.ATE && prev_ctx.TEST.ATST > ATST_ALWAYS);
|
||||
if (frame_required)
|
||||
{
|
||||
const u32 draw_start_bp = m_mem.m_psm[prev_ctx.FRAME.PSM].info.bn(temp_draw_rect.x, temp_draw_rect.y, prev_ctx.FRAME.Block(), prev_ctx.FRAME.FBW);
|
||||
const u32 draw_end_bp = m_mem.m_psm[prev_ctx.FRAME.PSM].info.bn(temp_draw_rect.z - 1, temp_draw_rect.w - 1, prev_ctx.FRAME.Block(), prev_ctx.FRAME.FBW);
|
||||
|
||||
if (write_end_bp > draw_start_bp && write_start_bp <= draw_end_bp)
|
||||
Flush(GSFlushReason::UPLOADDIRTYFRAME);
|
||||
}
|
||||
|
||||
const bool zbuf_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && prev_ctx.TEST.AFAIL != 2) && !prev_ctx.ZBUF.ZMSK) || (prev_ctx.TEST.ZTE && prev_ctx.TEST.ZTST > ZTST_ALWAYS);
|
||||
if (zbuf_required)
|
||||
{
|
||||
const u32 draw_start_bp = m_mem.m_psm[prev_ctx.ZBUF.PSM].info.bn(temp_draw_rect.x, temp_draw_rect.y, prev_ctx.ZBUF.Block(), prev_ctx.FRAME.FBW);
|
||||
const u32 draw_end_bp = m_mem.m_psm[prev_ctx.ZBUF.PSM].info.bn(temp_draw_rect.z - 1, temp_draw_rect.w - 1, prev_ctx.ZBUF.Block(), prev_ctx.FRAME.FBW);
|
||||
|
||||
if (write_end_bp > draw_start_bp && write_start_bp <= draw_end_bp)
|
||||
Flush(GSFlushReason::UPLOADDIRTYZBUF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GSVector4i r;
|
||||
r.left = m_env.TRXPOS.DSAX;
|
||||
r.top = m_env.TRXPOS.DSAY;
|
||||
@@ -1969,9 +2047,6 @@ void GSState::Move()
|
||||
m_draw_transfers.push_back(new_transfer);
|
||||
}
|
||||
|
||||
// Invalid the CLUT if it crosses paths.
|
||||
m_mem.m_clut.InvalidateRange(write_start_bp, write_end_bp);
|
||||
|
||||
auto genericCopy = [=](const GSOffset& dpo, const GSOffset& spo, auto&& getPAHelper, auto&& pxCopyFn)
|
||||
{
|
||||
int _sy = sy, _dy = dy; // Faster with local copied variables, compiler optimizations are dumb
|
||||
@@ -2116,6 +2191,8 @@ void GSState::Move()
|
||||
(m_mem.*dpsm.wpa)(doff, (m_mem.*spsm.rpa)(soff));
|
||||
});
|
||||
}
|
||||
|
||||
m_env.TRXDIR.XDIR = 3;
|
||||
}
|
||||
|
||||
void GSState::SoftReset(u32 mask)
|
||||
@@ -2139,8 +2216,6 @@ void GSState::SoftReset(u32 mask)
|
||||
|
||||
void GSState::ReadFIFO(u8* mem, int size)
|
||||
{
|
||||
Flush(GSFlushReason::DOWNLOADFIFO);
|
||||
|
||||
size *= 16;
|
||||
|
||||
Read(mem, size);
|
||||
@@ -2845,13 +2920,14 @@ __forceinline bool GSState::IsAutoFlushDraw(u32 prim)
|
||||
const u32 frame_mask = GSLocalMemory::m_psm[m_context->FRAME.PSM].fmsk;
|
||||
const bool frame_hit = m_context->FRAME.Block() == m_context->TEX0.TBP0 && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL == 2) && ((m_context->FRAME.FBMSK & frame_mask) != frame_mask);
|
||||
// There's a strange behaviour we need to test on a PS2 here, if the FRAME is a Z format, like Powerdrome something swaps over, and it seems Alpha Fail of "FB Only" writes to the Z.. it's odd.
|
||||
const bool zbuf_hit = (m_context->ZBUF.Block() == m_context->TEX0.TBP0) && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL != 2) && !m_context->ZBUF.ZMSK;
|
||||
const bool z_needed = !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL != 2) && !m_context->ZBUF.ZMSK;
|
||||
const bool zbuf_hit = (m_context->ZBUF.Block() == m_context->TEX0.TBP0) && z_needed;
|
||||
const u32 frame_z_psm = frame_hit ? m_context->FRAME.PSM : m_context->ZBUF.PSM;
|
||||
const u32 frame_z_bp = frame_hit ? m_context->FRAME.Block() : m_context->ZBUF.Block();
|
||||
|
||||
if ((frame_hit || zbuf_hit) && GSUtil::HasSharedBits(frame_z_bp, frame_z_psm, m_context->TEX0.TBP0, m_context->TEX0.PSM))
|
||||
return true;
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2948,10 +3024,6 @@ __forceinline void GSState::HandleAutoFlush()
|
||||
break;
|
||||
}
|
||||
|
||||
const int page_mask_x = ~(GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.x - 1);
|
||||
const int page_mask_y = ~(GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.y - 1);
|
||||
const GSVector4i page_mask = { page_mask_x, page_mask_y, page_mask_x, page_mask_y };
|
||||
|
||||
GSVector4i tex_coord;
|
||||
// Prepare the currently processed vertex.
|
||||
if (PRIM->FST)
|
||||
@@ -2970,6 +3042,8 @@ __forceinline void GSState::HandleAutoFlush()
|
||||
|
||||
GSVector4i tex_rect = tex_coord.xyxy();
|
||||
|
||||
const GSLocalMemory::psm_t tex_psm = GSLocalMemory::m_psm[m_context->TEX0.PSM];
|
||||
const GSLocalMemory::psm_t frame_psm = GSLocalMemory::m_psm[m_context->FRAME.PSM];
|
||||
// Get the rest of the rect.
|
||||
for (int i = 0; i < (n - 1); i++)
|
||||
{
|
||||
@@ -2995,6 +3069,12 @@ __forceinline void GSState::HandleAutoFlush()
|
||||
tex_rect.w = std::max(tex_rect.w, tex_coord.y);
|
||||
}
|
||||
|
||||
// If the draw was 1 line thick, make it larger as rects are exclusive of ends.
|
||||
if (tex_rect.x == tex_rect.z)
|
||||
tex_rect += GSVector4i::cxpr(0, 0, 1, 0);
|
||||
if (tex_rect.y == tex_rect.w)
|
||||
tex_rect += GSVector4i::cxpr(0, 0, 0, 1);
|
||||
|
||||
// Get the last texture position from the last draw.
|
||||
const GSVertex* v = &m_vertex.buff[m_index.buff[m_index.tail - (index_swap ? n : 1)]];
|
||||
|
||||
@@ -3012,27 +3092,75 @@ __forceinline void GSState::HandleAutoFlush()
|
||||
tex_coord.y = (int)((1 << m_context->TEX0.TH) * t);
|
||||
}
|
||||
|
||||
const GSVector4i last_tex_page = tex_coord.xyxy() & page_mask;
|
||||
const GSVector4i tex_page = tex_rect.xyxy() & page_mask;
|
||||
const int clamp_minu = m_context->CLAMP.MINU;
|
||||
const int clamp_maxu = m_context->CLAMP.MAXU;
|
||||
const int clamp_minv = m_context->CLAMP.MINV;
|
||||
const int clamp_maxv = m_context->CLAMP.MAXV;
|
||||
|
||||
switch (m_context->CLAMP.WMS)
|
||||
{
|
||||
case CLAMP_REGION_CLAMP:
|
||||
tex_rect.x = std::max(tex_rect.x, clamp_minu);
|
||||
tex_rect.z = std::max(tex_rect.z, clamp_minu);
|
||||
tex_coord.x = std::max(tex_coord.x, clamp_minu);
|
||||
tex_rect.x = std::min(tex_rect.x, clamp_maxu);
|
||||
tex_rect.z = std::min(tex_rect.z, clamp_maxu);
|
||||
tex_coord.x = std::min(tex_coord.x, clamp_maxu);
|
||||
break;
|
||||
|
||||
case CLAMP_REGION_REPEAT:
|
||||
tex_rect.x = std::max(tex_rect.x, clamp_maxu);
|
||||
tex_rect.z = std::max(tex_rect.z, clamp_maxu);
|
||||
tex_coord.x = std::max(tex_coord.x, clamp_maxu);
|
||||
tex_rect.x = std::min(tex_rect.x, (clamp_maxu | clamp_minu));
|
||||
tex_rect.z = std::min(tex_rect.z, (clamp_maxu | clamp_minu));
|
||||
tex_coord.x = std::min(tex_coord.x, (clamp_maxu | clamp_minu));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (m_context->CLAMP.WMT)
|
||||
{
|
||||
case CLAMP_REGION_CLAMP:
|
||||
tex_rect.y = std::max(tex_rect.y, clamp_minv);
|
||||
tex_rect.w = std::max(tex_rect.w, clamp_minv);
|
||||
tex_coord.y = std::max(tex_coord.y, clamp_minv);
|
||||
tex_rect.y = std::min(tex_rect.y, clamp_maxv);
|
||||
tex_rect.w = std::min(tex_rect.w, clamp_maxv);
|
||||
tex_coord.y = std::min(tex_coord.y, clamp_maxv);
|
||||
break;
|
||||
case CLAMP_REGION_REPEAT:
|
||||
tex_rect.y = std::max(tex_rect.y, clamp_maxv);
|
||||
tex_rect.w = std::max(tex_rect.w, clamp_maxv);
|
||||
tex_coord.y = std::max(tex_coord.y, clamp_maxv);
|
||||
tex_rect.y = std::min(tex_rect.y, (clamp_maxv | clamp_minv));
|
||||
tex_rect.w = std::min(tex_rect.w, (clamp_maxv | clamp_minv));
|
||||
tex_coord.y = std::min(tex_coord.y, (clamp_maxv | clamp_minv));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Nothing being drawn intersect with the new texture, so no point in checking further.
|
||||
if (tex_psm.depth == frame_psm.depth && tex_rect.rintersect(temp_draw_rect).rempty())
|
||||
return;
|
||||
|
||||
const int tex_page_mask_x = ~(tex_psm.pgs.x - 1);
|
||||
const int tex_page_mask_y = ~(tex_psm.pgs.y - 1);
|
||||
const GSVector4i tex_page_mask = { tex_page_mask_x, tex_page_mask_y, tex_page_mask_x, tex_page_mask_y };
|
||||
const GSVector4i last_tex_page = tex_coord.xyxy() & tex_page_mask;
|
||||
const GSVector4i tex_page = tex_rect.xyxy() & tex_page_mask;
|
||||
|
||||
// Crossed page since last draw end
|
||||
if (!tex_page.eq(last_tex_page) || m_texflush_flag)
|
||||
{
|
||||
const u32 frame_mask = GSLocalMemory::m_psm[m_context->TEX0.PSM].fmsk;
|
||||
const bool frame_hit = (m_context->FRAME.Block() == m_context->TEX0.TBP0) && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL == 2) && ((m_context->FRAME.FBMSK & frame_mask) != frame_mask);
|
||||
const u32 frame_z_psm = frame_hit ? m_context->FRAME.PSM : m_context->ZBUF.PSM;
|
||||
// Make sure the format matches, otherwise the coordinates aren't gonna match, so the draws won't intersect.
|
||||
if (GSUtil::HasCompatibleBits(m_context->TEX0.PSM, frame_z_psm) && (m_context->FRAME.FBW == m_context->TEX0.TBW))
|
||||
if (tex_psm.bpp == frame_psm.bpp && (m_context->FRAME.FBW == m_context->TEX0.TBW))
|
||||
{
|
||||
// If the draw was 1 line thick, make it larger as rects are exclusive of ends.
|
||||
if (tex_rect.x == tex_rect.z)
|
||||
tex_rect += GSVector4i(0, 0, 1, 0);
|
||||
if (tex_rect.y == tex_rect.w)
|
||||
tex_rect += GSVector4i(0, 0, 0, 1);
|
||||
|
||||
const GSVector2i offset = GSVector2i(m_context->XYOFFSET.OFX, m_context->XYOFFSET.OFY);
|
||||
const GSVector4i scissor = m_context->scissor.in;
|
||||
GSVector4i old_tex_rect = GSVector4i::zero();
|
||||
GSVector4i old_draw_rect = GSVector4i::zero();
|
||||
int current_draw_end = m_index.tail;
|
||||
|
||||
while (current_draw_end >= n)
|
||||
@@ -3041,29 +3169,49 @@ __forceinline void GSState::HandleAutoFlush()
|
||||
{
|
||||
const GSVertex* v = &m_vertex.buff[m_index.buff[i]];
|
||||
|
||||
tex_coord.x = (static_cast<int>(v->XYZ.X) - offset.x) >> 4;
|
||||
tex_coord.y = (static_cast<int>(v->XYZ.Y) - offset.y) >> 4;
|
||||
|
||||
if (i == (current_draw_end - 1))
|
||||
if (prim == GS_SPRITE && (i & 1))
|
||||
{
|
||||
old_tex_rect = tex_coord.xyxy();
|
||||
tex_coord.x = ((static_cast<int>(v->XYZ.X) - offset.x) >> 4) - 1;
|
||||
tex_coord.y = ((static_cast<int>(v->XYZ.Y) - offset.y) >> 4) - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
old_tex_rect.x = std::min(old_tex_rect.x, tex_coord.x);
|
||||
old_tex_rect.z = std::max(old_tex_rect.z, tex_coord.x);
|
||||
old_tex_rect.y = std::min(old_tex_rect.y, tex_coord.y);
|
||||
old_tex_rect.w = std::max(old_tex_rect.w, tex_coord.y);
|
||||
tex_coord.x = (static_cast<int>(v->XYZ.X) - offset.x) >> 4;
|
||||
tex_coord.y = (static_cast<int>(v->XYZ.Y) - offset.y) >> 4;
|
||||
}
|
||||
|
||||
if (tex_psm.depth != frame_psm.depth)
|
||||
{
|
||||
tex_coord.x ^= (frame_psm.pgs.x / 2);
|
||||
tex_coord.y ^= (frame_psm.pgs.y / 2);
|
||||
}
|
||||
|
||||
if (prim == GS_SPRITE && (i & 1))
|
||||
{
|
||||
tex_coord.x += 1;
|
||||
tex_coord.y += 1;
|
||||
}
|
||||
|
||||
if (i == (current_draw_end - 1))
|
||||
{
|
||||
old_draw_rect = tex_coord.xyxy();
|
||||
}
|
||||
else
|
||||
{
|
||||
old_draw_rect.x = std::min(old_draw_rect.x, tex_coord.x);
|
||||
old_draw_rect.z = std::max(old_draw_rect.z, tex_coord.x);
|
||||
old_draw_rect.y = std::min(old_draw_rect.y, tex_coord.y);
|
||||
old_draw_rect.w = std::max(old_draw_rect.w, tex_coord.y);
|
||||
}
|
||||
}
|
||||
|
||||
if (old_tex_rect.x == old_tex_rect.z)
|
||||
old_tex_rect += GSVector4i(0, 0, 1, 0);
|
||||
if (old_tex_rect.y == old_tex_rect.w)
|
||||
old_tex_rect += GSVector4i(0, 0, 0, 1);
|
||||
if (old_draw_rect.x == old_draw_rect.z)
|
||||
old_draw_rect += GSVector4i::cxpr(0, 0, 1, 0);
|
||||
if (old_draw_rect.y == old_draw_rect.w)
|
||||
old_draw_rect += GSVector4i::cxpr(0, 0, 0, 1);
|
||||
|
||||
old_tex_rect = tex_rect.rintersect(old_tex_rect);
|
||||
if (!old_tex_rect.rintersect(scissor).rempty())
|
||||
old_draw_rect = tex_rect.rintersect(old_draw_rect);
|
||||
if (!old_draw_rect.rintersect(scissor).rempty())
|
||||
{
|
||||
Flush(GSFlushReason::AUTOFLUSH);
|
||||
return;
|
||||
@@ -3074,26 +3222,27 @@ __forceinline void GSState::HandleAutoFlush()
|
||||
}
|
||||
else // Storage of the TEX and FRAME/Z is different, so uhh, just fall back to flushing each page. It's slower, sorry.
|
||||
{
|
||||
if (m_context->FRAME.FBW == m_context->TEX0.TBW)
|
||||
const int frame_width = (m_context->FRAME.FBW * 64) / frame_psm.pgs.x;
|
||||
const int tex_width = (m_context->TEX0.TBW * 64) / tex_psm.pgs.x;
|
||||
if ((frame_width == tex_width) || ((tex_rect.w / tex_psm.pgs.y) <= 1 && frame_width >= tex_width))
|
||||
{
|
||||
tex_rect += GSVector4i(0, 0, tex_page_mask.z, tex_page_mask.w); // round up to the next page as we will be comparing by page.
|
||||
//We know we've changed page, so let's set the dimension to cover the page they're in (for different pixel orders)
|
||||
tex_rect = tex_rect & page_mask;
|
||||
tex_rect += GSVector4i(0, 0, 1, 1); // Intersect goes on space inside the rect
|
||||
tex_rect.z += GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.x;
|
||||
tex_rect.w += GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.y;
|
||||
|
||||
m_vt.Update(m_vertex.buff, m_index.buff, m_vertex.tail - m_vertex.head, m_index.tail, GSUtil::GetPrimClass(PRIM->PRIM));
|
||||
|
||||
GSVector4i area_out = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(m_context->scissor.in);
|
||||
area_out = area_out & page_mask;
|
||||
area_out += GSVector4i(0, 0, 1, 1); // Intersect goes on space inside the rect
|
||||
area_out.z += GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.x;
|
||||
area_out.w += GSLocalMemory::m_psm[m_context->TEX0.PSM].pgs.y;
|
||||
tex_rect &= tex_page_mask;
|
||||
tex_rect = GSVector4i(tex_rect.x / tex_psm.pgs.x, tex_rect.y / tex_psm.pgs.y, tex_rect.z / tex_psm.pgs.x, tex_rect.w / tex_psm.pgs.y);
|
||||
|
||||
const int frame_page_mask_x = ~(frame_psm.pgs.x - 1);
|
||||
const int frame_page_mask_y = ~(frame_psm.pgs.y - 1);
|
||||
const GSVector4i frame_page_mask = { frame_page_mask_x, frame_page_mask_y, frame_page_mask_x, frame_page_mask_y };
|
||||
GSVector4i area_out = temp_draw_rect;
|
||||
area_out += GSVector4i(0, 0, frame_page_mask.z, frame_page_mask.w); // round up to the next page as we will be comparing by page.
|
||||
area_out &= frame_page_mask;
|
||||
area_out = GSVector4i(area_out.x / frame_psm.pgs.x, area_out.y / frame_psm.pgs.y, area_out.z / frame_psm.pgs.x, area_out.w / frame_psm.pgs.y);
|
||||
|
||||
if (!area_out.rintersect(tex_rect).rempty())
|
||||
Flush(GSFlushReason::AUTOFLUSH);
|
||||
}
|
||||
else // Page width is different, so it's much more difficult to calculate where it's modifying.
|
||||
else // Formats are too different so just flush it.
|
||||
Flush(GSFlushReason::AUTOFLUSH);
|
||||
}
|
||||
}
|
||||
@@ -3368,6 +3517,11 @@ __forceinline void GSState::VertexKick(u32 skip)
|
||||
case GS_SPRITE:
|
||||
buff[0] = static_cast<u16>(head + 0);
|
||||
buff[1] = static_cast<u16>(head + 1);
|
||||
|
||||
// Update the first vert's Q for ease of doing Autoflush
|
||||
if (!m_env.PRIM.FST)
|
||||
m_vertex.buff[buff[0]].RGBAQ.Q = m_vertex.buff[buff[1]].RGBAQ.Q;
|
||||
|
||||
m_vertex.head = head + 2;
|
||||
m_vertex.next = head + 2;
|
||||
m_index.tail += 2;
|
||||
|
||||
@@ -393,6 +393,8 @@ public:
|
||||
|
||||
virtual void Move();
|
||||
|
||||
GSVector4i GetTEX0Rect();
|
||||
void CheckWriteOverlap(bool req_write, bool req_read);
|
||||
void Write(const u8* mem, int len);
|
||||
void Read(u8* mem, int len);
|
||||
void InitReadFIFO(u8* mem, int len);
|
||||
|
||||
@@ -316,6 +316,7 @@ struct alignas(16) GSHWDrawConfig
|
||||
u32 ltf : 1;
|
||||
// Shuffle and fbmask effect
|
||||
u32 shuffle : 1;
|
||||
u32 shuffle_same : 1;
|
||||
u32 real16src: 1;
|
||||
u32 read_ba : 1;
|
||||
u32 write_rg : 1;
|
||||
|
||||
@@ -40,6 +40,7 @@ protected:
|
||||
GSVector2i m_real_size{0, 0};
|
||||
bool m_texture_shuffle = false;
|
||||
bool m_copy_16bit_to_target_shuffle = false;
|
||||
bool m_same_group_texture_shuffle = false;
|
||||
|
||||
virtual GSTexture* GetOutput(int i, float& scale, int& y_offset) = 0;
|
||||
virtual GSTexture* GetFeedbackOutput(float& scale) { return nullptr; }
|
||||
|
||||
@@ -138,6 +138,7 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
|
||||
sm.AddMacro("PS_POINT_SAMPLER", sel.point_sampler);
|
||||
sm.AddMacro("PS_REGION_RECT", sel.region_rect);
|
||||
sm.AddMacro("PS_SHUFFLE", sel.shuffle);
|
||||
sm.AddMacro("PS_SHUFFLE_SAME", sel.shuffle_same);
|
||||
sm.AddMacro("PS_READ_BA", sel.read_ba);
|
||||
sm.AddMacro("PS_READ16_SRC", sel.real16src);
|
||||
sm.AddMacro("PS_CHANNEL_FETCH", sel.channel);
|
||||
|
||||
@@ -2743,6 +2743,7 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector&
|
||||
sm.AddMacro("PS_POINT_SAMPLER", sel.point_sampler);
|
||||
sm.AddMacro("PS_REGION_RECT", sel.region_rect);
|
||||
sm.AddMacro("PS_SHUFFLE", sel.shuffle);
|
||||
sm.AddMacro("PS_SHUFFLE_SAME", sel.shuffle_same);
|
||||
sm.AddMacro("PS_READ_BA", sel.read_ba);
|
||||
sm.AddMacro("PS_READ16_SRC", sel.real16src);
|
||||
sm.AddMacro("PS_CHANNEL_FETCH", sel.channel);
|
||||
|
||||
@@ -523,19 +523,6 @@ bool GSHwHack::GSC_SakuraWarsSoLongMyLove(GSRendererHW& r, int& skip)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSHwHack::GSC_GodHand(GSRendererHW& r, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
{
|
||||
if (RTME && (RFBP == 0x0) && (RTBP0 == 0x2800) && RFPSM == RTPSM && RTPSM == PSMCT32)
|
||||
{
|
||||
skip = 1; // Blur
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSHwHack::GSC_KnightsOfTheTemple2(GSRendererHW& r, int& skip)
|
||||
{
|
||||
if (skip == 0)
|
||||
@@ -864,6 +851,44 @@ bool GSHwHack::GSC_MetalGearSolid3(GSRendererHW& r, int& skip)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSHwHack::GSC_BigMuthaTruckers(GSRendererHW& r, int& skip)
|
||||
{
|
||||
// Rendering pattern:
|
||||
// CRTC frontbuffer at 0x0 is interlaced (half vertical resolution),
|
||||
// game needs to do a depth effect (so green channel to alpha),
|
||||
// but there is a vram limitation so green is pushed into the alpha channel of the CRCT buffer,
|
||||
// vertical resolution is half so only half is processed at once
|
||||
// We, however, don't have this limitation so we'll replace the draw with a full-screen TS.
|
||||
|
||||
const GIFRegTEX0& Texture = RTEX0;
|
||||
|
||||
GIFRegTEX0 Frame = {};
|
||||
Frame.TBW = RFRAME.FBW;
|
||||
Frame.TBP0 = RFRAME.Block();
|
||||
const int frame_offset_pal = GSLocalMemory::GetEndBlockAddress(0xa00, 10, PSMCT32, GSVector4i(0, 0, 640, 256)) + 1;
|
||||
const int frame_offset_ntsc = GSLocalMemory::GetEndBlockAddress(0xa00, 10, PSMCT32, GSVector4i(0, 0, 640, 224)) + 1;
|
||||
const GSVector4i rect = GSVector4i(r.m_vt.m_min.p.x, r.m_vt.m_min.p.y, r.m_vt.m_max.p.x, r.m_vt.m_max.p.y);
|
||||
|
||||
if (RPRIM->TME && Frame.TBW == 10 && Texture.TBW == 10 && Texture.PSM == PSMCT16 && ((rect.w == 512 && Frame.TBP0 == frame_offset_pal) || (Frame.TBP0 == frame_offset_ntsc && rect.w == 448)))
|
||||
{
|
||||
// 224 ntsc, 256 pal.
|
||||
GL_INS("GSC_BigMuthaTruckers half bottom offset %d", r.m_context->XYOFFSET.OFX >> 4);
|
||||
|
||||
const size_t count = r.m_vertex.next;
|
||||
GSVertex* v = &r.m_vertex.buff[0];
|
||||
const u16 offset = (u16)rect.w * 16;
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
v[i].XYZ.Y += offset;
|
||||
|
||||
r.m_vt.m_min.p.y += rect.w;
|
||||
r.m_vt.m_max.p.y += rect.w;
|
||||
r.m_cached_ctx.FRAME.FBP = 0x50; // 0xA00 >> 5
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSHwHack::OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
const u32 n_vertices = r.m_vertex.next;
|
||||
@@ -917,37 +942,6 @@ bool GSHwHack::OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSHwHack::OI_BigMuthaTruckers(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
// Rendering pattern:
|
||||
// CRTC frontbuffer at 0x0 is interlaced (half vertical resolution),
|
||||
// game needs to do a depth effect (so green channel to alpha),
|
||||
// but there is a vram limitation so green is pushed into the alpha channel of the CRCT buffer,
|
||||
// vertical resolution is half so only half is processed at once
|
||||
// We, however, don't have this limitation so we'll replace the draw with a full-screen TS.
|
||||
|
||||
const GIFRegTEX0& Texture = RTEX0;
|
||||
|
||||
GIFRegTEX0 Frame = {};
|
||||
Frame.TBW = RFRAME.FBW;
|
||||
Frame.TBP0 = RFRAME.Block();
|
||||
|
||||
if (RPRIM->TME && Frame.TBW == 10 && Texture.TBW == 10 && Frame.TBP0 == 0x00a00 && Texture.PSM == PSMT8H && (r.m_r.y == 256 || r.m_r.y == 224))
|
||||
{
|
||||
// 224 ntsc, 256 pal.
|
||||
GL_INS("OI_BigMuthaTruckers half bottom offset");
|
||||
|
||||
const size_t count = r.m_vertex.next;
|
||||
GSVertex* v = &r.m_vertex.buff[0];
|
||||
const u16 offset = (u16)r.m_r.y * 16;
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
v[i].V += offset;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSHwHack::OI_DBZBTGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
|
||||
{
|
||||
if (t && t->m_from_target) // Avoid slow framebuffer readback
|
||||
@@ -1065,9 +1059,11 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds,
|
||||
GSTextureCache::Target* rt_again = g_texture_cache->LookupTarget(Frame, src_size, src->m_scale, GSTextureCache::RenderTarget);
|
||||
if (rt_again->m_unscaled_size.x < src->m_unscaled_size.x || rt_again->m_unscaled_size.y < src->m_unscaled_size.y)
|
||||
{
|
||||
rt_again->ResizeTexture(std::max(rt_again->m_unscaled_size.x, src->m_unscaled_size.x),
|
||||
std::max(rt_again->m_unscaled_size.y, src->m_unscaled_size.y));
|
||||
GSVector2i new_size = GSVector2i(std::max(rt_again->m_unscaled_size.x, src->m_unscaled_size.x),
|
||||
std::max(rt_again->m_unscaled_size.y, src->m_unscaled_size.y));
|
||||
rt_again->ResizeTexture(new_size.x, new_size.y);
|
||||
rt = rt_again->m_texture;
|
||||
rt_size = new_size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1438,7 +1434,6 @@ bool GSHwHack::MV_Ico(GSRendererHW& r)
|
||||
#define CRC_F(name) { #name, &GSHwHack::name }
|
||||
|
||||
const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_functions[] = {
|
||||
CRC_F(GSC_GodHand),
|
||||
CRC_F(GSC_KnightsOfTheTemple2),
|
||||
CRC_F(GSC_Kunoichi),
|
||||
CRC_F(GSC_Manhunt2),
|
||||
@@ -1471,6 +1466,7 @@ const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_function
|
||||
|
||||
// Texture shuffle
|
||||
CRC_F(GSC_DeathByDegreesTekkenNinaWilliams), // + Upscaling issues
|
||||
CRC_F(GSC_BigMuthaTruckers),
|
||||
|
||||
// Upscaling hacks
|
||||
CRC_F(GSC_UltramanFightingEvolution),
|
||||
@@ -1481,7 +1477,6 @@ const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_function
|
||||
|
||||
const GSHwHack::Entry<GSRendererHW::OI_Ptr> GSHwHack::s_before_draw_functions[] = {
|
||||
CRC_F(OI_PointListPalette),
|
||||
CRC_F(OI_BigMuthaTruckers),
|
||||
CRC_F(OI_DBZBTGames),
|
||||
CRC_F(OI_FFX),
|
||||
CRC_F(OI_RozenMaidenGebetGarden),
|
||||
|
||||
@@ -32,7 +32,6 @@ public:
|
||||
static bool GSC_Kunoichi(GSRendererHW& r, int& skip);
|
||||
static bool GSC_ZettaiZetsumeiToshi2(GSRendererHW& r, int& skip);
|
||||
static bool GSC_SakuraWarsSoLongMyLove(GSRendererHW& r, int& skip);
|
||||
static bool GSC_GodHand(GSRendererHW& r, int& skip);
|
||||
static bool GSC_KnightsOfTheTemple2(GSRendererHW& r, int& skip);
|
||||
static bool GSC_UltramanFightingEvolution(GSRendererHW& r, int& skip);
|
||||
static bool GSC_TalesofSymphonia(GSRendererHW& r, int& skip);
|
||||
@@ -45,9 +44,9 @@ public:
|
||||
static bool GSC_NFSUndercover(GSRendererHW& r, int& skip);
|
||||
static bool GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip);
|
||||
static bool GSC_MetalGearSolid3(GSRendererHW& r, int& skip);
|
||||
static bool GSC_BigMuthaTruckers(GSRendererHW& r, int& skip);
|
||||
|
||||
static bool OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
static bool OI_BigMuthaTruckers(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
static bool OI_DBZBTGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
static bool OI_FFX(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
static bool OI_RozenMaidenGebetGarden(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||
|
||||
@@ -337,7 +337,7 @@ void GSRendererHW::ExpandLineIndices()
|
||||
}
|
||||
|
||||
// Fix the vertex position/tex_coordinate from 16 bits color to 32 bits color
|
||||
void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
|
||||
void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba, GSTextureCache::Target* rt, GSTextureCache::Source* tex)
|
||||
{
|
||||
const u32 count = m_vertex.next;
|
||||
GSVertex* v = &m_vertex.buff[0];
|
||||
@@ -351,7 +351,8 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
|
||||
const float tw = static_cast<float>(1u << m_cached_ctx.TEX0.TW);
|
||||
int tex_pos = (PRIM->FST) ? first_vert.U : static_cast<int>(tw * first_vert.ST.S);
|
||||
tex_pos &= 0xFF;
|
||||
read_ba = (tex_pos > 112 && tex_pos < 144);
|
||||
// "same group" means it can read blue and write alpha using C32 tricks
|
||||
read_ba = (tex_pos > 112 && tex_pos < 144) || (m_same_group_texture_shuffle && (m_cached_ctx.FRAME.FBMSK & 0xFFFF0000) != 0xFFFF0000);
|
||||
|
||||
// Another way of selecting whether to read RG/BA is to use region repeat.
|
||||
// Ace Combat 04 reads RG, writes to RGBA by setting a MINU of 1015.
|
||||
@@ -373,7 +374,7 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
|
||||
// We can't use the draw rect exactly here, because if the target was actually larger
|
||||
// for some reason... unhandled clears, maybe, it won't have been halved correctly.
|
||||
// So, halve it ourselves.
|
||||
const GSVector4i dr = GetSplitTextureShuffleDrawRect();
|
||||
const GSVector4i dr = m_r;
|
||||
const GSVector4i r = dr.blend32<9>(dr.sra32(1));
|
||||
GL_CACHE("ConvertSpriteTextureShuffle: Rewrite from %d,%d => %d,%d to %d,%d => %d,%d",
|
||||
static_cast<int>(m_vt.m_min.p.x), static_cast<int>(m_vt.m_min.p.y), static_cast<int>(m_vt.m_min.p.z),
|
||||
@@ -406,60 +407,51 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
|
||||
return;
|
||||
}
|
||||
|
||||
bool half_bottom = false;
|
||||
switch (GSConfig.UserHacks_HalfBottomOverride)
|
||||
bool half_bottom_vert = true;
|
||||
bool half_right_vert = true;
|
||||
bool half_bottom_uv = true;
|
||||
bool half_right_uv = true;
|
||||
|
||||
if (m_same_group_texture_shuffle)
|
||||
{
|
||||
case 0:
|
||||
// Force Disabled.
|
||||
// Force Disabled will help games such as Xenosaga.
|
||||
// Xenosaga handles the half bottom as an vertex offset instead of a buffer offset which does the effect twice.
|
||||
// Half bottom won't trigger a cache miss that skip the draw because it is still the normal buffer but with a vertices offset.
|
||||
half_bottom = false;
|
||||
break;
|
||||
case 1:
|
||||
// Force Enabled.
|
||||
// Force Enabled will help games such as Superman Shadows of Apokolips, The Lord of the Rings: The Two Towers,
|
||||
// Demon Stone, Midnight Club 3.
|
||||
half_bottom = true;
|
||||
break;
|
||||
case -1:
|
||||
default:
|
||||
// Default, Automatic.
|
||||
// Here's the idea
|
||||
// TS effect is 16 bits but we emulate it on a 32 bits format
|
||||
// Normally this means we need to divide size by 2.
|
||||
//
|
||||
// Some games do two TS effects on each half of the buffer.
|
||||
// This makes a mess for us in the TC because we end up with two targets
|
||||
// when we only want one, thus half screen bug.
|
||||
//
|
||||
// 32bits emulation means we can do the effect once but double the size.
|
||||
// Test cases: Crash Twinsantiy and DBZ BT3
|
||||
// Test Case: NFS: HP2 splits the effect h:256 and h:192 so 64
|
||||
// Other games: Midnight Club 3 headlights, black bar in Xenosaga 3 dialogue,
|
||||
// Firefighter FD18 fire occlusion, PSI Ops half screen green overlay, Lord of the Rings - Two Towers,
|
||||
// Demon Stone , Sonic Unleashed, Lord of the Rings Two Towers,
|
||||
// Superman Shadow of Apokolips, Matrix Path of Neo, Big Mutha Truckers
|
||||
|
||||
int maxvert = 0;
|
||||
int minvert = 4096;
|
||||
for (u32 i = 0; i < count; i++)
|
||||
if (m_cached_ctx.FRAME.FBW != rt->m_TEX0.TBW && m_cached_ctx.FRAME.FBW == rt->m_TEX0.TBW * 2)
|
||||
half_right_vert = false;
|
||||
else
|
||||
half_bottom_vert = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Different source (maybe?)
|
||||
// If a game does the texture and frame doubling differently, they can burn in hell.
|
||||
if (m_cached_ctx.TEX0.TBP0 != m_cached_ctx.FRAME.Block())
|
||||
{
|
||||
// No super source of truth here, since the width can get batted around, the valid is probably our best bet.
|
||||
int tex_width = tex->m_target ? tex->m_from_target->m_valid.z : (tex->m_TEX0.TBW * 64);
|
||||
int tex_tbw = tex->m_target ? tex->m_from_target_TEX0.TBW : tex->m_TEX0.TBW;
|
||||
if (static_cast<int>(m_cached_ctx.TEX0.TBW * 64) >= std::min(tex_width * 2, 1024) && tex_tbw != m_cached_ctx.TEX0.TBW || (m_cached_ctx.TEX0.TBW * 64) < floor(m_vt.m_max.t.x))
|
||||
{
|
||||
int YCord = 0;
|
||||
|
||||
if (!PRIM->FST)
|
||||
YCord = static_cast<int>((1 << m_cached_ctx.TEX0.TH) * (v[i].ST.T / v[i].RGBAQ.Q));
|
||||
else
|
||||
YCord = (v[i].V >> 4);
|
||||
|
||||
if (maxvert < YCord)
|
||||
maxvert = YCord;
|
||||
if (minvert > YCord)
|
||||
minvert = YCord;
|
||||
half_right_uv = false;
|
||||
half_right_vert = false;
|
||||
}
|
||||
|
||||
half_bottom = minvert == 0 && m_r.height() <= maxvert;
|
||||
break;
|
||||
else
|
||||
{
|
||||
half_bottom_uv = false;
|
||||
half_bottom_vert = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((floor(m_vt.m_max.p.y) <= rt->m_valid.w) && ((floor(m_vt.m_max.p.x) > (m_cached_ctx.FRAME.FBW * 64)) || (rt->m_TEX0.TBW != m_cached_ctx.FRAME.FBW)))
|
||||
{
|
||||
half_right_vert = false;
|
||||
half_right_uv = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
half_bottom_vert = false;
|
||||
half_bottom_uv = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (PRIM->FST)
|
||||
@@ -469,16 +461,16 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
|
||||
for (u32 i = 0; i < count; i += 2)
|
||||
{
|
||||
if (write_ba)
|
||||
v[i].XYZ.X -= 128u;
|
||||
v[i].XYZ.X -= 128u;
|
||||
else
|
||||
v[i+1].XYZ.X += 128u;
|
||||
v[i + 1].XYZ.X += 128u;
|
||||
|
||||
if (read_ba)
|
||||
v[i].U -= 128u;
|
||||
v[i].U -= 128u;
|
||||
else
|
||||
v[i+1].U += 128u;
|
||||
v[i + 1].U += 128u;
|
||||
|
||||
if (!half_bottom)
|
||||
if (!half_bottom_vert)
|
||||
{
|
||||
// Height is too big (2x).
|
||||
const int tex_offset = v[i].V & 0xF;
|
||||
@@ -488,9 +480,13 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
|
||||
tmp = GSVector4i(tmp - offset).srl32(1) + offset;
|
||||
|
||||
v[i].XYZ.Y = static_cast<u16>(tmp.x);
|
||||
v[i].V = static_cast<u16>(tmp.y);
|
||||
v[i + 1].XYZ.Y = static_cast<u16>(tmp.z);
|
||||
v[i + 1].V = static_cast<u16>(tmp.w);
|
||||
|
||||
if (!half_bottom_uv)
|
||||
{
|
||||
v[i].V = static_cast<u16>(tmp.y);
|
||||
v[i + 1].V = static_cast<u16>(tmp.w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -502,16 +498,16 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
|
||||
for (u32 i = 0; i < count; i += 2)
|
||||
{
|
||||
if (write_ba)
|
||||
v[i].XYZ.X -= 128u;
|
||||
v[i].XYZ.X -= 128u;
|
||||
else
|
||||
v[i+1].XYZ.X += 128u;
|
||||
v[i + 1].XYZ.X += 128u;
|
||||
|
||||
if (read_ba)
|
||||
v[i].ST.S -= offset_8pix;
|
||||
v[i].ST.S -= offset_8pix;
|
||||
else
|
||||
v[i+1].ST.S += offset_8pix;
|
||||
v[i + 1].ST.S += offset_8pix;
|
||||
|
||||
if (!half_bottom)
|
||||
if (!half_bottom_vert)
|
||||
{
|
||||
// Height is too big (2x).
|
||||
const GSVector4i offset(o.OFY, o.OFY);
|
||||
@@ -521,9 +517,13 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
|
||||
|
||||
//fprintf(stderr, "Before %d, After %d\n", v[i + 1].XYZ.Y, tmp.y);
|
||||
v[i].XYZ.Y = static_cast<u16>(tmp.x);
|
||||
v[i].ST.T /= 2.0f;
|
||||
v[i + 1].XYZ.Y = static_cast<u16>(tmp.y);
|
||||
v[i + 1].ST.T /= 2.0f;
|
||||
|
||||
if (!half_bottom_uv)
|
||||
{
|
||||
v[i].ST.T /= 2.0f;
|
||||
v[i + 1].ST.T /= 2.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -534,21 +534,41 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
|
||||
else
|
||||
m_vt.m_max.p.x += 8.0f;
|
||||
|
||||
if (!half_bottom)
|
||||
if (!m_same_group_texture_shuffle)
|
||||
{
|
||||
const float delta_Y = m_vt.m_max.p.y - m_vt.m_min.p.y;
|
||||
m_vt.m_max.p.y -= delta_Y / 2.0f;
|
||||
if (read_ba)
|
||||
m_vt.m_min.t.x -= 8.0f;
|
||||
else
|
||||
m_vt.m_max.t.x += 8.0f;
|
||||
}
|
||||
|
||||
if (read_ba)
|
||||
m_vt.m_min.t.x -= 8.0f;
|
||||
else
|
||||
m_vt.m_max.t.x += 8.0f;
|
||||
|
||||
if (!half_bottom)
|
||||
if (!half_right_vert)
|
||||
{
|
||||
const float delta_T = m_vt.m_max.t.y - m_vt.m_min.t.y;
|
||||
m_vt.m_max.t.y -= delta_T / 2.0f;
|
||||
m_vt.m_min.p.x /= 2.0f;
|
||||
m_vt.m_max.p.x /= 2.0f;
|
||||
m_context->scissor.in.x = m_vt.m_min.p.x;
|
||||
m_context->scissor.in.z = m_vt.m_max.p.x + 8.0f;
|
||||
}
|
||||
|
||||
if (!half_bottom_vert)
|
||||
{
|
||||
m_vt.m_min.p.y /= 2.0f;
|
||||
m_vt.m_max.p.y /= 2.0f;
|
||||
m_context->scissor.in.y = m_vt.m_min.p.y;
|
||||
m_context->scissor.in.w = m_vt.m_max.p.y + 8.0f;
|
||||
}
|
||||
|
||||
// Only do this is the source is being interpreted as 16bit
|
||||
if (!half_bottom_uv)
|
||||
{
|
||||
m_vt.m_min.t.y /= 2.0f;
|
||||
m_vt.m_max.t.y /= 2.0f;
|
||||
}
|
||||
|
||||
if (!half_right_uv)
|
||||
{
|
||||
m_vt.m_min.t.y /= 2.0f;
|
||||
m_vt.m_max.t.y /= 2.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -786,12 +806,8 @@ bool GSRendererHW::IsPossibleChannelShuffle() const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GSRendererHW::IsSplitTextureShuffle()
|
||||
bool GSRendererHW::NextDrawMatchesShuffle() const
|
||||
{
|
||||
// For this to work, we're peeking into the next draw, therefore we need dirty registers.
|
||||
if (m_dirty_gs_regs == 0)
|
||||
return false;
|
||||
|
||||
// Make sure nothing unexpected has changed.
|
||||
// Twinsanity seems to screw with ZBUF here despite it being irrelevant.
|
||||
const GSDrawingContext& next_ctx = m_env.CTXT[m_backed_up_ctx];
|
||||
@@ -805,13 +821,24 @@ bool GSRendererHW::IsSplitTextureShuffle()
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GSRendererHW::IsSplitTextureShuffle(u32 rt_tbw)
|
||||
{
|
||||
// For this to work, we're peeking into the next draw, therefore we need dirty registers.
|
||||
if (m_dirty_gs_regs == 0)
|
||||
return false;
|
||||
|
||||
if (!NextDrawMatchesShuffle())
|
||||
return false;
|
||||
|
||||
// Different channel being shuffled, so needs to be handled separately (misdetection in 50 Cent)
|
||||
if (m_vertex.buff[m_index.buff[0]].U != m_v.U)
|
||||
return false;
|
||||
|
||||
// Check that both the position and texture coordinates are page aligned, so we can work in pages instead of coordinates.
|
||||
// For texture shuffles, the U will be offset by 8.
|
||||
const GSLocalMemory::psm_t& frame_psm = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM];
|
||||
|
||||
const GSVector4i pos_rc = GSVector4i(m_vt.m_min.p.upld(m_vt.m_max.p + GSVector4::cxpr(0.5f)));
|
||||
const GSVector4i tex_rc = GSVector4i(m_vt.m_min.t.upld(m_vt.m_max.t));
|
||||
@@ -823,6 +850,8 @@ bool GSRendererHW::IsSplitTextureShuffle()
|
||||
// X might be offset by up to -8/+8, but either the position or UV should be aligned.
|
||||
GSVector4i aligned_rc = pos_rc.min_i32(tex_rc).blend32<12>(pos_rc.max_i32(tex_rc));
|
||||
|
||||
const GSLocalMemory::psm_t& frame_psm = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM];
|
||||
const GSDrawingContext& next_ctx = m_env.CTXT[m_backed_up_ctx];
|
||||
// Y should be page aligned. X should be too, but if it's doing a copy with a shuffle (which is kinda silly), both the
|
||||
// position and coordinates may be offset by +8. See Psi-Ops - The Mindgate Conspiracy.
|
||||
if ((aligned_rc.x & 7) != 0 || aligned_rc.x > 8 || (aligned_rc.z & 7) != 0 ||
|
||||
@@ -836,23 +865,26 @@ bool GSRendererHW::IsSplitTextureShuffle()
|
||||
|
||||
// We should have the same number of pages in both the position and UV.
|
||||
const u32 pages_high = static_cast<u32>(aligned_rc.height()) / frame_psm.pgs.y;
|
||||
const u32 num_pages = m_cached_ctx.FRAME.FBW * pages_high;
|
||||
const u32 num_pages = m_context->FRAME.FBW * pages_high;
|
||||
|
||||
// If this is a split texture shuffle, the next draw's FRAME/TEX0 should line up.
|
||||
// Re-add the offset we subtracted in Draw() to get the original FBP/TBP0.. this won't handle wrapping. Oh well.
|
||||
// "Potential" ones are for Jak3 which does a split shuffle on a 128x128 texture with a width of 256, writing to the lower half then offsetting 2 pages.
|
||||
const u32 expected_next_FBP = (m_cached_ctx.FRAME.FBP + m_split_texture_shuffle_pages) + num_pages;
|
||||
const u32 potential_expected_next_FBP = m_cached_ctx.FRAME.FBP + ((m_context->FRAME.FBW * 64) / aligned_rc.width());
|
||||
const u32 expected_next_TBP0 = (m_cached_ctx.TEX0.TBP0 + (m_split_texture_shuffle_pages + num_pages) * BLOCKS_PER_PAGE);
|
||||
const u32 potential_expected_next_TBP0 = m_cached_ctx.TEX0.TBP0 + (BLOCKS_PER_PAGE * ((m_context->TEX0.TBW * 64) / aligned_rc.width()));
|
||||
GL_CACHE("IsSplitTextureShuffle: Draw covers %ux%u pages, next FRAME %x TEX %x",
|
||||
static_cast<u32>(aligned_rc.width()) / frame_psm.pgs.x, pages_high, expected_next_FBP * BLOCKS_PER_PAGE,
|
||||
expected_next_TBP0);
|
||||
if (next_ctx.TEX0.TBP0 != expected_next_TBP0)
|
||||
if (next_ctx.TEX0.TBP0 != expected_next_TBP0 && next_ctx.TEX0.TBP0 != potential_expected_next_TBP0)
|
||||
{
|
||||
GL_CACHE("IsSplitTextureShuffle: Mismatch on TBP0, expecting %x, got %x", expected_next_TBP0, next_ctx.TEX0.TBP0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Some games don't offset the FBP.
|
||||
if (next_ctx.FRAME.FBP != expected_next_FBP && next_ctx.FRAME.FBP != m_cached_ctx.FRAME.FBP)
|
||||
if (next_ctx.FRAME.FBP != expected_next_FBP && next_ctx.FRAME.FBP != m_cached_ctx.FRAME.FBP && next_ctx.FRAME.FBP != potential_expected_next_FBP)
|
||||
{
|
||||
GL_CACHE("IsSplitTextureShuffle: Mismatch on FBP, expecting %x, got %x", expected_next_FBP * BLOCKS_PER_PAGE,
|
||||
next_ctx.FRAME.FBP * BLOCKS_PER_PAGE);
|
||||
@@ -866,10 +898,19 @@ bool GSRendererHW::IsSplitTextureShuffle()
|
||||
{
|
||||
m_split_texture_shuffle_start_FBP = m_cached_ctx.FRAME.FBP;
|
||||
m_split_texture_shuffle_start_TBP = m_cached_ctx.TEX0.TBP0;
|
||||
|
||||
// If the game has changed the texture width to 1 we need to retanslate it to whatever the rt has so the final rect is correct.
|
||||
if (m_cached_ctx.FRAME.FBW == 1)
|
||||
m_split_texture_shuffle_fbw = rt_tbw;
|
||||
else
|
||||
m_split_texture_shuffle_fbw = m_cached_ctx.FRAME.FBW;
|
||||
}
|
||||
|
||||
if ((m_split_texture_shuffle_pages % m_split_texture_shuffle_fbw) == 0)
|
||||
m_split_texture_shuffle_pages_high += pages_high;
|
||||
|
||||
m_split_texture_shuffle_pages += num_pages;
|
||||
m_split_texture_shuffle_pages_high += pages_high;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -881,7 +922,6 @@ GSVector4i GSRendererHW::GetSplitTextureShuffleDrawRect() const
|
||||
// Some games (e.g. Crash Twinsanity) adjust both FBP and TBP0, so the rectangle will be half the size
|
||||
// of the actual shuffle. Others leave the FBP alone, but only adjust TBP0, and offset the draw rectangle
|
||||
// to the second half of the fb. In which case, the rectangle bounds will be correct.
|
||||
|
||||
if (m_context->FRAME.FBP != m_split_texture_shuffle_start_FBP)
|
||||
{
|
||||
const int pages_high = (r.height() + frame_psm.pgs.y - 1) / frame_psm.pgs.y;
|
||||
@@ -1221,6 +1261,9 @@ void GSRendererHW::Move()
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_env.TRXDIR.XDIR == 3)
|
||||
return;
|
||||
|
||||
const int sx = m_env.TRXPOS.SSAX;
|
||||
const int sy = m_env.TRXPOS.SSAY;
|
||||
const int dx = m_env.TRXPOS.DSAX;
|
||||
@@ -1232,6 +1275,7 @@ void GSRendererHW::Move()
|
||||
if (g_texture_cache->Move(m_env.BITBLTBUF.SBP, m_env.BITBLTBUF.SBW, m_env.BITBLTBUF.SPSM, sx, sy,
|
||||
m_env.BITBLTBUF.DBP, m_env.BITBLTBUF.DBW, m_env.BITBLTBUF.DPSM, dx, dy, w, h))
|
||||
{
|
||||
m_env.TRXDIR.XDIR = 3;
|
||||
// Handled entirely in TC, no need to update local memory.
|
||||
return;
|
||||
}
|
||||
@@ -1883,6 +1927,7 @@ void GSRendererHW::Draw()
|
||||
|
||||
m_texture_shuffle = false;
|
||||
m_copy_16bit_to_target_shuffle = false;
|
||||
m_same_group_texture_shuffle = false;
|
||||
|
||||
const bool is_split_texture_shuffle = (m_split_texture_shuffle_pages > 0);
|
||||
if (is_split_texture_shuffle)
|
||||
@@ -1902,7 +1947,41 @@ void GSRendererHW::Draw()
|
||||
|
||||
// Fudge FRAME and TEX0 to point to the start of the shuffle.
|
||||
m_cached_ctx.TEX0.TBP0 = m_split_texture_shuffle_start_TBP;
|
||||
SetNewFRAME(m_split_texture_shuffle_start_FBP << 5, m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM);
|
||||
|
||||
// We correct this again at the end of the split
|
||||
SetNewFRAME(m_split_texture_shuffle_start_FBP << 5, m_context->FRAME.FBW, m_cached_ctx.FRAME.PSM);
|
||||
|
||||
// TEX0 may also be just using single width with offsets also, so let's deal with that.
|
||||
if (m_split_texture_shuffle_pages > 1 && !NextDrawMatchesShuffle())
|
||||
{
|
||||
if (m_context->FRAME.FBW != m_split_texture_shuffle_fbw && m_cached_ctx.TEX0.TBW == 1)
|
||||
{
|
||||
const GSLocalMemory::psm_t& frame_psm = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM];
|
||||
const GSLocalMemory::psm_t& tex_psm = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM];
|
||||
// This is the final draw of the shuffle, so let's fudge the numbers
|
||||
// Need to update the final rect as it could be wrong.
|
||||
if (m_context->FRAME.FBW == 1 && m_split_texture_shuffle_fbw != m_context->FRAME.FBW)
|
||||
{
|
||||
m_r.x = 0; // Need to keep the X offset to calculate the shuffle.
|
||||
m_r.z = m_split_texture_shuffle_fbw * frame_psm.pgs.x;
|
||||
m_r.y = 0;
|
||||
m_r.w = std::min(1024U, m_split_texture_shuffle_pages_high * frame_psm.pgs.y); // Max we can shuffle is 1024 (512)
|
||||
|
||||
//Fudge the scissor and frame
|
||||
m_context->scissor.in = m_r;
|
||||
|
||||
SetNewFRAME(m_split_texture_shuffle_start_FBP << 5, m_split_texture_shuffle_fbw, m_cached_ctx.FRAME.PSM);
|
||||
}
|
||||
|
||||
const int pages = m_split_texture_shuffle_pages + 1;
|
||||
const int width = m_split_texture_shuffle_fbw;
|
||||
const int height = (pages >= width) ? (pages / width) : 1;
|
||||
// We must update the texture size! It will likely be 64x64, which is no good, so let's fudge that.
|
||||
m_cached_ctx.TEX0.TW = std::ceil(std::log2(std::min(1024, width * tex_psm.pgs.x)));
|
||||
m_cached_ctx.TEX0.TH = std::ceil(std::log2(std::min(1024, height * tex_psm.pgs.y)));
|
||||
m_cached_ctx.TEX0.TBW = m_split_texture_shuffle_fbw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto cleanup_draw = [&]() {
|
||||
@@ -2112,9 +2191,30 @@ void GSRendererHW::Draw()
|
||||
GL_CACHE("Estimated texture region: %u,%u -> %u,%u", MIP_CLAMP.MINU, MIP_CLAMP.MINV, MIP_CLAMP.MAXU + 1,
|
||||
MIP_CLAMP.MAXV + 1);
|
||||
}
|
||||
const bool possible_shuffle = m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0 || IsPossibleChannelShuffle();
|
||||
src = tex_psm.depth ? g_texture_cache->LookupDepthSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, possible_shuffle) :
|
||||
g_texture_cache->LookupSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, (GSConfig.HWMipmap >= HWMipmapLevel::Basic || GSConfig.TriFilter == TriFiltering::Forced) ? &hash_lod_range : nullptr, possible_shuffle);
|
||||
|
||||
GIFRegTEX0 FRAME_TEX0;
|
||||
bool rt_32bit = false;
|
||||
if (!no_rt && m_cached_ctx.FRAME.Block() != m_cached_ctx.TEX0.TBP0 && GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16)
|
||||
{
|
||||
// FBW is going to be wrong for channel shuffling into a new target, so take it from the source.
|
||||
FRAME_TEX0.U64 = 0;
|
||||
FRAME_TEX0.TBP0 = m_cached_ctx.FRAME.Block();
|
||||
FRAME_TEX0.TBW = m_cached_ctx.FRAME.FBW;
|
||||
FRAME_TEX0.PSM = m_cached_ctx.FRAME.PSM;
|
||||
|
||||
GSTextureCache::Target* tgt = g_texture_cache->LookupTarget(FRAME_TEX0, GSVector2i(m_vt.m_max.p.x, m_vt.m_max.p.y), GetTextureScaleFactor(), GSTextureCache::RenderTarget, true,
|
||||
fm);
|
||||
|
||||
if (tgt)
|
||||
rt_32bit = tgt->m_32_bits_fmt;
|
||||
|
||||
tgt = nullptr;
|
||||
}
|
||||
const bool possible_shuffle = ((rt_32bit && GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16) || m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0) || IsPossibleChannelShuffle();
|
||||
|
||||
src = tex_psm.depth ? g_texture_cache->LookupDepthSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, possible_shuffle, m_vt.IsLinear(), m_cached_ctx.FRAME.Block()) :
|
||||
g_texture_cache->LookupSource(TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, (GSConfig.HWMipmap >= HWMipmapLevel::Basic || GSConfig.TriFilter == TriFiltering::Forced) ? &hash_lod_range : nullptr, possible_shuffle, m_vt.IsLinear(), m_cached_ctx.FRAME.Block());
|
||||
|
||||
if (unlikely(!src))
|
||||
{
|
||||
GL_INS("ERROR: Source lookup failed, skipping.");
|
||||
@@ -2156,7 +2256,9 @@ void GSRendererHW::Draw()
|
||||
|
||||
// Ensure draw rect is clamped to framebuffer size. Necessary for updating valid area.
|
||||
const GSVector4i unclamped_draw_rect = m_r;
|
||||
m_r = m_r.rintersect(t_size_rect);
|
||||
// Don't clamp on shuffle, the height cache may troll us with the REAL height.
|
||||
if (!m_texture_shuffle && m_split_texture_shuffle_pages == 0)
|
||||
m_r = m_r.rintersect(t_size_rect);
|
||||
|
||||
float target_scale = GetTextureScaleFactor();
|
||||
|
||||
@@ -2200,7 +2302,7 @@ void GSRendererHW::Draw()
|
||||
}
|
||||
|
||||
rt = g_texture_cache->CreateTarget(FRAME_TEX0, t_size, GetValidSize(src), target_scale, GSTextureCache::RenderTarget, true,
|
||||
fm, false, force_preload, preserve_rt_color, m_r);
|
||||
fm, false, force_preload, preserve_rt_color, m_r, src);
|
||||
if (unlikely(!rt))
|
||||
{
|
||||
GL_INS("ERROR: Failed to create FRAME target, skipping.");
|
||||
@@ -2224,7 +2326,7 @@ void GSRendererHW::Draw()
|
||||
if (!ds)
|
||||
{
|
||||
ds = g_texture_cache->CreateTarget(ZBUF_TEX0, t_size, GetValidSize(src), target_scale, GSTextureCache::DepthStencil,
|
||||
m_cached_ctx.DepthWrite(), 0, false, force_preload, preserve_depth, m_r);
|
||||
m_cached_ctx.DepthWrite(), 0, false, force_preload, preserve_depth, m_r, src);
|
||||
if (unlikely(!ds))
|
||||
{
|
||||
GL_INS("ERROR: Failed to create ZBUF target, skipping.");
|
||||
@@ -2237,20 +2339,33 @@ void GSRendererHW::Draw()
|
||||
if (process_texture)
|
||||
{
|
||||
GIFRegCLAMP MIP_CLAMP = m_cached_ctx.CLAMP;
|
||||
const GSVertex* v = &m_vertex.buff[0];
|
||||
|
||||
if (rt)
|
||||
{
|
||||
// Hypothesis: texture shuffle is used as a postprocessing effect so texture will be an old target.
|
||||
// Initially code also tested the RT but it gives too much false-positive
|
||||
const int first_x = ((v[0].XYZ.X - m_context->XYOFFSET.OFX) + 8) >> 4;
|
||||
const int first_u = PRIM->FST ? ((v[0].U + 8) >> 4) : static_cast<int>(((1 << m_cached_ctx.TEX0.TW) * (v[0].ST.S / v[1].RGBAQ.Q)) + 0.5f);
|
||||
const bool shuffle_coords = (first_x ^ first_u) & 8;
|
||||
const u32 draw_end = GSLocalMemory::GetEndBlockAddress(m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, m_r) + 1;
|
||||
const bool draw_uses_target = src->m_from_target && ((src->m_from_target_TEX0.TBP0 <= m_cached_ctx.FRAME.Block() &&
|
||||
src->m_from_target->UnwrappedEndBlock() > m_cached_ctx.FRAME.Block()) ||
|
||||
(m_cached_ctx.FRAME.Block() < src->m_from_target_TEX0.TBP0 && draw_end > src->m_from_target_TEX0.TBP0));
|
||||
|
||||
// copy of a 16bit source in to this target, make sure it's opaque and not bilinear to reduce false positives.
|
||||
m_copy_16bit_to_target_shuffle = m_cached_ctx.TEX0.TBP0 != m_cached_ctx.FRAME.Block() && rt->m_32_bits_fmt == true && IsOpaque()
|
||||
&& !(context->TEX1.MMIN & 1) && !src->m_32_bits_fmt && m_cached_ctx.FRAME.FBMSK;
|
||||
}
|
||||
|
||||
// Hypothesis: texture shuffle is used as a postprocessing effect so texture will be an old target.
|
||||
// Initially code also tested the RT but it gives too much false-positive
|
||||
//
|
||||
// Both input and output are 16 bits and texture was initially 32 bits!
|
||||
m_texture_shuffle = (GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16) && (tex_psm.bpp == 16)
|
||||
&& draw_sprite_tex && (src->m_32_bits_fmt || m_copy_16bit_to_target_shuffle);
|
||||
// It's not actually possible to do a C16->C16 texture shuffle of B to A as they are the same group
|
||||
// However you can do it by using C32 and offsetting the target verticies to point to B A, then mask as appropriate.
|
||||
m_same_group_texture_shuffle = draw_uses_target && (m_cached_ctx.TEX0.PSM & 0xE) == PSMCT32 && (m_cached_ctx.FRAME.PSM & 0x7) == PSMCT16 && (m_vt.m_min.p.x == 8.0f);
|
||||
|
||||
// Both input and output are 16 bits and texture was initially 32 bits! Same for the target, Sonic Unleash makes a new target which really is 16bit.
|
||||
m_texture_shuffle = ((m_same_group_texture_shuffle || (tex_psm.bpp == 16)) && (GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16) &&
|
||||
(shuffle_coords || rt->m_32_bits_fmt))
|
||||
&& draw_sprite_tex && (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_cached_ctx.FRAME.FBMSK & fm_mask) == 0))
|
||||
@@ -2261,7 +2376,6 @@ void GSRendererHW::Draw()
|
||||
|
||||
// Shadow of Memories/Destiny shouldn't call this code.
|
||||
// Causes shadow flickering.
|
||||
const GSVertex* v = &m_vertex.buff[0];
|
||||
m_texture_shuffle = ((v[1].U - v[0].U) < 256) ||
|
||||
// Tomb Raider Angel of Darkness relies on this behavior to produce a fog effect.
|
||||
// In this case, the address of the framebuffer and texture are the same.
|
||||
@@ -2276,7 +2390,7 @@ void GSRendererHW::Draw()
|
||||
GL_INS("WARNING: Possible misdetection of effect, texture shuffle is %s", m_texture_shuffle ? "Enabled" : "Disabled");
|
||||
}
|
||||
|
||||
if (m_texture_shuffle && IsSplitTextureShuffle())
|
||||
if (m_texture_shuffle && IsSplitTextureShuffle(rt->m_TEX0.TBW))
|
||||
{
|
||||
// If TEX0 == FBP, we're going to have a source left in the TC.
|
||||
// That source will get used in the actual draw unsafely, so kick it out.
|
||||
@@ -2452,9 +2566,23 @@ void GSRendererHW::Draw()
|
||||
GSTextureCache::Target* old_rt = nullptr;
|
||||
GSTextureCache::Target* old_ds = nullptr;
|
||||
{
|
||||
GSVector2i new_size = t_size;
|
||||
|
||||
// We need to adjust the size if it's a texture shuffle as we could end up making the RT twice the size.
|
||||
if (rt && m_texture_shuffle && m_split_texture_shuffle_pages == 0)
|
||||
{
|
||||
if ((new_size.x > rt->m_valid.z && m_vt.m_max.p.x == new_size.x) || (new_size.y > rt->m_valid.w && m_vt.m_max.p.y == new_size.y))
|
||||
{
|
||||
if (new_size.y <= rt->m_valid.w && (rt->m_TEX0.TBW != m_cached_ctx.FRAME.FBW))
|
||||
new_size.x /= 2;
|
||||
else
|
||||
new_size.y /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
// We still need to make sure the dimensions of the targets match.
|
||||
const int new_w = std::max(t_size.x, std::max(rt ? rt->m_unscaled_size.x : 0, ds ? ds->m_unscaled_size.x : 0));
|
||||
const int new_h = std::max(t_size.y, std::max(rt ? rt->m_unscaled_size.y : 0, ds ? ds->m_unscaled_size.y : 0));
|
||||
const int new_w = std::max(new_size.x, std::max(rt ? rt->m_unscaled_size.x : 0, ds ? ds->m_unscaled_size.x : 0));
|
||||
const int new_h = std::max(new_size.y, std::max(rt ? rt->m_unscaled_size.y : 0, ds ? ds->m_unscaled_size.y : 0));
|
||||
if (rt)
|
||||
{
|
||||
const u32 old_end_block = rt->m_end_block;
|
||||
@@ -2465,6 +2593,7 @@ void GSRendererHW::Draw()
|
||||
pxAssert(rt->GetScale() == target_scale);
|
||||
if (rt->GetUnscaledWidth() != new_w || rt->GetUnscaledHeight() != new_h)
|
||||
GL_INS("Resize RT from %dx%d to %dx%d", rt->GetUnscaledWidth(), rt->GetUnscaledHeight(), new_w, new_h);
|
||||
|
||||
rt->ResizeTexture(new_w, new_h);
|
||||
|
||||
if (!m_texture_shuffle && !m_channel_shuffle)
|
||||
@@ -2474,8 +2603,8 @@ void GSRendererHW::Draw()
|
||||
}
|
||||
|
||||
// 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->UpdateDrawn(m_r, can_update_size || m_r.w <= (resolution.y * 2));
|
||||
rt->UpdateValidity(m_r, can_update_size || (m_r.w <= (resolution.y * 2) && !m_texture_shuffle));
|
||||
rt->UpdateDrawn(m_r, can_update_size || (m_r.w <= (resolution.y * 2) && !m_texture_shuffle));
|
||||
// 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.
|
||||
@@ -2529,7 +2658,7 @@ void GSRendererHW::Draw()
|
||||
{
|
||||
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();
|
||||
GL_INS("DS double buffer copy from FBP 0x%x, %dx%d => %d,%d", old_rt->m_TEX0.TBP0, copy_width, copy_height, 0, old_height);
|
||||
GL_INS("DS double buffer copy from FBP 0x%x, %dx%d => %d,%d", old_ds->m_TEX0.TBP0, copy_width, copy_height, 0, old_height);
|
||||
|
||||
g_gs_device->CopyRect(old_ds->m_texture, ds->m_texture, GSVector4i(0, 0, copy_width, copy_height), 0, old_height);
|
||||
preserve_depth = true;
|
||||
@@ -2678,7 +2807,7 @@ void GSRendererHW::Draw()
|
||||
{
|
||||
//rt->m_valid = rt->m_valid.runion(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->UpdateValidity(m_r, can_update_size || (m_r.w <= (resolution.y * 2) && !m_texture_shuffle));
|
||||
|
||||
g_texture_cache->InvalidateVideoMem(context->offset.fb, m_r, false);
|
||||
|
||||
@@ -2691,7 +2820,7 @@ void GSRendererHW::Draw()
|
||||
{
|
||||
//ds->m_valid = ds->m_valid.runion(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->UpdateValidity(m_r, can_update_size || m_r.w <= (resolution.y * 2) && !m_texture_shuffle);
|
||||
|
||||
g_texture_cache->InvalidateVideoMem(context->offset.zb, m_r, false);
|
||||
|
||||
@@ -2939,7 +3068,7 @@ void GSRendererHW::EmulateZbuffer(const GSTextureCache::Target* ds)
|
||||
}
|
||||
}
|
||||
|
||||
void GSRendererHW::EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt)
|
||||
void GSRendererHW::EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GSTextureCache::Source* tex)
|
||||
{
|
||||
// Uncomment to disable texture shuffle emulation.
|
||||
// m_texture_shuffle = false;
|
||||
@@ -2982,7 +3111,7 @@ void GSRendererHW::EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt)
|
||||
bool write_ba;
|
||||
bool read_ba;
|
||||
|
||||
ConvertSpriteTextureShuffle(write_ba, read_ba);
|
||||
ConvertSpriteTextureShuffle(write_ba, read_ba, rt, tex);
|
||||
|
||||
// If date is enabled you need to test the green channel instead of the
|
||||
// alpha channel. Only enable this code in DATE mode to reduce the number
|
||||
@@ -2991,6 +3120,7 @@ void GSRendererHW::EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt)
|
||||
|
||||
m_conf.ps.read_ba = read_ba;
|
||||
m_conf.ps.real16src = m_copy_16bit_to_target_shuffle;
|
||||
m_conf.ps.shuffle_same = m_same_group_texture_shuffle;
|
||||
// Please bang my head against the wall!
|
||||
// 1/ Reduce the frame mask to a 16 bit format
|
||||
const u32 m = m_cached_ctx.FRAME.FBMSK & GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].fmsk;
|
||||
@@ -3336,21 +3466,23 @@ __ri bool GSRendererHW::EmulateChannelShuffle(GSTextureCache::Target* src, bool
|
||||
|
||||
void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DATE_PRIMID, bool& DATE_BARRIER, bool& blending_alpha_pass)
|
||||
{
|
||||
// AA1: Don't enable blending on AA1, not yet implemented on hardware mode,
|
||||
// it requires coverage sample so it's safer to turn it off instead.
|
||||
const bool AA1 = PRIM->AA1 && (m_vt.m_primclass == GS_LINE_CLASS || m_vt.m_primclass == GS_TRIANGLE_CLASS);
|
||||
// PABE: Check condition early as an optimization.
|
||||
const bool PABE = PRIM->ABE && m_draw_env->PABE.PABE && (GetAlphaMinMax().max < 128);
|
||||
// FBMASK: Color is not written, no need to do blending.
|
||||
const u32 temp_fbmask = m_conf.ps.dfmt == 2 ? 0x00F8F8F8 : 0x00FFFFFF;
|
||||
const bool FBMASK = (m_cached_ctx.FRAME.FBMSK & temp_fbmask) == temp_fbmask;
|
||||
|
||||
// No blending or coverage anti-aliasing so early exit
|
||||
if (FBMASK || PABE || !(PRIM->ABE || AA1))
|
||||
{
|
||||
m_conf.blend = {};
|
||||
m_conf.ps.no_color1 = true;
|
||||
return;
|
||||
// AA1: Don't enable blending on AA1, not yet implemented on hardware mode,
|
||||
// it requires coverage sample so it's safer to turn it off instead.
|
||||
const bool AA1 = PRIM->AA1 && (m_vt.m_primclass == GS_LINE_CLASS || m_vt.m_primclass == GS_TRIANGLE_CLASS);
|
||||
// PABE: Check condition early as an optimization.
|
||||
const bool PABE = PRIM->ABE && m_draw_env->PABE.PABE && (GetAlphaMinMax().max < 128);
|
||||
// FBMASK: Color is not written, no need to do blending.
|
||||
const u32 temp_fbmask = m_conf.ps.dfmt == 2 ? 0x00F8F8F8 : 0x00FFFFFF;
|
||||
const bool FBMASK = (m_cached_ctx.FRAME.FBMSK & temp_fbmask) == temp_fbmask;
|
||||
|
||||
// No blending or coverage anti-aliasing so early exit
|
||||
if (FBMASK || PABE || !(PRIM->ABE || AA1))
|
||||
{
|
||||
m_conf.blend = {};
|
||||
m_conf.ps.no_color1 = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the blending equation to detect special case
|
||||
@@ -3380,11 +3512,20 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT
|
||||
m_conf.ps.fixed_one_a = 1;
|
||||
m_conf.ps.blend_c = 0;
|
||||
}
|
||||
// 24 bits doesn't have an alpha channel so use 128 (1.0f) fix factor as equivalent.
|
||||
else if (m_conf.ps.dfmt == 1 && m_conf.ps.blend_c == 1)
|
||||
else if (m_conf.ps.blend_c == 1)
|
||||
{
|
||||
AFIX = 128;
|
||||
m_conf.ps.blend_c = 2;
|
||||
// When both rt alpha min and max are equal replace Ad with Af, easier to manage.
|
||||
if (rt_alpha_min == rt_alpha_max)
|
||||
{
|
||||
AFIX = rt_alpha_min;
|
||||
m_conf.ps.blend_c = 2;
|
||||
}
|
||||
// 24 bits doesn't have an alpha channel so use 128 (1.0f) fix factor as equivalent.
|
||||
else if (m_conf.ps.dfmt == 1)
|
||||
{
|
||||
AFIX = 128;
|
||||
m_conf.ps.blend_c = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Get alpha value
|
||||
@@ -3392,12 +3533,10 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT
|
||||
const bool alpha_c0_one = (m_conf.ps.blend_c == 0 && (GetAlphaMinMax().min == 128) && (GetAlphaMinMax().max == 128));
|
||||
const bool alpha_c0_high_min_one = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().min > 128);
|
||||
const bool alpha_c0_high_max_one = (m_conf.ps.blend_c == 0 && GetAlphaMinMax().max > 128);
|
||||
const bool alpha_c1_zero = (m_conf.ps.blend_c == 1 && rt_alpha_min == 0 && rt_alpha_max == 0);
|
||||
const bool alpha_c1_one = (m_conf.ps.blend_c == 1 && rt_alpha_min == 128 && rt_alpha_max == 128);
|
||||
const bool alpha_c2_zero = (m_conf.ps.blend_c == 2 && AFIX == 0u);
|
||||
const bool alpha_c2_one = (m_conf.ps.blend_c == 2 && AFIX == 128u);
|
||||
const bool alpha_c2_high_one = (m_conf.ps.blend_c == 2 && AFIX > 128u);
|
||||
const bool alpha_one = alpha_c0_one || alpha_c1_one || alpha_c2_one;
|
||||
const bool alpha_one = alpha_c0_one || alpha_c2_one;
|
||||
|
||||
// Optimize blending equations, must be done before index calculation
|
||||
if ((m_conf.ps.blend_a == m_conf.ps.blend_b) || ((m_conf.ps.blend_b == m_conf.ps.blend_d) && alpha_one))
|
||||
@@ -3416,7 +3555,7 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT
|
||||
m_conf.ps.blend_b = 0;
|
||||
m_conf.ps.blend_c = 0;
|
||||
}
|
||||
else if (alpha_c0_zero || alpha_c1_zero || alpha_c2_zero)
|
||||
else if (alpha_c0_zero || alpha_c2_zero)
|
||||
{
|
||||
// C == 0.0f
|
||||
// (A - B) * C, result will be 0.0f so set A B to Cs
|
||||
@@ -3887,7 +4026,7 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DAT
|
||||
{
|
||||
m_conf.ps.blend_hw = 1;
|
||||
}
|
||||
else if (blend_flag & (BLEND_HW_CLR2))
|
||||
else if (blend_flag & BLEND_HW_CLR2)
|
||||
{
|
||||
if (m_conf.ps.blend_c == 2)
|
||||
m_conf.cb_ps.TA_MaxDepth_Af.a = static_cast<float>(AFIX) / 128.0f;
|
||||
@@ -4044,7 +4183,7 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
|
||||
// Force a 32 bits access (normally shuffle is done on 16 bits)
|
||||
// m_ps_sel.tex_fmt = 0; // removed as an optimization
|
||||
m_conf.ps.aem = TEXA.AEM;
|
||||
ASSERT(tex->m_target);
|
||||
//ASSERT(tex->m_target);
|
||||
|
||||
// Require a float conversion if the texure is a depth otherwise uses Integral scaling
|
||||
if (psm.depth)
|
||||
@@ -4612,7 +4751,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
||||
|
||||
m_prim_overlap = PrimitiveOverlap();
|
||||
|
||||
EmulateTextureShuffleAndFbmask(rt);
|
||||
EmulateTextureShuffleAndFbmask(rt, tex);
|
||||
|
||||
const GSDevice::FeatureSupport features = g_gs_device->Features();
|
||||
|
||||
@@ -5461,7 +5600,7 @@ bool GSRendererHW::DetectDoubleHalfClear(bool& no_rt, bool& no_ds)
|
||||
const bool clear_depth = (m_cached_ctx.FRAME.FBP > m_cached_ctx.ZBUF.ZBP);
|
||||
const u32 base = clear_depth ? m_cached_ctx.ZBUF.ZBP : m_cached_ctx.FRAME.FBP;
|
||||
const u32 half = clear_depth ? m_cached_ctx.FRAME.FBP : m_cached_ctx.ZBUF.ZBP;
|
||||
|
||||
const bool enough_bits = clear_depth ? (frame_psm.trbpp >= zbuf_psm.trbpp) : (zbuf_psm.trbpp >= frame_psm.trbpp);
|
||||
// Size of the current draw
|
||||
const u32 w_pages = (m_r.z + (frame_psm.pgs.x - 1)) / frame_psm.pgs.x;
|
||||
const u32 h_pages = (m_r.w + (frame_psm.pgs.y - 1)) / frame_psm.pgs.y;
|
||||
@@ -5475,7 +5614,7 @@ bool GSRendererHW::DetectDoubleHalfClear(bool& no_rt, bool& no_ds)
|
||||
// GTA: LCS does this setup, along with a few other games. Thankfully if it's a zero clear, we'll clear both
|
||||
// separately, and the end result is the same because it gets invalidated. That's better than falsely detecting
|
||||
// double half clears, and ending up with 1024 high render targets which really shouldn't be.
|
||||
if ((frame_psm.fmt != zbuf_psm.fmt && m_cached_ctx.FRAME.FBMSK != ((zbuf_psm.fmt == 1) ? 0xFF000000u : 0)) ||
|
||||
if ((!enough_bits && frame_psm.fmt != zbuf_psm.fmt && m_cached_ctx.FRAME.FBMSK != ((zbuf_psm.fmt == 1) ? 0xFF000000u : 0)) ||
|
||||
!GSUtil::HasCompatibleBits(m_cached_ctx.FRAME.PSM & ~0x30, m_cached_ctx.ZBUF.PSM & ~0x30)) // Bit depth is not the same (i.e. 32bit + 16bit).
|
||||
{
|
||||
GL_INS("Inconsistent FRAME [%s, %08x] and ZBUF [%s] formats, not using double-half clear.",
|
||||
|
||||
@@ -86,7 +86,7 @@ private:
|
||||
|
||||
void ResetStates();
|
||||
void SetupIA(float target_scale, float sx, float sy);
|
||||
void EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt);
|
||||
void EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GSTextureCache::Source* tex);
|
||||
bool EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only);
|
||||
void EmulateBlending(int rt_alpha_min, int rt_alpha_max, bool& DATE_PRIMID, bool& DATE_BARRIER, bool& blending_alpha_pass);
|
||||
|
||||
@@ -104,7 +104,8 @@ private:
|
||||
void SetTCOffset();
|
||||
|
||||
bool IsPossibleChannelShuffle() const;
|
||||
bool IsSplitTextureShuffle();
|
||||
bool NextDrawMatchesShuffle() const;
|
||||
bool IsSplitTextureShuffle(u32 rt_tbw);
|
||||
GSVector4i GetSplitTextureShuffleDrawRect() const;
|
||||
u32 GetEffectiveTextureShuffleFbmsk() const;
|
||||
|
||||
@@ -155,6 +156,7 @@ private:
|
||||
u32 m_split_texture_shuffle_pages_high = 0;
|
||||
u32 m_split_texture_shuffle_start_FBP = 0;
|
||||
u32 m_split_texture_shuffle_start_TBP = 0;
|
||||
u32 m_split_texture_shuffle_fbw = 0;
|
||||
|
||||
u32 m_last_channel_shuffle_fbmsk = 0;
|
||||
|
||||
@@ -192,7 +194,7 @@ public:
|
||||
void Lines2Sprites();
|
||||
bool VerifyIndices();
|
||||
void ExpandLineIndices();
|
||||
void ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba);
|
||||
void ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba, GSTextureCache::Target* rt, GSTextureCache::Source* tex);
|
||||
GSVector4 RealignTargetTextureCoordinate(const GSTextureCache::Source* tex);
|
||||
GSVector4i ComputeBoundingBox(const GSVector2i& rtsize, float rtscale);
|
||||
void MergeSprite(GSTextureCache::Source* tex);
|
||||
|
||||
@@ -586,7 +586,7 @@ __ri static GSTextureCache::Source* FindSourceInMap(const GIFRegTEX0& TEX0, cons
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const bool possible_shuffle, bool palette)
|
||||
GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const bool possible_shuffle, const bool linear, const u32 frame_fbp, bool palette)
|
||||
{
|
||||
if (GSConfig.UserHacks_DisableDepthSupport)
|
||||
{
|
||||
@@ -646,12 +646,19 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
|
||||
const GSVector2i page_size = GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs;
|
||||
const bool can_translate = CanTranslate(bp, TEX0.TBW, psm, r, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||
const bool swizzle_match = psm_s.depth == GSLocalMemory::m_psm[t->m_TEX0.PSM].depth;
|
||||
GSVector4i new_rect = r;
|
||||
|
||||
if (linear)
|
||||
{
|
||||
new_rect.z -= 1;
|
||||
new_rect.w -= 1;
|
||||
}
|
||||
|
||||
if (can_translate)
|
||||
{
|
||||
if (swizzle_match)
|
||||
{
|
||||
target_rc = TranslateAlignedRectByPage(t, bp, psm, TEX0.TBW, r);
|
||||
target_rc = TranslateAlignedRectByPage(t, bp, psm, TEX0.TBW, new_rect);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -669,8 +676,8 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
|
||||
{
|
||||
target_rc.x &= ~(page_size.x - 1);
|
||||
target_rc.y &= ~(page_size.y - 1);
|
||||
target_rc.z = (r.z + (page_size.x - 1)) & ~(page_size.x - 1);
|
||||
target_rc.w = (r.w + (page_size.y - 1)) & ~(page_size.y - 1);
|
||||
target_rc.z = (new_rect.z + (page_size.x - 1)) & ~(page_size.x - 1);
|
||||
target_rc.w = (new_rect.w + (page_size.y - 1)) & ~(page_size.y - 1);
|
||||
}
|
||||
target_rc = TranslateAlignedRectByPage(t, bp & ~((1 << 5) - 1), psm, TEX0.TBW, target_rc);
|
||||
}
|
||||
@@ -681,6 +688,11 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
|
||||
inside_target = true;
|
||||
}
|
||||
}
|
||||
if (linear)
|
||||
{
|
||||
new_rect.z += 1;
|
||||
new_rect.w += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -688,7 +700,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
|
||||
{
|
||||
for (Target* t : m_dst[RenderTarget])
|
||||
{
|
||||
if (t->m_age <= 1 && t->m_TEX0.TBP0 == bp && t->HasValidAlpha())
|
||||
if (t->m_age <= 1 && t->m_TEX0.TBP0 == bp && t->m_TEX0.TBW == TEX0.TBW && t->HasValidAlpha())
|
||||
{
|
||||
GL_CACHE("TC depth: Using RT %x instead of depth because of missing alpha", t->m_TEX0.TBP0);
|
||||
|
||||
@@ -764,7 +776,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
|
||||
else
|
||||
{
|
||||
// This is a bit of a worry, since it could load junk from local memory... but it's better than skipping the draw.
|
||||
return LookupSource(TEX0, TEXA, CLAMP, r, nullptr, possible_shuffle);
|
||||
return LookupSource(TEX0, TEXA, CLAMP, r, nullptr, possible_shuffle, linear, frame_fbp);
|
||||
}
|
||||
|
||||
ASSERT(src->m_texture);
|
||||
@@ -773,7 +785,7 @@ GSTextureCache::Source* GSTextureCache::LookupDepthSource(const GIFRegTEX0& TEX0
|
||||
return src;
|
||||
}
|
||||
|
||||
GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const GSVector2i* lod, const bool possible_shuffle)
|
||||
GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const GSVector2i* lod, const bool possible_shuffle, const bool linear, const u32 frame_fbp)
|
||||
{
|
||||
GL_CACHE("TC: Lookup Source <%d,%d => %d,%d> (0x%x, %s, BW: %u, CBP: 0x%x, TW: %d, TH: %d)", r.x, r.y, r.z, r.w, TEX0.TBP0, psm_str(TEX0.PSM), TEX0.TBW, TEX0.CBP, 1 << TEX0.TW, 1 << TEX0.TH);
|
||||
|
||||
@@ -857,6 +869,13 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
if (rect_clean && tex_overlaps && !t->m_dirty.empty() && width_match)
|
||||
{
|
||||
GSVector4i new_rect = r;
|
||||
|
||||
if (linear)
|
||||
{
|
||||
new_rect.z -= 1;
|
||||
new_rect.w -= 1;
|
||||
}
|
||||
|
||||
bool partial = false;
|
||||
// If it's compatible and page aligned, then handle it this way.
|
||||
// It's quicker, and Surface Offsets can get it wrong.
|
||||
@@ -865,14 +884,14 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
if (bp > t->m_TEX0.TBP0)
|
||||
{
|
||||
const GSVector2i page_size = GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs;
|
||||
const bool can_translate = CanTranslate(bp, bw, psm, r, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||
const bool can_translate = CanTranslate(bp, bw, psm, new_rect, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||
const bool swizzle_match = GSLocalMemory::m_psm[psm].depth == GSLocalMemory::m_psm[t->m_TEX0.PSM].depth;
|
||||
|
||||
if (can_translate)
|
||||
{
|
||||
if (swizzle_match)
|
||||
{
|
||||
new_rect = TranslateAlignedRectByPage(t, bp, psm, bw, r);
|
||||
new_rect = TranslateAlignedRectByPage(t, bp, psm, bw, new_rect);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -887,8 +906,8 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
{
|
||||
new_rect.x &= ~(page_size.x - 1);
|
||||
new_rect.y &= ~(page_size.y - 1);
|
||||
new_rect.z = (r.z + (page_size.x - 1)) & ~(page_size.x - 1);
|
||||
new_rect.w = (r.w + (page_size.y - 1)) & ~(page_size.y - 1);
|
||||
new_rect.z = (new_rect.z + (page_size.x - 1)) & ~(page_size.x - 1);
|
||||
new_rect.w = (new_rect.w + (page_size.y - 1)) & ~(page_size.y - 1);
|
||||
}
|
||||
new_rect = TranslateAlignedRectByPage(t, bp & ~((1 << 5) - 1), psm, bw, new_rect);
|
||||
}
|
||||
@@ -901,7 +920,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
sok.elems[0].bp = bp;
|
||||
sok.elems[0].bw = bw;
|
||||
sok.elems[0].psm = psm;
|
||||
sok.elems[0].rect = r;
|
||||
sok.elems[0].rect = new_rect;
|
||||
sok.elems[1].bp = t->m_TEX0.TBP0;
|
||||
sok.elems[1].bw = t->m_TEX0.TBW;
|
||||
sok.elems[1].psm = t->m_TEX0.PSM;
|
||||
@@ -941,6 +960,12 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
// If not all channels are clean/dirty or only part of the rect is dirty, we need to update the target.
|
||||
if (((channels & channel_mask) != channel_mask || partial))
|
||||
t->Update();
|
||||
|
||||
if (linear)
|
||||
{
|
||||
new_rect.z += 1;
|
||||
new_rect.w += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1006,51 +1031,80 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
// Make sure the texture actually is INSIDE the RT, it's possibly not valid if it isn't.
|
||||
// Also check BP >= TBP, create source isn't equpped to expand it backwards and all data comes from the target. (GH3)
|
||||
else if (GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets && psm >= PSMCT32 &&
|
||||
psm <= PSMCT16S && GSUtil::HasCompatibleBits(t->m_TEX0.PSM, psm) && (t->Overlaps(bp, bw, psm, r) || t->Wraps()) &&
|
||||
t->m_age <= 1 && (!found_t || dst->m_TEX0.TBW < bw))
|
||||
psm <= PSMCT16S && (GSUtil::HasCompatibleBits(t->m_TEX0.PSM, psm) ||
|
||||
(possible_shuffle && t->m_TEX0.PSM <= PSMCT24 && ((((t->UnwrappedEndBlock() + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) == bp))
|
||||
&& (t->Overlaps(bp, bw, psm, r) || t->Wraps()) &&
|
||||
t->m_age <= 1 && (!found_t || dst->m_TEX0.TBW < bw))
|
||||
{
|
||||
// PSM equality needed because CreateSource does not handle PSM conversion.
|
||||
// Only inclusive hit to limit false hits.
|
||||
GSVector4i rect = r;
|
||||
int src_bw = bw;
|
||||
int src_psm = psm;
|
||||
|
||||
// If the input is C16 and it's actually a shuffle of 32bits we need to correct the size.
|
||||
if ((t->m_TEX0.PSM & 0xF) == PSMCT32 && (psm & 0x7) == PSMCT16 && possible_shuffle)
|
||||
{
|
||||
src_psm = t->m_TEX0.PSM;
|
||||
// If it's taking double width for the shuffle, half that.
|
||||
if (src_bw == (t->m_TEX0.TBW * 2))
|
||||
{
|
||||
src_bw = t->m_TEX0.TBW;
|
||||
|
||||
rect.x /= 2;
|
||||
rect.z /= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
rect.y /= 2;
|
||||
rect.w /= 2;
|
||||
}
|
||||
}
|
||||
if (bp > t->m_TEX0.TBP0)
|
||||
{
|
||||
GSVector4i new_rect = rect;
|
||||
if (linear)
|
||||
{
|
||||
new_rect.z -= 1;
|
||||
new_rect.w -= 1;
|
||||
}
|
||||
// Check if it is possible to hit with valid <x,y> offset on the given Target.
|
||||
// Fixes Jak eyes rendering.
|
||||
// Fixes Xenosaga 3 last dungeon graphic bug.
|
||||
// Fixes Pause menu in The Getaway.
|
||||
const bool can_translate = CanTranslate(bp, bw, psm, r, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||
const bool can_translate = CanTranslate(bp, bw, psm, new_rect, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||
if (can_translate)
|
||||
{
|
||||
const bool swizzle_match = GSLocalMemory::m_psm[psm].depth == GSLocalMemory::m_psm[t->m_TEX0.PSM].depth;
|
||||
const bool swizzle_match = GSLocalMemory::m_psm[src_psm].depth == GSLocalMemory::m_psm[t->m_TEX0.PSM].depth;
|
||||
const GSVector2i& page_size = GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs;
|
||||
const GSVector4i page_mask(GSVector4i((page_size.x - 1), (page_size.y - 1)).xyxy());
|
||||
GSVector4i rect = r & ~page_mask;
|
||||
rect = new_rect & ~page_mask;
|
||||
|
||||
if (swizzle_match)
|
||||
{
|
||||
rect = TranslateAlignedRectByPage(t, bp, psm, bw, r);
|
||||
rect.x -= r.x;
|
||||
rect.y -= r.y;
|
||||
rect = TranslateAlignedRectByPage(t, bp, psm, bw, new_rect);
|
||||
rect.x -= new_rect.x;
|
||||
rect.y -= new_rect.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If it's not page aligned, grab the whole pages it covers, to be safe.
|
||||
if (GSLocalMemory::m_psm[psm].bpp != GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp)
|
||||
{
|
||||
const GSVector2i& dst_page_size = GSLocalMemory::m_psm[psm].pgs;
|
||||
rect = GSVector4i(rect.x / page_size.x, rect.y / page_size.y, (rect.z + (page_size.x - 1)) / page_size.x, (rect.w + (page_size.y - 1)) / page_size.y);
|
||||
rect = GSVector4i(rect.x * dst_page_size.x, rect.y * dst_page_size.y, rect.z * dst_page_size.x, rect.w * dst_page_size.y);
|
||||
const GSVector2i& dst_page_size = GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs;
|
||||
new_rect = GSVector4i(new_rect.x / page_size.x, new_rect.y / page_size.y, (new_rect.z + (page_size.x - 1)) / page_size.x, (new_rect.w + (page_size.y - 1)) / page_size.y);
|
||||
new_rect = GSVector4i(new_rect.x * dst_page_size.x, new_rect.y * dst_page_size.y, new_rect.z * dst_page_size.x, new_rect.w * dst_page_size.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
rect.x &= ~(page_size.x - 1);
|
||||
rect.y &= ~(page_size.y - 1);
|
||||
rect.z = (r.z + (page_size.x - 1)) & ~(page_size.x - 1);
|
||||
rect.w = (r.w + (page_size.y - 1)) & ~(page_size.y - 1);
|
||||
new_rect.x &= ~(page_size.x - 1);
|
||||
new_rect.y &= ~(page_size.y - 1);
|
||||
new_rect.z = (new_rect.z + (page_size.x - 1)) & ~(page_size.x - 1);
|
||||
new_rect.w = (new_rect.w + (page_size.y - 1)) & ~(page_size.y - 1);
|
||||
}
|
||||
rect = TranslateAlignedRectByPage(t, bp & ~((1 << 5) - 1), psm, bw, rect);
|
||||
rect.x -= r.x & ~(page_size.y - 1);
|
||||
rect.y -= r.x & ~(page_size.y - 1);
|
||||
rect = TranslateAlignedRectByPage(t, bp & ~((1 << 5) - 1), psm, bw, new_rect);
|
||||
rect.x -= new_rect.x & ~(page_size.y - 1);
|
||||
rect.y -= new_rect.x & ~(page_size.y - 1);
|
||||
}
|
||||
|
||||
rect = rect.rintersect(t->m_valid);
|
||||
@@ -1076,16 +1130,19 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
dst = t;
|
||||
tex_merge_rt = false;
|
||||
found_t = true;
|
||||
continue;
|
||||
if (dst->m_TEX0.TBP0 == frame_fbp && possible_shuffle)
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
SurfaceOffset so = ComputeSurfaceOffset(bp, bw, psm, r, t);
|
||||
SurfaceOffset so = ComputeSurfaceOffset(bp, bw, psm, new_rect, t);
|
||||
if (!so.is_valid && t->Wraps())
|
||||
{
|
||||
// Improves Beyond Good & Evil shadow.
|
||||
const u32 bp_unwrap = bp + GSTextureCache::MAX_BP + 0x1;
|
||||
so = ComputeSurfaceOffset(bp_unwrap, bw, psm, r, t);
|
||||
so = ComputeSurfaceOffset(bp_unwrap, bw, psm, new_rect, t);
|
||||
}
|
||||
if (so.is_valid)
|
||||
{
|
||||
@@ -1096,7 +1153,10 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
tex_merge_rt = false;
|
||||
found_t = true;
|
||||
// Keep looking, just in case there is an exact match (Situation: Target frame drawn inside target frame, current makes a separate texture)
|
||||
continue;
|
||||
if (dst->m_TEX0.TBP0 == frame_fbp && possible_shuffle)
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1125,7 +1185,10 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
dst = t;
|
||||
tex_merge_rt = false;
|
||||
found_t = true;
|
||||
continue;
|
||||
if (dst->m_TEX0.TBP0 == frame_fbp && possible_shuffle)
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
// Strictly speaking this path is no longer needed, but I'm leaving it here for now because Guitar
|
||||
@@ -1139,7 +1202,10 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
|
||||
// Prefer a target inside over a target outside.
|
||||
found_t = false;
|
||||
continue;
|
||||
if (dst->m_TEX0.TBP0 == frame_fbp && possible_shuffle)
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1172,11 +1238,11 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
|
||||
GIFRegTEX0 depth_TEX0;
|
||||
depth_TEX0.U32[0] = TEX0.U32[0] | (0x30u << 20u);
|
||||
depth_TEX0.U32[1] = TEX0.U32[1];
|
||||
return LookupDepthSource(depth_TEX0, TEXA, CLAMP, r, possible_shuffle);
|
||||
return LookupDepthSource(depth_TEX0, TEXA, CLAMP, r, possible_shuffle, linear, frame_fbp);
|
||||
}
|
||||
else
|
||||
{
|
||||
return LookupDepthSource(TEX0, TEXA, CLAMP, r, possible_shuffle, true);
|
||||
return LookupDepthSource(TEX0, TEXA, CLAMP, r, possible_shuffle, linear, frame_fbp, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1275,6 +1341,15 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
|
||||
if (bp == t->m_TEX0.TBP0)
|
||||
{
|
||||
// if It's an old target and it's being completely overwritten, kill it.
|
||||
if (!preserve_rgb && !preserve_alpha && TEX0.TBW != t->m_TEX0.TBW && TEX0.TBW > 1 && t->m_age >= 1)
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to width change to %d", t->m_TEX0.TBP0, t->m_TEX0.TBW, psm_str(t->m_TEX0.PSM), TEX0.TBW);
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list.erase(i);
|
||||
delete t;
|
||||
continue;
|
||||
}
|
||||
list.MoveFront(i.Index());
|
||||
|
||||
dst = t;
|
||||
@@ -1476,7 +1551,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
dst_match = t;
|
||||
}
|
||||
}
|
||||
|
||||
// We only want to use a matched target if it's actually being used.
|
||||
if (dst_match)
|
||||
{
|
||||
calcRescale(dst_match);
|
||||
@@ -1501,6 +1576,9 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
dst->m_valid_alpha_high = dst_match->m_valid_alpha_high && psm_s.trbpp != 24;
|
||||
dst->m_valid_rgb = dst_match->m_valid_rgb;
|
||||
|
||||
if(GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 16 && GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].bpp > 16)
|
||||
dst->m_TEX0.TBW = dst_match->m_TEX0.TBW; // Be careful of shuffles of the depth as C16, but using a buffer width of 16 (Mercenaries).
|
||||
|
||||
ShaderConvert shader;
|
||||
// m_32_bits_fmt gets set on a shuffle or if the format isn't 16bit.
|
||||
// In this case it needs to make sure it isn't part of a shuffle, where it needs to be interpreted as 32bits.
|
||||
@@ -1580,7 +1658,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
}
|
||||
|
||||
GSTextureCache::Target* GSTextureCache::CreateTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, float scale, int type,
|
||||
bool used, u32 fbmask, bool is_frame, bool preload, bool preserve_target, const GSVector4i draw_rect)
|
||||
bool used, u32 fbmask, bool is_frame, bool preload, bool preserve_target, const GSVector4i draw_rect, GSTextureCache::Source* src)
|
||||
{
|
||||
if (type == DepthStencil)
|
||||
{
|
||||
@@ -1595,7 +1673,7 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
|
||||
Target* dst = Target::Create(TEX0, size.x, size.y, scale, type, true);
|
||||
|
||||
PreloadTarget(TEX0, size, valid_size, is_frame, preload, preserve_target, draw_rect, dst);;
|
||||
PreloadTarget(TEX0, size, valid_size, is_frame, preload, preserve_target, draw_rect, dst, src);
|
||||
|
||||
dst->m_is_frame = is_frame;
|
||||
|
||||
@@ -1618,7 +1696,7 @@ GSTextureCache::Target* GSTextureCache::CreateTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
}
|
||||
|
||||
void GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, bool is_frame,
|
||||
bool preload, bool preserve_target, const GSVector4i draw_rect, Target* dst)
|
||||
bool preload, bool preserve_target, const GSVector4i draw_rect, Target* dst, GSTextureCache::Source* src)
|
||||
{
|
||||
// In theory new textures contain invalidated data. Still in theory a new target
|
||||
// must contains the content of the GS memory.
|
||||
@@ -1750,17 +1828,62 @@ void GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
|
||||
auto j = i;
|
||||
Target* t = *j;
|
||||
|
||||
// could be overwriting a double buffer, so if it's the second half of it, just reduce the size down to half.
|
||||
if (dst != t && t->m_TEX0.TBW == dst->m_TEX0.TBW &&
|
||||
t->m_TEX0.PSM == dst->m_TEX0.PSM &&
|
||||
((((t->m_end_block + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) == dst->m_TEX0.TBP0)
|
||||
{
|
||||
//DevCon.Warning("Found one %x->%x BW %d PSM %x (new target %x->%x BW %d PSM %x)", t->m_TEX0.TBP0, t->m_end_block, t->m_TEX0.TBW, t->m_TEX0.PSM, dst->m_TEX0.TBP0, dst->m_end_block, dst->m_TEX0.TBW, dst->m_TEX0.PSM);
|
||||
GSVector4i new_valid = t->m_valid;
|
||||
new_valid.w /= 2;
|
||||
t->ResizeValidity(new_valid);
|
||||
return;
|
||||
}
|
||||
if (dst != t && t->m_TEX0.TBW == dst->m_TEX0.TBW && t->m_TEX0.PSM == dst->m_TEX0.PSM && t->m_TEX0.TBW > 4)
|
||||
if(t->Overlaps(dst->m_TEX0.TBP0, dst->m_TEX0.TBW, dst->m_TEX0.PSM, dst->m_valid))
|
||||
{
|
||||
// could be overwriting a double buffer, so if it's the second half of it, just reduce the size down to half.
|
||||
if (((((t->UnwrappedEndBlock() + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) == dst->m_TEX0.TBP0)
|
||||
{
|
||||
GSVector4i new_valid = t->m_valid;
|
||||
new_valid.w /= 2;
|
||||
GL_INS("RT resize buffer for FBP 0x%x, %dx%d => %d,%d", t->m_TEX0.TBP0, t->m_valid.width(), t->m_valid.height(), new_valid.width(), new_valid.height());
|
||||
t->ResizeValidity(new_valid);
|
||||
return;
|
||||
}
|
||||
// The new texture is behind it but engulfs the whole thing, shrink the new target so it grows in the HW Draw resize.
|
||||
else if (((((dst->UnwrappedEndBlock() + 1) - dst->m_TEX0.TBP0) >> 1) + dst->m_TEX0.TBP0) == t->m_TEX0.TBP0)
|
||||
{
|
||||
if (dst->m_TEX0.TBW == 2)
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
int overlapping_pages = ((dst->UnwrappedEndBlock() + 1) - t->m_TEX0.TBP0) >> 5;
|
||||
int y_reduction = (overlapping_pages / dst->m_TEX0.TBW) * GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y;
|
||||
|
||||
if (y_reduction == 0 || (overlapping_pages % dst->m_TEX0.TBW))
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const int copy_width = (t->m_texture->GetWidth()) > (dst->m_texture->GetWidth()) ? (dst->m_texture->GetWidth()) : t->m_texture->GetWidth();
|
||||
const int copy_height = y_reduction * t->m_scale;
|
||||
const int old_height = (dst->m_valid.w - y_reduction) * dst->m_scale;
|
||||
GL_INS("RT double buffer copy from FBP 0x%x, %dx%d => %d,%d", t->m_TEX0.TBP0, copy_width, copy_height, 0, old_height);
|
||||
|
||||
// Clear the dirty first
|
||||
dst->Update();
|
||||
// Invalidate has been moved to after DrawPrims(), because we might kill the current sources' backing.
|
||||
g_gs_device->CopyRect(t->m_texture, dst->m_texture, GSVector4i(0, 0, copy_width, copy_height), 0, old_height);
|
||||
if (src && src->m_target && src->m_from_target == t)
|
||||
{
|
||||
// This should never happen as we're making a new target so the src should never be something it overlaps, but just incase..
|
||||
GSVector4i new_valid = t->m_valid;
|
||||
new_valid.y = std::max(new_valid.y - y_reduction, 0);
|
||||
new_valid.w = std::max(new_valid.w - y_reduction, 0);
|
||||
t->m_TEX0.TBP0 += (y_reduction / GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y) << 5;
|
||||
t->ResizeValidity(new_valid);
|
||||
}
|
||||
else
|
||||
{
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list.erase(j);
|
||||
delete t;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
@@ -3666,6 +3789,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||
src->m_alpha_minmax.second = (using_both ? std::max(TEXA.TA1, TEXA.TA0) : (using_ta1 ? TEXA.TA1 : TEXA.TA0));
|
||||
}
|
||||
}
|
||||
src->m_32_bits_fmt = dst->m_32_bits_fmt;
|
||||
|
||||
if (psm.pal > 0)
|
||||
{
|
||||
@@ -3726,6 +3850,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||
src->m_alpha_minmax.second = (using_both ? std::max(TEXA.TA1, TEXA.TA0) : (using_ta1 ? TEXA.TA1 : TEXA.TA0));
|
||||
}
|
||||
}
|
||||
src->m_32_bits_fmt = dst->m_32_bits_fmt;
|
||||
|
||||
dst->Update();
|
||||
|
||||
|
||||
@@ -424,7 +424,7 @@ protected:
|
||||
Source* CreateSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, Target* t, bool half_right, int x_offset, int y_offset, const GSVector2i* lod, const GSVector4i* src_range, GSTexture* gpu_clut, SourceRegion region);
|
||||
|
||||
void PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, bool is_frame,
|
||||
bool preload, bool preserve_target, const GSVector4i draw_rect, Target* dst);
|
||||
bool preload, bool preserve_target, const GSVector4i draw_rect, Target* dst, GSTextureCache::Source* src = nullptr);
|
||||
|
||||
// Returns scaled texture size.
|
||||
static GSVector2i ScaleRenderTargetSize(const GSVector2i& sz, float scale);
|
||||
@@ -473,8 +473,8 @@ public:
|
||||
GSTexture* LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, float* scale, const GSVector2i& size);
|
||||
std::shared_ptr<Palette> LookupPaletteObject(const u32* clut, u16 pal, bool need_gs_texture);
|
||||
|
||||
Source* LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const GSVector2i* lod, const bool possible_shuffle);
|
||||
Source* LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const bool possible_shuffle, bool palette = false);
|
||||
Source* LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const GSVector2i* lod, const bool possible_shuffle, const bool linear, const u32 frame_fbp = 0xFFFFFFFF);
|
||||
Source* LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const bool possible_shuffle, const bool linear, const u32 frame_fbp = 0xFFFFFFFF, bool palette = false);
|
||||
|
||||
Target* FindTargetOverlap(Target* target, int type, int psm);
|
||||
Target* LookupTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type, bool used = true, u32 fbmask = 0,
|
||||
@@ -482,7 +482,7 @@ public:
|
||||
const GSVector4i draw_rc = GSVector4i::zero());
|
||||
Target* CreateTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size,float scale, int type, bool used = true, u32 fbmask = 0,
|
||||
bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_target = true,
|
||||
const GSVector4i draw_rc = GSVector4i::zero());
|
||||
const GSVector4i draw_rc = GSVector4i::zero(), GSTextureCache::Source* src = nullptr);
|
||||
Target* LookupDisplayTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale);
|
||||
|
||||
/// Looks up a target in the cache, and only returns it if the BP/BW match exactly.
|
||||
|
||||
@@ -673,7 +673,7 @@ MRCOwned<id<MTLFunction>> GSDeviceMTL::LoadShader(NSString* name)
|
||||
{
|
||||
NSString* msg = [NSString stringWithFormat:@"Failed to load shader %@: %@", name, [err localizedDescription]];
|
||||
Console.Error("%s", [msg UTF8String]);
|
||||
pxFailRel("Failed to load shader, check the log for more details.");
|
||||
pxFailRel([msg UTF8String]);
|
||||
}
|
||||
return fn;
|
||||
}
|
||||
@@ -689,7 +689,7 @@ MRCOwned<id<MTLRenderPipelineState>> GSDeviceMTL::MakePipeline(MTLRenderPipeline
|
||||
{
|
||||
NSString* msg = [NSString stringWithFormat:@"Failed to create pipeline %@: %@", name, [err localizedDescription]];
|
||||
Console.Error("%s", [msg UTF8String]);
|
||||
pxFailRel("Failed to create pipeline, check the log for more details.");
|
||||
pxFailRel([msg UTF8String]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -709,7 +709,7 @@ MRCOwned<id<MTLComputePipelineState>> GSDeviceMTL::MakeComputePipeline(id<MTLFun
|
||||
{
|
||||
NSString* msg = [NSString stringWithFormat:@"Failed to create pipeline %@: %@", name, [err localizedDescription]];
|
||||
Console.Error("%s", [msg UTF8String]);
|
||||
pxFailRel("Failed to create pipeline, check the log for more details.");
|
||||
pxFailRel([msg UTF8String]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -1803,6 +1803,7 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
|
||||
setFnConstantB(m_fn_constants, pssel.adjt, GSMTLConstantIndex_PS_ADJT);
|
||||
setFnConstantB(m_fn_constants, pssel.ltf, GSMTLConstantIndex_PS_LTF);
|
||||
setFnConstantB(m_fn_constants, pssel.shuffle, GSMTLConstantIndex_PS_SHUFFLE);
|
||||
setFnConstantB(m_fn_constants, pssel.shuffle_same, GSMTLConstantIndex_PS_SHUFFLE_SAME);
|
||||
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);
|
||||
@@ -2048,6 +2049,18 @@ static id<MTLTexture> getTexture(GSTexture* tex)
|
||||
return tex ? static_cast<GSTextureMTL*>(tex)->GetTexture() : nil;
|
||||
}
|
||||
|
||||
static bool usesStencil(GSHWDrawConfig::DestinationAlphaMode dstalpha)
|
||||
{
|
||||
switch (dstalpha)
|
||||
{
|
||||
case GSHWDrawConfig::DestinationAlphaMode::Stencil:
|
||||
case GSHWDrawConfig::DestinationAlphaMode::StencilOne:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void GSDeviceMTL::MREInitHWDraw(GSHWDrawConfig& config, const Map& verts)
|
||||
{
|
||||
MRESetScissor(config.scissor);
|
||||
@@ -2141,8 +2154,6 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
|
||||
}
|
||||
|
||||
// Try to reduce render pass restarts
|
||||
if (!stencil && config.depth.key == DepthStencilSelector::NoDepth().key && (m_current_render.color_target != rt || m_current_render.depth_target != config.ds))
|
||||
config.ds = nullptr;
|
||||
if (!config.ds && m_current_render.color_target == rt && stencil == m_current_render.stencil_target && m_current_render.depth_target != config.tex)
|
||||
config.ds = m_current_render.depth_target;
|
||||
if (!rt && config.ds == m_current_render.depth_target && m_current_render.color_target != config.tex)
|
||||
@@ -2160,6 +2171,8 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
|
||||
BeginRenderPass(@"RenderHW", rt, MTLLoadActionLoad, config.ds, MTLLoadActionLoad, stencil, MTLLoadActionLoad);
|
||||
id<MTLRenderCommandEncoder> mtlenc = m_current_render.encoder;
|
||||
FlushDebugEntries(mtlenc);
|
||||
if (usesStencil(config.destination_alpha))
|
||||
[mtlenc setStencilReferenceValue:1];
|
||||
MREInitHWDraw(config, allocation);
|
||||
if (config.require_one_barrier || config.require_full_barrier)
|
||||
MRESetTexture(config.rt, GSMTLTextureIndexRenderTarget);
|
||||
|
||||
@@ -182,6 +182,7 @@ enum GSMTLFnConstants
|
||||
GSMTLConstantIndex_PS_ADJT,
|
||||
GSMTLConstantIndex_PS_LTF,
|
||||
GSMTLConstantIndex_PS_SHUFFLE,
|
||||
GSMTLConstantIndex_PS_SHUFFLE_SAME,
|
||||
GSMTLConstantIndex_PS_READ_BA,
|
||||
GSMTLConstantIndex_PS_READ16_SRC,
|
||||
GSMTLConstantIndex_PS_WRITE_RG,
|
||||
|
||||
@@ -41,6 +41,7 @@ constant bool PS_ADJS [[function_constant(GSMTLConstantIndex_PS_AD
|
||||
constant bool PS_ADJT [[function_constant(GSMTLConstantIndex_PS_ADJT)]];
|
||||
constant bool PS_LTF [[function_constant(GSMTLConstantIndex_PS_LTF)]];
|
||||
constant bool PS_SHUFFLE [[function_constant(GSMTLConstantIndex_PS_SHUFFLE)]];
|
||||
constant bool PS_SHUFFLE_SAME [[function_constant(GSMTLConstantIndex_PS_SHUFFLE_SAME)]];
|
||||
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)]];
|
||||
@@ -1021,7 +1022,16 @@ struct PSMain
|
||||
uint4 denorm_c = uint4(C);
|
||||
uint2 denorm_TA = uint2(cb.ta * 255.5f);
|
||||
|
||||
if (PS_READ16_SRC)
|
||||
// Special case for 32bit input and 16bit output, shuffle used by The Godfather
|
||||
if (PS_SHUFFLE_SAME)
|
||||
{
|
||||
if (PS_READ_BA)
|
||||
C = (denorm_c.b & 0x7Fu) | (denorm_c.a & 0x80);
|
||||
else
|
||||
C.ga = C.rg;
|
||||
}
|
||||
// Copy of a 16bit source in to this target
|
||||
else if (PS_READ16_SRC)
|
||||
{
|
||||
C.rb = (denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7u) << 5);
|
||||
if (denorm_c.a & 0x80)
|
||||
@@ -1029,13 +1039,16 @@ struct PSMain
|
||||
else
|
||||
C.ga = (denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80);
|
||||
}
|
||||
// Write RB part. Mask will take care of the correct destination
|
||||
else if (PS_READ_BA)
|
||||
{
|
||||
C.rb = C.bb;
|
||||
C.ga = (denorm_c.a & 0x7F) | (denorm_c.a & 0x80 ? denorm_TA.y & 0x80 : denorm_TA.x & 0x80);
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
C.rb = C.rr;
|
||||
C.ga = (denorm_c.g & 0x7F) | (denorm_c.g & 0x80 ? denorm_TA.y & 0x80 : denorm_TA.x & 0x80);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1378,6 +1378,7 @@ std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
|
||||
+ fmt::format("#define PS_BLEND_D {}\n", sel.blend_d)
|
||||
+ fmt::format("#define PS_IIP {}\n", sel.iip)
|
||||
+ fmt::format("#define PS_SHUFFLE {}\n", sel.shuffle)
|
||||
+ fmt::format("#define PS_SHUFFLE_SAME {}\n", sel.shuffle_same)
|
||||
+ 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)
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "GS/Renderers/Common/GSDevice.h"
|
||||
#include "GS/GSVector.h"
|
||||
#include "GS/Renderers/Vulkan/GSTextureVK.h"
|
||||
#include "GS/Renderers/Vulkan/VKLoader.h"
|
||||
#include "GS/Renderers/Vulkan/VKStreamBuffer.h"
|
||||
#include "GS/GSVector.h"
|
||||
|
||||
#include "common/HashCombine.h"
|
||||
#include "common/ReadbackSpinManager.h"
|
||||
@@ -50,13 +50,10 @@ public:
|
||||
bool vk_ext_provoking_vertex : 1;
|
||||
bool vk_ext_memory_budget : 1;
|
||||
bool vk_ext_calibrated_timestamps : 1;
|
||||
bool vk_ext_line_rasterization : 1;
|
||||
bool vk_ext_rasterization_order_attachment_access : 1;
|
||||
bool vk_ext_attachment_feedback_loop_layout : 1;
|
||||
bool vk_ext_full_screen_exclusive : 1;
|
||||
bool vk_khr_driver_properties : 1;
|
||||
bool vk_khr_fragment_shader_barycentric : 1;
|
||||
bool vk_khr_shader_draw_parameters : 1;
|
||||
};
|
||||
|
||||
// Global state accessors
|
||||
@@ -108,9 +105,6 @@ public:
|
||||
__fi VKStreamBuffer& GetTextureUploadBuffer() { return m_texture_stream_buffer; }
|
||||
VkCommandBuffer GetCurrentInitCommandBuffer();
|
||||
|
||||
/// Allocates a descriptor set from the pool reserved for the current frame.
|
||||
VkDescriptorSet AllocateDescriptorSet(VkDescriptorSetLayout set_layout);
|
||||
|
||||
/// Allocates a descriptor set from the pool reserved for the current frame.
|
||||
VkDescriptorSet AllocatePersistentDescriptorSet(VkDescriptorSetLayout set_layout);
|
||||
|
||||
@@ -199,12 +193,11 @@ private:
|
||||
};
|
||||
|
||||
using ExtensionList = std::vector<const char*>;
|
||||
static bool SelectInstanceExtensions(
|
||||
ExtensionList* extension_list, const WindowInfo& wi, bool enable_debug_utils);
|
||||
static bool SelectInstanceExtensions(ExtensionList* extension_list, const WindowInfo& wi, bool enable_debug_utils);
|
||||
bool SelectDeviceExtensions(ExtensionList* extension_list, bool enable_surface);
|
||||
bool SelectDeviceFeatures();
|
||||
bool CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer);
|
||||
void ProcessDeviceExtensions();
|
||||
bool ProcessDeviceExtensions();
|
||||
|
||||
bool CreateAllocator();
|
||||
bool CreateCommandBuffers();
|
||||
@@ -237,7 +230,6 @@ private:
|
||||
// [0] - Init (upload) command buffer, [1] - draw command buffer
|
||||
VkCommandPool command_pool = VK_NULL_HANDLE;
|
||||
std::array<VkCommandBuffer, 2> command_buffers{VK_NULL_HANDLE, VK_NULL_HANDLE};
|
||||
VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
|
||||
VkFence fence = VK_NULL_HANDLE;
|
||||
u64 fence_counter = 0;
|
||||
s32 spin_id = -1;
|
||||
@@ -388,11 +380,7 @@ public:
|
||||
enum : u32
|
||||
{
|
||||
NUM_TFX_DYNAMIC_OFFSETS = 2,
|
||||
NUM_TFX_DRAW_TEXTURES = 2,
|
||||
NUM_TFX_RT_TEXTURES = 2,
|
||||
NUM_TFX_TEXTURES = NUM_TFX_DRAW_TEXTURES + NUM_TFX_RT_TEXTURES,
|
||||
NUM_CONVERT_TEXTURES = 1,
|
||||
NUM_CONVERT_SAMPLERS = 1,
|
||||
NUM_UTILITY_SAMPLERS = 1,
|
||||
CONVERT_PUSH_CONSTANTS_SIZE = 96,
|
||||
|
||||
NUM_CAS_PIPELINES = 2,
|
||||
@@ -401,10 +389,18 @@ public:
|
||||
{
|
||||
TFX_DESCRIPTOR_SET_UBO,
|
||||
TFX_DESCRIPTOR_SET_TEXTURES,
|
||||
TFX_DESCRIPTOR_SET_RT,
|
||||
|
||||
NUM_TFX_DESCRIPTOR_SETS,
|
||||
};
|
||||
enum TFX_TEXTURES : u32
|
||||
{
|
||||
TFX_TEXTURE_TEXTURE,
|
||||
TFX_TEXTURE_PALETTE,
|
||||
TFX_TEXTURE_RT,
|
||||
TFX_TEXTURE_PRIMID,
|
||||
|
||||
NUM_TFX_TEXTURES
|
||||
};
|
||||
enum DATE_RENDER_PASS : u32
|
||||
{
|
||||
DATE_RENDER_PASS_NONE = 0,
|
||||
@@ -419,8 +415,7 @@ private:
|
||||
VkPipelineLayout m_utility_pipeline_layout = VK_NULL_HANDLE;
|
||||
|
||||
VkDescriptorSetLayout m_tfx_ubo_ds_layout = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout m_tfx_sampler_ds_layout = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout m_tfx_rt_texture_ds_layout = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout m_tfx_texture_ds_layout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_tfx_pipeline_layout = VK_NULL_HANDLE;
|
||||
|
||||
VKStreamBuffer m_vertex_stream_buffer;
|
||||
@@ -449,7 +444,8 @@ private:
|
||||
VkPipeline m_shadeboost_pipeline = {};
|
||||
|
||||
std::unordered_map<u32, VkShaderModule> m_tfx_vertex_shaders;
|
||||
std::unordered_map<GSHWDrawConfig::PSSelector, VkShaderModule, GSHWDrawConfig::PSSelectorHash> m_tfx_fragment_shaders;
|
||||
std::unordered_map<GSHWDrawConfig::PSSelector, VkShaderModule, GSHWDrawConfig::PSSelectorHash>
|
||||
m_tfx_fragment_shaders;
|
||||
std::unordered_map<PipelineSelector, VkPipeline, PipelineSelectorHash> m_tfx_pipelines;
|
||||
|
||||
VkRenderPass m_utility_color_render_pass_load = VK_NULL_HANDLE;
|
||||
@@ -473,15 +469,18 @@ private:
|
||||
|
||||
std::string m_tfx_source;
|
||||
|
||||
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
|
||||
GSTexture* CreateSurface(
|
||||
GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
|
||||
|
||||
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE,
|
||||
const GSRegEXTBUF& EXTBUF, u32 c, const bool linear) final;
|
||||
void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) final;
|
||||
void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
|
||||
ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) final;
|
||||
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final;
|
||||
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
|
||||
|
||||
bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) final;
|
||||
bool DoCAS(
|
||||
GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) final;
|
||||
|
||||
VkSampler GetSampler(GSHWDrawConfig::SamplerSelector ss);
|
||||
void ClearSamplerCache() final;
|
||||
@@ -520,7 +519,8 @@ public:
|
||||
|
||||
__fi static GSDeviceVK* GetInstance() { return static_cast<GSDeviceVK*>(g_gs_device.get()); }
|
||||
|
||||
static void GetAdaptersAndFullscreenModes(std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes);
|
||||
static void GetAdaptersAndFullscreenModes(
|
||||
std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes);
|
||||
|
||||
/// Returns true if Vulkan is suitable as a default for the devices in the system.
|
||||
static bool IsSuitableDefaultRenderer();
|
||||
@@ -571,7 +571,8 @@ public:
|
||||
bool green, bool blue, bool alpha) override;
|
||||
void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
|
||||
PresentShader shader, float shaderTime, bool linear) override;
|
||||
void DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader) override;
|
||||
void DrawMultiStretchRects(
|
||||
const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader) override;
|
||||
void DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTextureVK* dTex, ShaderConvert shader);
|
||||
|
||||
void BeginRenderPassForStretchRect(
|
||||
@@ -583,8 +584,10 @@ public:
|
||||
void BlitRect(GSTexture* sTex, const GSVector4i& sRect, u32 sLevel, GSTexture* dTex, const GSVector4i& dRect,
|
||||
u32 dLevel, bool linear);
|
||||
|
||||
void UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) override;
|
||||
void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) override;
|
||||
void UpdateCLUTTexture(
|
||||
GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) override;
|
||||
void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM,
|
||||
GSTexture* dTex, u32 DBW, u32 DPSM) override;
|
||||
|
||||
void SetupDATE(GSTexture* rt, GSTexture* ds, bool datm, const GSVector4i& bbox);
|
||||
GSTextureVK* SetupPrimitiveTrackingDATE(GSHWDrawConfig& config);
|
||||
@@ -631,8 +634,7 @@ public:
|
||||
bool ApplyUtilityState(bool already_execed = false);
|
||||
bool ApplyTFXState(bool already_execed = false);
|
||||
|
||||
void SetVertexBuffer(VkBuffer buffer, VkDeviceSize offset);
|
||||
void SetIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType type);
|
||||
void SetIndexBuffer(VkBuffer buffer);
|
||||
void SetBlendConstants(u8 color);
|
||||
void SetLineWidth(float width);
|
||||
|
||||
@@ -657,25 +659,32 @@ public:
|
||||
private:
|
||||
enum DIRTY_FLAG : u32
|
||||
{
|
||||
DIRTY_FLAG_TFX_SAMPLERS_DS = (1 << 0),
|
||||
DIRTY_FLAG_TFX_RT_TEXTURE_DS = (1 << 1),
|
||||
DIRTY_FLAG_TFX_DYNAMIC_OFFSETS = (1 << 2),
|
||||
DIRTY_FLAG_UTILITY_TEXTURE = (1 << 3),
|
||||
DIRTY_FLAG_BLEND_CONSTANTS = (1 << 4),
|
||||
DIRTY_FLAG_LINE_WIDTH = (1 << 5),
|
||||
DIRTY_FLAG_VERTEX_BUFFER = (1 << 6),
|
||||
DIRTY_FLAG_INDEX_BUFFER = (1 << 7),
|
||||
DIRTY_FLAG_VIEWPORT = (1 << 8),
|
||||
DIRTY_FLAG_SCISSOR = (1 << 9),
|
||||
DIRTY_FLAG_PIPELINE = (1 << 10),
|
||||
DIRTY_FLAG_VS_CONSTANT_BUFFER = (1 << 11),
|
||||
DIRTY_FLAG_PS_CONSTANT_BUFFER = (1 << 12),
|
||||
DIRTY_FLAG_TFX_TEXTURE_0 = (1 << 0), // 0, 1, 2, 3
|
||||
DIRTY_FLAG_TFX_UBO = (1 << 4),
|
||||
DIRTY_FLAG_UTILITY_TEXTURE = (1 << 5),
|
||||
DIRTY_FLAG_BLEND_CONSTANTS = (1 << 6),
|
||||
DIRTY_FLAG_LINE_WIDTH = (1 << 7),
|
||||
DIRTY_FLAG_INDEX_BUFFER = (1 << 8),
|
||||
DIRTY_FLAG_VIEWPORT = (1 << 9),
|
||||
DIRTY_FLAG_SCISSOR = (1 << 10),
|
||||
DIRTY_FLAG_PIPELINE = (1 << 11),
|
||||
DIRTY_FLAG_VS_CONSTANT_BUFFER = (1 << 12),
|
||||
DIRTY_FLAG_PS_CONSTANT_BUFFER = (1 << 13),
|
||||
|
||||
DIRTY_BASE_STATE = DIRTY_FLAG_VERTEX_BUFFER | DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_PIPELINE |
|
||||
DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR | DIRTY_FLAG_BLEND_CONSTANTS | DIRTY_FLAG_LINE_WIDTH,
|
||||
DIRTY_TFX_STATE = DIRTY_BASE_STATE | DIRTY_FLAG_TFX_SAMPLERS_DS | DIRTY_FLAG_TFX_RT_TEXTURE_DS,
|
||||
DIRTY_FLAG_TFX_TEXTURE_TEX = (DIRTY_FLAG_TFX_TEXTURE_0 << 0),
|
||||
DIRTY_FLAG_TFX_TEXTURE_PALETTE = (DIRTY_FLAG_TFX_TEXTURE_0 << 1),
|
||||
DIRTY_FLAG_TFX_TEXTURE_RT = (DIRTY_FLAG_TFX_TEXTURE_0 << 2),
|
||||
DIRTY_FLAG_TFX_TEXTURE_PRIMID = (DIRTY_FLAG_TFX_TEXTURE_0 << 3),
|
||||
|
||||
DIRTY_FLAG_TFX_TEXTURES = DIRTY_FLAG_TFX_TEXTURE_TEX | DIRTY_FLAG_TFX_TEXTURE_PALETTE |
|
||||
DIRTY_FLAG_TFX_TEXTURE_RT | DIRTY_FLAG_TFX_TEXTURE_PRIMID,
|
||||
|
||||
DIRTY_BASE_STATE = DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_PIPELINE | DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR |
|
||||
DIRTY_FLAG_BLEND_CONSTANTS | DIRTY_FLAG_LINE_WIDTH,
|
||||
DIRTY_TFX_STATE = DIRTY_BASE_STATE | DIRTY_FLAG_TFX_TEXTURES,
|
||||
DIRTY_UTILITY_STATE = DIRTY_BASE_STATE | DIRTY_FLAG_UTILITY_TEXTURE,
|
||||
DIRTY_CONSTANT_BUFFER_STATE = DIRTY_FLAG_VS_CONSTANT_BUFFER | DIRTY_FLAG_PS_CONSTANT_BUFFER,
|
||||
ALL_DIRTY_STATE = DIRTY_BASE_STATE | DIRTY_TFX_STATE | DIRTY_UTILITY_STATE | DIRTY_CONSTANT_BUFFER_STATE,
|
||||
};
|
||||
|
||||
enum class PipelineLayout
|
||||
@@ -688,6 +697,7 @@ private:
|
||||
void InitializeState();
|
||||
bool CreatePersistentDescriptorSets();
|
||||
|
||||
void SetInitialState(VkCommandBuffer cmdbuf);
|
||||
void ApplyBaseState(u32 flags, VkCommandBuffer cmdbuf);
|
||||
|
||||
// Which bindings/state has to be updated before the next draw.
|
||||
@@ -695,12 +705,7 @@ private:
|
||||
FeedbackLoopFlag m_current_framebuffer_feedback_loop = FeedbackLoopFlag_None;
|
||||
bool m_warned_slow_spin = false;
|
||||
|
||||
// input assembly
|
||||
VkBuffer m_vertex_buffer = VK_NULL_HANDLE;
|
||||
VkDeviceSize m_vertex_buffer_offset = 0;
|
||||
VkBuffer m_index_buffer = VK_NULL_HANDLE;
|
||||
VkDeviceSize m_index_buffer_offset = 0;
|
||||
VkIndexType m_index_type = VK_INDEX_TYPE_UINT16;
|
||||
|
||||
GSTextureVK* m_current_render_target = nullptr;
|
||||
GSTextureVK* m_current_depth_target = nullptr;
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "GS/GSGL.h"
|
||||
#include "GS/GSPerfMon.h"
|
||||
#include "GS/Renderers/Vulkan/GSDeviceVK.h"
|
||||
#include "GS/Renderers/Vulkan/GSTextureVK.h"
|
||||
#include "GS/Renderers/Vulkan/VKBuilders.h"
|
||||
#include "GS/GSPerfMon.h"
|
||||
#include "GS/GSGL.h"
|
||||
|
||||
#include "common/BitUtils.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/BitUtils.h"
|
||||
|
||||
static constexpr const VkComponentMapping s_identity_swizzle{VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
|
||||
@@ -52,7 +52,8 @@ static VkImageLayout GetVkImageLayout(GSTextureVK::Layout layout)
|
||||
|
||||
static VkAccessFlagBits GetFeedbackLoopInputAccessBits()
|
||||
{
|
||||
return GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_ACCESS_SHADER_READ_BIT : VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
|
||||
return GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_ACCESS_SHADER_READ_BIT :
|
||||
VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
|
||||
}
|
||||
|
||||
GSTextureVK::GSTextureVK(Type type, Format format, int width, int height, int levels, VkImage image,
|
||||
@@ -111,10 +112,11 @@ std::unique_ptr<GSTextureVK> GSTextureVK::Create(Type type, Format format, int w
|
||||
case Type::RenderTarget:
|
||||
{
|
||||
pxAssert(levels == 1);
|
||||
ici.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||
(GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT :
|
||||
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
|
||||
ici.usage =
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||
(GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT :
|
||||
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -124,7 +126,8 @@ std::unique_ptr<GSTextureVK> GSTextureVK::Create(Type type, Format format, int w
|
||||
ici.usage =
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||
(GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT : 0);
|
||||
(GSDeviceVK::GetInstance()->UseFeedbackLoopLayout() ? VK_IMAGE_USAGE_ATTACHMENT_FEEDBACK_LOOP_BIT_EXT :
|
||||
0);
|
||||
vci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
}
|
||||
break;
|
||||
@@ -430,8 +433,8 @@ bool GSTextureVK::Map(GSMap& m, const GSVector4i* r, int layer)
|
||||
m_map_area = r ? *r : GetRect();
|
||||
m_map_level = layer;
|
||||
|
||||
m.pitch =
|
||||
Common::AlignUpPow2(CalcUploadPitch(m_map_area.width()), GSDeviceVK::GetInstance()->GetBufferCopyRowPitchAlignment());
|
||||
m.pitch = Common::AlignUpPow2(
|
||||
CalcUploadPitch(m_map_area.width()), GSDeviceVK::GetInstance()->GetBufferCopyRowPitchAlignment());
|
||||
|
||||
// see note in Update() for the reason why.
|
||||
const u32 required_size = CalcUploadSize(m_map_area.height(), m.pitch);
|
||||
@@ -459,7 +462,8 @@ void GSTextureVK::Unmap()
|
||||
|
||||
const u32 width = m_map_area.width();
|
||||
const u32 height = m_map_area.height();
|
||||
const u32 pitch = Common::AlignUpPow2(CalcUploadPitch(width), GSDeviceVK::GetInstance()->GetBufferCopyRowPitchAlignment());
|
||||
const u32 pitch =
|
||||
Common::AlignUpPow2(CalcUploadPitch(width), GSDeviceVK::GetInstance()->GetBufferCopyRowPitchAlignment());
|
||||
const u32 required_size = CalcUploadSize(height, pitch);
|
||||
VKStreamBuffer& buffer = GSDeviceVK::GetInstance()->GetTextureUploadBuffer();
|
||||
const u32 buffer_offset = buffer.GetCurrentOffset();
|
||||
@@ -826,7 +830,8 @@ GSDownloadTextureVK::~GSDownloadTextureVK()
|
||||
|
||||
std::unique_ptr<GSDownloadTextureVK> GSDownloadTextureVK::Create(u32 width, u32 height, GSTexture::Format format)
|
||||
{
|
||||
const u32 buffer_size = GetBufferSize(width, height, format, GSDeviceVK::GetInstance()->GetBufferCopyRowPitchAlignment());
|
||||
const u32 buffer_size =
|
||||
GetBufferSize(width, height, format, GSDeviceVK::GetInstance()->GetBufferCopyRowPitchAlignment());
|
||||
|
||||
const VkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, nullptr, 0u, buffer_size,
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_SHARING_MODE_EXCLUSIVE, 0u, nullptr};
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GS/GS.h"
|
||||
#include "GS/Renderers/Common/GSTexture.h"
|
||||
#include "GS/GS.h"
|
||||
#include "GS/Renderers/Vulkan/VKLoader.h"
|
||||
|
||||
#include <limits>
|
||||
@@ -76,8 +76,8 @@ public:
|
||||
void OverrideImageLayout(Layout new_layout);
|
||||
|
||||
void TransitionToLayout(VkCommandBuffer command_buffer, Layout new_layout);
|
||||
void TransitionSubresourcesToLayout(VkCommandBuffer command_buffer, int start_level, int num_levels,
|
||||
Layout old_layout, Layout new_layout);
|
||||
void TransitionSubresourcesToLayout(
|
||||
VkCommandBuffer command_buffer, int start_level, int num_levels, Layout old_layout, Layout new_layout);
|
||||
|
||||
/// Framebuffers are lazily allocated.
|
||||
VkFramebuffer GetFramebuffer(bool feedback_loop);
|
||||
|
||||
@@ -141,6 +141,11 @@ void Vulkan::DescriptorSetLayoutBuilder::Clear()
|
||||
m_ci.bindingCount = 0;
|
||||
}
|
||||
|
||||
void Vulkan::DescriptorSetLayoutBuilder::SetPushFlag()
|
||||
{
|
||||
m_ci.flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayout Vulkan::DescriptorSetLayoutBuilder::Create(VkDevice device)
|
||||
{
|
||||
VkDescriptorSetLayout layout;
|
||||
@@ -724,6 +729,17 @@ void Vulkan::DescriptorSetUpdateBuilder::Update(VkDevice device, bool clear /*=
|
||||
Clear();
|
||||
}
|
||||
|
||||
void Vulkan::DescriptorSetUpdateBuilder::PushUpdate(
|
||||
VkCommandBuffer cmdbuf, VkPipelineBindPoint bind_point, VkPipelineLayout layout, u32 set, bool clear /*= true*/)
|
||||
{
|
||||
pxAssert(m_num_writes > 0);
|
||||
|
||||
vkCmdPushDescriptorSetKHR(cmdbuf, bind_point, layout, set, m_num_writes, m_writes.data());
|
||||
|
||||
if (clear)
|
||||
Clear();
|
||||
}
|
||||
|
||||
void Vulkan::DescriptorSetUpdateBuilder::AddImageDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view,
|
||||
VkImageLayout layout /*= VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL*/)
|
||||
{
|
||||
|
||||
@@ -48,6 +48,7 @@ namespace Vulkan
|
||||
DescriptorSetLayoutBuilder();
|
||||
|
||||
void Clear();
|
||||
void SetPushFlag();
|
||||
|
||||
VkDescriptorSetLayout Create(VkDevice device);
|
||||
|
||||
@@ -209,7 +210,7 @@ namespace Vulkan
|
||||
|
||||
VkSpecializationInfo m_si;
|
||||
std::array<VkSpecializationMapEntry, MAX_SPECIALIZATION_CONSTANTS> m_smap_entries;
|
||||
std::array<u8, SPECIALIZATION_CONSTANT_SIZE* MAX_SPECIALIZATION_CONSTANTS> m_smap_constants;
|
||||
std::array<u8, SPECIALIZATION_CONSTANT_SIZE * MAX_SPECIALIZATION_CONSTANTS> m_smap_constants;
|
||||
};
|
||||
|
||||
class SamplerBuilder
|
||||
@@ -248,6 +249,8 @@ namespace Vulkan
|
||||
void Clear();
|
||||
|
||||
void Update(VkDevice device, bool clear = true);
|
||||
void PushUpdate(VkCommandBuffer cmdbuf, VkPipelineBindPoint bind_point, VkPipelineLayout layout, u32 set,
|
||||
bool clear = true);
|
||||
|
||||
void AddImageDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view,
|
||||
VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
@@ -243,4 +243,7 @@ VULKAN_DEVICE_ENTRY_POINT(vkReleaseFullScreenExclusiveModeEXT, false)
|
||||
// VK_EXT_calibrated_timestamps
|
||||
VULKAN_DEVICE_ENTRY_POINT(vkGetCalibratedTimestampsEXT, false)
|
||||
|
||||
// VK_KHR_push_descriptor
|
||||
VULKAN_DEVICE_ENTRY_POINT(vkCmdPushDescriptorSetKHR, false)
|
||||
|
||||
#endif // VULKAN_DEVICE_ENTRY_POINT
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "GS/Renderers/Vulkan/VKShaderCache.h"
|
||||
#include "GS/GS.h"
|
||||
#include "GS/Renderers/Vulkan/GSDeviceVK.h"
|
||||
#include "GS/Renderers/Vulkan/VKBuilders.h"
|
||||
#include "GS/GS.h"
|
||||
#include "GS/Renderers/Vulkan/VKShaderCache.h"
|
||||
|
||||
#include "Config.h"
|
||||
#include "ShaderCacheVersion.h"
|
||||
@@ -44,28 +44,29 @@
|
||||
|
||||
std::unique_ptr<VKShaderCache> g_vulkan_shader_cache;
|
||||
|
||||
namespace {
|
||||
namespace
|
||||
{
|
||||
#pragma pack(push, 4)
|
||||
struct VK_PIPELINE_CACHE_HEADER
|
||||
{
|
||||
u32 header_length;
|
||||
u32 header_version;
|
||||
u32 vendor_id;
|
||||
u32 device_id;
|
||||
u8 uuid[VK_UUID_SIZE];
|
||||
};
|
||||
struct VK_PIPELINE_CACHE_HEADER
|
||||
{
|
||||
u32 header_length;
|
||||
u32 header_version;
|
||||
u32 vendor_id;
|
||||
u32 device_id;
|
||||
u8 uuid[VK_UUID_SIZE];
|
||||
};
|
||||
|
||||
struct CacheIndexEntry
|
||||
{
|
||||
u64 source_hash_low;
|
||||
u64 source_hash_high;
|
||||
u32 source_length;
|
||||
u32 shader_type;
|
||||
u32 file_offset;
|
||||
u32 blob_size;
|
||||
};
|
||||
struct CacheIndexEntry
|
||||
{
|
||||
u64 source_hash_low;
|
||||
u64 source_hash_high;
|
||||
u32 source_length;
|
||||
u32 shader_type;
|
||||
u32 file_offset;
|
||||
u32 blob_size;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
}
|
||||
} // namespace
|
||||
|
||||
static bool ValidatePipelineCacheHeader(const VK_PIPELINE_CACHE_HEADER& header)
|
||||
{
|
||||
@@ -478,7 +479,8 @@ bool VKShaderCache::FlushPipelineCache()
|
||||
return false;
|
||||
|
||||
size_t data_size;
|
||||
VkResult res = vkGetPipelineCacheData(GSDeviceVK::GetInstance()->GetDevice(), m_pipeline_cache, &data_size, nullptr);
|
||||
VkResult res =
|
||||
vkGetPipelineCacheData(GSDeviceVK::GetInstance()->GetDevice(), m_pipeline_cache, &data_size, nullptr);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPipelineCacheData() failed: ");
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "GS/Renderers/Vulkan/GSDeviceVK.h"
|
||||
#include "GS/Renderers/Vulkan/VKStreamBuffer.h"
|
||||
#include "GS/Renderers/Vulkan/VKBuilders.h"
|
||||
#include "GS/Renderers/Vulkan/VKStreamBuffer.h"
|
||||
|
||||
#include "common/BitUtils.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/BitUtils.h"
|
||||
#include "common/Console.h"
|
||||
|
||||
VKStreamBuffer::VKStreamBuffer() = default;
|
||||
@@ -79,7 +79,8 @@ bool VKStreamBuffer::Create(VkBufferUsageFlags usage, u32 size)
|
||||
VmaAllocationInfo ai = {};
|
||||
VkBuffer new_buffer = VK_NULL_HANDLE;
|
||||
VmaAllocation new_allocation = VK_NULL_HANDLE;
|
||||
VkResult res = vmaCreateBuffer(GSDeviceVK::GetInstance()->GetAllocator(), &bci, &aci, &new_buffer, &new_allocation, &ai);
|
||||
VkResult res =
|
||||
vmaCreateBuffer(GSDeviceVK::GetInstance()->GetAllocator(), &bci, &aci, &new_buffer, &new_allocation, &ai);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateBuffer failed: ");
|
||||
|
||||
@@ -35,6 +35,7 @@ public:
|
||||
|
||||
__fi bool IsValid() const { return (m_buffer != VK_NULL_HANDLE); }
|
||||
__fi VkBuffer GetBuffer() const { return m_buffer; }
|
||||
__fi const VkBuffer* GetBufferPtr() const { return &m_buffer; }
|
||||
__fi u8* GetHostPointer() const { return m_host_pointer; }
|
||||
__fi u8* GetCurrentHostPointer() const { return m_host_pointer + m_current_offset; }
|
||||
__fi u32 GetCurrentSize() const { return m_size; }
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "GS/Renderers/Vulkan/GSDeviceVK.h"
|
||||
#include "GS/Renderers/Vulkan/VKSwapChain.h"
|
||||
#include "GS/Renderers/Vulkan/VKBuilders.h"
|
||||
#include "GS/Renderers/Vulkan/VKSwapChain.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/CocoaTools.h"
|
||||
@@ -183,8 +183,8 @@ static VkFormat GetLinearFormat(VkFormat format)
|
||||
std::optional<VkSurfaceFormatKHR> VKSwapChain::SelectSurfaceFormat(VkSurfaceKHR surface)
|
||||
{
|
||||
u32 format_count;
|
||||
VkResult res =
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR(GSDeviceVK::GetInstance()->GetPhysicalDevice(), surface, &format_count, nullptr);
|
||||
VkResult res = vkGetPhysicalDeviceSurfaceFormatsKHR(
|
||||
GSDeviceVK::GetInstance()->GetPhysicalDevice(), surface, &format_count, nullptr);
|
||||
if (res != VK_SUCCESS || format_count == 0)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceFormatsKHR failed: ");
|
||||
@@ -254,8 +254,8 @@ std::optional<VkPresentModeKHR> VKSwapChain::SelectPresentMode(VkSurfaceKHR surf
|
||||
{
|
||||
VkResult res;
|
||||
u32 mode_count;
|
||||
res =
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR(GSDeviceVK::GetInstance()->GetPhysicalDevice(), surface, &mode_count, nullptr);
|
||||
res = vkGetPhysicalDeviceSurfacePresentModesKHR(
|
||||
GSDeviceVK::GetInstance()->GetPhysicalDevice(), surface, &mode_count, nullptr);
|
||||
if (res != VK_SUCCESS || mode_count == 0)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceFormatsKHR failed: ");
|
||||
@@ -376,7 +376,8 @@ bool VKSwapChain::CreateSwapChain()
|
||||
GSDeviceVK::GetInstance()->GetGraphicsQueueFamilyIndex(),
|
||||
GSDeviceVK::GetInstance()->GetPresentQueueFamilyIndex(),
|
||||
}};
|
||||
if (GSDeviceVK::GetInstance()->GetGraphicsQueueFamilyIndex() != GSDeviceVK::GetInstance()->GetPresentQueueFamilyIndex())
|
||||
if (GSDeviceVK::GetInstance()->GetGraphicsQueueFamilyIndex() !=
|
||||
GSDeviceVK::GetInstance()->GetPresentQueueFamilyIndex())
|
||||
{
|
||||
swap_chain_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
||||
swap_chain_info.queueFamilyIndexCount = 2;
|
||||
@@ -462,7 +463,8 @@ bool VKSwapChain::CreateSwapChain()
|
||||
ImageSemaphores sema;
|
||||
|
||||
const VkSemaphoreCreateInfo semaphore_info = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0};
|
||||
res = vkCreateSemaphore(GSDeviceVK::GetInstance()->GetDevice(), &semaphore_info, nullptr, &sema.available_semaphore);
|
||||
res = vkCreateSemaphore(
|
||||
GSDeviceVK::GetInstance()->GetDevice(), &semaphore_info, nullptr, &sema.available_semaphore);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateSemaphore failed: ");
|
||||
|
||||
@@ -601,9 +601,6 @@ bool GameDatabaseSchema::GameEntry::configMatchesHWFix(const Pcsx2Config::GSOpti
|
||||
case GSHWFixId::SkipDrawEnd:
|
||||
return (config.SkipDrawEnd == value);
|
||||
|
||||
case GSHWFixId::HalfBottomOverride:
|
||||
return (config.UserHacks_HalfBottomOverride == value);
|
||||
|
||||
case GSHWFixId::HalfPixelOffset:
|
||||
return (config.UpscaleMultiplier <= 1.0f || config.UserHacks_HalfPixelOffset == value);
|
||||
|
||||
@@ -782,10 +779,6 @@ void GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions&
|
||||
config.SkipDrawEnd = value;
|
||||
break;
|
||||
|
||||
case GSHWFixId::HalfBottomOverride:
|
||||
config.UserHacks_HalfBottomOverride = value;
|
||||
break;
|
||||
|
||||
case GSHWFixId::HalfPixelOffset:
|
||||
config.UserHacks_HalfPixelOffset = value;
|
||||
break;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "Elfheader.h"
|
||||
#include "GameList.h"
|
||||
#include "Host.h"
|
||||
#include "INISettingsInterface.h"
|
||||
#include "VMManager.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
@@ -75,10 +76,10 @@ namespace GameList
|
||||
|
||||
static bool GetGameListEntryFromCache(const std::string& path, GameList::Entry* entry);
|
||||
static void ScanDirectory(const char* path, bool recursive, bool only_cache, const std::vector<std::string>& excluded_paths,
|
||||
const PlayedTimeMap& played_time_map, ProgressCallback* progress);
|
||||
const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini, ProgressCallback* progress);
|
||||
static bool AddFileFromCache(const std::string& path, std::time_t timestamp, const PlayedTimeMap& played_time_map);
|
||||
static bool ScanFile(
|
||||
std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock, const PlayedTimeMap& played_time_map);
|
||||
static bool ScanFile(std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock,
|
||||
const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini);
|
||||
|
||||
static void LoadCache();
|
||||
static bool LoadEntriesFromCache(std::FILE* stream);
|
||||
@@ -94,6 +95,8 @@ namespace GameList
|
||||
static PlayedTimeMap LoadPlayedTimeMap(const std::string& path);
|
||||
static PlayedTimeEntry UpdatePlayedTimeFile(
|
||||
const std::string& path, const std::string& serial, std::time_t last_time, std::time_t add_time);
|
||||
|
||||
static std::string GetCustomPropertiesFile();
|
||||
} // namespace GameList
|
||||
|
||||
static std::vector<GameList::Entry> s_entries;
|
||||
@@ -582,11 +585,11 @@ void GameList::RewriteCacheFile()
|
||||
|
||||
static bool IsPathExcluded(const std::vector<std::string>& excluded_paths, const std::string& path)
|
||||
{
|
||||
return (std::find(excluded_paths.begin(), excluded_paths.end(), path) != excluded_paths.end());
|
||||
return std::find_if(excluded_paths.begin(), excluded_paths.end(), [&path](const std::string& entry) { return path.starts_with(entry); }) != excluded_paths.end();
|
||||
}
|
||||
|
||||
void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache, const std::vector<std::string>& excluded_paths,
|
||||
const PlayedTimeMap& played_time_map, ProgressCallback* progress)
|
||||
const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini, ProgressCallback* progress)
|
||||
{
|
||||
Console.WriteLn("Scanning %s%s", path, recursive ? " (recursively)" : "");
|
||||
|
||||
@@ -596,7 +599,7 @@ void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache,
|
||||
FileSystem::FindResultsArray files;
|
||||
FileSystem::FindFiles(path, "*",
|
||||
recursive ? (FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES | FILESYSTEM_FIND_RECURSIVE) :
|
||||
(FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES),
|
||||
(FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_HIDDEN_FILES),
|
||||
&files);
|
||||
|
||||
u32 files_scanned = 0;
|
||||
@@ -619,7 +622,7 @@ void GameList::ScanDirectory(const char* path, bool recursive, bool only_cache,
|
||||
}
|
||||
|
||||
progress->SetFormattedStatusText("Scanning '%s'...", FileSystem::GetDisplayNameFromPath(ffd.FileName).c_str());
|
||||
ScanFile(std::move(ffd.FileName), ffd.ModificationTime, lock, played_time_map);
|
||||
ScanFile(std::move(ffd.FileName), ffd.ModificationTime, lock, played_time_map, custom_attributes_ini);
|
||||
progress->SetProgressValue(files_scanned);
|
||||
}
|
||||
|
||||
@@ -648,8 +651,8 @@ bool GameList::AddFileFromCache(const std::string& path, std::time_t timestamp,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameList::ScanFile(
|
||||
std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock, const PlayedTimeMap& played_time_map)
|
||||
bool GameList::ScanFile(std::string path, std::time_t timestamp, std::unique_lock<std::recursive_mutex>& lock,
|
||||
const PlayedTimeMap& played_time_map, const INISettingsInterface& custom_attributes_ini)
|
||||
{
|
||||
// don't block UI while scanning
|
||||
lock.unlock();
|
||||
@@ -675,13 +678,28 @@ bool GameList::ScanFile(
|
||||
return true;
|
||||
}
|
||||
|
||||
auto iter = played_time_map.find(entry.serial);
|
||||
const auto iter = played_time_map.find(entry.serial);
|
||||
if (iter != played_time_map.end())
|
||||
{
|
||||
entry.last_played_time = iter->second.last_played_time;
|
||||
entry.total_played_time = iter->second.total_played_time;
|
||||
}
|
||||
|
||||
auto custom_title = custom_attributes_ini.GetOptionalStringValue(entry.path.c_str(), "Title");
|
||||
if (custom_title)
|
||||
{
|
||||
entry.title = std::move(custom_title.value());
|
||||
}
|
||||
const auto custom_region = custom_attributes_ini.GetOptionalIntValue(entry.path.c_str(), "Region");
|
||||
if (custom_region)
|
||||
{
|
||||
const int custom_region_value = custom_region.value();
|
||||
if (custom_region_value >= 0 && custom_region_value < static_cast<int>(Region::Count))
|
||||
{
|
||||
entry.region = static_cast<Region>(custom_region_value);
|
||||
}
|
||||
}
|
||||
|
||||
lock.lock();
|
||||
|
||||
// remove if present
|
||||
@@ -764,6 +782,8 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback*
|
||||
const std::vector<std::string> dirs(Host::GetBaseStringListSetting("GameList", "Paths"));
|
||||
const std::vector<std::string> recursive_dirs(Host::GetBaseStringListSetting("GameList", "RecursivePaths"));
|
||||
const PlayedTimeMap played_time(LoadPlayedTimeMap(GetPlayedTimeFile()));
|
||||
INISettingsInterface custom_attributes_ini(GetCustomPropertiesFile());
|
||||
custom_attributes_ini.Load();
|
||||
|
||||
if (!dirs.empty() || !recursive_dirs.empty())
|
||||
{
|
||||
@@ -777,7 +797,7 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback*
|
||||
if (progress->IsCancelled())
|
||||
break;
|
||||
|
||||
ScanDirectory(dir.c_str(), false, only_cache, excluded_paths, played_time, progress);
|
||||
ScanDirectory(dir.c_str(), false, only_cache, excluded_paths, played_time, custom_attributes_ini, progress);
|
||||
progress->SetProgressValue(++directory_counter);
|
||||
}
|
||||
for (const std::string& dir : recursive_dirs)
|
||||
@@ -785,7 +805,7 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback*
|
||||
if (progress->IsCancelled())
|
||||
break;
|
||||
|
||||
ScanDirectory(dir.c_str(), true, only_cache, excluded_paths, played_time, progress);
|
||||
ScanDirectory(dir.c_str(), true, only_cache, excluded_paths, played_time, custom_attributes_ini, progress);
|
||||
progress->SetProgressValue(++directory_counter);
|
||||
}
|
||||
}
|
||||
@@ -804,16 +824,18 @@ bool GameList::RescanPath(const std::string& path)
|
||||
std::unique_lock lock(s_mutex);
|
||||
|
||||
const PlayedTimeMap played_time(LoadPlayedTimeMap(GetPlayedTimeFile()));
|
||||
INISettingsInterface custom_attributes_ini(GetCustomPropertiesFile());
|
||||
custom_attributes_ini.Load();
|
||||
|
||||
{
|
||||
// cancel if excluded
|
||||
const std::vector<std::string> excluded_paths(Host::GetBaseStringListSetting("GameList", "ExcludedPaths"));
|
||||
if (std::find(excluded_paths.begin(), excluded_paths.end(), path) != excluded_paths.end())
|
||||
if (IsPathExcluded(excluded_paths, path))
|
||||
return false;
|
||||
}
|
||||
|
||||
// re-scan!
|
||||
if (!ScanFile(path, sd.ModificationTime, lock, played_time))
|
||||
if (!ScanFile(path, sd.ModificationTime, lock, played_time, custom_attributes_ini))
|
||||
return true;
|
||||
|
||||
// update cache.. this is far from ideal, but since everything's variable length, all we can do.
|
||||
@@ -1319,3 +1341,74 @@ bool GameList::DownloadCovers(const std::vector<std::string>& url_templates, boo
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string GameList::GetCustomPropertiesFile()
|
||||
{
|
||||
return Path::Combine(EmuFolders::Settings, "custom_properties.ini");
|
||||
}
|
||||
|
||||
void GameList::CheckCustomAttributesForPath(const std::string& path, bool& has_custom_title, bool& has_custom_region)
|
||||
{
|
||||
INISettingsInterface names(GetCustomPropertiesFile());
|
||||
if (names.Load())
|
||||
{
|
||||
has_custom_title = names.ContainsValue(path.c_str(), "Title");
|
||||
has_custom_region = names.ContainsValue(path.c_str(), "Region");
|
||||
}
|
||||
}
|
||||
|
||||
void GameList::SaveCustomTitleForPath(const std::string& path, const std::string& custom_title)
|
||||
{
|
||||
INISettingsInterface names(GetCustomPropertiesFile());
|
||||
names.Load();
|
||||
|
||||
if (!custom_title.empty())
|
||||
{
|
||||
names.SetStringValue(path.c_str(), "Title", custom_title.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
names.DeleteValue(path.c_str(), "Title");
|
||||
}
|
||||
|
||||
if (names.Save())
|
||||
{
|
||||
// Let the cache update by rescanning
|
||||
RescanPath(path);
|
||||
}
|
||||
}
|
||||
|
||||
void GameList::SaveCustomRegionForPath(const std::string& path, int custom_region)
|
||||
{
|
||||
INISettingsInterface names(GetCustomPropertiesFile());
|
||||
names.Load();
|
||||
|
||||
if (custom_region >= 0)
|
||||
{
|
||||
names.SetIntValue(path.c_str(), "Region", custom_region);
|
||||
}
|
||||
else
|
||||
{
|
||||
names.DeleteValue(path.c_str(), "Region");
|
||||
}
|
||||
|
||||
if (names.Save())
|
||||
{
|
||||
// Let the cache update by rescanning
|
||||
RescanPath(path);
|
||||
}
|
||||
}
|
||||
|
||||
std::string GameList::GetCustomTitleForPath(const std::string& path)
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
std::unique_lock lock(s_mutex);
|
||||
const GameList::Entry* entry = GetEntryForPath(path.c_str());
|
||||
if (entry)
|
||||
{
|
||||
ret = entry->title;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -154,4 +154,10 @@ namespace GameList
|
||||
/// the use_serial parameter. save_callback optionall takes the entry and the path the new cover is saved to.
|
||||
bool DownloadCovers(const std::vector<std::string>& url_templates, bool use_serial = false, ProgressCallback* progress = nullptr,
|
||||
std::function<void(const Entry*, std::string)> save_callback = {});
|
||||
|
||||
// Custom properties support
|
||||
void CheckCustomAttributesForPath(const std::string& path, bool& has_custom_title, bool& has_custom_region);
|
||||
void SaveCustomTitleForPath(const std::string& path, const std::string& custom_title);
|
||||
void SaveCustomRegionForPath(const std::string& path, int custom_region);
|
||||
std::string GetCustomTitleForPath(const std::string& path);
|
||||
} // namespace GameList
|
||||
|
||||
@@ -329,7 +329,7 @@ static u32 WRITERING_DMA(u32* pMem, u32 qwc)
|
||||
// so we can get away with transferring "most" of it when it's a big packet.
|
||||
// Use Wallace and Gromit Project Zoo or The Suffering for testing
|
||||
if (qwc > 64)
|
||||
qwc = qwc - 64;
|
||||
qwc = qwc * 0.5f;
|
||||
else
|
||||
qwc = std::min(qwc, 8u);
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ __fi void IPUProcessInterrupt()
|
||||
if (ipuRegs.ctrl.BUSY && !CommandExecuteQueued)
|
||||
IPUWorker();
|
||||
|
||||
if (ipuRegs.ctrl.BUSY)
|
||||
if (ipuRegs.ctrl.BUSY && !IPU1Status.DataRequested && !(cpuRegs.interrupt & 1 << IPU_PROCESS))
|
||||
{
|
||||
CPU_INT(IPU_PROCESS, ProcessedData ? ProcessedData : 64);
|
||||
}
|
||||
|
||||
@@ -93,6 +93,8 @@ int IPU_Fifo_Input::write(const u32* pMem, int size)
|
||||
if (g_BP.IFC == 8)
|
||||
IPU1Status.DataRequested = false;
|
||||
|
||||
CPU_INT(IPU_PROCESS, transfer_size * BIAS);
|
||||
|
||||
return transfer_size;
|
||||
}
|
||||
|
||||
@@ -106,7 +108,7 @@ int IPU_Fifo_Input::read(void *value)
|
||||
|
||||
if(ipu1ch.chcr.STR && cpuRegs.eCycle[4] == 0x9999)
|
||||
{
|
||||
CPU_INT( DMAC_TO_IPU, 4);
|
||||
CPU_INT( DMAC_TO_IPU, std::min(8U, ipu1ch.qwc));
|
||||
}
|
||||
|
||||
if (g_BP.IFC == 0) return 0;
|
||||
@@ -183,7 +185,7 @@ void WriteFIFO_IPUin(const mem128_t* value)
|
||||
{
|
||||
if (ipuRegs.ctrl.BUSY && !CommandExecuteQueued)
|
||||
{
|
||||
CommandExecuteQueued = true;
|
||||
CommandExecuteQueued = false;
|
||||
CPU_INT(IPU_PROCESS, 8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -983,7 +983,7 @@ __fi static void finishmpeg2sliceIDEC()
|
||||
__ri static bool mpeg2sliceIDEC()
|
||||
{
|
||||
u16 code;
|
||||
|
||||
static bool ready_to_decode = true;
|
||||
switch (ipu_cmd.pos[0])
|
||||
{
|
||||
case 0:
|
||||
@@ -1007,6 +1007,13 @@ __ri static bool mpeg2sliceIDEC()
|
||||
ipu_cmd.pos[0] = 2;
|
||||
while (1)
|
||||
{
|
||||
if (ready_to_decode == true)
|
||||
{
|
||||
ready_to_decode = false;
|
||||
CPU_INT(IPU_PROCESS, 64); // Should probably be much higher, but myst 3 doesn't like it right now.
|
||||
ipu_cmd.pos[0] = 2;
|
||||
return false;
|
||||
}
|
||||
// IPU0 isn't ready for data, so let's wait for it to be
|
||||
if ((!ipu0ch.chcr.STR || ipuRegs.ctrl.OFC || ipu0ch.qwc == 0) && ipu_cmd.pos[1] <= 2)
|
||||
{
|
||||
@@ -1116,15 +1123,13 @@ __ri static bool mpeg2sliceIDEC()
|
||||
ipu_dither(rgb32, rgb16, decoder.dte);
|
||||
decoder.SetOutputTo(rgb16);
|
||||
}
|
||||
ProcessedData += decoder.ipu0_data;
|
||||
ipu_cmd.pos[1] = 2;
|
||||
return false;
|
||||
|
||||
[[fallthrough]];
|
||||
case 2:
|
||||
{
|
||||
|
||||
pxAssert(decoder.ipu0_data > 0);
|
||||
|
||||
ready_to_decode = true;
|
||||
uint read = ipu_fifo.out.write((u32*)decoder.GetIpuDataPtr(), decoder.ipu0_data);
|
||||
decoder.AdvanceIpuDataBy(read);
|
||||
|
||||
@@ -1205,6 +1210,7 @@ __ri static bool mpeg2sliceIDEC()
|
||||
|
||||
ipu_cmd.pos[1] = 0;
|
||||
ipu_cmd.pos[2] = 0;
|
||||
ready_to_decode = true;
|
||||
}
|
||||
|
||||
finish_idec:
|
||||
@@ -1269,6 +1275,7 @@ finish_idec:
|
||||
__fi static bool mpeg2_slice()
|
||||
{
|
||||
int DCT_offset, DCT_stride;
|
||||
static bool ready_to_decode = true;
|
||||
|
||||
macroblock_8& mb8 = decoder.mb8;
|
||||
macroblock_16& mb16 = decoder.mb16;
|
||||
@@ -1299,7 +1306,12 @@ __fi static bool mpeg2_slice()
|
||||
|
||||
case 2:
|
||||
ipu_cmd.pos[0] = 2;
|
||||
|
||||
if (ready_to_decode == true)
|
||||
{
|
||||
ready_to_decode = false;
|
||||
CPU_INT(IPU_PROCESS, 64); // Should probably be much higher, but myst 3 doesn't like it right now.
|
||||
return false;
|
||||
}
|
||||
// IPU0 isn't ready for data, so let's wait for it to be
|
||||
if ((!ipu0ch.chcr.STR || ipuRegs.ctrl.OFC || ipu0ch.qwc == 0) && ipu_cmd.pos[0] <= 3)
|
||||
{
|
||||
@@ -1406,23 +1418,23 @@ __fi static bool mpeg2_slice()
|
||||
{
|
||||
if (decoder.macroblock_modes & MACROBLOCK_PATTERN)
|
||||
{
|
||||
switch(ipu_cmd.pos[1])
|
||||
switch (ipu_cmd.pos[1])
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// Get coded block pattern
|
||||
const CBPtab* tab;
|
||||
u16 code = UBITS(16);
|
||||
{
|
||||
// Get coded block pattern
|
||||
const CBPtab* tab;
|
||||
u16 code = UBITS(16);
|
||||
|
||||
if (code >= 0x2000)
|
||||
tab = CBP_7 + (UBITS(7) - 16);
|
||||
else
|
||||
tab = CBP_9 + UBITS(9);
|
||||
if (code >= 0x2000)
|
||||
tab = CBP_7 + (UBITS(7) - 16);
|
||||
else
|
||||
tab = CBP_9 + UBITS(9);
|
||||
|
||||
DUMPBITS(tab->len);
|
||||
decoder.coded_block_pattern = tab->cbp;
|
||||
}
|
||||
[[fallthrough]];
|
||||
DUMPBITS(tab->len);
|
||||
decoder.coded_block_pattern = tab->cbp;
|
||||
}
|
||||
[[fallthrough]];
|
||||
|
||||
case 1:
|
||||
if (decoder.coded_block_pattern & 0x20)
|
||||
@@ -1490,9 +1502,11 @@ __fi static bool mpeg2_slice()
|
||||
}
|
||||
break;
|
||||
|
||||
jNO_DEFAULT;
|
||||
jNO_DEFAULT;
|
||||
}
|
||||
}
|
||||
else
|
||||
DevCon.Warning("No macroblock mode");
|
||||
}
|
||||
|
||||
// Send The MacroBlock via DmaIpuFrom
|
||||
@@ -1500,14 +1514,11 @@ __fi static bool mpeg2_slice()
|
||||
coded_block_pattern = decoder.coded_block_pattern;
|
||||
|
||||
decoder.SetOutputTo(mb16);
|
||||
ProcessedData += decoder.ipu0_data;
|
||||
ipu_cmd.pos[0] = 3;
|
||||
return false;
|
||||
|
||||
[[fallthrough]];
|
||||
case 3:
|
||||
{
|
||||
pxAssert(decoder.ipu0_data > 0);
|
||||
|
||||
ready_to_decode = true;
|
||||
uint read = ipu_fifo.out.write((u32*)decoder.GetIpuDataPtr(), decoder.ipu0_data);
|
||||
decoder.AdvanceIpuDataBy(read);
|
||||
|
||||
@@ -1577,6 +1588,7 @@ __fi static bool mpeg2_slice()
|
||||
break;
|
||||
}
|
||||
|
||||
ready_to_decode = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -149,12 +149,6 @@ void IPU1dma()
|
||||
}
|
||||
}
|
||||
|
||||
if (ipuRegs.ctrl.BUSY && !CommandExecuteQueued)
|
||||
{
|
||||
CommandExecuteQueued = true;
|
||||
CPU_INT(IPU_PROCESS, totalqwc * BIAS);
|
||||
}
|
||||
|
||||
IPU_LOG("Completed Call IPU1 DMA QWC Remaining %x Finished %d In Progress %d tadr %x", ipu1ch.qwc, IPU1Status.DMAFinished, IPU1Status.InProgress, ipu1ch.tadr);
|
||||
}
|
||||
|
||||
@@ -198,13 +192,14 @@ void IPU0dma()
|
||||
dmacRegs.stadr.ADDR = ipu0ch.madr;
|
||||
}
|
||||
|
||||
IPU_INT_FROM( readsize * BIAS );
|
||||
if (!ipu0ch.qwc)
|
||||
IPU_INT_FROM(readsize * BIAS);
|
||||
|
||||
if (ipuRegs.ctrl.BUSY && !CommandExecuteQueued)
|
||||
{
|
||||
CommandExecuteQueued = true;
|
||||
CommandExecuteQueued = false;
|
||||
CPU_SET_DMASTALL(DMAC_FROM_IPU, true);
|
||||
CPU_INT(IPU_PROCESS, readsize * BIAS);
|
||||
IPUProcessInterrupt();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3232,8 +3232,6 @@ void FullscreenUI::DrawGraphicsSettingsPage()
|
||||
static constexpr const char* s_auto_flush_options[] = {
|
||||
"Disabled (Default)", "Enabled (Sprites Only)", "Enabled (All Primitives)"};
|
||||
|
||||
DrawIntListSetting(bsi, "Half-Bottom Override", "Control the half-screen fix detection on texture shuffling.", "EmuCore/GS",
|
||||
"UserHacks_Half_Bottom_Override", -1, s_generic_options, std::size(s_generic_options), -1);
|
||||
DrawIntListSetting(bsi, "CPU Sprite Render Size", "Uses software renderer to draw texture decompression-like sprites.",
|
||||
"EmuCore/GS", "UserHacks_CPUSpriteRenderBW", 0, s_cpu_sprite_render_bw_options, std::size(s_cpu_sprite_render_bw_options));
|
||||
DrawIntListSetting(bsi, "CPU Sprite Render Level", "Determines filter level for CPU sprite render.", "EmuCore/GS",
|
||||
|
||||
@@ -138,9 +138,10 @@ bool ImGuiManager::Initialize()
|
||||
io.BackendUsingLegacyNavInputArray = 0;
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard | ImGuiConfigFlags_NavEnableGamepad;
|
||||
|
||||
s_window_width = static_cast<float>(g_gs_device->GetWindowWidth());
|
||||
s_window_height = static_cast<float>(g_gs_device->GetWindowHeight());
|
||||
io.DisplayFramebufferScale = ImVec2(1, 1); // We already scale things ourselves, this would double-apply scaling
|
||||
io.DisplaySize.x = static_cast<float>(g_gs_device->GetWindowWidth());
|
||||
io.DisplaySize.y = static_cast<float>(g_gs_device->GetWindowHeight());
|
||||
io.DisplaySize = ImVec2(s_window_width, s_window_height);
|
||||
|
||||
SetKeyMap();
|
||||
SetStyle();
|
||||
@@ -656,7 +657,7 @@ void ImGuiManager::DrawOSDMessages()
|
||||
const float margin = std::ceil(10.0f * scale);
|
||||
const float padding = std::ceil(8.0f * scale);
|
||||
const float rounding = std::ceil(5.0f * scale);
|
||||
const float max_width = ImGui::GetIO().DisplaySize.x - (margin + padding) * 2.0f;
|
||||
const float max_width = s_window_width - (margin + padding) * 2.0f;
|
||||
float position_x = margin;
|
||||
float position_y = margin;
|
||||
|
||||
@@ -679,7 +680,7 @@ void ImGuiManager::DrawOSDMessages()
|
||||
const float opacity = std::min(time_remaining, 1.0f);
|
||||
const u32 alpha = static_cast<u32>(opacity * 255.0f);
|
||||
|
||||
if (position_y >= ImGui::GetIO().DisplaySize.y)
|
||||
if (position_y >= s_window_height)
|
||||
break;
|
||||
|
||||
const ImVec2 pos(position_x, position_y);
|
||||
|
||||
@@ -118,9 +118,9 @@ void ImGuiManager::DrawPerformanceOverlay(float& position_y)
|
||||
{ \
|
||||
text_size = font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
|
||||
dl->AddText(font, font->FontSize, \
|
||||
ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x + shadow_offset, position_y + shadow_offset), \
|
||||
ImVec2(GetWindowWidth() - margin - text_size.x + shadow_offset, position_y + shadow_offset), \
|
||||
IM_COL32(0, 0, 0, 100), (text)); \
|
||||
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y), color, (text)); \
|
||||
dl->AddText(font, font->FontSize, ImVec2(GetWindowWidth() - margin - text_size.x, position_y), color, (text)); \
|
||||
position_y += text_size.y + spacing; \
|
||||
} while (0)
|
||||
|
||||
@@ -261,7 +261,7 @@ void ImGuiManager::DrawPerformanceOverlay(float& position_y)
|
||||
{
|
||||
const ImVec2 history_size(200.0f * scale, 50.0f * scale);
|
||||
ImGui::SetNextWindowSize(ImVec2(history_size.x, history_size.y));
|
||||
ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x - margin - history_size.x, position_y));
|
||||
ImGui::SetNextWindowPos(ImVec2(GetWindowWidth() - margin - history_size.x, position_y));
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.25f));
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_PlotLines, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
@@ -390,8 +390,6 @@ void ImGuiManager::DrawSettingsOverlay()
|
||||
APPEND("AF={} ", EmuConfig.GS.MaxAnisotropy);
|
||||
if (GSConfig.Dithering != 2)
|
||||
APPEND("DI={} ", GSConfig.Dithering);
|
||||
if (GSConfig.UserHacks_HalfBottomOverride >= 0)
|
||||
APPEND("HBO={} ", GSConfig.UserHacks_HalfBottomOverride);
|
||||
if (GSConfig.UserHacks_HalfPixelOffset > 0)
|
||||
APPEND("HPO={} ", GSConfig.UserHacks_HalfPixelOffset);
|
||||
if (GSConfig.UserHacks_RoundSprite > 0)
|
||||
@@ -451,15 +449,15 @@ void ImGuiManager::DrawSettingsOverlay()
|
||||
const float shadow_offset = 1.0f * scale;
|
||||
const float margin = 10.0f * scale;
|
||||
ImFont* font = ImGuiManager::GetFixedFont();
|
||||
const float position_y = ImGui::GetIO().DisplaySize.y - margin - font->FontSize;
|
||||
const float position_y = GetWindowHeight() - margin - font->FontSize;
|
||||
|
||||
ImDrawList* dl = ImGui::GetBackgroundDrawList();
|
||||
ImVec2 text_size =
|
||||
font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, text.c_str(), text.c_str() + text.length(), nullptr);
|
||||
dl->AddText(font, font->FontSize,
|
||||
ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x + shadow_offset, position_y + shadow_offset), IM_COL32(0, 0, 0, 100),
|
||||
ImVec2(GetWindowWidth() - margin - text_size.x + shadow_offset, position_y + shadow_offset), IM_COL32(0, 0, 0, 100),
|
||||
text.c_str(), text.c_str() + text.length());
|
||||
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y), IM_COL32(255, 255, 255, 255),
|
||||
dl->AddText(font, font->FontSize, ImVec2(GetWindowWidth() - margin - text_size.x, position_y), IM_COL32(255, 255, 255, 255),
|
||||
text.c_str(), text.c_str() + text.length());
|
||||
}
|
||||
|
||||
@@ -631,9 +629,9 @@ void ImGuiManager::DrawInputRecordingOverlay(float& position_y)
|
||||
{ \
|
||||
text_size = font->CalcTextSizeA(font->FontSize, std::numeric_limits<float>::max(), -1.0f, (text), nullptr, nullptr); \
|
||||
dl->AddText(font, font->FontSize, \
|
||||
ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x + shadow_offset, position_y + shadow_offset), \
|
||||
ImVec2(GetWindowWidth() - margin - text_size.x + shadow_offset, position_y + shadow_offset), \
|
||||
IM_COL32(0, 0, 0, 100), (text)); \
|
||||
dl->AddText(font, font->FontSize, ImVec2(ImGui::GetIO().DisplaySize.x - margin - text_size.x, position_y), color, (text)); \
|
||||
dl->AddText(font, font->FontSize, ImVec2(GetWindowWidth() - margin - text_size.x, position_y), color, (text)); \
|
||||
position_y += text_size.y + spacing; \
|
||||
} while (0)
|
||||
// TODO - icon list that would be nice to add
|
||||
|
||||
@@ -616,7 +616,6 @@ bool Pcsx2Config::GSOptions::OptionsAreEqual(const GSOptions& right) const
|
||||
OpEqu(SkipDrawStart) &&
|
||||
|
||||
OpEqu(UserHacks_AutoFlush) &&
|
||||
OpEqu(UserHacks_HalfBottomOverride) &&
|
||||
OpEqu(UserHacks_HalfPixelOffset) &&
|
||||
OpEqu(UserHacks_RoundSprite) &&
|
||||
OpEqu(UserHacks_TCOffsetX) &&
|
||||
@@ -814,7 +813,6 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
|
||||
GSSettingIntEx(SkipDrawEnd, "UserHacks_SkipDraw_End");
|
||||
SkipDrawEnd = std::max(SkipDrawStart, SkipDrawEnd);
|
||||
|
||||
GSSettingIntEx(UserHacks_HalfBottomOverride, "UserHacks_Half_Bottom_Override");
|
||||
GSSettingIntEx(UserHacks_HalfPixelOffset, "UserHacks_HalfPixelOffset");
|
||||
GSSettingIntEx(UserHacks_RoundSprite, "UserHacks_round_sprite_offset");
|
||||
GSSettingIntEx(UserHacks_TCOffsetX, "UserHacks_TCOffsetX");
|
||||
@@ -882,7 +880,6 @@ void Pcsx2Config::GSOptions::MaskUserHacks()
|
||||
UserHacks_NativePaletteDraw = false;
|
||||
UserHacks_DisableSafeFeatures = false;
|
||||
UserHacks_DisableRenderFixes = false;
|
||||
UserHacks_HalfBottomOverride = -1;
|
||||
UserHacks_HalfPixelOffset = 0;
|
||||
UserHacks_RoundSprite = 0;
|
||||
UserHacks_AutoFlush = GSHWAutoFlushLevel::Disabled;
|
||||
@@ -1429,6 +1426,7 @@ Pcsx2Config::AchievementsOptions::AchievementsOptions()
|
||||
Notifications = true;
|
||||
SoundEffects = true;
|
||||
PrimedIndicators = true;
|
||||
NotificationsDuration = 5;
|
||||
}
|
||||
|
||||
void Pcsx2Config::AchievementsOptions::LoadSave(SettingsWrapper& wrap)
|
||||
@@ -1444,6 +1442,13 @@ void Pcsx2Config::AchievementsOptions::LoadSave(SettingsWrapper& wrap)
|
||||
SettingsWrapBitBool(Notifications);
|
||||
SettingsWrapBitBool(SoundEffects);
|
||||
SettingsWrapBitBool(PrimedIndicators);
|
||||
SettingsWrapBitfield(NotificationsDuration);
|
||||
|
||||
if (wrap.IsLoading())
|
||||
{
|
||||
//Clamp in case setting was updated manually using the INI
|
||||
NotificationsDuration = std::clamp(NotificationsDuration, 3, 10);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -888,6 +888,11 @@ std::vector<AvailableMcdInfo> FileMcd_GetAvailableCards(bool include_in_use_card
|
||||
continue;
|
||||
}
|
||||
|
||||
// We only want relevant file types.
|
||||
if (!(StringUtil::EndsWith(fd.FileName, ".ps2") || StringUtil::EndsWith(fd.FileName, ".mcr") ||
|
||||
StringUtil::EndsWith(fd.FileName, ".mcd") || StringUtil::EndsWith(fd.FileName, ".bin")))
|
||||
continue;
|
||||
|
||||
if (fd.Attributes & FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
if (!IsMemoryCardFolder(fd.FileName))
|
||||
|
||||
@@ -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 = 29;
|
||||
static constexpr u32 SHADER_CACHE_VERSION = 33;
|
||||
|
||||
@@ -267,7 +267,7 @@ namespace usb_eyetoy
|
||||
return devList;
|
||||
}
|
||||
|
||||
static int v4l_open(std::string selectedDevice)
|
||||
static int v4l_open(const std::string& selectedDevice)
|
||||
{
|
||||
char dev_name[64];
|
||||
struct v4l2_capability cap;
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace usb_eyetoy
|
||||
return devList;
|
||||
}
|
||||
|
||||
int DirectShow::InitializeDevice(std::wstring selectedDevice)
|
||||
int DirectShow::InitializeDevice(const std::wstring& selectedDevice)
|
||||
{
|
||||
|
||||
// Create the Capture Graph Builder.
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace usb_eyetoy
|
||||
void SetCallback(DShowVideoCaptureCallback cb) { callbackhandler->SetCallback(cb); }
|
||||
bool Start();
|
||||
void Stop();
|
||||
int InitializeDevice(std::wstring selectedDevice);
|
||||
int InitializeDevice(const std::wstring& selectedDevice);
|
||||
|
||||
private:
|
||||
wil::unique_couninitialize_call dshowCoInitialize;
|
||||
|
||||
@@ -536,14 +536,19 @@ namespace usb_lightgun
|
||||
ImGuiManager::ClearSoftwareCursor(prev_pointer_index);
|
||||
|
||||
// Pointer changed, so need to update software cursor.
|
||||
if (!cursor_path.empty())
|
||||
ImGuiManager::SetSoftwareCursor(new_pointer_index, cursor_path, cursor_scale, cursor_color);
|
||||
else if (!s->cursor_path.empty())
|
||||
ImGuiManager::ClearSoftwareCursor(new_pointer_index);
|
||||
|
||||
const bool had_software_cursor = !s->cursor_path.empty();
|
||||
s->cursor_path = std::move(cursor_path);
|
||||
s->cursor_scale = cursor_scale;
|
||||
s->cursor_color = cursor_color;
|
||||
if (!s->cursor_path.empty())
|
||||
{
|
||||
ImGuiManager::SetSoftwareCursor(new_pointer_index, s->cursor_path, s->cursor_scale, s->cursor_color);
|
||||
s->UpdateSoftwarePointerPosition();
|
||||
}
|
||||
else if (had_software_cursor)
|
||||
{
|
||||
ImGuiManager::ClearSoftwareCursor(new_pointer_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -479,7 +479,7 @@ namespace usb_pad
|
||||
static constexpr const SettingInfo info[] = {
|
||||
{SettingInfo::Type::Float, "TurntableMultiplier", TRANSLATE_NOOP("USB", "Turntable Multiplier"),
|
||||
TRANSLATE_NOOP("USB", "Apply a multiplier to the turntable"),
|
||||
"0.00", "0.00", "100.0", "1.0", "%.0fx", nullptr, nullptr, 1.0f}};
|
||||
"1.00", "0.00", "100.0", "1.0", "%.0fx", nullptr, nullptr, 1.0f}};
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -849,19 +849,22 @@ void VMManager::UpdateDiscDetails(bool booting)
|
||||
|
||||
SaveSessionTime(old_serial);
|
||||
|
||||
std::string custom_title = GameList::GetCustomTitleForPath(CDVDsys_GetFile(CDVDsys_GetSourceType()));
|
||||
if (serial_is_valid)
|
||||
{
|
||||
if (const GameDatabaseSchema::GameEntry* game = GameDatabase::findGame(s_disc_serial))
|
||||
{
|
||||
std::string game_title = custom_title.empty() ? game->name : std::move(custom_title);
|
||||
|
||||
// Append the ELF override if we're using it with a disc.
|
||||
if (!s_elf_override.empty())
|
||||
{
|
||||
title = fmt::format(
|
||||
"{} [{}]", game->name, Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(s_elf_override)));
|
||||
"{} [{}]", game_title, Path::GetFileTitle(FileSystem::GetDisplayNameFromPath(s_elf_override)));
|
||||
}
|
||||
else
|
||||
{
|
||||
title = game->name;
|
||||
title = std::move(game_title);
|
||||
}
|
||||
|
||||
memcardFilters = game->memcardFiltersAsString();
|
||||
@@ -872,6 +875,11 @@ void VMManager::UpdateDiscDetails(bool booting)
|
||||
}
|
||||
}
|
||||
|
||||
if (title.empty())
|
||||
{
|
||||
title = std::move(custom_title);
|
||||
}
|
||||
|
||||
if (title.empty())
|
||||
{
|
||||
if (!s_disc_serial.empty())
|
||||
|
||||