mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-07-31 03:08:40 +02:00
Compare commits
@@ -158,6 +158,7 @@ declare -a SYSLIBS=(
|
||||
"libhx509.so.5"
|
||||
"libsqlite3.so.0"
|
||||
"libcrypt.so.1"
|
||||
"libdbus-1.so.3"
|
||||
)
|
||||
|
||||
declare -a DEPLIBS=(
|
||||
|
||||
Vendored
+2
-7
@@ -33,14 +33,9 @@
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<WarningLevel>TurnOffAllWarnings</WarningLevel>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\glad\include;$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(SolutionDir)3rdparty\glad\glad.vcxproj">
|
||||
<Project>{c0293b32-5acf-40f0-aa6c-e6da6f3bf33a}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="include\imconfig.h" />
|
||||
<ClInclude Include="include\imgui.h" />
|
||||
@@ -60,4 +55,4 @@
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jpgd", "3rdparty\jpgd\jpgd.
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "common\common.vcxproj", "{4639972E-424E-4E13-8B07-CA403C481346}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pcsx2core", "pcsx2\pcsx2core.vcxproj", "{6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pcsx2", "pcsx2\pcsx2.vcxproj", "{6C7986C4-3E4D-4DCC-B3C6-6BB12B238995}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glad", "3rdparty\glad\glad.vcxproj", "{C0293B32-5ACF-40F0-AA6C-E6DA6F3BF33A}"
|
||||
EndProject
|
||||
|
||||
+559
-185
File diff suppressed because it is too large
Load Diff
@@ -534,10 +534,11 @@
|
||||
030000009b2800002300000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows,
|
||||
030000009b2800006900000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows,
|
||||
030000009b2800000800000000000000,Raphnet Dreamcast Adapter,a:b2,b:b1,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,lefttrigger:a2,leftx:a0,righttrigger:a3,righty:a1,start:b3,x:b10,y:b9,platform:Windows,
|
||||
030000009b2800006200000000000000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,
|
||||
030000009b2800003200000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,
|
||||
030000009b2800006000000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,
|
||||
030000009b2800001800000000000000,Raphnet Jaguar Adapter,a:b2,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b10,start:b3,x:b11,y:b12,platform:Windows,
|
||||
030000009b2800006300000000000000,Raphnet N64 Adapter,a:b0,b:b1,start:b3,lefttrigger:b2,dpup:b10,dpleft:b12,dpdown:b11,dpright:b13,leftx:a0,lefty:a1,-rightx:b8,+rightx:b9,-righty:b6,+righty:b7,leftshoulder:b4,rightshoulder:b5,platform:Windows,
|
||||
030000009b2800006300000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows,
|
||||
030000009b2800000200000000000000,Raphnet NES Adapter,a:b7,b:b6,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b4,platform:Windows,
|
||||
030000009b2800004400000000000000,Raphnet PS1 and PS2 Adapter,a:b1,b:b2,back:b5,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b9,rightx:a3,righty:a4,start:b4,x:b0,y:b3,platform:Windows,
|
||||
030000009b2800004300000000000000,Raphnet Saturn,a:b0,b:b1,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,
|
||||
@@ -868,7 +869,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
030000000d0f0000ee00000000010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
|
||||
03000000242e0000ff0b000000010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Mac OS X,
|
||||
03000000790000004e95000000010000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Mac OS X,
|
||||
03000000830500006020000000000000,iBuffalo Gamepad,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,
|
||||
03000000830500006020000000000000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,
|
||||
03000000ef0500000300000000020000,InterAct AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Mac OS X,
|
||||
03000000fd0500000030000010010000,Interact GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Mac OS X,
|
||||
030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Mac OS X,
|
||||
@@ -1154,6 +1155,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000558500001b06000010010000,GameSir G4 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
05000000ac0500002d0200001b010000,GameSir G4s,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b33,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,
|
||||
03000000ac0500007a05000011010000,GameSir G5,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000bc2000005656000011010000,GameSir T4w,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000ac0500001a06000011010000,GameSir-T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
@@ -1211,7 +1213,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000242e00006a38000010010000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Linux,
|
||||
03000000242e00008816000001010000,Hyperkin X91,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,
|
||||
03000000f00300008d03000011010000,HyperX Clutch,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,
|
||||
03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,
|
||||
03000000830500006020000010010000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,
|
||||
050000006964726f69643a636f6e0000,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:Linux,
|
||||
03000000b50700001503000010010000,Impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,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:Linux,
|
||||
03000000d80400008200000003000000,IMS PCU0,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux,
|
||||
@@ -1334,9 +1336,12 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
05000000010000000100000003000000,Nintendo Wii Remote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,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,
|
||||
050000007e0500003003000001000000,Nintendo Wii U Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,
|
||||
030000000d0500000308000010010000,Nostromo n45 Dual Analog,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux,
|
||||
030000007e0500001920000011810000,NSO N64 Controller,+rightx:b10,+righty:b8,-rightx:b9,-righty:b7,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b3,lefttrigger:b2,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b4,righttrigger:b5,start:b6,platform:Linux,
|
||||
050000007e0500001920000001000000,NSO N64 Controller,+rightx:b8,+righty:b7,-rightx:b3,-righty:b2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Linux,
|
||||
050000007e0500001920000001800000,NSO N64 Controller,+rightx:b10,+righty:b8,-rightx:b9,-righty:b7,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b3,lefttrigger:b2,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b4,righttrigger:b5,start:b6,platform:Linux,
|
||||
050000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,
|
||||
030000007e0500001720000011810000,NSO SNES Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,
|
||||
050000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,lefttrigger:b7,rightshoulder:b6,righttrigger:b8,start:b10,x:b3,y:b2,platform:Linux,
|
||||
050000007e0500001720000001800000,NSO SNES Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,
|
||||
03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000550900001472000011010000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,
|
||||
05000000550900001472000001000000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,
|
||||
@@ -1508,6 +1513,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
030000008f0e00001431000010010000,SZMY Power PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000ba2200000701000001010000,Technology Innovation PS2 Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b2,platform:Linux,
|
||||
03000000790000001c18000011010000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000591c00002400000010010000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000591c00002600000010010000,THEGamepad,a:b2,b:b1,back:b6,leftx:a0,lefty:a1,leftshoulder:b4,rightshoulder:b5,x:b3,y:b0,start:b7,platform:Linux,
|
||||
030000004f04000015b3000001010000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,
|
||||
030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,
|
||||
030000004f04000020b3000010010000,Thrustmaster Dual Trigger,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,
|
||||
@@ -1583,6 +1590,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
050000005e040000130b000015050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
060000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
060000005e040000120b00000b050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
060000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000450c00002043000010010000,XEOX SL6556 BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
05000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,
|
||||
|
||||
@@ -30,7 +30,7 @@ layout(binding=0, rgba8) uniform writeonly image2D imgDst;
|
||||
|
||||
AF3 CasLoad(ASU2 p)
|
||||
{
|
||||
return texelFetch(imgSrc, srcOffset + ivec2(p), 0).rgb;
|
||||
return texelFetch(imgSrc, srcOffset + ivec2(p), 0).rgb;
|
||||
}
|
||||
|
||||
// Lets you transform input from the load into a linear color space between 0 and 1. See ffx_cas.h
|
||||
@@ -42,23 +42,23 @@ void CasInput(inout AF1 r, inout AF1 g, inout AF1 b) {}
|
||||
layout(local_size_x=64) in;
|
||||
void main()
|
||||
{
|
||||
// Do remapping of local xy in workgroup for a more PS-like swizzle pattern.
|
||||
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x)+AU2(gl_WorkGroupID.x<<4u,gl_WorkGroupID.y<<4u);
|
||||
// Do remapping of local xy in workgroup for a more PS-like swizzle pattern.
|
||||
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x)+AU2(gl_WorkGroupID.x<<4u,gl_WorkGroupID.y<<4u);
|
||||
|
||||
// Filter.
|
||||
AF4 c;
|
||||
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
|
||||
imageStore(imgDst, ASU2(gxy), c);
|
||||
gxy.x += 8u;
|
||||
// Filter.
|
||||
AF4 c;
|
||||
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
|
||||
imageStore(imgDst, ASU2(gxy), c);
|
||||
gxy.x += 8u;
|
||||
|
||||
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
|
||||
imageStore(imgDst, ASU2(gxy), c);
|
||||
gxy.y += 8u;
|
||||
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
|
||||
imageStore(imgDst, ASU2(gxy), c);
|
||||
gxy.y += 8u;
|
||||
|
||||
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
|
||||
imageStore(imgDst, ASU2(gxy), c);
|
||||
gxy.x -= 8u;
|
||||
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
|
||||
imageStore(imgDst, ASU2(gxy), c);
|
||||
gxy.x -= 8u;
|
||||
|
||||
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
|
||||
imageStore(imgDst, ASU2(gxy), c);
|
||||
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
|
||||
imageStore(imgDst, ASU2(gxy), c);
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@ out vec4 PSin_c;
|
||||
|
||||
void vs_main()
|
||||
{
|
||||
PSin_p = vec4(POSITION, 0.5f, 1.0f);
|
||||
PSin_t = TEXCOORD0;
|
||||
PSin_c = COLOR;
|
||||
gl_Position = vec4(POSITION, 0.5f, 1.0f); // NOTE I don't know if it is possible to merge POSITION_OUT and gl_Position
|
||||
PSin_p = vec4(POSITION, 0.5f, 1.0f);
|
||||
PSin_t = TEXCOORD0;
|
||||
PSin_c = COLOR;
|
||||
gl_Position = vec4(POSITION, 0.5f, 1.0f); // NOTE I don't know if it is possible to merge POSITION_OUT and gl_Position
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -46,13 +46,13 @@ layout(location = 0) out vec4 SV_Target0;
|
||||
|
||||
vec4 sample_c()
|
||||
{
|
||||
return texture(TextureSampler, PSin_t);
|
||||
return texture(TextureSampler, PSin_t);
|
||||
}
|
||||
|
||||
#ifdef ps_copy
|
||||
void ps_copy()
|
||||
{
|
||||
SV_Target0 = sample_c();
|
||||
SV_Target0 = sample_c();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -67,20 +67,20 @@ void ps_depth_copy()
|
||||
// Need to be careful with precision here, it can break games like Spider-Man 3 and Dogs Life
|
||||
void ps_convert_rgba8_16bits()
|
||||
{
|
||||
highp uvec4 i = uvec4(sample_c() * vec4(255.5f, 255.5f, 255.5f, 255.5f));
|
||||
highp uvec4 i = uvec4(sample_c() * vec4(255.5f, 255.5f, 255.5f, 255.5f));
|
||||
|
||||
SV_Target1 = ((i.x & 0x00F8u) >> 3) | ((i.y & 0x00F8u) << 2) | ((i.z & 0x00f8u) << 7) | ((i.w & 0x80u) << 8);
|
||||
SV_Target1 = ((i.x & 0x00F8u) >> 3) | ((i.y & 0x00F8u) << 2) | ((i.z & 0x00f8u) << 7) | ((i.w & 0x80u) << 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_float32_32bits
|
||||
void ps_convert_float32_32bits()
|
||||
{
|
||||
// Convert a GL_FLOAT32 depth texture into a 32 bits UINT texture
|
||||
// Convert a GL_FLOAT32 depth texture into a 32 bits UINT texture
|
||||
#if HAS_CLIP_CONTROL
|
||||
SV_Target1 = uint(exp2(32.0f) * sample_c().r);
|
||||
SV_Target1 = uint(exp2(32.0f) * sample_c().r);
|
||||
#else
|
||||
SV_Target1 = uint(exp2(24.0f) * sample_c().r);
|
||||
SV_Target1 = uint(exp2(24.0f) * sample_c().r);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@@ -88,150 +88,150 @@ void ps_convert_float32_32bits()
|
||||
#ifdef ps_convert_float32_rgba8
|
||||
void ps_convert_float32_rgba8()
|
||||
{
|
||||
// Convert a GL_FLOAT32 depth texture into a RGBA color texture
|
||||
// Convert a GL_FLOAT32 depth texture into a RGBA color texture
|
||||
#if HAS_CLIP_CONTROL
|
||||
uint d = uint(sample_c().r * exp2(32.0f));
|
||||
uint d = uint(sample_c().r * exp2(32.0f));
|
||||
#else
|
||||
uint d = uint(sample_c().r * exp2(24.0f));
|
||||
uint d = uint(sample_c().r * exp2(24.0f));
|
||||
#endif
|
||||
SV_Target0 = vec4(uvec4((d & 0xFFu), ((d >> 8) & 0xFFu), ((d >> 16) & 0xFFu), (d >> 24))) / vec4(255.0);
|
||||
SV_Target0 = vec4(uvec4((d & 0xFFu), ((d >> 8) & 0xFFu), ((d >> 16) & 0xFFu), (d >> 24))) / vec4(255.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_float16_rgb5a1
|
||||
void ps_convert_float16_rgb5a1()
|
||||
{
|
||||
// Convert a GL_FLOAT32 (only 16 lsb) depth into a RGB5A1 color texture
|
||||
// Convert a GL_FLOAT32 (only 16 lsb) depth into a RGB5A1 color texture
|
||||
#if HAS_CLIP_CONTROL
|
||||
uint d = uint(sample_c().r * exp2(32.0f));
|
||||
uint d = uint(sample_c().r * exp2(32.0f));
|
||||
#else
|
||||
uint d = uint(sample_c().r * exp2(24.0f));
|
||||
uint d = uint(sample_c().r * exp2(24.0f));
|
||||
#endif
|
||||
SV_Target0 = vec4(uvec4((d & 0x1Fu), ((d >> 5) & 0x1Fu), ((d >> 10) & 0x1Fu), (d >> 15) & 0x01u)) / vec4(32.0f, 32.0f, 32.0f, 1.0f);
|
||||
SV_Target0 = vec4(uvec4((d & 0x1Fu), ((d >> 5) & 0x1Fu), ((d >> 10) & 0x1Fu), (d >> 15) & 0x01u)) / vec4(32.0f, 32.0f, 32.0f, 1.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
float rgba8_to_depth32(vec4 unorm)
|
||||
{
|
||||
uvec4 c = uvec4(unorm * vec4(255.5f));
|
||||
uvec4 c = uvec4(unorm * vec4(255.5f));
|
||||
#if HAS_CLIP_CONTROL
|
||||
return float(c.r | (c.g << 8) | (c.b << 16) | (c.a << 24)) * exp2(-32.0f);
|
||||
return float(c.r | (c.g << 8) | (c.b << 16) | (c.a << 24)) * exp2(-32.0f);
|
||||
#else
|
||||
return float(c.r | (c.g << 8) | (c.b << 16) | (c.a << 24)) * exp2(-24.0f);
|
||||
return float(c.r | (c.g << 8) | (c.b << 16) | (c.a << 24)) * exp2(-24.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
float rgba8_to_depth24(vec4 unorm)
|
||||
{
|
||||
uvec3 c = uvec3(unorm.rgb * vec3(255.5f));
|
||||
uvec3 c = uvec3(unorm.rgb * vec3(255.5f));
|
||||
#if HAS_CLIP_CONTROL
|
||||
return float(c.r | (c.g << 8) | (c.b << 16)) * exp2(-32.0f);
|
||||
return float(c.r | (c.g << 8) | (c.b << 16)) * exp2(-32.0f);
|
||||
#else
|
||||
return float(c.r | (c.g << 8) | (c.b << 16)) * exp2(-24.0f);
|
||||
return float(c.r | (c.g << 8) | (c.b << 16)) * exp2(-24.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
float rgba8_to_depth16(vec4 unorm)
|
||||
{
|
||||
uvec2 c = uvec2(unorm.rg * vec2(255.5f));
|
||||
uvec2 c = uvec2(unorm.rg * vec2(255.5f));
|
||||
#if HAS_CLIP_CONTROL
|
||||
return float(c.r | (c.g << 8)) * exp2(-32.0f);
|
||||
return float(c.r | (c.g << 8)) * exp2(-32.0f);
|
||||
#else
|
||||
return float(c.r | (c.g << 8)) * exp2(-24.0f);
|
||||
return float(c.r | (c.g << 8)) * exp2(-24.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
float rgb5a1_to_depth16(vec4 unorm)
|
||||
{
|
||||
uvec4 c = uvec4(unorm * vec4(255.5f));
|
||||
uvec4 c = uvec4(unorm * vec4(255.5f));
|
||||
#if HAS_CLIP_CONTROL
|
||||
return float(((c.r & 0xF8u) >> 3) | ((c.g & 0xF8u) << 2) | ((c.b & 0xF8u) << 7) | ((c.a & 0x80u) << 8)) * exp2(-32.0f);
|
||||
return float(((c.r & 0xF8u) >> 3) | ((c.g & 0xF8u) << 2) | ((c.b & 0xF8u) << 7) | ((c.a & 0x80u) << 8)) * exp2(-32.0f);
|
||||
#else
|
||||
return float(((c.r & 0xF8u) >> 3) | ((c.g & 0xF8u) << 2) | ((c.b & 0xF8u) << 7) | ((c.a & 0x80u) << 8)) * exp2(-24.0f);
|
||||
return float(((c.r & 0xF8u) >> 3) | ((c.g & 0xF8u) << 2) | ((c.b & 0xF8u) << 7) | ((c.a & 0x80u) << 8)) * exp2(-24.0f);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ps_convert_rgba8_float32
|
||||
void ps_convert_rgba8_float32()
|
||||
{
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
gl_FragDepth = rgba8_to_depth32(sample_c());
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
gl_FragDepth = rgba8_to_depth32(sample_c());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgba8_float24
|
||||
void ps_convert_rgba8_float24()
|
||||
{
|
||||
// Same as above but without the alpha channel (24 bits Z)
|
||||
// Same as above but without the alpha channel (24 bits Z)
|
||||
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
gl_FragDepth = rgba8_to_depth24(sample_c());
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
gl_FragDepth = rgba8_to_depth24(sample_c());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgba8_float16
|
||||
void ps_convert_rgba8_float16()
|
||||
{
|
||||
// Same as above but without the A/B channels (16 bits Z)
|
||||
// Same as above but without the A/B channels (16 bits Z)
|
||||
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
gl_FragDepth = rgba8_to_depth16(sample_c());
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
gl_FragDepth = rgba8_to_depth16(sample_c());
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgb5a1_float16
|
||||
void ps_convert_rgb5a1_float16()
|
||||
{
|
||||
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
|
||||
gl_FragDepth = rgb5a1_to_depth16(sample_c());
|
||||
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
|
||||
gl_FragDepth = rgb5a1_to_depth16(sample_c());
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SAMPLE_RGBA_DEPTH_BILN(CONVERT_FN) \
|
||||
ivec2 dims = textureSize(TextureSampler, 0); \
|
||||
vec2 top_left_f = PSin_t * vec2(dims) - 0.5f; \
|
||||
ivec2 top_left = ivec2(floor(top_left_f)); \
|
||||
ivec4 coords = clamp(ivec4(top_left, top_left + 1), ivec4(0), dims.xyxy - 1); \
|
||||
vec2 mix_vals = fract(top_left_f); \
|
||||
float depthTL = CONVERT_FN(texelFetch(TextureSampler, coords.xy, 0)); \
|
||||
float depthTR = CONVERT_FN(texelFetch(TextureSampler, coords.zy, 0)); \
|
||||
float depthBL = CONVERT_FN(texelFetch(TextureSampler, coords.xw, 0)); \
|
||||
float depthBR = CONVERT_FN(texelFetch(TextureSampler, coords.zw, 0)); \
|
||||
gl_FragDepth = mix(mix(depthTL, depthTR, mix_vals.x), mix(depthBL, depthBR, mix_vals.x), mix_vals.y);
|
||||
ivec2 dims = textureSize(TextureSampler, 0); \
|
||||
vec2 top_left_f = PSin_t * vec2(dims) - 0.5f; \
|
||||
ivec2 top_left = ivec2(floor(top_left_f)); \
|
||||
ivec4 coords = clamp(ivec4(top_left, top_left + 1), ivec4(0), dims.xyxy - 1); \
|
||||
vec2 mix_vals = fract(top_left_f); \
|
||||
float depthTL = CONVERT_FN(texelFetch(TextureSampler, coords.xy, 0)); \
|
||||
float depthTR = CONVERT_FN(texelFetch(TextureSampler, coords.zy, 0)); \
|
||||
float depthBL = CONVERT_FN(texelFetch(TextureSampler, coords.xw, 0)); \
|
||||
float depthBR = CONVERT_FN(texelFetch(TextureSampler, coords.zw, 0)); \
|
||||
gl_FragDepth = mix(mix(depthTL, depthTR, mix_vals.x), mix(depthBL, depthBR, mix_vals.x), mix_vals.y);
|
||||
|
||||
#ifdef ps_convert_rgba8_float32_biln
|
||||
void ps_convert_rgba8_float32_biln()
|
||||
{
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth32);
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth32);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgba8_float24_biln
|
||||
void ps_convert_rgba8_float24_biln()
|
||||
{
|
||||
// Same as above but without the alpha channel (24 bits Z)
|
||||
// Same as above but without the alpha channel (24 bits Z)
|
||||
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth24);
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth24);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgba8_float16_biln
|
||||
void ps_convert_rgba8_float16_biln()
|
||||
{
|
||||
// Same as above but without the A/B channels (16 bits Z)
|
||||
// Same as above but without the A/B channels (16 bits Z)
|
||||
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth16);
|
||||
// Convert an RGBA texture into a float depth texture
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth16);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_convert_rgb5a1_float16_biln
|
||||
void ps_convert_rgb5a1_float16_biln()
|
||||
{
|
||||
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgb5a1_to_depth16);
|
||||
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
|
||||
SAMPLE_RGBA_DEPTH_BILN(rgb5a1_to_depth16);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -242,51 +242,51 @@ uniform float ScaleFactor;
|
||||
|
||||
void ps_convert_rgba_8i()
|
||||
{
|
||||
// Convert a RGBA texture into a 8 bits packed texture
|
||||
// Input column: 8x2 RGBA pixels
|
||||
// 0: 8 RGBA
|
||||
// 1: 8 RGBA
|
||||
// Output column: 16x4 Index pixels
|
||||
// 0: 8 R | 8 B
|
||||
// 1: 8 R | 8 B
|
||||
// 2: 8 G | 8 A
|
||||
// 3: 8 G | 8 A
|
||||
uvec2 pos = uvec2(gl_FragCoord.xy);
|
||||
// Convert a RGBA texture into a 8 bits packed texture
|
||||
// Input column: 8x2 RGBA pixels
|
||||
// 0: 8 RGBA
|
||||
// 1: 8 RGBA
|
||||
// Output column: 16x4 Index pixels
|
||||
// 0: 8 R | 8 B
|
||||
// 1: 8 R | 8 B
|
||||
// 2: 8 G | 8 A
|
||||
// 3: 8 G | 8 A
|
||||
uvec2 pos = uvec2(gl_FragCoord.xy);
|
||||
|
||||
// Collapse separate R G B A areas into their base pixel
|
||||
uvec2 block = (pos & ~uvec2(15u, 3u)) >> 1;
|
||||
uvec2 subblock = pos & uvec2(7u, 1u);
|
||||
uvec2 coord = block | subblock;
|
||||
// Collapse separate R G B A areas into their base pixel
|
||||
uvec2 block = (pos & ~uvec2(15u, 3u)) >> 1;
|
||||
uvec2 subblock = pos & uvec2(7u, 1u);
|
||||
uvec2 coord = block | subblock;
|
||||
|
||||
// Compensate for potentially differing page pitch.
|
||||
// Compensate for potentially differing page pitch.
|
||||
uvec2 block_xy = coord / uvec2(64u, 32u);
|
||||
uint block_num = (block_xy.y * (DBW / 128u)) + block_xy.x;
|
||||
uvec2 block_offset = uvec2((block_num % (SBW / 64u)) * 64u, (block_num / (SBW / 64u)) * 32u);
|
||||
coord = (coord % uvec2(64u, 32u)) + block_offset;
|
||||
|
||||
// Apply offset to cols 1 and 2
|
||||
uint is_col23 = pos.y & 4u;
|
||||
uint is_col13 = pos.y & 2u;
|
||||
uint is_col12 = is_col23 ^ (is_col13 << 1);
|
||||
coord.x ^= is_col12; // If cols 1 or 2, flip bit 3 of x
|
||||
// Apply offset to cols 1 and 2
|
||||
uint is_col23 = pos.y & 4u;
|
||||
uint is_col13 = pos.y & 2u;
|
||||
uint is_col12 = is_col23 ^ (is_col13 << 1);
|
||||
coord.x ^= is_col12; // If cols 1 or 2, flip bit 3 of x
|
||||
|
||||
if (floor(ScaleFactor) != ScaleFactor)
|
||||
coord = uvec2(vec2(coord) * ScaleFactor);
|
||||
else
|
||||
coord *= uvec2(ScaleFactor);
|
||||
if (floor(ScaleFactor) != ScaleFactor)
|
||||
coord = uvec2(vec2(coord) * ScaleFactor);
|
||||
else
|
||||
coord *= uvec2(ScaleFactor);
|
||||
|
||||
vec4 pixel = texelFetch(TextureSampler, ivec2(coord), 0);
|
||||
vec2 sel0 = (pos.y & 2u) == 0u ? pixel.rb : pixel.ga;
|
||||
float sel1 = (pos.x & 8u) == 0u ? sel0.x : sel0.y;
|
||||
SV_Target0 = vec4(sel1);
|
||||
vec4 pixel = texelFetch(TextureSampler, ivec2(coord), 0);
|
||||
vec2 sel0 = (pos.y & 2u) == 0u ? pixel.rb : pixel.ga;
|
||||
float sel1 = (pos.x & 8u) == 0u ? sel0.x : sel0.y;
|
||||
SV_Target0 = vec4(sel1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_filter_transparency
|
||||
void ps_filter_transparency()
|
||||
{
|
||||
vec4 c = sample_c();
|
||||
SV_Target0 = vec4(c.rgb, 1.0);
|
||||
vec4 c = sample_c();
|
||||
SV_Target0 = vec4(c.rgb, 1.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -295,8 +295,8 @@ void ps_filter_transparency()
|
||||
#ifdef ps_datm1
|
||||
void ps_datm1()
|
||||
{
|
||||
if(sample_c().a < (127.5f / 255.0f)) // >= 0x80 pass
|
||||
discard;
|
||||
if(sample_c().a < (127.5f / 255.0f)) // >= 0x80 pass
|
||||
discard;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -305,24 +305,24 @@ void ps_datm1()
|
||||
#ifdef ps_datm0
|
||||
void ps_datm0()
|
||||
{
|
||||
if((127.5f / 255.0f) < sample_c().a) // < 0x80 pass (== 0x80 should not pass)
|
||||
discard;
|
||||
if((127.5f / 255.0f) < sample_c().a) // < 0x80 pass (== 0x80 should not pass)
|
||||
discard;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_hdr_init
|
||||
void ps_hdr_init()
|
||||
{
|
||||
vec4 value = sample_c();
|
||||
SV_Target0 = vec4(round(value.rgb * 255.0f) / 65535.0f, value.a);
|
||||
vec4 value = sample_c();
|
||||
SV_Target0 = vec4(round(value.rgb * 255.0f) / 65535.0f, value.a);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ps_hdr_resolve
|
||||
void ps_hdr_resolve()
|
||||
{
|
||||
vec4 value = sample_c();
|
||||
SV_Target0 = vec4(vec3(uvec3(value.rgb * 65535.0f) & 255u) / 255.0f, value.a);
|
||||
vec4 value = sample_c();
|
||||
SV_Target0 = vec4(vec3(uvec3(value.rgb * 65535.0f) & 255u) / 255.0f, value.a);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -366,51 +366,51 @@ uniform ivec2 EMOD;
|
||||
|
||||
void ps_yuv()
|
||||
{
|
||||
vec4 i = sample_c();
|
||||
vec4 o;
|
||||
vec4 i = sample_c();
|
||||
vec4 o;
|
||||
|
||||
mat3 rgb2yuv; // Value from GS manual
|
||||
rgb2yuv[0] = vec3(0.587, -0.311, -0.419);
|
||||
rgb2yuv[1] = vec3(0.114, 0.500, -0.081);
|
||||
rgb2yuv[2] = vec3(0.299, -0.169, 0.500);
|
||||
mat3 rgb2yuv; // Value from GS manual
|
||||
rgb2yuv[0] = vec3(0.587, -0.311, -0.419);
|
||||
rgb2yuv[1] = vec3(0.114, 0.500, -0.081);
|
||||
rgb2yuv[2] = vec3(0.299, -0.169, 0.500);
|
||||
|
||||
vec3 yuv = rgb2yuv * i.gbr;
|
||||
vec3 yuv = rgb2yuv * i.gbr;
|
||||
|
||||
float Y = float(0xDB)/255.0f * yuv.x + float(0x10)/255.0f;
|
||||
float Cr = float(0xE0)/255.0f * yuv.y + float(0x80)/255.0f;
|
||||
float Cb = float(0xE0)/255.0f * yuv.z + float(0x80)/255.0f;
|
||||
float Y = float(0xDB)/255.0f * yuv.x + float(0x10)/255.0f;
|
||||
float Cr = float(0xE0)/255.0f * yuv.y + float(0x80)/255.0f;
|
||||
float Cb = float(0xE0)/255.0f * yuv.z + float(0x80)/255.0f;
|
||||
|
||||
switch(EMOD.x) {
|
||||
case 0:
|
||||
o.a = i.a;
|
||||
break;
|
||||
case 1:
|
||||
o.a = Y;
|
||||
break;
|
||||
case 2:
|
||||
o.a = Y/2.0f;
|
||||
break;
|
||||
case 3:
|
||||
o.a = 0.0f;
|
||||
break;
|
||||
}
|
||||
switch(EMOD.x) {
|
||||
case 0:
|
||||
o.a = i.a;
|
||||
break;
|
||||
case 1:
|
||||
o.a = Y;
|
||||
break;
|
||||
case 2:
|
||||
o.a = Y/2.0f;
|
||||
break;
|
||||
case 3:
|
||||
o.a = 0.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
switch(EMOD.y) {
|
||||
case 0:
|
||||
o.rgb = i.rgb;
|
||||
break;
|
||||
case 1:
|
||||
o.rgb = vec3(Y);
|
||||
break;
|
||||
case 2:
|
||||
o.rgb = vec3(Y, Cb, Cr);
|
||||
break;
|
||||
case 3:
|
||||
o.rgb = vec3(i.a);
|
||||
break;
|
||||
}
|
||||
switch(EMOD.y) {
|
||||
case 0:
|
||||
o.rgb = i.rgb;
|
||||
break;
|
||||
case 1:
|
||||
o.rgb = vec3(Y);
|
||||
break;
|
||||
case 2:
|
||||
o.rgb = vec3(Y, Cb, Cr);
|
||||
break;
|
||||
case 3:
|
||||
o.rgb = vec3(i.a);
|
||||
break;
|
||||
}
|
||||
|
||||
SV_Target0 = o;
|
||||
SV_Target0 = o;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -418,16 +418,16 @@ void ps_yuv()
|
||||
|
||||
void main()
|
||||
{
|
||||
SV_Target0 = vec4(0x7FFFFFFF);
|
||||
SV_Target0 = vec4(0x7FFFFFFF);
|
||||
|
||||
#ifdef ps_stencil_image_init_0
|
||||
if((127.5f / 255.0f) < sample_c().a) // < 0x80 pass (== 0x80 should not pass)
|
||||
SV_Target0 = vec4(-1);
|
||||
#endif
|
||||
#ifdef ps_stencil_image_init_1
|
||||
if(sample_c().a < (127.5f / 255.0f)) // >= 0x80 pass
|
||||
SV_Target0 = vec4(-1);
|
||||
#endif
|
||||
#ifdef ps_stencil_image_init_0
|
||||
if((127.5f / 255.0f) < sample_c().a) // < 0x80 pass (== 0x80 should not pass)
|
||||
SV_Target0 = vec4(-1);
|
||||
#endif
|
||||
#ifdef ps_stencil_image_init_1
|
||||
if(sample_c().a < (127.5f / 255.0f)) // >= 0x80 pass
|
||||
SV_Target0 = vec4(-1);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -14,17 +14,17 @@ layout(location = 0) out vec4 SV_Target0;
|
||||
|
||||
void ps_main0()
|
||||
{
|
||||
vec4 c = texture(TextureSampler, PSin_t);
|
||||
// Note: clamping will be done by fixed unit
|
||||
c.a *= 2.0f;
|
||||
SV_Target0 = c;
|
||||
vec4 c = texture(TextureSampler, PSin_t);
|
||||
// Note: clamping will be done by fixed unit
|
||||
c.a *= 2.0f;
|
||||
SV_Target0 = c;
|
||||
}
|
||||
|
||||
void ps_main1()
|
||||
{
|
||||
vec4 c = texture(TextureSampler, PSin_t);
|
||||
c.a = BGColor.a;
|
||||
SV_Target0 = c;
|
||||
vec4 c = texture(TextureSampler, PSin_t);
|
||||
c.a = BGColor.a;
|
||||
SV_Target0 = c;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -119,10 +119,10 @@ void ps_filter_triangular() // triangular
|
||||
#ifdef ps_filter_complex
|
||||
void ps_filter_complex()
|
||||
{
|
||||
const float PI = 3.14159265359f;
|
||||
vec2 texdim = vec2(textureSize(TextureSampler, 0));
|
||||
float factor = (0.9f - 0.4f * cos(2.0f * PI * PSin_t.y * texdim.y));
|
||||
vec4 c = factor * texture(TextureSampler, vec2(PSin_t.x, (floor(PSin_t.y * texdim.y) + 0.5f) / texdim.y));
|
||||
const float PI = 3.14159265359f;
|
||||
vec2 texdim = vec2(textureSize(TextureSampler, 0));
|
||||
float factor = (0.9f - 0.4f * cos(2.0f * PI * PSin_t.y * texdim.y));
|
||||
vec4 c = factor * texture(TextureSampler, vec2(PSin_t.x, (floor(PSin_t.y * texdim.y) + 0.5f) / texdim.y));
|
||||
|
||||
SV_Target0 = c;
|
||||
}
|
||||
|
||||
@@ -24,33 +24,33 @@ layout(location = 0) out vec4 SV_Target0;
|
||||
// For all settings: 1.0 = 100% 0.5=50% 1.5 = 150%
|
||||
vec4 ContrastSaturationBrightness(vec4 color)
|
||||
{
|
||||
float brt = params.x;
|
||||
float con = params.y;
|
||||
float sat = params.z;
|
||||
float brt = params.x;
|
||||
float con = params.y;
|
||||
float sat = params.z;
|
||||
|
||||
// Increase or decrease these values to adjust r, g and b color channels separately
|
||||
const float AvgLumR = 0.5;
|
||||
const float AvgLumG = 0.5;
|
||||
const float AvgLumB = 0.5;
|
||||
// Increase or decrease these values to adjust r, g and b color channels separately
|
||||
const float AvgLumR = 0.5;
|
||||
const float AvgLumG = 0.5;
|
||||
const float AvgLumB = 0.5;
|
||||
|
||||
const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721);
|
||||
const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721);
|
||||
|
||||
vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB);
|
||||
vec3 brtColor = color.rgb * brt;
|
||||
float dot_intensity = dot(brtColor, LumCoeff);
|
||||
vec3 intensity = vec3(dot_intensity, dot_intensity, dot_intensity);
|
||||
vec3 satColor = mix(intensity, brtColor, sat);
|
||||
vec3 conColor = mix(AvgLumin, satColor, con);
|
||||
vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB);
|
||||
vec3 brtColor = color.rgb * brt;
|
||||
float dot_intensity = dot(brtColor, LumCoeff);
|
||||
vec3 intensity = vec3(dot_intensity, dot_intensity, dot_intensity);
|
||||
vec3 satColor = mix(intensity, brtColor, sat);
|
||||
vec3 conColor = mix(AvgLumin, satColor, con);
|
||||
|
||||
color.rgb = conColor;
|
||||
return color;
|
||||
color.rgb = conColor;
|
||||
return color;
|
||||
}
|
||||
|
||||
|
||||
void ps_main()
|
||||
{
|
||||
vec4 c = texture(TextureSampler, PSin_t);
|
||||
SV_Target0 = ContrastSaturationBrightness(c);
|
||||
vec4 c = texture(TextureSampler, PSin_t);
|
||||
SV_Target0 = ContrastSaturationBrightness(c);
|
||||
}
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,28 +2,28 @@
|
||||
|
||||
layout(std140, binding = 1) uniform cb20
|
||||
{
|
||||
vec2 VertexScale;
|
||||
vec2 VertexOffset;
|
||||
vec2 VertexScale;
|
||||
vec2 VertexOffset;
|
||||
|
||||
vec2 TextureScale;
|
||||
vec2 TextureOffset;
|
||||
vec2 TextureScale;
|
||||
vec2 TextureOffset;
|
||||
|
||||
vec2 PointSize;
|
||||
uint MaxDepth;
|
||||
uint pad_cb20;
|
||||
vec2 PointSize;
|
||||
uint MaxDepth;
|
||||
uint pad_cb20;
|
||||
};
|
||||
|
||||
#ifdef VERTEX_SHADER
|
||||
|
||||
out SHADER
|
||||
{
|
||||
vec4 t_float;
|
||||
vec4 t_int;
|
||||
#if VS_IIP != 0
|
||||
vec4 c;
|
||||
#else
|
||||
flat vec4 c;
|
||||
#endif
|
||||
vec4 t_float;
|
||||
vec4 t_int;
|
||||
#if VS_IIP != 0
|
||||
vec4 c;
|
||||
#else
|
||||
flat vec4 c;
|
||||
#endif
|
||||
} VSout;
|
||||
|
||||
const float exp_min32 = exp2(-32.0f);
|
||||
@@ -40,198 +40,198 @@ layout(location = 7) in vec4 i_f;
|
||||
|
||||
void texture_coord()
|
||||
{
|
||||
vec2 uv = vec2(i_uv) - TextureOffset;
|
||||
vec2 st = i_st - TextureOffset;
|
||||
vec2 uv = vec2(i_uv) - TextureOffset;
|
||||
vec2 st = i_st - TextureOffset;
|
||||
|
||||
// Float coordinate
|
||||
VSout.t_float.xy = st;
|
||||
VSout.t_float.w = i_q;
|
||||
// Float coordinate
|
||||
VSout.t_float.xy = st;
|
||||
VSout.t_float.w = i_q;
|
||||
|
||||
// Integer coordinate => normalized
|
||||
VSout.t_int.xy = uv * TextureScale;
|
||||
// Integer coordinate => normalized
|
||||
VSout.t_int.xy = uv * TextureScale;
|
||||
#if VS_FST
|
||||
// Integer coordinate => integral
|
||||
VSout.t_int.zw = uv;
|
||||
// Integer coordinate => integral
|
||||
VSout.t_int.zw = uv;
|
||||
#else
|
||||
// Some games uses float coordinate for post-processing effect
|
||||
VSout.t_int.zw = st / TextureScale;
|
||||
// Some games uses float coordinate for post-processing effect
|
||||
VSout.t_int.zw = st / TextureScale;
|
||||
#endif
|
||||
}
|
||||
|
||||
void vs_main()
|
||||
{
|
||||
// Clamp to max depth, gs doesn't wrap
|
||||
highp uint z = min(i_z, MaxDepth);
|
||||
// Clamp to max depth, gs doesn't wrap
|
||||
highp uint z = min(i_z, MaxDepth);
|
||||
|
||||
// pos -= 0.05 (1/320 pixel) helps avoiding rounding problems (integral part of pos is usually 5 digits, 0.05 is about as low as we can go)
|
||||
// example: ceil(afterseveralvertextransformations(y = 133)) => 134 => line 133 stays empty
|
||||
// input granularity is 1/16 pixel, anything smaller than that won't step drawing up/left by one pixel
|
||||
// example: 133.0625 (133 + 1/16) should start from line 134, ceil(133.0625 - 0.05) still above 133
|
||||
vec4 p;
|
||||
// pos -= 0.05 (1/320 pixel) helps avoiding rounding problems (integral part of pos is usually 5 digits, 0.05 is about as low as we can go)
|
||||
// example: ceil(afterseveralvertextransformations(y = 133)) => 134 => line 133 stays empty
|
||||
// input granularity is 1/16 pixel, anything smaller than that won't step drawing up/left by one pixel
|
||||
// example: 133.0625 (133 + 1/16) should start from line 134, ceil(133.0625 - 0.05) still above 133
|
||||
vec4 p;
|
||||
|
||||
p.xy = vec2(i_p) - vec2(0.05f, 0.05f);
|
||||
p.xy = p.xy * VertexScale - VertexOffset;
|
||||
p.w = 1.0f;
|
||||
p.xy = vec2(i_p) - vec2(0.05f, 0.05f);
|
||||
p.xy = p.xy * VertexScale - VertexOffset;
|
||||
p.w = 1.0f;
|
||||
|
||||
#if HAS_CLIP_CONTROL
|
||||
p.z = float(z) * exp_min32;
|
||||
p.z = float(z) * exp_min32;
|
||||
#else
|
||||
// GLES doesn't support ARB_clip_control, so remap it to -1..1. We also reduce the range from 32 bits
|
||||
// to 24 bits, which means some games with very large depth ranges will not render correctly. But,
|
||||
// for most, it's okay, and really, the best we can do.
|
||||
p.z = min(float(z) * exp2(-23.0f), 2.0f) - 1.0f;
|
||||
// GLES doesn't support ARB_clip_control, so remap it to -1..1. We also reduce the range from 32 bits
|
||||
// to 24 bits, which means some games with very large depth ranges will not render correctly. But,
|
||||
// for most, it's okay, and really, the best we can do.
|
||||
p.z = min(float(z) * exp2(-23.0f), 2.0f) - 1.0f;
|
||||
#endif
|
||||
|
||||
gl_Position = p;
|
||||
gl_Position = p;
|
||||
|
||||
texture_coord();
|
||||
texture_coord();
|
||||
|
||||
VSout.c = i_c;
|
||||
VSout.t_float.z = i_f.x; // pack for with texture
|
||||
VSout.c = i_c;
|
||||
VSout.t_float.z = i_f.x; // pack for with texture
|
||||
|
||||
#if VS_POINT_SIZE
|
||||
gl_PointSize = PointSize.x;
|
||||
#endif
|
||||
#if VS_POINT_SIZE
|
||||
gl_PointSize = PointSize.x;
|
||||
#endif
|
||||
}
|
||||
|
||||
#else // VS_EXPAND
|
||||
|
||||
struct RawVertex
|
||||
{
|
||||
vec2 ST;
|
||||
uint RGBA;
|
||||
float Q;
|
||||
uint XY;
|
||||
uint Z;
|
||||
uint UV;
|
||||
uint FOG;
|
||||
vec2 ST;
|
||||
uint RGBA;
|
||||
float Q;
|
||||
uint XY;
|
||||
uint Z;
|
||||
uint UV;
|
||||
uint FOG;
|
||||
};
|
||||
|
||||
layout(std140, binding = 2) readonly buffer VertexBuffer {
|
||||
RawVertex vertex_buffer[];
|
||||
RawVertex vertex_buffer[];
|
||||
};
|
||||
|
||||
struct ProcessedVertex
|
||||
{
|
||||
vec4 p;
|
||||
vec4 t_float;
|
||||
vec4 t_int;
|
||||
vec4 c;
|
||||
vec4 p;
|
||||
vec4 t_float;
|
||||
vec4 t_int;
|
||||
vec4 c;
|
||||
};
|
||||
|
||||
ProcessedVertex load_vertex(uint index)
|
||||
{
|
||||
#if defined(GL_ARB_shader_draw_parameters) && GL_ARB_shader_draw_parameters
|
||||
RawVertex rvtx = vertex_buffer[index + gl_BaseVertexARB];
|
||||
RawVertex rvtx = vertex_buffer[index + gl_BaseVertexARB];
|
||||
#else
|
||||
RawVertex rvtx = vertex_buffer[index];
|
||||
RawVertex rvtx = vertex_buffer[index];
|
||||
#endif
|
||||
|
||||
vec2 i_st = rvtx.ST;
|
||||
vec4 i_c = vec4(uvec4(bitfieldExtract(rvtx.RGBA, 0, 8), bitfieldExtract(rvtx.RGBA, 8, 8),
|
||||
vec2 i_st = rvtx.ST;
|
||||
vec4 i_c = vec4(uvec4(bitfieldExtract(rvtx.RGBA, 0, 8), bitfieldExtract(rvtx.RGBA, 8, 8),
|
||||
bitfieldExtract(rvtx.RGBA, 16, 8), bitfieldExtract(rvtx.RGBA, 24, 8)));
|
||||
float i_q = rvtx.Q;
|
||||
uvec2 i_p = uvec2(bitfieldExtract(rvtx.XY, 0, 16), bitfieldExtract(rvtx.XY, 16, 16));
|
||||
uint i_z = rvtx.Z;
|
||||
uvec2 i_uv = uvec2(bitfieldExtract(rvtx.UV, 0, 16), bitfieldExtract(rvtx.UV, 16, 16));
|
||||
vec4 i_f = unpackUnorm4x8(rvtx.FOG);
|
||||
float i_q = rvtx.Q;
|
||||
uvec2 i_p = uvec2(bitfieldExtract(rvtx.XY, 0, 16), bitfieldExtract(rvtx.XY, 16, 16));
|
||||
uint i_z = rvtx.Z;
|
||||
uvec2 i_uv = uvec2(bitfieldExtract(rvtx.UV, 0, 16), bitfieldExtract(rvtx.UV, 16, 16));
|
||||
vec4 i_f = unpackUnorm4x8(rvtx.FOG);
|
||||
|
||||
ProcessedVertex vtx;
|
||||
ProcessedVertex vtx;
|
||||
|
||||
uint z = min(i_z, MaxDepth);
|
||||
vtx.p.xy = vec2(i_p) - vec2(0.05f, 0.05f);
|
||||
vtx.p.xy = vtx.p.xy * VertexScale - VertexOffset;
|
||||
vtx.p.w = 1.0f;
|
||||
uint z = min(i_z, MaxDepth);
|
||||
vtx.p.xy = vec2(i_p) - vec2(0.05f, 0.05f);
|
||||
vtx.p.xy = vtx.p.xy * VertexScale - VertexOffset;
|
||||
vtx.p.w = 1.0f;
|
||||
|
||||
#if HAS_CLIP_CONTROL
|
||||
vtx.p.z = float(z) * exp_min32;
|
||||
vtx.p.z = float(z) * exp_min32;
|
||||
#else
|
||||
vtx.p.z = min(float(z) * exp2(-23.0f), 2.0f) - 1.0f;
|
||||
vtx.p.z = min(float(z) * exp2(-23.0f), 2.0f) - 1.0f;
|
||||
#endif
|
||||
|
||||
vec2 uv = vec2(i_uv) - TextureOffset;
|
||||
vec2 st = i_st - TextureOffset;
|
||||
vec2 uv = vec2(i_uv) - TextureOffset;
|
||||
vec2 st = i_st - TextureOffset;
|
||||
|
||||
vtx.t_float.xy = st;
|
||||
vtx.t_float.w = i_q;
|
||||
vtx.t_float.xy = st;
|
||||
vtx.t_float.w = i_q;
|
||||
|
||||
vtx.t_int.xy = uv * TextureScale;
|
||||
vtx.t_int.xy = uv * TextureScale;
|
||||
#if VS_FST
|
||||
vtx.t_int.zw = uv;
|
||||
vtx.t_int.zw = uv;
|
||||
#else
|
||||
vtx.t_int.zw = st / TextureScale;
|
||||
vtx.t_int.zw = st / TextureScale;
|
||||
#endif
|
||||
|
||||
vtx.c = i_c;
|
||||
vtx.t_float.z = i_f.x;
|
||||
vtx.c = i_c;
|
||||
vtx.t_float.z = i_f.x;
|
||||
|
||||
return vtx;
|
||||
return vtx;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ProcessedVertex vtx;
|
||||
ProcessedVertex vtx;
|
||||
|
||||
#if defined(GL_ARB_shader_draw_parameters) && GL_ARB_shader_draw_parameters
|
||||
uint vid = uint(gl_VertexID - gl_BaseVertexARB);
|
||||
uint vid = uint(gl_VertexID - gl_BaseVertexARB);
|
||||
#else
|
||||
uint vid = uint(gl_VertexID);
|
||||
uint vid = uint(gl_VertexID);
|
||||
#endif
|
||||
|
||||
#if VS_EXPAND == 1 // Point
|
||||
|
||||
vtx = load_vertex(vid >> 2);
|
||||
vtx = load_vertex(vid >> 2);
|
||||
|
||||
vtx.p.x += ((vid & 1u) != 0u) ? PointSize.x : 0.0f;
|
||||
vtx.p.y += ((vid & 2u) != 0u) ? PointSize.y : 0.0f;
|
||||
vtx.p.x += ((vid & 1u) != 0u) ? PointSize.x : 0.0f;
|
||||
vtx.p.y += ((vid & 2u) != 0u) ? PointSize.y : 0.0f;
|
||||
|
||||
#elif VS_EXPAND == 2 // Line
|
||||
|
||||
uint vid_base = vid >> 2;
|
||||
bool is_bottom = (vid & 2u) != 0u;
|
||||
bool is_right = (vid & 1u) != 0u;
|
||||
uint vid_other = is_bottom ? vid_base - 1 : vid_base + 1;
|
||||
vtx = load_vertex(vid_base);
|
||||
ProcessedVertex other = load_vertex(vid_other);
|
||||
uint vid_base = vid >> 2;
|
||||
bool is_bottom = (vid & 2u) != 0u;
|
||||
bool is_right = (vid & 1u) != 0u;
|
||||
uint vid_other = is_bottom ? vid_base - 1 : vid_base + 1;
|
||||
vtx = load_vertex(vid_base);
|
||||
ProcessedVertex other = load_vertex(vid_other);
|
||||
|
||||
vec2 line_vector = normalize(vtx.p.xy - other.p.xy);
|
||||
vec2 line_normal = vec2(line_vector.y, -line_vector.x);
|
||||
vec2 line_width = (line_normal * PointSize) / 2;
|
||||
// line_normal is inverted for bottom point
|
||||
vec2 offset = ((uint(is_bottom) ^ uint(is_right)) != 0u) ? line_width : -line_width;
|
||||
vtx.p.xy += offset;
|
||||
vec2 line_vector = normalize(vtx.p.xy - other.p.xy);
|
||||
vec2 line_normal = vec2(line_vector.y, -line_vector.x);
|
||||
vec2 line_width = (line_normal * PointSize) / 2;
|
||||
// line_normal is inverted for bottom point
|
||||
vec2 offset = ((uint(is_bottom) ^ uint(is_right)) != 0u) ? line_width : -line_width;
|
||||
vtx.p.xy += offset;
|
||||
|
||||
// Lines will be run as (0 1 2) (1 2 3)
|
||||
// This means that both triangles will have a point based off the top line point as their first point
|
||||
// So we don't have to do anything for !IIP
|
||||
// Lines will be run as (0 1 2) (1 2 3)
|
||||
// This means that both triangles will have a point based off the top line point as their first point
|
||||
// So we don't have to do anything for !IIP
|
||||
|
||||
#elif VS_EXPAND == 3 // Sprite
|
||||
|
||||
// Sprite points are always in pairs
|
||||
uint vid_base = vid >> 1;
|
||||
uint vid_lt = vid_base & ~1u;
|
||||
uint vid_rb = vid_base | 1u;
|
||||
// Sprite points are always in pairs
|
||||
uint vid_base = vid >> 1;
|
||||
uint vid_lt = vid_base & ~1u;
|
||||
uint vid_rb = vid_base | 1u;
|
||||
|
||||
ProcessedVertex lt = load_vertex(vid_lt);
|
||||
ProcessedVertex rb = load_vertex(vid_rb);
|
||||
vtx = rb;
|
||||
ProcessedVertex lt = load_vertex(vid_lt);
|
||||
ProcessedVertex rb = load_vertex(vid_rb);
|
||||
vtx = rb;
|
||||
|
||||
bool is_right = ((vid & 1u) != 0u);
|
||||
vtx.p.x = is_right ? lt.p.x : vtx.p.x;
|
||||
vtx.t_float.x = is_right ? lt.t_float.x : vtx.t_float.x;
|
||||
vtx.t_int.xz = is_right ? lt.t_int.xz : vtx.t_int.xz;
|
||||
bool is_right = ((vid & 1u) != 0u);
|
||||
vtx.p.x = is_right ? lt.p.x : vtx.p.x;
|
||||
vtx.t_float.x = is_right ? lt.t_float.x : vtx.t_float.x;
|
||||
vtx.t_int.xz = is_right ? lt.t_int.xz : vtx.t_int.xz;
|
||||
|
||||
bool is_bottom = ((vid & 2u) != 0u);
|
||||
vtx.p.y = is_bottom ? lt.p.y : vtx.p.y;
|
||||
vtx.t_float.y = is_bottom ? lt.t_float.y : vtx.t_float.y;
|
||||
vtx.t_int.yw = is_bottom ? lt.t_int.yw : vtx.t_int.yw;
|
||||
bool is_bottom = ((vid & 2u) != 0u);
|
||||
vtx.p.y = is_bottom ? lt.p.y : vtx.p.y;
|
||||
vtx.t_float.y = is_bottom ? lt.t_float.y : vtx.t_float.y;
|
||||
vtx.t_int.yw = is_bottom ? lt.t_int.yw : vtx.t_int.yw;
|
||||
|
||||
#endif
|
||||
|
||||
gl_Position = vtx.p;
|
||||
VSout.t_float = vtx.t_float;
|
||||
VSout.t_int = vtx.t_int;
|
||||
VSout.c = vtx.c;
|
||||
gl_Position = vtx.p;
|
||||
VSout.t_float = vtx.t_float;
|
||||
VSout.t_int = vtx.t_int;
|
||||
VSout.c = vtx.c;
|
||||
}
|
||||
|
||||
#endif // VS_EXPAND
|
||||
|
||||
@@ -34,6 +34,7 @@ if(UNIX AND NOT APPLE)
|
||||
option(USE_LEGACY_USER_DIRECTORY "Use legacy home/PCSX2 user directory instead of XDG standard" OFF)
|
||||
option(X11_API "Enable X11 support" ON)
|
||||
option(WAYLAND_API "Enable Wayland support" ON)
|
||||
option(DBUS_API "Enable DBus support for screensaver inhibiting" ON)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
@@ -117,8 +118,7 @@ if(${PCSX2_TARGET_ARCHITECTURES} MATCHES "x86_64")
|
||||
elseif(NOT DEFINED ARCH_FLAG AND USE_CLANG_CL)
|
||||
set(ARCH_FLAG "-msse4.1")
|
||||
endif()
|
||||
list(APPEND PCSX2_DEFS _ARCH_64=1 _M_X86=1)
|
||||
set(_ARCH_64 1)
|
||||
list(APPEND PCSX2_DEFS _M_X86=1)
|
||||
set(_M_X86 1)
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported architecture: ${PCSX2_TARGET_ARCHITECTURES}")
|
||||
@@ -135,12 +135,15 @@ option(USE_PGO_OPTIMIZE "Enable PGO optimization (use profile)")
|
||||
# Note1: Builtin strcmp/memcmp was proved to be slower on Mesa than stdlib version.
|
||||
# Note2: float operation SSE is impacted by the PCSX2 SSE configuration. In particular, flush to zero denormal.
|
||||
if(MSVC AND NOT USE_CLANG_CL)
|
||||
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:/Zc:externConstexpr>")
|
||||
add_compile_options(
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:/Zc:externConstexpr>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:/Zc:__cplusplus>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:/permissive->"
|
||||
"/Zo"
|
||||
"/utf-8"
|
||||
)
|
||||
elseif(NOT MSVC)
|
||||
add_compile_options(-pipe -fvisibility=hidden -pthread -fno-builtin-strcmp -fno-builtin-memcmp -mfpmath=sse)
|
||||
|
||||
# -fno-operator-names should only be for C++ files, not C files.
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fno-operator-names>)
|
||||
endif()
|
||||
|
||||
set(CONFIG_REL_NO_DEB $<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>>)
|
||||
|
||||
+14
-115
@@ -128,50 +128,6 @@ target_sources(common PRIVATE
|
||||
emitter/x86types.h
|
||||
)
|
||||
|
||||
if(USE_OPENGL)
|
||||
target_sources(common PRIVATE
|
||||
GL/Context.cpp
|
||||
GL/Program.cpp
|
||||
GL/ShaderCache.cpp
|
||||
GL/StreamBuffer.cpp
|
||||
)
|
||||
target_sources(common PRIVATE
|
||||
GL/Context.h
|
||||
GL/Program.h
|
||||
GL/ShaderCache.h
|
||||
GL/StreamBuffer.h
|
||||
)
|
||||
target_link_libraries(common PUBLIC glad)
|
||||
endif()
|
||||
|
||||
if(USE_VULKAN)
|
||||
target_link_libraries(common PUBLIC
|
||||
Vulkan-Headers glslang
|
||||
)
|
||||
target_sources(common PRIVATE
|
||||
Vulkan/ShaderCache.cpp
|
||||
Vulkan/Texture.cpp
|
||||
Vulkan/Loader.cpp
|
||||
Vulkan/ShaderCompiler.cpp
|
||||
Vulkan/Util.cpp
|
||||
Vulkan/SwapChain.cpp
|
||||
Vulkan/StreamBuffer.cpp
|
||||
Vulkan/Context.cpp
|
||||
Vulkan/Builders.cpp
|
||||
Vulkan/vk_mem_alloc.cpp
|
||||
Vulkan/Context.h
|
||||
Vulkan/Texture.h
|
||||
Vulkan/ShaderCompiler.h
|
||||
Vulkan/SwapChain.h
|
||||
Vulkan/Builders.h
|
||||
Vulkan/StreamBuffer.h
|
||||
Vulkan/ShaderCache.h
|
||||
Vulkan/EntryPoints.h
|
||||
Vulkan/Loader.h
|
||||
Vulkan/Util.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if(USE_VTUNE)
|
||||
target_link_libraries(common PUBLIC Vtune::Vtune)
|
||||
endif()
|
||||
@@ -179,7 +135,7 @@ endif()
|
||||
if(WIN32)
|
||||
enable_language(ASM_MASM)
|
||||
target_sources(common PRIVATE FastJmp.asm)
|
||||
target_link_libraries(common PUBLIC WIL::WIL D3D12MemAlloc winmm)
|
||||
target_link_libraries(common PUBLIC WIL::WIL winmm)
|
||||
target_sources(common PRIVATE
|
||||
CrashHandler.cpp
|
||||
CrashHandler.h
|
||||
@@ -188,24 +144,6 @@ if(WIN32)
|
||||
HTTPDownloaderWinHTTP.h
|
||||
StackWalker.cpp
|
||||
StackWalker.h
|
||||
D3D11/ShaderCache.cpp
|
||||
D3D11/ShaderCache.h
|
||||
D3D11/ShaderCompiler.cpp
|
||||
D3D11/ShaderCompiler.h
|
||||
D3D12/Builders.cpp
|
||||
D3D12/Builders.h
|
||||
D3D12/Context.cpp
|
||||
D3D12/Context.h
|
||||
D3D12/DescriptorHeapManager.cpp
|
||||
D3D12/DescriptorHeapManager.h
|
||||
D3D12/ShaderCache.cpp
|
||||
D3D12/ShaderCache.h
|
||||
D3D12/StreamBuffer.cpp
|
||||
D3D12/StreamBuffer.h
|
||||
D3D12/Texture.cpp
|
||||
D3D12/Texture.h
|
||||
D3D12/Util.cpp
|
||||
D3D12/Util.h
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -216,62 +154,23 @@ if(APPLE)
|
||||
)
|
||||
target_compile_options(common PRIVATE -fobjc-arc)
|
||||
target_link_options(common PRIVATE -fobjc-link-runtime)
|
||||
target_link_libraries(common PRIVATE
|
||||
"-framework Foundation"
|
||||
"-framework IOKit"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(USE_OPENGL)
|
||||
if(WIN32)
|
||||
target_sources(common PRIVATE
|
||||
GL/ContextWGL.cpp
|
||||
GL/ContextWGL.h
|
||||
)
|
||||
target_link_libraries(common PUBLIC opengl32.lib)
|
||||
elseif(APPLE)
|
||||
target_sources(common PRIVATE
|
||||
GL/ContextAGL.mm
|
||||
GL/ContextAGL.h
|
||||
)
|
||||
else()
|
||||
if(X11_API OR WAYLAND_API)
|
||||
target_sources(common PRIVATE
|
||||
GL/ContextEGL.cpp
|
||||
GL/ContextEGL.h
|
||||
)
|
||||
target_link_libraries(common PRIVATE PkgConfig::EGL)
|
||||
endif()
|
||||
|
||||
if(X11_API)
|
||||
target_sources(common PRIVATE
|
||||
GL/ContextEGLX11.cpp
|
||||
GL/ContextEGLX11.h
|
||||
)
|
||||
if(TARGET PkgConfig::XRANDR)
|
||||
target_link_libraries(common PRIVATE PkgConfig::XRANDR)
|
||||
target_compile_definitions(common PRIVATE "HAS_XRANDR=1")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WAYLAND_API)
|
||||
target_sources(common PRIVATE
|
||||
GL/ContextEGLWayland.cpp
|
||||
GL/ContextEGLWayland.h
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
if(DBUS_API)
|
||||
target_compile_definitions(common PRIVATE DBUS_API)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(DBUS REQUIRED dbus-1)
|
||||
target_include_directories(common PRIVATE ${DBUS_INCLUDE_DIRS})
|
||||
target_link_libraries(common PRIVATE ${DBUS_LINK_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(USE_VULKAN)
|
||||
if(APPLE)
|
||||
# Needed for Metal surface creation.
|
||||
target_compile_options(common PRIVATE -fobjc-arc)
|
||||
target_link_options(common PRIVATE -fobjc-link-runtime)
|
||||
elseif(NOT WIN32)
|
||||
if(X11_API)
|
||||
target_compile_definitions(common PUBLIC "VULKAN_USE_X11=1")
|
||||
endif()
|
||||
if(WAYLAND_API)
|
||||
target_compile_definitions(common PUBLIC "VULKAN_USE_WAYLAND=1")
|
||||
endif()
|
||||
endif()
|
||||
if(X11_API AND TARGET PkgConfig::XRANDR)
|
||||
target_link_libraries(common PRIVATE PkgConfig::XRANDR)
|
||||
target_compile_definitions(common PRIVATE "HAS_XRANDR=1")
|
||||
endif()
|
||||
|
||||
if (USE_GCC AND CMAKE_INTERPROCEDURAL_OPTIMIZATION)
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/HashCombine.h"
|
||||
#include "common/RedtapeWindows.h"
|
||||
#include "common/RedtapeWilCom.h"
|
||||
#include "common/D3D11/ShaderCompiler.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <d3d11.h>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace D3D11
|
||||
{
|
||||
class ShaderCache
|
||||
{
|
||||
public:
|
||||
ShaderCache();
|
||||
~ShaderCache();
|
||||
|
||||
D3D_FEATURE_LEVEL GetFeatureLevel() const { return m_feature_level; }
|
||||
bool UsingDebugShaders() const { return m_debug; }
|
||||
|
||||
bool Open(std::string_view base_path, D3D_FEATURE_LEVEL feature_level, u32 version, bool debug);
|
||||
void Close();
|
||||
|
||||
wil::com_ptr_nothrow<ID3DBlob> GetShaderBlob(ShaderCompiler::Type type, const std::string_view& shader_code,
|
||||
const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11VertexShader> GetVertexShader(ID3D11Device* device, const std::string_view& shader_code,
|
||||
const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
|
||||
|
||||
bool GetVertexShaderAndInputLayout(ID3D11Device* device,
|
||||
ID3D11VertexShader** vs, ID3D11InputLayout** il,
|
||||
const D3D11_INPUT_ELEMENT_DESC* layout, size_t layout_size,
|
||||
const std::string_view& shader_code, const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11PixelShader> GetPixelShader(ID3D11Device* device, const std::string_view& shader_code,
|
||||
const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11ComputeShader> GetComputeShader(ID3D11Device* device, const std::string_view& shader_code,
|
||||
const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
|
||||
|
||||
private:
|
||||
static constexpr u32 FILE_VERSION = 1;
|
||||
|
||||
struct CacheIndexKey
|
||||
{
|
||||
u64 source_hash_low;
|
||||
u64 source_hash_high;
|
||||
u64 macro_hash_low;
|
||||
u64 macro_hash_high;
|
||||
u64 entry_point_low;
|
||||
u64 entry_point_high;
|
||||
u32 source_length;
|
||||
ShaderCompiler::Type shader_type;
|
||||
|
||||
bool operator==(const CacheIndexKey& key) const;
|
||||
bool operator!=(const CacheIndexKey& key) const;
|
||||
};
|
||||
|
||||
struct CacheIndexEntryHasher
|
||||
{
|
||||
std::size_t operator()(const CacheIndexKey& e) const noexcept
|
||||
{
|
||||
std::size_t h = 0;
|
||||
HashCombine(h, e.entry_point_low, e.entry_point_high, e.macro_hash_low, e.macro_hash_high,
|
||||
e.source_hash_low, e.source_hash_high, e.source_length, e.shader_type);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
struct CacheIndexData
|
||||
{
|
||||
u32 file_offset;
|
||||
u32 blob_size;
|
||||
};
|
||||
|
||||
using CacheIndex = std::unordered_map<CacheIndexKey, CacheIndexData, CacheIndexEntryHasher>;
|
||||
|
||||
static std::string GetCacheBaseFileName(const std::string_view& base_path, D3D_FEATURE_LEVEL feature_level,
|
||||
bool debug);
|
||||
static CacheIndexKey GetCacheKey(ShaderCompiler::Type type, const std::string_view& shader_code,
|
||||
const D3D_SHADER_MACRO* macros, const char* entry_point);
|
||||
|
||||
bool CreateNew(const std::string& index_filename, const std::string& blob_filename);
|
||||
bool ReadExisting(const std::string& index_filename, const std::string& blob_filename);
|
||||
|
||||
wil::com_ptr_nothrow<ID3DBlob> CompileAndAddShaderBlob(const CacheIndexKey& key, const std::string_view& shader_code,
|
||||
const D3D_SHADER_MACRO* macros, const char* entry_point);
|
||||
|
||||
std::FILE* m_index_file = nullptr;
|
||||
std::FILE* m_blob_file = nullptr;
|
||||
|
||||
CacheIndex m_index;
|
||||
|
||||
D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0;
|
||||
u32 m_version = 0;
|
||||
bool m_debug = false;
|
||||
};
|
||||
} // namespace D3D11
|
||||
@@ -1,186 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/PrecompiledHeader.h"
|
||||
#include "common/D3D11/ShaderCompiler.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include <array>
|
||||
#include <d3dcompiler.h>
|
||||
#include <fstream>
|
||||
|
||||
static unsigned s_next_bad_shader_id = 1;
|
||||
|
||||
wil::com_ptr_nothrow<ID3DBlob> D3D11::ShaderCompiler::CompileShader(Type type, D3D_FEATURE_LEVEL feature_level, bool debug,
|
||||
const std::string_view& code, const D3D_SHADER_MACRO* macros /* = nullptr */, const char* entry_point /* = "main" */)
|
||||
{
|
||||
const char* target;
|
||||
switch (feature_level)
|
||||
{
|
||||
case D3D_FEATURE_LEVEL_10_0:
|
||||
{
|
||||
static constexpr std::array<const char*, 4> targets = {{"vs_4_0", "ps_4_0", "cs_4_0"}};
|
||||
target = targets[static_cast<int>(type)];
|
||||
}
|
||||
break;
|
||||
|
||||
case D3D_FEATURE_LEVEL_10_1:
|
||||
{
|
||||
static constexpr std::array<const char*, 4> targets = {{"vs_4_1", "ps_4_1", "cs_4_1"}};
|
||||
target = targets[static_cast<int>(type)];
|
||||
}
|
||||
break;
|
||||
|
||||
case D3D_FEATURE_LEVEL_11_0:
|
||||
{
|
||||
static constexpr std::array<const char*, 4> targets = {{"vs_5_0", "ps_5_0", "cs_5_0"}};
|
||||
target = targets[static_cast<int>(type)];
|
||||
}
|
||||
break;
|
||||
|
||||
case D3D_FEATURE_LEVEL_11_1:
|
||||
default:
|
||||
{
|
||||
static constexpr std::array<const char*, 4> targets = {{"vs_5_1", "ps_5_1", "cs_5_1"}};
|
||||
target = targets[static_cast<int>(type)];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
static constexpr UINT flags_non_debug = D3DCOMPILE_OPTIMIZATION_LEVEL3;
|
||||
static constexpr UINT flags_debug = D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_DEBUG;
|
||||
|
||||
wil::com_ptr_nothrow<ID3DBlob> blob;
|
||||
wil::com_ptr_nothrow<ID3DBlob> error_blob;
|
||||
const HRESULT hr =
|
||||
D3DCompile(code.data(), code.size(), "0", macros, nullptr, entry_point, target, debug ? flags_debug : flags_non_debug,
|
||||
0, blob.put(), error_blob.put());
|
||||
|
||||
std::string error_string;
|
||||
if (error_blob)
|
||||
{
|
||||
error_string.append(static_cast<const char*>(error_blob->GetBufferPointer()), error_blob->GetBufferSize());
|
||||
error_blob.reset();
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.WriteLn("Failed to compile '%s':\n%s", target, error_string.c_str());
|
||||
|
||||
std::ofstream ofs(StringUtil::StdStringFromFormat("pcsx2_bad_shader_%u.txt", s_next_bad_shader_id++).c_str(),
|
||||
std::ofstream::out | std::ofstream::binary);
|
||||
if (ofs.is_open())
|
||||
{
|
||||
ofs << code;
|
||||
ofs << "\n\nCompile as " << target << " failed: " << hr << "\n";
|
||||
ofs.write(error_string.c_str(), error_string.size());
|
||||
ofs.close();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!error_string.empty())
|
||||
Console.Warning("'%s' compiled with warnings:\n%s", target, error_string.c_str());
|
||||
|
||||
return blob;
|
||||
}
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11VertexShader> D3D11::ShaderCompiler::CompileAndCreateVertexShader(ID3D11Device* device, bool debug,
|
||||
const std::string_view& code, const D3D_SHADER_MACRO* macros /* = nullptr */, const char* entry_point /* = "main" */)
|
||||
{
|
||||
wil::com_ptr_nothrow<ID3DBlob> blob = CompileShader(Type::Vertex, device->GetFeatureLevel(), debug, code, macros, entry_point);
|
||||
if (!blob)
|
||||
return {};
|
||||
|
||||
return CreateVertexShader(device, blob.get());
|
||||
}
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11PixelShader> D3D11::ShaderCompiler::CompileAndCreatePixelShader(ID3D11Device* device, bool debug,
|
||||
const std::string_view& code, const D3D_SHADER_MACRO* macros /* = nullptr */, const char* entry_point /* = "main" */)
|
||||
{
|
||||
wil::com_ptr_nothrow<ID3DBlob> blob = CompileShader(Type::Pixel, device->GetFeatureLevel(), debug, code, macros, entry_point);
|
||||
if (!blob)
|
||||
return {};
|
||||
|
||||
return CreatePixelShader(device, blob.get());
|
||||
}
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11ComputeShader> D3D11::ShaderCompiler::CompileAndCreateComputeShader(ID3D11Device* device, bool debug,
|
||||
const std::string_view& code, const D3D_SHADER_MACRO* macros /* = nullptr */, const char* entry_point /* = "main" */)
|
||||
{
|
||||
wil::com_ptr_nothrow<ID3DBlob> blob = CompileShader(Type::Compute, device->GetFeatureLevel(), debug, code, macros, entry_point);
|
||||
if (!blob)
|
||||
return {};
|
||||
|
||||
return CreateComputeShader(device, blob.get());
|
||||
}
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11VertexShader> D3D11::ShaderCompiler::CreateVertexShader(ID3D11Device* device, const void* bytecode, size_t bytecode_length)
|
||||
{
|
||||
wil::com_ptr_nothrow<ID3D11VertexShader> shader;
|
||||
const HRESULT hr = device->CreateVertexShader(bytecode, bytecode_length, nullptr, shader.put());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("Failed to create vertex shader: 0x%08X", hr);
|
||||
return {};
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11VertexShader> D3D11::ShaderCompiler::CreateVertexShader(ID3D11Device* device, const ID3DBlob* blob)
|
||||
{
|
||||
return CreateVertexShader(device, const_cast<ID3DBlob*>(blob)->GetBufferPointer(),
|
||||
const_cast<ID3DBlob*>(blob)->GetBufferSize());
|
||||
}
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11PixelShader> D3D11::ShaderCompiler::CreatePixelShader(ID3D11Device* device, const void* bytecode, size_t bytecode_length)
|
||||
{
|
||||
wil::com_ptr_nothrow<ID3D11PixelShader> shader;
|
||||
const HRESULT hr = device->CreatePixelShader(bytecode, bytecode_length, nullptr, shader.put());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("Failed to create pixel shader: 0x%08X", hr);
|
||||
return {};
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11PixelShader> D3D11::ShaderCompiler::CreatePixelShader(ID3D11Device* device, const ID3DBlob* blob)
|
||||
{
|
||||
return CreatePixelShader(device, const_cast<ID3DBlob*>(blob)->GetBufferPointer(),
|
||||
const_cast<ID3DBlob*>(blob)->GetBufferSize());
|
||||
}
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11ComputeShader> D3D11::ShaderCompiler::CreateComputeShader(ID3D11Device* device, const void* bytecode, size_t bytecode_length)
|
||||
{
|
||||
wil::com_ptr_nothrow<ID3D11ComputeShader> shader;
|
||||
const HRESULT hr = device->CreateComputeShader(bytecode, bytecode_length, nullptr, shader.put());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("Failed to create compute shader: 0x%08X", hr);
|
||||
return {};
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11ComputeShader> D3D11::ShaderCompiler::CreateComputeShader(ID3D11Device* device, const ID3DBlob* blob)
|
||||
{
|
||||
return CreateComputeShader(device, const_cast<ID3DBlob*>(blob)->GetBufferPointer(),
|
||||
const_cast<ID3DBlob*>(blob)->GetBufferSize());
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/RedtapeWindows.h"
|
||||
#include "common/RedtapeWilCom.h"
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
namespace D3D11::ShaderCompiler
|
||||
{
|
||||
enum class Type
|
||||
{
|
||||
Vertex,
|
||||
Pixel,
|
||||
Compute
|
||||
};
|
||||
|
||||
wil::com_ptr_nothrow<ID3DBlob> CompileShader(Type type, D3D_FEATURE_LEVEL feature_level, bool debug, const std::string_view& code,
|
||||
const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11VertexShader> CompileAndCreateVertexShader(ID3D11Device* device, bool debug, const std::string_view& code,
|
||||
const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
|
||||
wil::com_ptr_nothrow<ID3D11PixelShader> CompileAndCreatePixelShader(ID3D11Device* device, bool debug, const std::string_view& code,
|
||||
const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
|
||||
wil::com_ptr_nothrow<ID3D11ComputeShader> CompileAndCreateComputeShader(ID3D11Device* device, bool debug, const std::string_view& code,
|
||||
const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11VertexShader> CreateVertexShader(ID3D11Device* device, const void* bytecode, size_t bytecode_length);
|
||||
wil::com_ptr_nothrow<ID3D11VertexShader> CreateVertexShader(ID3D11Device* device, const ID3DBlob* blob);
|
||||
wil::com_ptr_nothrow<ID3D11PixelShader> CreatePixelShader(ID3D11Device* device, const void* bytecode, size_t bytecode_length);
|
||||
wil::com_ptr_nothrow<ID3D11PixelShader> CreatePixelShader(ID3D11Device* device, const ID3DBlob* blob);
|
||||
wil::com_ptr_nothrow<ID3D11ComputeShader> CreateComputeShader(ID3D11Device* device, const void* bytecode, size_t bytecode_length);
|
||||
wil::com_ptr_nothrow<ID3D11ComputeShader> CreateComputeShader(ID3D11Device* device, const ID3DBlob* blob);
|
||||
}; // namespace D3D11::ShaderCompiler
|
||||
@@ -1,219 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/RedtapeWindows.h"
|
||||
#include "common/D3D12/DescriptorHeapManager.h"
|
||||
#include "common/D3D12/StreamBuffer.h"
|
||||
|
||||
#include <array>
|
||||
#include <d3d12.h>
|
||||
#include <dxgi1_5.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <wil/com.h>
|
||||
|
||||
struct IDXGIAdapter;
|
||||
struct IDXGIFactory;
|
||||
namespace D3D12MA
|
||||
{
|
||||
class Allocator;
|
||||
class Allocation;
|
||||
} // namespace D3D12MA
|
||||
|
||||
namespace D3D12
|
||||
{
|
||||
class Context
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
using ComPtr = wil::com_ptr_nothrow<T>;
|
||||
|
||||
enum : u32
|
||||
{
|
||||
/// Number of command lists. One is being built while the other(s) are executed.
|
||||
NUM_COMMAND_LISTS = 3,
|
||||
|
||||
/// Textures that don't fit into this buffer will be uploaded with a staging buffer.
|
||||
TEXTURE_UPLOAD_BUFFER_SIZE = 64 * 1024 * 1024,
|
||||
|
||||
/// Maximum number of samples in a single allocation group.
|
||||
SAMPLER_GROUP_SIZE = 2,
|
||||
|
||||
/// Start/End timestamp queries.
|
||||
NUM_TIMESTAMP_QUERIES_PER_CMDLIST = 2,
|
||||
};
|
||||
|
||||
~Context();
|
||||
|
||||
/// Creates new device and context.
|
||||
static bool Create(IDXGIFactory5* dxgi_factory, IDXGIAdapter1* adapter, bool enable_debug_layer);
|
||||
|
||||
/// Destroys active context.
|
||||
static void Destroy();
|
||||
|
||||
__fi IDXGIAdapter* GetAdapter() const { return m_adapter.get(); }
|
||||
__fi ID3D12Device* GetDevice() const { return m_device.get(); }
|
||||
__fi ID3D12CommandQueue* GetCommandQueue() const { return m_command_queue.get(); }
|
||||
__fi D3D12MA::Allocator* GetAllocator() const { return m_allocator.get(); }
|
||||
|
||||
/// Returns the PCI vendor ID of the device, if known.
|
||||
u32 GetAdapterVendorID() const;
|
||||
|
||||
/// Returns the current command list, commands can be recorded directly.
|
||||
ID3D12GraphicsCommandList4* GetCommandList() const
|
||||
{
|
||||
return m_command_lists[m_current_command_list].command_lists[1].get();
|
||||
}
|
||||
|
||||
/// Returns the init command list for uploading.
|
||||
ID3D12GraphicsCommandList4* GetInitCommandList();
|
||||
|
||||
/// Returns the per-frame SRV/CBV/UAV allocator.
|
||||
DescriptorAllocator& GetDescriptorAllocator()
|
||||
{
|
||||
return m_command_lists[m_current_command_list].descriptor_allocator;
|
||||
}
|
||||
|
||||
/// Returns the per-frame sampler allocator.
|
||||
GroupedSamplerAllocator<SAMPLER_GROUP_SIZE>& GetSamplerAllocator()
|
||||
{
|
||||
return m_command_lists[m_current_command_list].sampler_allocator;
|
||||
}
|
||||
|
||||
/// Invalidates GPU-side sampler caches for all command lists. Call after you've freed samplers,
|
||||
/// and are going to re-use the handles from GetSamplerHeapManager().
|
||||
void InvalidateSamplerGroups();
|
||||
|
||||
// Descriptor manager access.
|
||||
DescriptorHeapManager& GetDescriptorHeapManager() { return m_descriptor_heap_manager; }
|
||||
DescriptorHeapManager& GetRTVHeapManager() { return m_rtv_heap_manager; }
|
||||
DescriptorHeapManager& GetDSVHeapManager() { return m_dsv_heap_manager; }
|
||||
DescriptorHeapManager& GetSamplerHeapManager() { return m_sampler_heap_manager; }
|
||||
const DescriptorHandle& GetNullSRVDescriptor() const { return m_null_srv_descriptor; }
|
||||
StreamBuffer& GetTextureStreamBuffer() { return m_texture_stream_buffer; }
|
||||
|
||||
// Root signature access.
|
||||
ComPtr<ID3DBlob> SerializeRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc);
|
||||
ComPtr<ID3D12RootSignature> CreateRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc);
|
||||
|
||||
/// Fence value for current command list.
|
||||
u64 GetCurrentFenceValue() const { return m_current_fence_value; }
|
||||
|
||||
/// Last "completed" fence.
|
||||
u64 GetCompletedFenceValue() const { return m_completed_fence_value; }
|
||||
|
||||
/// Feature level to use when compiling shaders.
|
||||
D3D_FEATURE_LEVEL GetFeatureLevel() const { return m_feature_level; }
|
||||
|
||||
/// Test for support for the specified texture format.
|
||||
bool SupportsTextureFormat(DXGI_FORMAT format);
|
||||
|
||||
enum class WaitType
|
||||
{
|
||||
None, ///< Don't wait (async)
|
||||
Sleep, ///< Wait normally
|
||||
Spin, ///< Wait by spinning
|
||||
};
|
||||
|
||||
/// Executes the current command list.
|
||||
bool ExecuteCommandList(WaitType wait_for_completion);
|
||||
|
||||
/// Waits for a specific fence.
|
||||
void WaitForFence(u64 fence, bool spin);
|
||||
|
||||
/// Waits for any in-flight command buffers to complete.
|
||||
void WaitForGPUIdle();
|
||||
|
||||
/// Defers destruction of a D3D resource (associates it with the current list).
|
||||
void DeferObjectDestruction(ID3D12DeviceChild* resource);
|
||||
|
||||
/// Defers destruction of a D3D resource (associates it with the current list).
|
||||
void DeferResourceDestruction(D3D12MA::Allocation* allocation, ID3D12Resource* resource);
|
||||
|
||||
/// Defers destruction of a descriptor handle (associates it with the current list).
|
||||
void DeferDescriptorDestruction(DescriptorHeapManager& manager, u32 index);
|
||||
void DeferDescriptorDestruction(DescriptorHeapManager& manager, DescriptorHandle* handle);
|
||||
|
||||
float GetAndResetAccumulatedGPUTime();
|
||||
void SetEnableGPUTiming(bool enabled);
|
||||
|
||||
// Allocates a temporary CPU staging buffer, fires the callback with it to populate, then copies to a GPU buffer.
|
||||
bool AllocatePreinitializedGPUBuffer(u32 size, ID3D12Resource** gpu_buffer, D3D12MA::Allocation** gpu_allocation,
|
||||
const std::function<void(void*)>& fill_callback);
|
||||
|
||||
private:
|
||||
struct CommandListResources
|
||||
{
|
||||
std::array<ComPtr<ID3D12CommandAllocator>, 2> command_allocators;
|
||||
std::array<ComPtr<ID3D12GraphicsCommandList4>, 2> command_lists;
|
||||
DescriptorAllocator descriptor_allocator;
|
||||
GroupedSamplerAllocator<SAMPLER_GROUP_SIZE> sampler_allocator;
|
||||
std::vector<std::pair<D3D12MA::Allocation*, ID3D12DeviceChild*>> pending_resources;
|
||||
std::vector<std::pair<DescriptorHeapManager&, u32>> pending_descriptors;
|
||||
u64 ready_fence_value = 0;
|
||||
bool init_command_list_used = false;
|
||||
bool has_timestamp_query = false;
|
||||
};
|
||||
|
||||
Context();
|
||||
|
||||
bool CreateDevice(IDXGIFactory5* dxgi_factory, IDXGIAdapter1* adapter, bool enable_debug_layer);
|
||||
bool CreateCommandQueue();
|
||||
bool CreateAllocator();
|
||||
bool CreateFence();
|
||||
bool CreateDescriptorHeaps();
|
||||
bool CreateCommandLists();
|
||||
bool CreateTextureStreamBuffer();
|
||||
bool CreateTimestampQuery();
|
||||
void MoveToNextCommandList();
|
||||
void DestroyPendingResources(CommandListResources& cmdlist);
|
||||
void DestroyResources();
|
||||
|
||||
ComPtr<IDXGIAdapter> m_adapter;
|
||||
ComPtr<ID3D12Debug> m_debug_interface;
|
||||
ComPtr<ID3D12Device> m_device;
|
||||
ComPtr<ID3D12CommandQueue> m_command_queue;
|
||||
ComPtr<D3D12MA::Allocator> m_allocator;
|
||||
|
||||
ComPtr<ID3D12Fence> m_fence;
|
||||
HANDLE m_fence_event = {};
|
||||
u32 m_current_fence_value = 0;
|
||||
u64 m_completed_fence_value = 0;
|
||||
|
||||
std::array<CommandListResources, NUM_COMMAND_LISTS> m_command_lists;
|
||||
u32 m_current_command_list = NUM_COMMAND_LISTS - 1;
|
||||
|
||||
ComPtr<ID3D12QueryHeap> m_timestamp_query_heap;
|
||||
ComPtr<ID3D12Resource> m_timestamp_query_buffer;
|
||||
ComPtr<D3D12MA::Allocation> m_timestamp_query_allocation;
|
||||
double m_timestamp_frequency = 0.0;
|
||||
float m_accumulated_gpu_time = 0.0f;
|
||||
bool m_gpu_timing_enabled = false;
|
||||
|
||||
DescriptorHeapManager m_descriptor_heap_manager;
|
||||
DescriptorHeapManager m_rtv_heap_manager;
|
||||
DescriptorHeapManager m_dsv_heap_manager;
|
||||
DescriptorHeapManager m_sampler_heap_manager;
|
||||
DescriptorHandle m_null_srv_descriptor;
|
||||
StreamBuffer m_texture_stream_buffer;
|
||||
|
||||
D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0;
|
||||
};
|
||||
} // namespace D3D12
|
||||
|
||||
extern std::unique_ptr<D3D12::Context> g_d3d12_context;
|
||||
@@ -1,269 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/HashCombine.h"
|
||||
#include "common/RedtapeWindows.h"
|
||||
#include "common/RedtapeWilCom.h"
|
||||
|
||||
#include <bitset>
|
||||
#include <cstring>
|
||||
#include <d3d12.h>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace D3D12
|
||||
{
|
||||
// This class provides an abstraction for D3D12 descriptor heaps.
|
||||
struct DescriptorHandle final
|
||||
{
|
||||
enum : u32
|
||||
{
|
||||
INVALID_INDEX = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle{};
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle{};
|
||||
u32 index = INVALID_INDEX;
|
||||
|
||||
__fi operator bool() const { return index != INVALID_INDEX; }
|
||||
|
||||
__fi operator D3D12_CPU_DESCRIPTOR_HANDLE() const { return cpu_handle; }
|
||||
__fi operator D3D12_GPU_DESCRIPTOR_HANDLE() const { return gpu_handle; }
|
||||
|
||||
__fi bool operator==(const DescriptorHandle& rhs) const { return (index == rhs.index); }
|
||||
__fi bool operator!=(const DescriptorHandle& rhs) const { return (index != rhs.index); }
|
||||
__fi bool operator<(const DescriptorHandle& rhs) const { return (index < rhs.index); }
|
||||
__fi bool operator<=(const DescriptorHandle& rhs) const { return (index <= rhs.index); }
|
||||
__fi bool operator>(const DescriptorHandle& rhs) const { return (index > rhs.index); }
|
||||
__fi bool operator>=(const DescriptorHandle& rhs) const { return (index >= rhs.index); }
|
||||
|
||||
__fi void Clear()
|
||||
{
|
||||
cpu_handle = {};
|
||||
gpu_handle = {};
|
||||
index = INVALID_INDEX;
|
||||
}
|
||||
};
|
||||
|
||||
class DescriptorHeapManager final
|
||||
{
|
||||
public:
|
||||
DescriptorHeapManager();
|
||||
~DescriptorHeapManager();
|
||||
|
||||
ID3D12DescriptorHeap* GetDescriptorHeap() const { return m_descriptor_heap.get(); }
|
||||
u32 GetDescriptorIncrementSize() const { return m_descriptor_increment_size; }
|
||||
|
||||
bool Create(ID3D12Device* device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 num_descriptors, bool shader_visible);
|
||||
void Destroy();
|
||||
|
||||
bool Allocate(DescriptorHandle* handle);
|
||||
void Free(DescriptorHandle* handle);
|
||||
void Free(u32 index);
|
||||
|
||||
private:
|
||||
wil::com_ptr_nothrow<ID3D12DescriptorHeap> m_descriptor_heap;
|
||||
u32 m_num_descriptors = 0;
|
||||
u32 m_descriptor_increment_size = 0;
|
||||
bool m_shader_visible = false;
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE m_heap_base_cpu = {};
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE m_heap_base_gpu = {};
|
||||
|
||||
static constexpr u32 BITSET_SIZE = 1024;
|
||||
using BitSetType = std::bitset<BITSET_SIZE>;
|
||||
std::vector<BitSetType> m_free_slots = {};
|
||||
};
|
||||
|
||||
class DescriptorAllocator
|
||||
{
|
||||
public:
|
||||
DescriptorAllocator();
|
||||
~DescriptorAllocator();
|
||||
|
||||
__fi ID3D12DescriptorHeap* GetDescriptorHeap() const { return m_descriptor_heap.get(); }
|
||||
__fi u32 GetDescriptorIncrementSize() const { return m_descriptor_increment_size; }
|
||||
|
||||
bool Create(ID3D12Device* device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 num_descriptors);
|
||||
void Destroy();
|
||||
|
||||
bool Allocate(u32 num_handles, DescriptorHandle* out_base_handle);
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
wil::com_ptr_nothrow<ID3D12DescriptorHeap> m_descriptor_heap;
|
||||
u32 m_descriptor_increment_size = 0;
|
||||
u32 m_num_descriptors = 0;
|
||||
u32 m_current_offset = 0;
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE m_heap_base_cpu = {};
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE m_heap_base_gpu = {};
|
||||
};
|
||||
|
||||
template <u32 NumSamplers>
|
||||
class GroupedSamplerAllocator : private DescriptorAllocator
|
||||
{
|
||||
struct Key
|
||||
{
|
||||
u32 idx[NumSamplers];
|
||||
|
||||
__fi bool operator==(const Key& rhs) const
|
||||
{
|
||||
return (std::memcmp(idx, rhs.idx, sizeof(idx)) == 0);
|
||||
}
|
||||
__fi bool operator!=(const Key& rhs) const
|
||||
{
|
||||
return (std::memcmp(idx, rhs.idx, sizeof(idx)) != 0);
|
||||
}
|
||||
};
|
||||
|
||||
struct KeyHash
|
||||
{
|
||||
__fi std::size_t operator()(const Key& key) const
|
||||
{
|
||||
size_t seed = 0;
|
||||
for (u32 key : key.idx)
|
||||
HashCombine(seed, key);
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
GroupedSamplerAllocator();
|
||||
~GroupedSamplerAllocator();
|
||||
|
||||
using DescriptorAllocator::GetDescriptorHeap;
|
||||
using DescriptorAllocator::GetDescriptorIncrementSize;
|
||||
|
||||
bool Create(ID3D12Device* device, u32 num_descriptors);
|
||||
void Destroy();
|
||||
|
||||
bool LookupSingle(DescriptorHandle* gpu_handle, const DescriptorHandle& cpu_handle);
|
||||
bool LookupGroup(DescriptorHandle* gpu_handle, const DescriptorHandle* cpu_handles);
|
||||
|
||||
// Clears cache but doesn't reset allocator.
|
||||
void InvalidateCache();
|
||||
|
||||
void Reset();
|
||||
bool ShouldReset() const;
|
||||
|
||||
private:
|
||||
wil::com_ptr_nothrow<ID3D12Device> m_device;
|
||||
std::unordered_map<Key, D3D12::DescriptorHandle, KeyHash> m_groups;
|
||||
};
|
||||
|
||||
template <u32 NumSamplers>
|
||||
GroupedSamplerAllocator<NumSamplers>::GroupedSamplerAllocator() = default;
|
||||
|
||||
template <u32 NumSamplers>
|
||||
GroupedSamplerAllocator<NumSamplers>::~GroupedSamplerAllocator() = default;
|
||||
|
||||
template <u32 NumSamplers>
|
||||
bool GroupedSamplerAllocator<NumSamplers>::Create(ID3D12Device* device, u32 num_descriptors)
|
||||
{
|
||||
if (!DescriptorAllocator::Create(device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, num_descriptors))
|
||||
return false;
|
||||
|
||||
m_device = device;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <u32 NumSamplers>
|
||||
void GroupedSamplerAllocator<NumSamplers>::Destroy()
|
||||
{
|
||||
DescriptorAllocator::Destroy();
|
||||
m_device.reset();
|
||||
}
|
||||
|
||||
template <u32 NumSamplers>
|
||||
void GroupedSamplerAllocator<NumSamplers>::Reset()
|
||||
{
|
||||
m_groups.clear();
|
||||
DescriptorAllocator::Reset();
|
||||
}
|
||||
|
||||
template <u32 NumSamplers>
|
||||
void GroupedSamplerAllocator<NumSamplers>::InvalidateCache()
|
||||
{
|
||||
m_groups.clear();
|
||||
}
|
||||
|
||||
template <u32 NumSamplers>
|
||||
bool GroupedSamplerAllocator<NumSamplers>::LookupSingle(DescriptorHandle* gpu_handle, const DescriptorHandle& cpu_handle)
|
||||
{
|
||||
Key key;
|
||||
key.idx[0] = cpu_handle.index;
|
||||
for (u32 i = 1; i < NumSamplers; i++)
|
||||
key.idx[i] = 0;
|
||||
|
||||
auto it = m_groups.find(key);
|
||||
if (it != m_groups.end())
|
||||
{
|
||||
*gpu_handle = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Allocate(1, gpu_handle))
|
||||
return false;
|
||||
|
||||
m_device->CopyDescriptorsSimple(1, *gpu_handle, cpu_handle, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
|
||||
m_groups.emplace(key, *gpu_handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <u32 NumSamplers>
|
||||
bool GroupedSamplerAllocator<NumSamplers>::LookupGroup(DescriptorHandle* gpu_handle, const DescriptorHandle* cpu_handles)
|
||||
{
|
||||
Key key;
|
||||
for (u32 i = 0; i < NumSamplers; i++)
|
||||
key.idx[i] = cpu_handles[i].index;
|
||||
|
||||
auto it = m_groups.find(key);
|
||||
if (it != m_groups.end())
|
||||
{
|
||||
*gpu_handle = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Allocate(NumSamplers, gpu_handle))
|
||||
return false;
|
||||
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE dst_handle = *gpu_handle;
|
||||
UINT dst_size = NumSamplers;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE src_handles[NumSamplers];
|
||||
UINT src_sizes[NumSamplers];
|
||||
for (u32 i = 0; i < NumSamplers; i++)
|
||||
{
|
||||
src_handles[i] = cpu_handles[i];
|
||||
src_sizes[i] = 1;
|
||||
}
|
||||
m_device->CopyDescriptors(1, &dst_handle, &dst_size, NumSamplers, src_handles, src_sizes, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
|
||||
|
||||
m_groups.emplace(key, *gpu_handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <u32 NumSamplers>
|
||||
bool GroupedSamplerAllocator<NumSamplers>::ShouldReset() const
|
||||
{
|
||||
// We only reset the sampler heap if more than half of the descriptors are used.
|
||||
// This saves descriptor copying when there isn't a large number of sampler configs per frame.
|
||||
return m_groups.size() >= (D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE / 2);
|
||||
}
|
||||
} // namespace D3D12
|
||||
@@ -1,150 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/HashCombine.h"
|
||||
#include "common/RedtapeWindows.h"
|
||||
#include "common/RedtapeWilCom.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <d3d12.h>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace D3D12
|
||||
{
|
||||
class ShaderCache
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
using ComPtr = wil::com_ptr_nothrow<T>;
|
||||
|
||||
enum class EntryType
|
||||
{
|
||||
VertexShader,
|
||||
PixelShader,
|
||||
ComputeShader,
|
||||
GraphicsPipeline,
|
||||
ComputePipeline,
|
||||
};
|
||||
|
||||
ShaderCache();
|
||||
~ShaderCache();
|
||||
|
||||
__fi D3D_FEATURE_LEVEL GetFeatureLevel() const { return m_feature_level; }
|
||||
__fi u32 GetDataVersion() const { return m_data_version; }
|
||||
__fi bool UsingDebugShaders() const { return m_debug; }
|
||||
|
||||
bool Open(std::string_view base_path, D3D_FEATURE_LEVEL feature_level, u32 version, bool debug);
|
||||
void Close();
|
||||
|
||||
__fi ComPtr<ID3DBlob> GetVertexShader(std::string_view shader_code,
|
||||
const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main")
|
||||
{
|
||||
return GetShaderBlob(EntryType::VertexShader, shader_code, macros, entry_point);
|
||||
}
|
||||
__fi ComPtr<ID3DBlob> GetPixelShader(std::string_view shader_code,
|
||||
const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main")
|
||||
{
|
||||
return GetShaderBlob(EntryType::PixelShader, shader_code, macros, entry_point);
|
||||
}
|
||||
__fi ComPtr<ID3DBlob> GetComputeShader(std::string_view shader_code,
|
||||
const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main")
|
||||
{
|
||||
return GetShaderBlob(EntryType::ComputeShader, shader_code, macros, entry_point);
|
||||
}
|
||||
|
||||
ComPtr<ID3DBlob> GetShaderBlob(EntryType type, std::string_view shader_code,
|
||||
const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
|
||||
|
||||
ComPtr<ID3D12PipelineState> GetPipelineState(ID3D12Device* device, const D3D12_GRAPHICS_PIPELINE_STATE_DESC& desc);
|
||||
ComPtr<ID3D12PipelineState> GetPipelineState(ID3D12Device* device, const D3D12_COMPUTE_PIPELINE_STATE_DESC& desc);
|
||||
|
||||
private:
|
||||
static constexpr u32 FILE_VERSION = 1;
|
||||
|
||||
struct CacheIndexKey
|
||||
{
|
||||
u64 source_hash_low;
|
||||
u64 source_hash_high;
|
||||
u64 macro_hash_low;
|
||||
u64 macro_hash_high;
|
||||
u64 entry_point_low;
|
||||
u64 entry_point_high;
|
||||
u32 source_length;
|
||||
EntryType type;
|
||||
|
||||
bool operator==(const CacheIndexKey& key) const;
|
||||
bool operator!=(const CacheIndexKey& key) const;
|
||||
};
|
||||
|
||||
struct CacheIndexEntryHasher
|
||||
{
|
||||
std::size_t operator()(const CacheIndexKey& e) const noexcept
|
||||
{
|
||||
std::size_t h = 0;
|
||||
HashCombine(h, e.entry_point_low, e.entry_point_high, e.macro_hash_low, e.macro_hash_high,
|
||||
e.source_hash_low, e.source_hash_high, e.source_length, e.type);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
struct CacheIndexData
|
||||
{
|
||||
u32 file_offset;
|
||||
u32 blob_size;
|
||||
};
|
||||
|
||||
using CacheIndex = std::unordered_map<CacheIndexKey, CacheIndexData, CacheIndexEntryHasher>;
|
||||
|
||||
static std::string GetCacheBaseFileName(const std::string_view& base_path, const std::string_view& type,
|
||||
D3D_FEATURE_LEVEL feature_level, bool debug);
|
||||
static CacheIndexKey GetShaderCacheKey(EntryType type, const std::string_view& shader_code,
|
||||
const D3D_SHADER_MACRO* macros, const char* entry_point);
|
||||
static CacheIndexKey GetPipelineCacheKey(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& gpdesc);
|
||||
static CacheIndexKey GetPipelineCacheKey(const D3D12_COMPUTE_PIPELINE_STATE_DESC& gpdesc);
|
||||
|
||||
bool CreateNew(const std::string& index_filename, const std::string& blob_filename, std::FILE*& index_file,
|
||||
std::FILE*& blob_file);
|
||||
bool ReadExisting(const std::string& index_filename, const std::string& blob_filename, std::FILE*& index_file,
|
||||
std::FILE*& blob_file, CacheIndex& index);
|
||||
void InvalidatePipelineCache();
|
||||
|
||||
ComPtr<ID3DBlob> CompileAndAddShaderBlob(const CacheIndexKey& key, std::string_view shader_code,
|
||||
const D3D_SHADER_MACRO* macros, const char* entry_point);
|
||||
ComPtr<ID3D12PipelineState> CompileAndAddPipeline(ID3D12Device* device, const CacheIndexKey& key,
|
||||
const D3D12_GRAPHICS_PIPELINE_STATE_DESC& gpdesc);
|
||||
ComPtr<ID3D12PipelineState> CompileAndAddPipeline(ID3D12Device* device, const CacheIndexKey& key,
|
||||
const D3D12_COMPUTE_PIPELINE_STATE_DESC& gpdesc);
|
||||
bool AddPipelineToBlob(const CacheIndexKey& key, ID3D12PipelineState* pso);
|
||||
|
||||
std::string m_base_path;
|
||||
|
||||
std::FILE* m_shader_index_file = nullptr;
|
||||
std::FILE* m_shader_blob_file = nullptr;
|
||||
CacheIndex m_shader_index;
|
||||
|
||||
std::FILE* m_pipeline_index_file = nullptr;
|
||||
std::FILE* m_pipeline_blob_file = nullptr;
|
||||
CacheIndex m_pipeline_index;
|
||||
|
||||
D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0;
|
||||
u32 m_data_version = 0;
|
||||
bool m_debug = false;
|
||||
};
|
||||
} // namespace D3D12
|
||||
@@ -1,77 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/RedtapeWindows.h"
|
||||
#include "common/RedtapeWilCom.h"
|
||||
|
||||
#include <d3d12.h>
|
||||
#include <deque>
|
||||
#include <utility>
|
||||
|
||||
namespace D3D12MA
|
||||
{
|
||||
class Allocation;
|
||||
}
|
||||
|
||||
namespace D3D12
|
||||
{
|
||||
class StreamBuffer
|
||||
{
|
||||
public:
|
||||
StreamBuffer();
|
||||
~StreamBuffer();
|
||||
|
||||
bool Create(u32 size);
|
||||
|
||||
__fi bool IsValid() const { return static_cast<bool>(m_buffer); }
|
||||
__fi ID3D12Resource* GetBuffer() const { return m_buffer.get(); }
|
||||
__fi D3D12_GPU_VIRTUAL_ADDRESS GetGPUPointer() const { return m_gpu_pointer; }
|
||||
__fi void* GetHostPointer() const { return m_host_pointer; }
|
||||
__fi void* GetCurrentHostPointer() const { return m_host_pointer + m_current_offset; }
|
||||
__fi D3D12_GPU_VIRTUAL_ADDRESS GetCurrentGPUPointer() const { return m_gpu_pointer + m_current_offset; }
|
||||
__fi u32 GetSize() const { return m_size; }
|
||||
__fi u32 GetCurrentOffset() const { return m_current_offset; }
|
||||
__fi u32 GetCurrentSpace() const { return m_current_space; }
|
||||
|
||||
bool ReserveMemory(u32 num_bytes, u32 alignment);
|
||||
void CommitMemory(u32 final_num_bytes);
|
||||
|
||||
void Destroy(bool defer = true);
|
||||
|
||||
private:
|
||||
void UpdateCurrentFencePosition();
|
||||
void UpdateGPUPosition();
|
||||
|
||||
// Waits for as many fences as needed to allocate num_bytes bytes from the buffer.
|
||||
bool WaitForClearSpace(u32 num_bytes);
|
||||
|
||||
u32 m_size = 0;
|
||||
u32 m_current_offset = 0;
|
||||
u32 m_current_space = 0;
|
||||
u32 m_current_gpu_position = 0;
|
||||
|
||||
wil::com_ptr_nothrow<ID3D12Resource> m_buffer;
|
||||
wil::com_ptr_nothrow<D3D12MA::Allocation> m_allocation;
|
||||
D3D12_GPU_VIRTUAL_ADDRESS m_gpu_pointer = {};
|
||||
u8* m_host_pointer = nullptr;
|
||||
|
||||
// List of fences and the corresponding positions in the buffer
|
||||
std::deque<std::pair<u64, u32>> m_tracked_fences;
|
||||
};
|
||||
|
||||
} // namespace D3D12
|
||||
@@ -1,506 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/PrecompiledHeader.h"
|
||||
|
||||
#include "common/D3D12/Texture.h"
|
||||
#include "common/D3D12/Context.h"
|
||||
#include "common/D3D12/Util.h"
|
||||
#include "common/Align.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "D3D12MemAlloc.h"
|
||||
|
||||
using namespace D3D12;
|
||||
|
||||
Texture::Texture() = default;
|
||||
|
||||
Texture::Texture(ID3D12Resource* resource, D3D12_RESOURCE_STATES state)
|
||||
: m_resource(std::move(resource))
|
||||
{
|
||||
const D3D12_RESOURCE_DESC desc = GetDesc();
|
||||
m_width = static_cast<u32>(desc.Width);
|
||||
m_height = desc.Height;
|
||||
m_levels = desc.MipLevels;
|
||||
m_format = desc.Format;
|
||||
}
|
||||
|
||||
Texture::Texture(Texture&& texture)
|
||||
: m_resource(std::move(texture.m_resource))
|
||||
, m_allocation(std::move(texture.m_allocation))
|
||||
, m_srv_descriptor(texture.m_srv_descriptor)
|
||||
, m_write_descriptor(texture.m_write_descriptor)
|
||||
, m_width(texture.m_width)
|
||||
, m_height(texture.m_height)
|
||||
, m_levels(texture.m_levels)
|
||||
, m_format(texture.m_format)
|
||||
, m_state(texture.m_state)
|
||||
, m_write_descriptor_type(texture.m_write_descriptor_type)
|
||||
{
|
||||
texture.m_srv_descriptor = {};
|
||||
texture.m_write_descriptor = {};
|
||||
texture.m_width = 0;
|
||||
texture.m_height = 0;
|
||||
texture.m_levels = 0;
|
||||
texture.m_format = DXGI_FORMAT_UNKNOWN;
|
||||
texture.m_state = D3D12_RESOURCE_STATE_COMMON;
|
||||
texture.m_write_descriptor_type = WriteDescriptorType::None;
|
||||
}
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
Texture& Texture::operator=(Texture&& texture)
|
||||
{
|
||||
Destroy();
|
||||
m_resource = std::move(texture.m_resource);
|
||||
m_allocation = std::move(texture.m_allocation);
|
||||
m_srv_descriptor = texture.m_srv_descriptor;
|
||||
m_write_descriptor = texture.m_write_descriptor;
|
||||
m_width = texture.m_width;
|
||||
m_height = texture.m_height;
|
||||
m_levels = texture.m_levels;
|
||||
m_format = texture.m_format;
|
||||
m_state = texture.m_state;
|
||||
m_write_descriptor_type = texture.m_write_descriptor_type;
|
||||
texture.m_srv_descriptor = {};
|
||||
texture.m_write_descriptor = {};
|
||||
texture.m_width = 0;
|
||||
texture.m_height = 0;
|
||||
texture.m_levels = 0;
|
||||
texture.m_format = DXGI_FORMAT_UNKNOWN;
|
||||
texture.m_state = D3D12_RESOURCE_STATE_COMMON;
|
||||
texture.m_write_descriptor_type = WriteDescriptorType::None;
|
||||
return *this;
|
||||
}
|
||||
|
||||
D3D12_RESOURCE_DESC Texture::GetDesc() const
|
||||
{
|
||||
return m_resource->GetDesc();
|
||||
}
|
||||
|
||||
bool Texture::Create(u32 width, u32 height, u32 levels, DXGI_FORMAT format, DXGI_FORMAT srv_format,
|
||||
DXGI_FORMAT rtv_format, DXGI_FORMAT dsv_format, D3D12_RESOURCE_FLAGS flags, u32 alloc_flags)
|
||||
{
|
||||
D3D12_RESOURCE_DESC desc = {};
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
desc.Width = width;
|
||||
desc.Height = height;
|
||||
desc.DepthOrArraySize = 1;
|
||||
desc.MipLevels = levels;
|
||||
desc.Format = format;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||
desc.Flags = flags;
|
||||
|
||||
D3D12_CLEAR_VALUE optimized_clear_value = {};
|
||||
D3D12_RESOURCE_STATES state;
|
||||
if (rtv_format != DXGI_FORMAT_UNKNOWN)
|
||||
{
|
||||
optimized_clear_value.Format = rtv_format;
|
||||
state = D3D12_RESOURCE_STATE_RENDER_TARGET;
|
||||
}
|
||||
else if (dsv_format != DXGI_FORMAT_UNKNOWN)
|
||||
{
|
||||
optimized_clear_value.Format = dsv_format;
|
||||
state = D3D12_RESOURCE_STATE_DEPTH_WRITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
}
|
||||
|
||||
D3D12MA::ALLOCATION_DESC allocationDesc = {};
|
||||
allocationDesc.Flags = static_cast<D3D12MA::ALLOCATION_FLAGS>(alloc_flags) | D3D12MA::ALLOCATION_FLAG_WITHIN_BUDGET;
|
||||
allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
|
||||
|
||||
ComPtr<ID3D12Resource> resource;
|
||||
ComPtr<D3D12MA::Allocation> allocation;
|
||||
HRESULT hr = g_d3d12_context->GetAllocator()->CreateResource(
|
||||
&allocationDesc, &desc, state,
|
||||
(rtv_format != DXGI_FORMAT_UNKNOWN || dsv_format != DXGI_FORMAT_UNKNOWN) ? &optimized_clear_value : nullptr,
|
||||
allocation.put(), IID_PPV_ARGS(resource.put()));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// OOM isn't fatal.
|
||||
if (hr != E_OUTOFMEMORY)
|
||||
Console.Error("Create texture failed: 0x%08X", hr);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DescriptorHandle srv_descriptor, write_descriptor;
|
||||
WriteDescriptorType write_descriptor_type = WriteDescriptorType::None;
|
||||
if (srv_format != DXGI_FORMAT_UNKNOWN)
|
||||
{
|
||||
if (!CreateSRVDescriptor(resource.get(), levels, srv_format, &srv_descriptor))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rtv_format != DXGI_FORMAT_UNKNOWN)
|
||||
{
|
||||
pxAssert(dsv_format == DXGI_FORMAT_UNKNOWN && !(flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS));
|
||||
write_descriptor_type = Texture::WriteDescriptorType::RTV;
|
||||
if (!CreateRTVDescriptor(resource.get(), rtv_format, &write_descriptor))
|
||||
{
|
||||
g_d3d12_context->GetRTVHeapManager().Free(&srv_descriptor);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
else if (dsv_format != DXGI_FORMAT_UNKNOWN && !(flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS))
|
||||
{
|
||||
write_descriptor_type = Texture::WriteDescriptorType::DSV;
|
||||
if (!CreateDSVDescriptor(resource.get(), dsv_format, &write_descriptor))
|
||||
{
|
||||
g_d3d12_context->GetDSVHeapManager().Free(&srv_descriptor);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)
|
||||
{
|
||||
write_descriptor_type = Texture::WriteDescriptorType::UAV;
|
||||
if (!CreateUAVDescriptor(resource.get(), dsv_format, &write_descriptor))
|
||||
{
|
||||
g_d3d12_context->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Destroy(true);
|
||||
|
||||
m_resource = std::move(resource);
|
||||
m_allocation = std::move(allocation);
|
||||
m_srv_descriptor = std::move(srv_descriptor);
|
||||
m_write_descriptor = std::move(write_descriptor);
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_levels = levels;
|
||||
m_format = format;
|
||||
m_state = state;
|
||||
m_write_descriptor_type = write_descriptor_type;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Texture::Adopt(ComPtr<ID3D12Resource> texture, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format,
|
||||
DXGI_FORMAT dsv_format, D3D12_RESOURCE_STATES state)
|
||||
{
|
||||
const D3D12_RESOURCE_DESC desc(texture->GetDesc());
|
||||
|
||||
DescriptorHandle srv_descriptor, write_descriptor;
|
||||
WriteDescriptorType write_descriptor_type = WriteDescriptorType::None;
|
||||
if (srv_format != DXGI_FORMAT_UNKNOWN)
|
||||
{
|
||||
if (!CreateSRVDescriptor(texture.get(), desc.MipLevels, srv_format, &srv_descriptor))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rtv_format != DXGI_FORMAT_UNKNOWN)
|
||||
{
|
||||
pxAssert(dsv_format == DXGI_FORMAT_UNKNOWN);
|
||||
write_descriptor_type = Texture::WriteDescriptorType::RTV;
|
||||
if (!CreateRTVDescriptor(texture.get(), rtv_format, &write_descriptor))
|
||||
{
|
||||
g_d3d12_context->GetRTVHeapManager().Free(&srv_descriptor);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (dsv_format != DXGI_FORMAT_UNKNOWN)
|
||||
{
|
||||
write_descriptor_type = Texture::WriteDescriptorType::DSV;
|
||||
if (!CreateDSVDescriptor(texture.get(), dsv_format, &write_descriptor))
|
||||
{
|
||||
g_d3d12_context->GetDSVHeapManager().Free(&srv_descriptor);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)
|
||||
{
|
||||
write_descriptor_type = Texture::WriteDescriptorType::UAV;
|
||||
if (!CreateUAVDescriptor(texture.get(), srv_format, &write_descriptor))
|
||||
{
|
||||
g_d3d12_context->GetDescriptorHeapManager().Free(&srv_descriptor);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_resource = std::move(texture);
|
||||
m_allocation.reset();
|
||||
m_srv_descriptor = std::move(srv_descriptor);
|
||||
m_write_descriptor = std::move(write_descriptor);
|
||||
m_write_descriptor_type = write_descriptor_type;
|
||||
m_width = static_cast<u32>(desc.Width);
|
||||
m_height = desc.Height;
|
||||
m_levels = desc.MipLevels;
|
||||
m_format = desc.Format;
|
||||
m_state = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Texture::Destroy(bool defer /* = true */)
|
||||
{
|
||||
if (defer)
|
||||
{
|
||||
g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetDescriptorHeapManager(), &m_srv_descriptor);
|
||||
|
||||
switch (m_write_descriptor_type)
|
||||
{
|
||||
case Texture::WriteDescriptorType::RTV:
|
||||
g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetRTVHeapManager(), &m_write_descriptor);
|
||||
break;
|
||||
case Texture::WriteDescriptorType::DSV:
|
||||
g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetDSVHeapManager(), &m_write_descriptor);
|
||||
break;
|
||||
case Texture::WriteDescriptorType::UAV:
|
||||
g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetDescriptorHeapManager(), &m_write_descriptor);
|
||||
break;
|
||||
case Texture::WriteDescriptorType::None:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
g_d3d12_context->DeferResourceDestruction(m_allocation.get(), m_resource.get());
|
||||
m_resource.reset();
|
||||
m_allocation.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
g_d3d12_context->GetDescriptorHeapManager().Free(&m_srv_descriptor);
|
||||
|
||||
switch (m_write_descriptor_type)
|
||||
{
|
||||
case Texture::WriteDescriptorType::RTV:
|
||||
g_d3d12_context->GetRTVHeapManager().Free(&m_write_descriptor);
|
||||
break;
|
||||
case Texture::WriteDescriptorType::DSV:
|
||||
g_d3d12_context->GetDSVHeapManager().Free(&m_write_descriptor);
|
||||
break;
|
||||
case Texture::WriteDescriptorType::UAV:
|
||||
g_d3d12_context->GetDescriptorHeapManager().Free(&m_write_descriptor);
|
||||
break;
|
||||
case Texture::WriteDescriptorType::None:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_resource.reset();
|
||||
m_allocation.reset();
|
||||
}
|
||||
|
||||
m_width = 0;
|
||||
m_height = 0;
|
||||
m_levels = 0;
|
||||
m_format = DXGI_FORMAT_UNKNOWN;
|
||||
m_write_descriptor_type = WriteDescriptorType::None;
|
||||
}
|
||||
|
||||
void Texture::TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RESOURCE_STATES state)
|
||||
{
|
||||
if (m_state == state)
|
||||
return;
|
||||
|
||||
ResourceBarrier(cmdlist, m_resource.get(), m_state, state);
|
||||
m_state = state;
|
||||
}
|
||||
|
||||
void Texture::TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, u32 level,
|
||||
D3D12_RESOURCE_STATES before_state, D3D12_RESOURCE_STATES after_state) const
|
||||
{
|
||||
const D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
|
||||
D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
{{m_resource.get(), level, before_state, after_state}}};
|
||||
cmdlist->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
|
||||
ID3D12GraphicsCommandList* Texture::BeginStreamUpdate(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, void** out_data, u32* out_data_pitch)
|
||||
{
|
||||
const u32 copy_pitch = Common::AlignUpPow2(width * GetTexelSize(m_format), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
|
||||
const u32 upload_size = copy_pitch * height;
|
||||
|
||||
if (!g_d3d12_context->GetTextureStreamBuffer().ReserveMemory(upload_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT))
|
||||
{
|
||||
DevCon.WriteLn("Executing command buffer while waiting for %u bytes (%ux%u) in upload buffer", upload_size, width,
|
||||
height);
|
||||
g_d3d12_context->ExecuteCommandList(Context::WaitType::None);
|
||||
if (!g_d3d12_context->GetTextureStreamBuffer().ReserveMemory(upload_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT))
|
||||
{
|
||||
Console.Error("Failed to reserve %u bytes for %ux%u upload", upload_size, width, height);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// cmdlist change
|
||||
cmdlist = g_d3d12_context->GetInitCommandList();
|
||||
}
|
||||
|
||||
*out_data = g_d3d12_context->GetTextureStreamBuffer().GetCurrentHostPointer();
|
||||
*out_data_pitch = copy_pitch;
|
||||
return cmdlist;
|
||||
}
|
||||
|
||||
void Texture::EndStreamUpdate(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height)
|
||||
{
|
||||
const u32 copy_pitch = Common::AlignUpPow2(width * GetTexelSize(m_format), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
|
||||
const u32 upload_size = copy_pitch * height;
|
||||
|
||||
StreamBuffer& sb = g_d3d12_context->GetTextureStreamBuffer();
|
||||
const u32 sb_offset = sb.GetCurrentOffset();
|
||||
sb.CommitMemory(upload_size);
|
||||
|
||||
CopyFromBuffer(cmdlist, level, x, y, width, height, copy_pitch, sb.GetBuffer(), sb_offset);
|
||||
}
|
||||
|
||||
void Texture::CopyFromBuffer(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, u32 pitch, ID3D12Resource* buffer, u32 buffer_offset)
|
||||
{
|
||||
D3D12_TEXTURE_COPY_LOCATION src;
|
||||
src.pResource = buffer;
|
||||
src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||
src.PlacedFootprint.Offset = buffer_offset;
|
||||
src.PlacedFootprint.Footprint.Width = width;
|
||||
src.PlacedFootprint.Footprint.Height = height;
|
||||
src.PlacedFootprint.Footprint.Depth = 1;
|
||||
src.PlacedFootprint.Footprint.RowPitch = pitch;
|
||||
src.PlacedFootprint.Footprint.Format = m_format;
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION dst;
|
||||
dst.pResource = m_resource.get();
|
||||
dst.SubresourceIndex = level;
|
||||
dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||
|
||||
const D3D12_BOX src_box{0u, 0u, 0u, width, height, 1u};
|
||||
const D3D12_RESOURCE_STATES old_state = m_state;
|
||||
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_COPY_DEST);
|
||||
cmdlist->CopyTextureRegion(&dst, x, y, 0, &src, &src_box);
|
||||
TransitionToState(cmdlist, old_state);
|
||||
}
|
||||
|
||||
static ID3D12Resource* CreateStagingBuffer(u32 height, const void* data, u32 pitch, u32 upload_pitch, u32 upload_size)
|
||||
{
|
||||
wil::com_ptr_nothrow<ID3D12Resource> resource;
|
||||
wil::com_ptr_nothrow<D3D12MA::Allocation> allocation;
|
||||
|
||||
const D3D12MA::ALLOCATION_DESC allocation_desc = {D3D12MA::ALLOCATION_FLAG_NONE, D3D12_HEAP_TYPE_UPLOAD};
|
||||
const D3D12_RESOURCE_DESC resource_desc = {
|
||||
D3D12_RESOURCE_DIMENSION_BUFFER, 0, upload_size, 1, 1, 1, DXGI_FORMAT_UNKNOWN, {1, 0}, D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
|
||||
D3D12_RESOURCE_FLAG_NONE};
|
||||
HRESULT hr = g_d3d12_context->GetAllocator()->CreateResource(&allocation_desc, &resource_desc, D3D12_RESOURCE_STATE_GENERIC_READ,
|
||||
nullptr, allocation.put(), IID_PPV_ARGS(resource.put()));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("CreateResource() for upload staging buffer failed: %08X", hr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* map;
|
||||
const D3D12_RANGE read_range = {};
|
||||
hr = resource->Map(0, &read_range, &map);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Console.Error("Map() for upload staging buffer failed: %08X", hr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
StringUtil::StrideMemCpy(map, upload_pitch, data, pitch, std::min(pitch, upload_pitch), height);
|
||||
|
||||
const D3D12_RANGE write_range = {0u, upload_size};
|
||||
resource->Unmap(0, &write_range);
|
||||
|
||||
// queue them for destruction, since the upload happens in this cmdlist
|
||||
g_d3d12_context->DeferResourceDestruction(allocation.get(), resource.get());
|
||||
|
||||
// AddRef()'ed by the defer above.
|
||||
return resource.get();
|
||||
}
|
||||
|
||||
bool Texture::LoadData(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch)
|
||||
{
|
||||
const u32 texel_size = GetTexelSize(m_format);
|
||||
const u32 upload_pitch = Common::AlignUpPow2(width * texel_size, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
|
||||
const u32 upload_size = upload_pitch * height;
|
||||
if (upload_size >= g_d3d12_context->GetTextureStreamBuffer().GetSize())
|
||||
{
|
||||
ID3D12Resource* staging_buffer = CreateStagingBuffer(height, data, pitch, upload_pitch, upload_size);
|
||||
if (!staging_buffer)
|
||||
return false;
|
||||
|
||||
CopyFromBuffer(cmdlist, level, x, y, width, height, upload_pitch, staging_buffer, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void* write_ptr;
|
||||
u32 write_pitch;
|
||||
if (!(cmdlist = BeginStreamUpdate(cmdlist, level, x, y, width, height, &write_ptr, &write_pitch)))
|
||||
return false;
|
||||
|
||||
StringUtil::StrideMemCpy(write_ptr, write_pitch, data, pitch, std::min(pitch, upload_pitch), height);
|
||||
EndStreamUpdate(cmdlist, level, x, y, width, height);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Texture::CreateSRVDescriptor(ID3D12Resource* resource, u32 levels, DXGI_FORMAT format, DescriptorHandle* dh)
|
||||
{
|
||||
if (!g_d3d12_context->GetDescriptorHeapManager().Allocate(dh))
|
||||
{
|
||||
Console.Error("Failed to allocate SRV descriptor");
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC desc = {format, D3D12_SRV_DIMENSION_TEXTURE2D, D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING};
|
||||
desc.Texture2D.MipLevels = levels;
|
||||
|
||||
g_d3d12_context->GetDevice()->CreateShaderResourceView(resource, &desc, dh->cpu_handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Texture::CreateRTVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, DescriptorHandle* dh)
|
||||
{
|
||||
if (!g_d3d12_context->GetRTVHeapManager().Allocate(dh))
|
||||
{
|
||||
Console.Error("Failed to allocate SRV descriptor");
|
||||
return false;
|
||||
}
|
||||
|
||||
const D3D12_RENDER_TARGET_VIEW_DESC desc = {format, D3D12_RTV_DIMENSION_TEXTURE2D};
|
||||
g_d3d12_context->GetDevice()->CreateRenderTargetView(resource, &desc, dh->cpu_handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Texture::CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, DescriptorHandle* dh)
|
||||
{
|
||||
if (!g_d3d12_context->GetDSVHeapManager().Allocate(dh))
|
||||
{
|
||||
Console.Error("Failed to allocate SRV descriptor");
|
||||
return false;
|
||||
}
|
||||
|
||||
const D3D12_DEPTH_STENCIL_VIEW_DESC desc = {format, D3D12_DSV_DIMENSION_TEXTURE2D, D3D12_DSV_FLAG_NONE};
|
||||
g_d3d12_context->GetDevice()->CreateDepthStencilView(resource, &desc, dh->cpu_handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Texture::CreateUAVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, DescriptorHandle* dh)
|
||||
{
|
||||
if (!g_d3d12_context->GetDescriptorHeapManager().Allocate(dh))
|
||||
{
|
||||
Console.Error("Failed to allocate UAV descriptor");
|
||||
return false;
|
||||
}
|
||||
|
||||
const D3D12_UNORDERED_ACCESS_VIEW_DESC desc = {format, D3D12_UAV_DIMENSION_TEXTURE2D};
|
||||
g_d3d12_context->GetDevice()->CreateUnorderedAccessView(resource, nullptr, &desc, dh->cpu_handle);
|
||||
return true;
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/RedtapeWindows.h"
|
||||
#include "common/D3D12/DescriptorHeapManager.h"
|
||||
|
||||
#include <d3d12.h>
|
||||
#include <wil/com.h>
|
||||
|
||||
namespace D3D12MA
|
||||
{
|
||||
class Allocation;
|
||||
}
|
||||
|
||||
namespace D3D12
|
||||
{
|
||||
class StreamBuffer;
|
||||
|
||||
class Texture final
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
using ComPtr = wil::com_ptr_nothrow<T>;
|
||||
|
||||
Texture();
|
||||
Texture(ID3D12Resource* resource, D3D12_RESOURCE_STATES state);
|
||||
Texture(Texture&& texture);
|
||||
Texture(const Texture&) = delete;
|
||||
~Texture();
|
||||
|
||||
__fi ID3D12Resource* GetResource() const { return m_resource.get(); }
|
||||
__fi D3D12MA::Allocation* GetAllocation() const { return m_allocation.get(); }
|
||||
__fi const DescriptorHandle& GetSRVDescriptor() const { return m_srv_descriptor; }
|
||||
__fi const DescriptorHandle& GetWriteDescriptor() const { return m_write_descriptor; }
|
||||
__fi D3D12_RESOURCE_STATES GetState() const { return m_state; }
|
||||
|
||||
__fi u32 GetWidth() const { return m_width; }
|
||||
__fi u32 GetHeight() const { return m_height; }
|
||||
__fi u32 GetLevels() const { return m_levels; }
|
||||
__fi DXGI_FORMAT GetFormat() const { return m_format; }
|
||||
|
||||
__fi operator ID3D12Resource*() const { return m_resource.get(); }
|
||||
__fi operator bool() const { return static_cast<bool>(m_resource); }
|
||||
|
||||
bool Create(u32 width, u32 height, u32 levels, DXGI_FORMAT format, DXGI_FORMAT srv_format,
|
||||
DXGI_FORMAT rtv_format, DXGI_FORMAT dsv_format, D3D12_RESOURCE_FLAGS flags, u32 alloc_flags = 0);
|
||||
bool Adopt(ComPtr<ID3D12Resource> texture, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format, DXGI_FORMAT dsv_format,
|
||||
D3D12_RESOURCE_STATES state);
|
||||
|
||||
D3D12_RESOURCE_DESC GetDesc() const;
|
||||
|
||||
void Destroy(bool defer = true);
|
||||
|
||||
void TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RESOURCE_STATES state);
|
||||
void TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, u32 level,
|
||||
D3D12_RESOURCE_STATES before_state, D3D12_RESOURCE_STATES after_state) const;
|
||||
|
||||
Texture& operator=(const Texture&) = delete;
|
||||
Texture& operator=(Texture&& texture);
|
||||
|
||||
// NOTE: Does not handle compressed formats.
|
||||
ID3D12GraphicsCommandList* BeginStreamUpdate(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, void** out_data, u32* out_data_pitch);
|
||||
void EndStreamUpdate(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height);
|
||||
bool LoadData(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch);
|
||||
void CopyFromBuffer(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, u32 pitch, ID3D12Resource* buffer, u32 buffer_offset);
|
||||
|
||||
private:
|
||||
static bool CreateSRVDescriptor(ID3D12Resource* resource, u32 levels, DXGI_FORMAT format, DescriptorHandle* dh);
|
||||
static bool CreateRTVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, DescriptorHandle* dh);
|
||||
static bool CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, DescriptorHandle* dh);
|
||||
static bool CreateUAVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, DescriptorHandle* dh);
|
||||
|
||||
enum class WriteDescriptorType : u8
|
||||
{
|
||||
None,
|
||||
RTV,
|
||||
DSV,
|
||||
UAV
|
||||
};
|
||||
|
||||
ComPtr<ID3D12Resource> m_resource;
|
||||
ComPtr<D3D12MA::Allocation> m_allocation;
|
||||
DescriptorHandle m_srv_descriptor = {};
|
||||
DescriptorHandle m_write_descriptor = {};
|
||||
u32 m_width = 0;
|
||||
u32 m_height = 0;
|
||||
u32 m_levels = 0;
|
||||
DXGI_FORMAT m_format = DXGI_FORMAT_UNKNOWN;
|
||||
|
||||
D3D12_RESOURCE_STATES m_state = D3D12_RESOURCE_STATE_COMMON;
|
||||
|
||||
WriteDescriptorType m_write_descriptor_type = WriteDescriptorType::None;
|
||||
};
|
||||
} // namespace D3D12
|
||||
@@ -1,93 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/PrecompiledHeader.h"
|
||||
|
||||
#include "common/D3D12/Util.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
u32 D3D12::GetTexelSize(DXGI_FORMAT format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case DXGI_FORMAT_R32G32B32A32_FLOAT:
|
||||
case DXGI_FORMAT_BC1_UNORM:
|
||||
case DXGI_FORMAT_BC2_UNORM:
|
||||
case DXGI_FORMAT_BC3_UNORM:
|
||||
case DXGI_FORMAT_BC7_UNORM:
|
||||
return 16;
|
||||
|
||||
case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:
|
||||
return 4;
|
||||
|
||||
case DXGI_FORMAT_R8G8B8A8_UNORM:
|
||||
case DXGI_FORMAT_R8G8B8A8_SNORM:
|
||||
case DXGI_FORMAT_R8G8B8A8_TYPELESS:
|
||||
case DXGI_FORMAT_B8G8R8A8_UNORM:
|
||||
case DXGI_FORMAT_B8G8R8A8_TYPELESS:
|
||||
case DXGI_FORMAT_R32_UINT:
|
||||
case DXGI_FORMAT_R32_SINT:
|
||||
return 4;
|
||||
|
||||
case DXGI_FORMAT_B5G5R5A1_UNORM:
|
||||
case DXGI_FORMAT_B5G6R5_UNORM:
|
||||
case DXGI_FORMAT_R16_UINT:
|
||||
case DXGI_FORMAT_R16_SINT:
|
||||
return 2;
|
||||
|
||||
case DXGI_FORMAT_A8_UNORM:
|
||||
case DXGI_FORMAT_R8_UNORM:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
pxFailRel("Unknown format");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void D3D12::SetDefaultSampler(D3D12_SAMPLER_DESC* desc)
|
||||
{
|
||||
desc->Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
desc->AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
desc->AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
desc->AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
desc->MipLODBias = 0;
|
||||
desc->MaxAnisotropy = 1;
|
||||
desc->ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
|
||||
desc->BorderColor[0] = 1.0f;
|
||||
desc->BorderColor[1] = 1.0f;
|
||||
desc->BorderColor[2] = 1.0f;
|
||||
desc->BorderColor[3] = 1.0f;
|
||||
desc->MinLOD = -3.402823466e+38F; // -FLT_MAX
|
||||
desc->MaxLOD = 3.402823466e+38F; // FLT_MAX
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
void D3D12::SetObjectName(ID3D12Object* object, const char* name)
|
||||
{
|
||||
object->SetName(StringUtil::UTF8StringToWideString(name).c_str());
|
||||
}
|
||||
|
||||
void D3D12::SetObjectNameFormatted(ID3D12Object* object, const char* format, ...)
|
||||
{
|
||||
std::va_list ap;
|
||||
va_start(ap, format);
|
||||
SetObjectName(object, StringUtil::StdStringFromFormatV(format, ap).c_str());
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,77 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/RedtapeWindows.h"
|
||||
|
||||
#include <array>
|
||||
#include <d3d12.h>
|
||||
|
||||
namespace D3D12
|
||||
{
|
||||
static inline void ResourceBarrier(ID3D12GraphicsCommandList* cmdlist, ID3D12Resource* resource,
|
||||
D3D12_RESOURCE_STATES from_state, D3D12_RESOURCE_STATES to_state)
|
||||
{
|
||||
const D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
|
||||
D3D12_RESOURCE_BARRIER_FLAG_NONE,
|
||||
{{resource, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, from_state, to_state}}};
|
||||
cmdlist->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
|
||||
static inline void SetViewport(ID3D12GraphicsCommandList* cmdlist, int x, int y, int width, int height,
|
||||
float min_depth = 0.0f, float max_depth = 1.0f)
|
||||
{
|
||||
const D3D12_VIEWPORT vp{static_cast<float>(x),
|
||||
static_cast<float>(y),
|
||||
static_cast<float>(width),
|
||||
static_cast<float>(height),
|
||||
min_depth,
|
||||
max_depth};
|
||||
cmdlist->RSSetViewports(1, &vp);
|
||||
}
|
||||
|
||||
static inline void SetScissor(ID3D12GraphicsCommandList* cmdlist, int x, int y, int width, int height)
|
||||
{
|
||||
const D3D12_RECT r{x, y, x + width, y + height};
|
||||
cmdlist->RSSetScissorRects(1, &r);
|
||||
}
|
||||
|
||||
static inline void SetViewportAndScissor(ID3D12GraphicsCommandList* cmdlist, int x, int y, int width, int height,
|
||||
float min_depth = 0.0f, float max_depth = 1.0f)
|
||||
{
|
||||
SetViewport(cmdlist, x, y, width, height, min_depth, max_depth);
|
||||
SetScissor(cmdlist, x, y, width, height);
|
||||
}
|
||||
|
||||
u32 GetTexelSize(DXGI_FORMAT format);
|
||||
|
||||
void SetDefaultSampler(D3D12_SAMPLER_DESC* desc);
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
void SetObjectName(ID3D12Object* object, const char* name);
|
||||
void SetObjectNameFormatted(ID3D12Object* object, const char* format, ...);
|
||||
|
||||
#else
|
||||
|
||||
static inline void SetObjectName(ID3D12Object* object, const char* name)
|
||||
{
|
||||
}
|
||||
static inline void SetObjectNameFormatted(ID3D12Object* object, const char* format, ...) {}
|
||||
|
||||
#endif
|
||||
} // namespace D3D12
|
||||
@@ -1,171 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/PrecompiledHeader.h"
|
||||
|
||||
#include "common/Console.h"
|
||||
#include "common/GL/Context.h"
|
||||
#include "glad.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#ifdef __APPLE__
|
||||
#include <stdlib.h>
|
||||
#else
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && !defined(_M_ARM64)
|
||||
#include "common/GL/ContextWGL.h"
|
||||
#elif defined(__APPLE__)
|
||||
#include "common/GL/ContextAGL.h"
|
||||
#else
|
||||
#ifdef X11_API
|
||||
#include "common/GL/ContextEGLX11.h"
|
||||
#endif
|
||||
#ifdef WAYLAND_API
|
||||
#include "common/GL/ContextEGLWayland.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace GL
|
||||
{
|
||||
static bool ShouldPreferESContext()
|
||||
{
|
||||
#ifndef _MSC_VER
|
||||
const char* value = std::getenv("PREFER_GLES_CONTEXT");
|
||||
return (value && std::strcmp(value, "1") == 0);
|
||||
#else
|
||||
char buffer[2] = {};
|
||||
size_t buffer_size = sizeof(buffer);
|
||||
getenv_s(&buffer_size, buffer, "PREFER_GLES_CONTEXT");
|
||||
return (std::strcmp(buffer, "1") == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
Context::Context(const WindowInfo& wi)
|
||||
: m_wi(wi)
|
||||
{
|
||||
}
|
||||
|
||||
Context::~Context() = default;
|
||||
|
||||
std::vector<Context::FullscreenModeInfo> Context::EnumerateFullscreenModes()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<GL::Context> Context::Create(const WindowInfo& wi, gsl::span<const Version> versions_to_try)
|
||||
{
|
||||
if (ShouldPreferESContext())
|
||||
{
|
||||
// move ES versions to the front
|
||||
Version* new_versions_to_try = static_cast<Version*>(alloca(sizeof(Version) * versions_to_try.size()));
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < versions_to_try.size(); i++)
|
||||
{
|
||||
if (versions_to_try[i].profile == Profile::ES)
|
||||
new_versions_to_try[count++] = versions_to_try[i];
|
||||
}
|
||||
for (size_t i = 0; i < versions_to_try.size(); i++)
|
||||
{
|
||||
if (versions_to_try[i].profile != Profile::ES)
|
||||
new_versions_to_try[count++] = versions_to_try[i];
|
||||
}
|
||||
versions_to_try = gsl::span<const Version>(new_versions_to_try, versions_to_try.size());
|
||||
}
|
||||
|
||||
std::unique_ptr<Context> context;
|
||||
#if defined(_WIN32) && !defined(_M_ARM64)
|
||||
context = ContextWGL::Create(wi, versions_to_try);
|
||||
#elif defined(__APPLE__)
|
||||
context = ContextAGL::Create(wi, versions_to_try);
|
||||
#endif
|
||||
|
||||
#if defined(X11_API)
|
||||
if (wi.type == WindowInfo::Type::X11)
|
||||
context = ContextEGLX11::Create(wi, versions_to_try);
|
||||
#endif
|
||||
|
||||
#if defined(WAYLAND_API)
|
||||
if (wi.type == WindowInfo::Type::Wayland)
|
||||
context = ContextEGLWayland::Create(wi, versions_to_try);
|
||||
#endif
|
||||
|
||||
if (!context)
|
||||
return nullptr;
|
||||
|
||||
Console.WriteLn("Created an %s context", context->IsGLES() ? "OpenGL ES" : "OpenGL");
|
||||
|
||||
// NOTE: Not thread-safe. But this is okay, since we're not going to be creating more than one context at a time.
|
||||
static Context* context_being_created;
|
||||
context_being_created = context.get();
|
||||
|
||||
// load up glad
|
||||
if (!context->IsGLES())
|
||||
{
|
||||
if (!gladLoadGLLoader([](const char* name) { return context_being_created->GetProcAddress(name); }))
|
||||
{
|
||||
Console.Error("Failed to load GL functions for GLAD");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!gladLoadGLES2Loader([](const char* name) { return context_being_created->GetProcAddress(name); }))
|
||||
{
|
||||
Console.Error("Failed to load GLES functions for GLAD");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
context_being_created = nullptr;
|
||||
|
||||
const char* gl_vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
|
||||
const char* gl_renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
|
||||
const char* gl_version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
||||
const char* gl_shading_language_version = reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||
DevCon.WriteLn(Color_Magenta, "GL_VENDOR: %s", gl_vendor);
|
||||
DevCon.WriteLn(Color_Magenta, "GL_RENDERER: %s", gl_renderer);
|
||||
DevCon.WriteLn(Color_Magenta, "GL_VERSION: %s", gl_version);
|
||||
DevCon.WriteLn(Color_Magenta, "GL_SHADING_LANGUAGE_VERSION: %s", gl_shading_language_version);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
gsl::span<const Context::Version> Context::GetAllVersionsList()
|
||||
{
|
||||
static constexpr Version vlist[] = {
|
||||
{Profile::Core, 4, 6},
|
||||
{Profile::Core, 4, 5},
|
||||
{Profile::Core, 4, 4},
|
||||
{Profile::Core, 4, 3},
|
||||
{Profile::Core, 4, 2},
|
||||
{Profile::Core, 4, 1},
|
||||
{Profile::Core, 4, 0},
|
||||
{Profile::Core, 3, 3},
|
||||
{Profile::Core, 3, 2},
|
||||
{Profile::Core, 3, 1},
|
||||
{Profile::Core, 3, 0},
|
||||
{Profile::ES, 3, 2},
|
||||
{Profile::ES, 3, 1},
|
||||
{Profile::ES, 3, 0},
|
||||
{Profile::ES, 2, 0},
|
||||
{Profile::NoProfile, 0, 0}
|
||||
};
|
||||
return vlist;
|
||||
}
|
||||
} // namespace GL
|
||||
@@ -1,80 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/WindowInfo.h"
|
||||
|
||||
#include <gsl/span>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace GL {
|
||||
class Context
|
||||
{
|
||||
public:
|
||||
Context(const WindowInfo& wi);
|
||||
virtual ~Context();
|
||||
|
||||
enum class Profile
|
||||
{
|
||||
NoProfile,
|
||||
Core,
|
||||
ES
|
||||
};
|
||||
|
||||
struct Version
|
||||
{
|
||||
Profile profile;
|
||||
int major_version;
|
||||
int minor_version;
|
||||
};
|
||||
|
||||
struct FullscreenModeInfo
|
||||
{
|
||||
u32 width;
|
||||
u32 height;
|
||||
float refresh_rate;
|
||||
};
|
||||
|
||||
__fi const WindowInfo& GetWindowInfo() const { return m_wi; }
|
||||
__fi bool IsGLES() const { return (m_version.profile == Profile::ES); }
|
||||
__fi u32 GetSurfaceWidth() const { return m_wi.surface_width; }
|
||||
__fi u32 GetSurfaceHeight() const { return m_wi.surface_height; }
|
||||
|
||||
virtual void* GetProcAddress(const char* name) = 0;
|
||||
virtual bool ChangeSurface(const WindowInfo& new_wi) = 0;
|
||||
virtual void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) = 0;
|
||||
virtual bool SwapBuffers() = 0;
|
||||
virtual bool MakeCurrent() = 0;
|
||||
virtual bool DoneCurrent() = 0;
|
||||
virtual bool SetSwapInterval(s32 interval) = 0;
|
||||
virtual std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) = 0;
|
||||
|
||||
virtual std::vector<FullscreenModeInfo> EnumerateFullscreenModes();
|
||||
|
||||
static std::unique_ptr<Context> Create(const WindowInfo& wi, gsl::span<const Version> versions_to_try);
|
||||
|
||||
static std::unique_ptr<Context> Create(const WindowInfo& wi) { return Create(wi, GetAllVersionsList()); }
|
||||
|
||||
static gsl::span<const Version> GetAllVersionsList();
|
||||
|
||||
protected:
|
||||
WindowInfo m_wi;
|
||||
Version m_version = {};
|
||||
};
|
||||
} // namespace GL
|
||||
@@ -1,62 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "common/GL/Context.h"
|
||||
#include "glad.h"
|
||||
|
||||
#if defined(__APPLE__) && defined(__OBJC__)
|
||||
#import <AppKit/AppKit.h>
|
||||
#else
|
||||
struct NSView;
|
||||
struct NSOpenGLContext;
|
||||
struct NSOpenGLPixelFormat;
|
||||
#endif
|
||||
|
||||
namespace GL
|
||||
{
|
||||
class ContextAGL final : public Context
|
||||
{
|
||||
public:
|
||||
ContextAGL(const WindowInfo& wi);
|
||||
~ContextAGL() override;
|
||||
|
||||
static std::unique_ptr<Context> Create(const WindowInfo& wi, gsl::span<const Version> versions_to_try);
|
||||
|
||||
void* GetProcAddress(const char* name) override;
|
||||
bool ChangeSurface(const WindowInfo& new_wi) override;
|
||||
void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
|
||||
bool SwapBuffers() override;
|
||||
bool MakeCurrent() override;
|
||||
bool DoneCurrent() override;
|
||||
bool SetSwapInterval(s32 interval) override;
|
||||
std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
|
||||
|
||||
private:
|
||||
bool Initialize(gsl::span<const Version> versions_to_try);
|
||||
bool CreateContext(NSOpenGLContext* share_context, int profile, bool make_current);
|
||||
void BindContextToView();
|
||||
void CleanupView();
|
||||
|
||||
// returns true if dimensions have changed
|
||||
bool UpdateDimensions();
|
||||
|
||||
NSView* m_view = nullptr;
|
||||
NSOpenGLContext* m_context = nullptr;
|
||||
NSOpenGLPixelFormat* m_pixel_format = nullptr;
|
||||
void* m_opengl_module_handle = nullptr;
|
||||
};
|
||||
|
||||
} // namespace GL
|
||||
@@ -1,243 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/GL/ContextAGL.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
#include "glad.h"
|
||||
#include <dlfcn.h>
|
||||
|
||||
#if ! __has_feature(objc_arc)
|
||||
#error "Compile this with -fobjc-arc"
|
||||
#endif
|
||||
|
||||
namespace GL
|
||||
{
|
||||
ContextAGL::ContextAGL(const WindowInfo& wi)
|
||||
: Context(wi)
|
||||
{
|
||||
m_opengl_module_handle = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL", RTLD_NOW);
|
||||
if (!m_opengl_module_handle)
|
||||
Console.Error("Could not open OpenGL.framework, function lookups will probably fail");
|
||||
}
|
||||
|
||||
ContextAGL::~ContextAGL()
|
||||
{
|
||||
if ([NSOpenGLContext currentContext] == m_context)
|
||||
[NSOpenGLContext clearCurrentContext];
|
||||
|
||||
CleanupView();
|
||||
|
||||
if (m_opengl_module_handle)
|
||||
dlclose(m_opengl_module_handle);
|
||||
}
|
||||
|
||||
std::unique_ptr<Context> ContextAGL::Create(const WindowInfo& wi, gsl::span<const Version> versions_to_try)
|
||||
{
|
||||
std::unique_ptr<ContextAGL> context = std::make_unique<ContextAGL>(wi);
|
||||
if (!context->Initialize(versions_to_try))
|
||||
return nullptr;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
bool ContextAGL::Initialize(gsl::span<const Version> versions_to_try)
|
||||
{
|
||||
for (const Version& cv : versions_to_try)
|
||||
{
|
||||
if (cv.profile == Profile::NoProfile && CreateContext(nullptr, NSOpenGLProfileVersionLegacy, true))
|
||||
{
|
||||
// we already have the dummy context, so just use that
|
||||
m_version = cv;
|
||||
return true;
|
||||
}
|
||||
else if (cv.profile == Profile::Core)
|
||||
{
|
||||
if (cv.major_version > 4 || cv.minor_version > 1)
|
||||
continue;
|
||||
|
||||
const NSOpenGLPixelFormatAttribute profile = (cv.major_version > 3 || cv.minor_version > 2) ? NSOpenGLProfileVersion4_1Core : NSOpenGLProfileVersion3_2Core;
|
||||
if (CreateContext(nullptr, static_cast<int>(profile), true))
|
||||
{
|
||||
m_version = cv;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void* ContextAGL::GetProcAddress(const char* name)
|
||||
{
|
||||
void* addr = m_opengl_module_handle ? dlsym(m_opengl_module_handle, name) : nullptr;
|
||||
if (addr)
|
||||
return addr;
|
||||
|
||||
return dlsym(RTLD_NEXT, name);
|
||||
}
|
||||
|
||||
bool ContextAGL::ChangeSurface(const WindowInfo& new_wi)
|
||||
{
|
||||
m_wi = new_wi;
|
||||
BindContextToView();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ContextAGL::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/)
|
||||
{
|
||||
UpdateDimensions();
|
||||
}
|
||||
|
||||
bool ContextAGL::UpdateDimensions()
|
||||
{
|
||||
if (![NSThread isMainThread])
|
||||
{
|
||||
bool ret;
|
||||
dispatch_sync(dispatch_get_main_queue(), [this, &ret]{ ret = UpdateDimensions(); });
|
||||
return ret;
|
||||
}
|
||||
|
||||
const NSSize window_size = [m_view frame].size;
|
||||
const CGFloat window_scale = [[m_view window] backingScaleFactor];
|
||||
const u32 new_width = static_cast<u32>(window_size.width * window_scale);
|
||||
const u32 new_height = static_cast<u32>(window_size.height * window_scale);
|
||||
|
||||
if (m_wi.surface_width == new_width && m_wi.surface_height == new_height)
|
||||
return false;
|
||||
|
||||
m_wi.surface_width = new_width;
|
||||
m_wi.surface_height = new_height;
|
||||
|
||||
[m_context update];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContextAGL::SwapBuffers()
|
||||
{
|
||||
[m_context flushBuffer];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContextAGL::MakeCurrent()
|
||||
{
|
||||
[m_context makeCurrentContext];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContextAGL::DoneCurrent()
|
||||
{
|
||||
[NSOpenGLContext clearCurrentContext];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContextAGL::SetSwapInterval(s32 interval)
|
||||
{
|
||||
GLint gl_interval = static_cast<GLint>(interval);
|
||||
[m_context setValues:&gl_interval forParameter:NSOpenGLCPSwapInterval];
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<Context> ContextAGL::CreateSharedContext(const WindowInfo& wi)
|
||||
{
|
||||
std::unique_ptr<ContextAGL> context = std::make_unique<ContextAGL>(wi);
|
||||
|
||||
context->m_context = [[NSOpenGLContext alloc] initWithFormat:m_pixel_format shareContext:m_context];
|
||||
if (context->m_context == nil)
|
||||
return nullptr;
|
||||
|
||||
context->m_version = m_version;
|
||||
context->m_pixel_format = m_pixel_format;
|
||||
|
||||
if (wi.type == WindowInfo::Type::MacOS)
|
||||
context->BindContextToView();
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
bool ContextAGL::CreateContext(NSOpenGLContext* share_context, int profile, bool make_current)
|
||||
{
|
||||
if (m_context)
|
||||
m_context = nullptr;
|
||||
|
||||
const NSOpenGLPixelFormatAttribute attribs[] = {
|
||||
NSOpenGLPFADoubleBuffer,
|
||||
NSOpenGLPFAOpenGLProfile, static_cast<NSOpenGLPixelFormatAttribute>(profile),
|
||||
NSOpenGLPFAAccelerated,
|
||||
0
|
||||
};
|
||||
|
||||
m_pixel_format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
|
||||
if (m_pixel_format == nil)
|
||||
{
|
||||
Console.Error("(ContextAGL) Failed to initialize pixel format");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_context = [[NSOpenGLContext alloc] initWithFormat:m_pixel_format shareContext:nil];
|
||||
if (m_context == nil)
|
||||
return false;
|
||||
|
||||
if (m_wi.type == WindowInfo::Type::MacOS)
|
||||
BindContextToView();
|
||||
|
||||
if (make_current)
|
||||
[m_context makeCurrentContext];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ContextAGL::BindContextToView()
|
||||
{
|
||||
if (![NSThread isMainThread])
|
||||
{
|
||||
dispatch_sync(dispatch_get_main_queue(), [this]{ BindContextToView(); });
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef PCSX2_CORE
|
||||
m_view = (__bridge NSView*)m_wi.window_handle;
|
||||
#else
|
||||
// Drawing to wx's wxView somehow causes fighting between us and wx, resulting in massive CPU usage on the main thread and no image
|
||||
// Avoid that by adding our own subview
|
||||
CleanupView();
|
||||
NSView* const superview = (__bridge NSView*)m_wi.window_handle;
|
||||
m_view = [[NSView alloc] initWithFrame:[superview frame]];
|
||||
[superview addSubview:m_view];
|
||||
[m_view setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
|
||||
#endif
|
||||
[m_view setWantsBestResolutionOpenGLSurface:YES];
|
||||
|
||||
UpdateDimensions();
|
||||
|
||||
[m_context setView:m_view];
|
||||
}
|
||||
|
||||
void ContextAGL::CleanupView()
|
||||
{
|
||||
#ifndef PCSX2_CORE
|
||||
if (![NSThread isMainThread])
|
||||
{
|
||||
dispatch_sync(dispatch_get_main_queue(), [this]{ CleanupView(); });
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_view)
|
||||
[m_view removeFromSuperview];
|
||||
#endif
|
||||
m_view = nullptr;
|
||||
}
|
||||
} // namespace GL
|
||||
@@ -1,391 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/PrecompiledHeader.h"
|
||||
|
||||
#include "common/Console.h"
|
||||
#include "ContextEGL.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace GL
|
||||
{
|
||||
ContextEGL::ContextEGL(const WindowInfo& wi)
|
||||
: Context(wi)
|
||||
{
|
||||
}
|
||||
|
||||
ContextEGL::~ContextEGL()
|
||||
{
|
||||
DestroySurface();
|
||||
DestroyContext();
|
||||
}
|
||||
|
||||
std::unique_ptr<Context> ContextEGL::Create(const WindowInfo& wi, gsl::span<const Version> versions_to_try)
|
||||
{
|
||||
std::unique_ptr<ContextEGL> context = std::make_unique<ContextEGL>(wi);
|
||||
if (!context->Initialize(versions_to_try))
|
||||
return nullptr;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
bool ContextEGL::Initialize(gsl::span<const Version> versions_to_try)
|
||||
{
|
||||
if (!gladLoadEGL())
|
||||
{
|
||||
Console.Error("Loading GLAD EGL functions failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetDisplay())
|
||||
return false;
|
||||
|
||||
int egl_major, egl_minor;
|
||||
if (!eglInitialize(m_display, &egl_major, &egl_minor))
|
||||
{
|
||||
Console.Error("eglInitialize() failed: %d", eglGetError());
|
||||
return false;
|
||||
}
|
||||
Console.WriteLn("EGL Version: %d.%d", egl_major, egl_minor);
|
||||
|
||||
const char* extensions = eglQueryString(m_display, EGL_EXTENSIONS);
|
||||
if (extensions)
|
||||
{
|
||||
Console.WriteLn("EGL Extensions: %s", extensions);
|
||||
m_supports_surfaceless = std::strstr(extensions, "EGL_KHR_surfaceless_context") != nullptr;
|
||||
}
|
||||
if (!m_supports_surfaceless)
|
||||
Console.Warning("EGL implementation does not support surfaceless contexts, emulating with pbuffers");
|
||||
|
||||
for (const Version& version : versions_to_try)
|
||||
{
|
||||
if (CreateContextAndSurface(version, nullptr, true))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ContextEGL::SetDisplay()
|
||||
{
|
||||
m_display = eglGetDisplay(static_cast<EGLNativeDisplayType>(m_wi.display_connection));
|
||||
if (!m_display)
|
||||
{
|
||||
Console.Error("eglGetDisplay() failed: %d", eglGetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void* ContextEGL::GetProcAddress(const char* name)
|
||||
{
|
||||
return reinterpret_cast<void*>(eglGetProcAddress(name));
|
||||
}
|
||||
|
||||
bool ContextEGL::ChangeSurface(const WindowInfo& new_wi)
|
||||
{
|
||||
const bool was_current = (eglGetCurrentContext() == m_context);
|
||||
if (was_current)
|
||||
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
|
||||
if (m_surface != EGL_NO_SURFACE)
|
||||
{
|
||||
eglDestroySurface(m_display, m_surface);
|
||||
m_surface = EGL_NO_SURFACE;
|
||||
}
|
||||
|
||||
m_wi = new_wi;
|
||||
if (!CreateSurface())
|
||||
return false;
|
||||
|
||||
if (was_current && !eglMakeCurrent(m_display, m_surface, m_surface, m_context))
|
||||
{
|
||||
Console.Error("Failed to make context current again after surface change");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ContextEGL::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/)
|
||||
{
|
||||
if (new_surface_width == 0 && new_surface_height == 0)
|
||||
{
|
||||
EGLint surface_width, surface_height;
|
||||
if (eglQuerySurface(m_display, m_surface, EGL_WIDTH, &surface_width) &&
|
||||
eglQuerySurface(m_display, m_surface, EGL_HEIGHT, &surface_height))
|
||||
{
|
||||
m_wi.surface_width = static_cast<u32>(surface_width);
|
||||
m_wi.surface_height = static_cast<u32>(surface_height);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error("eglQuerySurface() failed: %d", eglGetError());
|
||||
}
|
||||
}
|
||||
|
||||
m_wi.surface_width = new_surface_width;
|
||||
m_wi.surface_height = new_surface_height;
|
||||
}
|
||||
|
||||
bool ContextEGL::SwapBuffers()
|
||||
{
|
||||
return eglSwapBuffers(m_display, m_surface);
|
||||
}
|
||||
|
||||
bool ContextEGL::MakeCurrent()
|
||||
{
|
||||
if (!eglMakeCurrent(m_display, m_surface, m_surface, m_context))
|
||||
{
|
||||
Console.Error("eglMakeCurrent() failed: %d", eglGetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContextEGL::DoneCurrent()
|
||||
{
|
||||
return eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
}
|
||||
|
||||
bool ContextEGL::SetSwapInterval(s32 interval)
|
||||
{
|
||||
return eglSwapInterval(m_display, interval);
|
||||
}
|
||||
|
||||
std::unique_ptr<Context> ContextEGL::CreateSharedContext(const WindowInfo& wi)
|
||||
{
|
||||
std::unique_ptr<ContextEGL> context = std::make_unique<ContextEGL>(wi);
|
||||
context->m_display = m_display;
|
||||
context->m_supports_surfaceless = m_supports_surfaceless;
|
||||
|
||||
if (!context->CreateContextAndSurface(m_version, m_context, false))
|
||||
return nullptr;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
EGLNativeWindowType ContextEGL::GetNativeWindow(EGLConfig config)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
bool ContextEGL::CreateSurface()
|
||||
{
|
||||
if (m_wi.type == WindowInfo::Type::Surfaceless)
|
||||
{
|
||||
if (m_supports_surfaceless)
|
||||
return true;
|
||||
else
|
||||
return CreatePBufferSurface();
|
||||
}
|
||||
|
||||
EGLNativeWindowType native_window = GetNativeWindow(m_config);
|
||||
m_surface = eglCreateWindowSurface(m_display, m_config, native_window, nullptr);
|
||||
if (!m_surface)
|
||||
{
|
||||
Console.Error("eglCreateWindowSurface() failed: %d", eglGetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Some implementations may require the size to be queried at runtime.
|
||||
EGLint surface_width, surface_height;
|
||||
if (eglQuerySurface(m_display, m_surface, EGL_WIDTH, &surface_width) &&
|
||||
eglQuerySurface(m_display, m_surface, EGL_HEIGHT, &surface_height))
|
||||
{
|
||||
m_wi.surface_width = static_cast<u32>(surface_width);
|
||||
m_wi.surface_height = static_cast<u32>(surface_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error("eglQuerySurface() failed: %d", eglGetError());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContextEGL::CreatePBufferSurface()
|
||||
{
|
||||
const u32 width = std::max<u32>(m_wi.surface_width, 1);
|
||||
const u32 height = std::max<u32>(m_wi.surface_height, 1);
|
||||
|
||||
EGLint attrib_list[] = {
|
||||
EGL_WIDTH, static_cast<EGLint>(width),
|
||||
EGL_HEIGHT, static_cast<EGLint>(height),
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
m_surface = eglCreatePbufferSurface(m_display, m_config, attrib_list);
|
||||
if (!m_surface)
|
||||
{
|
||||
Console.Error("eglCreatePbufferSurface() failed: %d", eglGetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
Console.WriteLn("Created %ux%u pbuffer surface", width, height);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContextEGL::CheckConfigSurfaceFormat(EGLConfig config) const
|
||||
{
|
||||
int red_size, green_size, blue_size;
|
||||
if (!eglGetConfigAttrib(m_display, config, EGL_RED_SIZE, &red_size) ||
|
||||
!eglGetConfigAttrib(m_display, config, EGL_GREEN_SIZE, &green_size) ||
|
||||
!eglGetConfigAttrib(m_display, config, EGL_BLUE_SIZE, &blue_size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (red_size == 8 && green_size == 8 && blue_size == 8);
|
||||
}
|
||||
|
||||
void ContextEGL::DestroyContext()
|
||||
{
|
||||
if (eglGetCurrentContext() == m_context)
|
||||
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
|
||||
if (m_context != EGL_NO_CONTEXT)
|
||||
{
|
||||
eglDestroyContext(m_display, m_context);
|
||||
m_context = EGL_NO_CONTEXT;
|
||||
}
|
||||
}
|
||||
|
||||
void ContextEGL::DestroySurface()
|
||||
{
|
||||
if (eglGetCurrentSurface(EGL_DRAW) == m_surface)
|
||||
eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
|
||||
if (m_surface != EGL_NO_SURFACE)
|
||||
{
|
||||
eglDestroySurface(m_display, m_surface);
|
||||
m_surface = EGL_NO_SURFACE;
|
||||
}
|
||||
}
|
||||
|
||||
bool ContextEGL::CreateContext(const Version& version, EGLContext share_context)
|
||||
{
|
||||
Console.WriteLn(
|
||||
"Trying version %u.%u (%s)", version.major_version, version.minor_version,
|
||||
version.profile == Context::Profile::ES ? "ES" : (version.profile == Context::Profile::Core ? "Core" : "None"));
|
||||
|
||||
const int renderable_type = version.profile == Profile::ES
|
||||
? ((version.major_version >= 3) ? EGL_OPENGL_ES3_BIT : ((version.major_version == 2) ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_ES_BIT))
|
||||
: EGL_OPENGL_BIT;
|
||||
const int surface_attribs[] = {
|
||||
EGL_RENDERABLE_TYPE, renderable_type,
|
||||
EGL_SURFACE_TYPE, (m_wi.type != WindowInfo::Type::Surfaceless) ? EGL_WINDOW_BIT : 0,
|
||||
EGL_RED_SIZE, 8,
|
||||
EGL_GREEN_SIZE, 8,
|
||||
EGL_BLUE_SIZE, 8,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
EGLint num_configs;
|
||||
if (!eglChooseConfig(m_display, surface_attribs, nullptr, 0, &num_configs) || num_configs == 0)
|
||||
{
|
||||
Console.Error("eglChooseConfig() failed: %d", eglGetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<EGLConfig> configs(static_cast<u32>(num_configs));
|
||||
if (!eglChooseConfig(m_display, surface_attribs, configs.data(), num_configs, &num_configs))
|
||||
{
|
||||
Console.Error("eglChooseConfig() failed: %d", eglGetError());
|
||||
return false;
|
||||
}
|
||||
configs.resize(static_cast<u32>(num_configs));
|
||||
|
||||
m_config = [this, &configs]() {
|
||||
const auto found_config = std::find_if(std::begin(configs), std::end(configs), [&](const auto& check_config) {
|
||||
return CheckConfigSurfaceFormat(check_config);
|
||||
});
|
||||
if (found_config == std::end(configs))
|
||||
{
|
||||
Console.Warning("No EGL configs matched exactly, using first.");
|
||||
return configs.front();
|
||||
}
|
||||
else
|
||||
{
|
||||
return *found_config;
|
||||
}
|
||||
}();
|
||||
|
||||
const auto attribs = [version]() -> std::array<int, 8> {
|
||||
if (version.profile != Profile::NoProfile)
|
||||
return {
|
||||
EGL_CONTEXT_MAJOR_VERSION, version.major_version,
|
||||
EGL_CONTEXT_MINOR_VERSION, version.minor_version,
|
||||
EGL_NONE
|
||||
};
|
||||
return {EGL_NONE};
|
||||
}();
|
||||
|
||||
if (!eglBindAPI((version.profile == Profile::ES) ? EGL_OPENGL_ES_API : EGL_OPENGL_API))
|
||||
{
|
||||
Console.Error("eglBindAPI(%s) failed", (version.profile == Profile::ES) ? "EGL_OPENGL_ES_API" : "EGL_OPENGL_API");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_context = eglCreateContext(m_display, m_config, share_context, attribs.data());
|
||||
if (!m_context)
|
||||
{
|
||||
Console.Error("eglCreateContext() failed: %d", eglGetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
Console.WriteLn(
|
||||
"Got version %u.%u (%s)", version.major_version, version.minor_version,
|
||||
version.profile == Context::Profile::ES ? "ES" : (version.profile == Context::Profile::Core ? "Core" : "None"));
|
||||
|
||||
m_version = version;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContextEGL::CreateContextAndSurface(const Version& version, EGLContext share_context, bool make_current)
|
||||
{
|
||||
if (!CreateContext(version, share_context))
|
||||
return false;
|
||||
|
||||
if (!CreateSurface())
|
||||
{
|
||||
Console.Error("Failed to create surface for context");
|
||||
eglDestroyContext(m_display, m_context);
|
||||
m_context = EGL_NO_CONTEXT;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (make_current && !eglMakeCurrent(m_display, m_surface, m_surface, m_context))
|
||||
{
|
||||
Console.Error("eglMakeCurrent() failed: %d", eglGetError());
|
||||
if (m_surface != EGL_NO_SURFACE)
|
||||
{
|
||||
eglDestroySurface(m_display, m_surface);
|
||||
m_surface = EGL_NO_SURFACE;
|
||||
}
|
||||
eglDestroyContext(m_display, m_context);
|
||||
m_context = EGL_NO_CONTEXT;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace GL
|
||||
@@ -1,63 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/GL/Context.h"
|
||||
#include "glad_egl.h"
|
||||
|
||||
namespace GL
|
||||
{
|
||||
class ContextEGL : public Context
|
||||
{
|
||||
public:
|
||||
ContextEGL(const WindowInfo& wi);
|
||||
~ContextEGL() override;
|
||||
|
||||
static std::unique_ptr<Context> Create(const WindowInfo& wi, gsl::span<const Version> versions_to_try);
|
||||
|
||||
void* GetProcAddress(const char* name) override;
|
||||
virtual bool ChangeSurface(const WindowInfo& new_wi) override;
|
||||
virtual void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
|
||||
bool SwapBuffers() override;
|
||||
bool MakeCurrent() override;
|
||||
bool DoneCurrent() override;
|
||||
bool SetSwapInterval(s32 interval) override;
|
||||
virtual std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
|
||||
|
||||
protected:
|
||||
virtual bool SetDisplay();
|
||||
virtual EGLNativeWindowType GetNativeWindow(EGLConfig config);
|
||||
|
||||
bool Initialize(gsl::span<const Version> versions_to_try);
|
||||
bool CreateDisplay();
|
||||
bool CreateContext(const Version& version, EGLContext share_context);
|
||||
bool CreateContextAndSurface(const Version& version, EGLContext share_context, bool make_current);
|
||||
bool CreateSurface();
|
||||
bool CreatePBufferSurface();
|
||||
bool CheckConfigSurfaceFormat(EGLConfig config) const;
|
||||
void DestroyContext();
|
||||
void DestroySurface();
|
||||
|
||||
EGLDisplay m_display = EGL_NO_DISPLAY;
|
||||
EGLSurface m_surface = EGL_NO_SURFACE;
|
||||
EGLContext m_context = EGL_NO_CONTEXT;
|
||||
|
||||
EGLConfig m_config = {};
|
||||
|
||||
bool m_supports_surfaceless = false;
|
||||
};
|
||||
|
||||
} // namespace GL
|
||||
@@ -1,104 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/PrecompiledHeader.h"
|
||||
#include "common/Console.h"
|
||||
|
||||
#include "ContextEGLWayland.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
namespace GL
|
||||
{
|
||||
static const char* WAYLAND_EGL_MODNAME = "libwayland-egl.so.1";
|
||||
|
||||
ContextEGLWayland::ContextEGLWayland(const WindowInfo& wi)
|
||||
: ContextEGL(wi)
|
||||
{
|
||||
}
|
||||
|
||||
ContextEGLWayland::~ContextEGLWayland()
|
||||
{
|
||||
if (m_wl_window)
|
||||
m_wl_egl_window_destroy(m_wl_window);
|
||||
if (m_wl_module)
|
||||
dlclose(m_wl_module);
|
||||
}
|
||||
|
||||
std::unique_ptr<Context> ContextEGLWayland::Create(const WindowInfo& wi, gsl::span<const Version> versions_to_try)
|
||||
{
|
||||
std::unique_ptr<ContextEGLWayland> context = std::make_unique<ContextEGLWayland>(wi);
|
||||
if (!context->LoadModule() || !context->Initialize(versions_to_try))
|
||||
return nullptr;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
std::unique_ptr<Context> ContextEGLWayland::CreateSharedContext(const WindowInfo& wi)
|
||||
{
|
||||
std::unique_ptr<ContextEGLWayland> context = std::make_unique<ContextEGLWayland>(wi);
|
||||
context->m_display = m_display;
|
||||
|
||||
if (!context->LoadModule() || !context->CreateContextAndSurface(m_version, m_context, false))
|
||||
return nullptr;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
void ContextEGLWayland::ResizeSurface(u32 new_surface_width, u32 new_surface_height)
|
||||
{
|
||||
if (m_wl_window)
|
||||
m_wl_egl_window_resize(m_wl_window, new_surface_width, new_surface_height, 0, 0);
|
||||
|
||||
ContextEGL::ResizeSurface(new_surface_width, new_surface_height);
|
||||
}
|
||||
|
||||
EGLNativeWindowType ContextEGLWayland::GetNativeWindow(EGLConfig config)
|
||||
{
|
||||
if (m_wl_window)
|
||||
{
|
||||
m_wl_egl_window_destroy(m_wl_window);
|
||||
m_wl_window = nullptr;
|
||||
}
|
||||
|
||||
m_wl_window =
|
||||
m_wl_egl_window_create(static_cast<wl_surface*>(m_wi.window_handle), m_wi.surface_width, m_wi.surface_height);
|
||||
if (!m_wl_window)
|
||||
return {};
|
||||
|
||||
return reinterpret_cast<EGLNativeWindowType>(m_wl_window);
|
||||
}
|
||||
|
||||
bool ContextEGLWayland::LoadModule()
|
||||
{
|
||||
m_wl_module = dlopen(WAYLAND_EGL_MODNAME, RTLD_NOW | RTLD_GLOBAL);
|
||||
if (!m_wl_module)
|
||||
{
|
||||
Console.Error("Failed to load %s.", WAYLAND_EGL_MODNAME);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_wl_egl_window_create = reinterpret_cast<decltype(m_wl_egl_window_create)>(dlsym(m_wl_module, "wl_egl_window_create"));
|
||||
m_wl_egl_window_destroy = reinterpret_cast<decltype(m_wl_egl_window_destroy)>(dlsym(m_wl_module, "wl_egl_window_destroy"));
|
||||
m_wl_egl_window_resize = reinterpret_cast<decltype(m_wl_egl_window_resize)>(dlsym(m_wl_module, "wl_egl_window_resize"));
|
||||
if (!m_wl_egl_window_create || !m_wl_egl_window_destroy || !m_wl_egl_window_resize)
|
||||
{
|
||||
Console.Error("Failed to load one or more functions from %s.", WAYLAND_EGL_MODNAME);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace GL
|
||||
@@ -1,49 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/GL/ContextEGL.h"
|
||||
|
||||
struct wl_egl_window;
|
||||
struct wl_surface;
|
||||
|
||||
namespace GL
|
||||
{
|
||||
class ContextEGLWayland final : public ContextEGL
|
||||
{
|
||||
public:
|
||||
ContextEGLWayland(const WindowInfo& wi);
|
||||
~ContextEGLWayland() override;
|
||||
|
||||
static std::unique_ptr<Context> Create(const WindowInfo& wi, gsl::span<const Version> versions_to_try);
|
||||
|
||||
std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
|
||||
void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
|
||||
|
||||
protected:
|
||||
EGLNativeWindowType GetNativeWindow(EGLConfig config) override;
|
||||
|
||||
private:
|
||||
bool LoadModule();
|
||||
|
||||
wl_egl_window* m_wl_window = nullptr;
|
||||
|
||||
void* m_wl_module = nullptr;
|
||||
wl_egl_window* (*m_wl_egl_window_create)(struct wl_surface* surface, int width, int height);
|
||||
void (*m_wl_egl_window_destroy)(struct wl_egl_window* egl_window);
|
||||
void (*m_wl_egl_window_resize)(struct wl_egl_window* egl_window, int width, int height, int dx, int dy);
|
||||
};
|
||||
} // namespace GL
|
||||
@@ -1,59 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/PrecompiledHeader.h"
|
||||
|
||||
#include "common/GL/ContextEGLX11.h"
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
namespace GL
|
||||
{
|
||||
ContextEGLX11::ContextEGLX11(const WindowInfo& wi)
|
||||
: ContextEGL(wi)
|
||||
{
|
||||
}
|
||||
ContextEGLX11::~ContextEGLX11() = default;
|
||||
|
||||
std::unique_ptr<Context> ContextEGLX11::Create(const WindowInfo& wi, gsl::span<const Version> versions_to_try)
|
||||
{
|
||||
std::unique_ptr<ContextEGLX11> context = std::make_unique<ContextEGLX11>(wi);
|
||||
if (!context->Initialize(versions_to_try))
|
||||
return nullptr;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
std::unique_ptr<Context> ContextEGLX11::CreateSharedContext(const WindowInfo& wi)
|
||||
{
|
||||
std::unique_ptr<ContextEGLX11> context = std::make_unique<ContextEGLX11>(wi);
|
||||
context->m_display = m_display;
|
||||
|
||||
if (!context->CreateContextAndSurface(m_version, m_context, false))
|
||||
return nullptr;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
void ContextEGLX11::ResizeSurface(u32 new_surface_width, u32 new_surface_height)
|
||||
{
|
||||
ContextEGL::ResizeSurface(new_surface_width, new_surface_height);
|
||||
}
|
||||
|
||||
EGLNativeWindowType ContextEGLX11::GetNativeWindow(EGLConfig config)
|
||||
{
|
||||
return (EGLNativeWindowType)reinterpret_cast<Window>(m_wi.window_handle);
|
||||
}
|
||||
} // namespace GL
|
||||
@@ -1,487 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/GL/ContextWGL.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/ScopedGuard.h"
|
||||
|
||||
static void* GetProcAddressCallback(const char* name)
|
||||
{
|
||||
void* addr = reinterpret_cast<void*>(wglGetProcAddress(name));
|
||||
if (addr)
|
||||
return addr;
|
||||
|
||||
// try opengl32.dll
|
||||
return reinterpret_cast<void*>(::GetProcAddress(GetModuleHandleA("opengl32.dll"), name));
|
||||
}
|
||||
|
||||
static bool ReloadWGL(HDC dc)
|
||||
{
|
||||
if (!gladLoadWGLLoader([](const char* name) -> void* { return reinterpret_cast<void*>(wglGetProcAddress(name)); }, dc))
|
||||
{
|
||||
Console.Error("Loading GLAD WGL functions failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace GL
|
||||
{
|
||||
ContextWGL::ContextWGL(const WindowInfo& wi)
|
||||
: Context(wi)
|
||||
{
|
||||
}
|
||||
|
||||
ContextWGL::~ContextWGL()
|
||||
{
|
||||
if (wglGetCurrentContext() == m_rc)
|
||||
wglMakeCurrent(m_dc, nullptr);
|
||||
|
||||
if (m_rc)
|
||||
wglDeleteContext(m_rc);
|
||||
|
||||
ReleaseDC();
|
||||
}
|
||||
|
||||
std::unique_ptr<Context> ContextWGL::Create(const WindowInfo& wi, gsl::span<const Version> versions_to_try)
|
||||
{
|
||||
std::unique_ptr<ContextWGL> context = std::make_unique<ContextWGL>(wi);
|
||||
if (!context->Initialize(versions_to_try))
|
||||
return nullptr;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
bool ContextWGL::Initialize(gsl::span<const Version> versions_to_try)
|
||||
{
|
||||
if (m_wi.type == WindowInfo::Type::Win32)
|
||||
{
|
||||
if (!InitializeDC())
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!CreatePBuffer())
|
||||
return false;
|
||||
}
|
||||
|
||||
// Everything including core/ES requires a dummy profile to load the WGL extensions.
|
||||
if (!CreateAnyContext(nullptr, true))
|
||||
return false;
|
||||
|
||||
for (const Version& cv : versions_to_try)
|
||||
{
|
||||
if (cv.profile == Profile::NoProfile)
|
||||
{
|
||||
// we already have the dummy context, so just use that
|
||||
m_version = cv;
|
||||
return true;
|
||||
}
|
||||
else if (CreateVersionContext(cv, nullptr, true))
|
||||
{
|
||||
m_version = cv;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void* ContextWGL::GetProcAddress(const char* name)
|
||||
{
|
||||
return GetProcAddressCallback(name);
|
||||
}
|
||||
|
||||
bool ContextWGL::ChangeSurface(const WindowInfo& new_wi)
|
||||
{
|
||||
const bool was_current = (wglGetCurrentContext() == m_rc);
|
||||
|
||||
ReleaseDC();
|
||||
|
||||
m_wi = new_wi;
|
||||
if (!InitializeDC())
|
||||
return false;
|
||||
|
||||
if (was_current && !wglMakeCurrent(m_dc, m_rc))
|
||||
{
|
||||
Console.Error("Failed to make context current again after surface change: 0x%08X", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ContextWGL::ResizeSurface(u32 new_surface_width /*= 0*/, u32 new_surface_height /*= 0*/)
|
||||
{
|
||||
RECT client_rc = {};
|
||||
GetClientRect(GetHWND(), &client_rc);
|
||||
m_wi.surface_width = static_cast<u32>(client_rc.right - client_rc.left);
|
||||
m_wi.surface_height = static_cast<u32>(client_rc.bottom - client_rc.top);
|
||||
}
|
||||
|
||||
bool ContextWGL::SwapBuffers()
|
||||
{
|
||||
return ::SwapBuffers(m_dc);
|
||||
}
|
||||
|
||||
bool ContextWGL::MakeCurrent()
|
||||
{
|
||||
if (!wglMakeCurrent(m_dc, m_rc))
|
||||
{
|
||||
Console.Error("wglMakeCurrent() failed: 0x%08X", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContextWGL::DoneCurrent()
|
||||
{
|
||||
return wglMakeCurrent(m_dc, nullptr);
|
||||
}
|
||||
|
||||
bool ContextWGL::SetSwapInterval(s32 interval)
|
||||
{
|
||||
if (!GLAD_WGL_EXT_swap_control)
|
||||
return false;
|
||||
|
||||
return wglSwapIntervalEXT(interval);
|
||||
}
|
||||
|
||||
std::unique_ptr<Context> ContextWGL::CreateSharedContext(const WindowInfo& wi)
|
||||
{
|
||||
std::unique_ptr<ContextWGL> context = std::make_unique<ContextWGL>(wi);
|
||||
if (wi.type == WindowInfo::Type::Win32)
|
||||
{
|
||||
if (!context->InitializeDC())
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!context->CreatePBuffer())
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (m_version.profile == Profile::NoProfile)
|
||||
{
|
||||
if (!context->CreateAnyContext(m_rc, false))
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!context->CreateVersionContext(m_version, m_rc, false))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
context->m_version = m_version;
|
||||
return context;
|
||||
}
|
||||
|
||||
HDC ContextWGL::GetDCAndSetPixelFormat(HWND hwnd)
|
||||
{
|
||||
PIXELFORMATDESCRIPTOR pfd = {};
|
||||
pfd.nSize = sizeof(pfd);
|
||||
pfd.nVersion = 1;
|
||||
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
|
||||
pfd.iPixelType = PFD_TYPE_RGBA;
|
||||
pfd.dwLayerMask = PFD_MAIN_PLANE;
|
||||
pfd.cRedBits = 8;
|
||||
pfd.cGreenBits = 8;
|
||||
pfd.cBlueBits = 8;
|
||||
pfd.cColorBits = 24;
|
||||
|
||||
HDC hDC = ::GetDC(hwnd);
|
||||
if (!hDC)
|
||||
{
|
||||
Console.Error("GetDC() failed: 0x%08X", GetLastError());
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!m_pixel_format.has_value())
|
||||
{
|
||||
const int pf = ChoosePixelFormat(hDC, &pfd);
|
||||
if (pf == 0)
|
||||
{
|
||||
Console.Error("ChoosePixelFormat() failed: 0x%08X", GetLastError());
|
||||
::ReleaseDC(hwnd, hDC);
|
||||
return {};
|
||||
}
|
||||
|
||||
m_pixel_format = pf;
|
||||
}
|
||||
|
||||
if (!SetPixelFormat(hDC, m_pixel_format.value(), &pfd))
|
||||
{
|
||||
Console.Error("SetPixelFormat() failed: 0x%08X", GetLastError());
|
||||
::ReleaseDC(hwnd, hDC);
|
||||
return {};
|
||||
}
|
||||
|
||||
return hDC;
|
||||
}
|
||||
|
||||
bool ContextWGL::InitializeDC()
|
||||
{
|
||||
if (m_wi.type == WindowInfo::Type::Win32)
|
||||
{
|
||||
m_dc = GetDCAndSetPixelFormat(GetHWND());
|
||||
if (!m_dc)
|
||||
{
|
||||
Console.Error("Failed to get DC for window");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (m_wi.type == WindowInfo::Type::Surfaceless)
|
||||
{
|
||||
return CreatePBuffer();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error("Unknown window info type %u", static_cast<unsigned>(m_wi.type));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ContextWGL::ReleaseDC()
|
||||
{
|
||||
if (m_pbuffer)
|
||||
{
|
||||
wglReleasePbufferDCARB(m_pbuffer, m_dc);
|
||||
m_dc = {};
|
||||
|
||||
wglDestroyPbufferARB(m_pbuffer);
|
||||
m_pbuffer = {};
|
||||
|
||||
::ReleaseDC(m_dummy_window, m_dummy_dc);
|
||||
m_dummy_dc = {};
|
||||
|
||||
DestroyWindow(m_dummy_window);
|
||||
m_dummy_window = {};
|
||||
}
|
||||
else if (m_dc)
|
||||
{
|
||||
::ReleaseDC(GetHWND(), m_dc);
|
||||
m_dc = {};
|
||||
}
|
||||
}
|
||||
|
||||
bool ContextWGL::CreatePBuffer()
|
||||
{
|
||||
static bool window_class_registered = false;
|
||||
static const wchar_t* window_class_name = L"ContextWGLPBuffer";
|
||||
|
||||
if (!window_class_registered)
|
||||
{
|
||||
WNDCLASSEXW wc = {};
|
||||
wc.cbSize = sizeof(WNDCLASSEXW);
|
||||
wc.style = 0;
|
||||
wc.lpfnWndProc = DefWindowProcW;
|
||||
wc.cbClsExtra = 0;
|
||||
wc.cbWndExtra = 0;
|
||||
wc.hInstance = GetModuleHandle(nullptr);
|
||||
wc.hIcon = NULL;
|
||||
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
||||
wc.lpszMenuName = NULL;
|
||||
wc.lpszClassName = window_class_name;
|
||||
wc.hIconSm = NULL;
|
||||
|
||||
if (!RegisterClassExW(&wc))
|
||||
{
|
||||
Console.Error("(ContextWGL::CreatePBuffer) RegisterClassExW() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
window_class_registered = true;
|
||||
}
|
||||
|
||||
HWND hwnd = CreateWindowExW(0, window_class_name, window_class_name, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
|
||||
if (!hwnd)
|
||||
{
|
||||
Console.Error("(ContextWGL::CreatePBuffer) CreateWindowEx() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
ScopedGuard hwnd_guard([hwnd]() { DestroyWindow(hwnd); });
|
||||
|
||||
HDC hdc = GetDCAndSetPixelFormat(hwnd);
|
||||
if (!hdc)
|
||||
return false;
|
||||
|
||||
ScopedGuard hdc_guard([hdc, hwnd]() { ::ReleaseDC(hwnd, hdc); });
|
||||
|
||||
static constexpr const int pb_attribs[] = {0, 0};
|
||||
|
||||
HGLRC temp_rc = nullptr;
|
||||
ScopedGuard temp_rc_guard([&temp_rc, hdc]() { if (temp_rc) {
|
||||
wglMakeCurrent(hdc, nullptr);
|
||||
wglDeleteContext(temp_rc);
|
||||
} });
|
||||
|
||||
if (!GLAD_WGL_ARB_pbuffer)
|
||||
{
|
||||
// we're probably running completely surfaceless... need a temporary context.
|
||||
temp_rc = wglCreateContext(hdc);
|
||||
if (!temp_rc || !wglMakeCurrent(hdc, temp_rc))
|
||||
{
|
||||
Console.Error("Failed to create temporary context to load WGL for pbuffer.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReloadWGL(hdc) || !GLAD_WGL_ARB_pbuffer)
|
||||
{
|
||||
Console.Error("Missing WGL_ARB_pbuffer");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
pxAssertRel(m_pixel_format.has_value(), "Has pixel format for pbuffer");
|
||||
HPBUFFERARB pbuffer = wglCreatePbufferARB(hdc, m_pixel_format.value(), 1, 1, pb_attribs);
|
||||
if (!pbuffer)
|
||||
{
|
||||
Console.Error("(ContextWGL::CreatePBuffer) wglCreatePbufferARB() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
ScopedGuard pbuffer_guard([pbuffer]() { wglDestroyPbufferARB(pbuffer); });
|
||||
|
||||
m_dc = wglGetPbufferDCARB(pbuffer);
|
||||
if (!m_dc)
|
||||
{
|
||||
Console.Error("(ContextWGL::CreatePbuffer) wglGetPbufferDCARB() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_dummy_window = hwnd;
|
||||
m_dummy_dc = hdc;
|
||||
m_pbuffer = pbuffer;
|
||||
|
||||
temp_rc_guard.Run();
|
||||
pbuffer_guard.Cancel();
|
||||
hdc_guard.Cancel();
|
||||
hwnd_guard.Cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContextWGL::CreateAnyContext(HGLRC share_context, bool make_current)
|
||||
{
|
||||
m_rc = wglCreateContext(m_dc);
|
||||
if (!m_rc)
|
||||
{
|
||||
Console.Error("wglCreateContext() failed: 0x%08X", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (make_current)
|
||||
{
|
||||
if (!wglMakeCurrent(m_dc, m_rc))
|
||||
{
|
||||
Console.Error("wglMakeCurrent() failed: 0x%08X", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// re-init glad-wgl
|
||||
if (!gladLoadWGLLoader([](const char* name) -> void* { return reinterpret_cast<void*>(wglGetProcAddress(name)); }, m_dc))
|
||||
{
|
||||
Console.Error("Loading GLAD WGL functions failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (share_context && !wglShareLists(share_context, m_rc))
|
||||
{
|
||||
Console.Error("wglShareLists() failed: 0x%08X", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContextWGL::CreateVersionContext(const Version& version, HGLRC share_context, bool make_current)
|
||||
{
|
||||
// we need create context attribs
|
||||
if (!GLAD_WGL_ARB_create_context)
|
||||
{
|
||||
Console.Error("Missing GLAD_WGL_ARB_create_context.");
|
||||
return false;
|
||||
}
|
||||
|
||||
HGLRC new_rc;
|
||||
if (version.profile == Profile::Core)
|
||||
{
|
||||
const int attribs[] = {
|
||||
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB, version.major_version,
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB, version.minor_version,
|
||||
#ifdef _DEBUG
|
||||
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | WGL_CONTEXT_DEBUG_BIT_ARB,
|
||||
#else
|
||||
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
|
||||
#endif
|
||||
0, 0};
|
||||
|
||||
new_rc = wglCreateContextAttribsARB(m_dc, share_context, attribs);
|
||||
}
|
||||
else if (version.profile == Profile::ES)
|
||||
{
|
||||
if ((version.major_version >= 2 && !GLAD_WGL_EXT_create_context_es2_profile) ||
|
||||
(version.major_version < 2 && !GLAD_WGL_EXT_create_context_es_profile))
|
||||
{
|
||||
Console.Error("WGL_EXT_create_context_es_profile not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
const int attribs[] = {
|
||||
WGL_CONTEXT_PROFILE_MASK_ARB, ((version.major_version >= 2) ? WGL_CONTEXT_ES2_PROFILE_BIT_EXT : WGL_CONTEXT_ES_PROFILE_BIT_EXT),
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB, version.major_version,
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB, version.minor_version,
|
||||
0, 0};
|
||||
|
||||
new_rc = wglCreateContextAttribsARB(m_dc, share_context, attribs);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error("Unknown profile");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!new_rc)
|
||||
return false;
|
||||
|
||||
// destroy and swap contexts
|
||||
if (m_rc)
|
||||
{
|
||||
if (!wglMakeCurrent(m_dc, make_current ? new_rc : nullptr))
|
||||
{
|
||||
Console.Error("wglMakeCurrent() failed: 0x%08X", GetLastError());
|
||||
wglDeleteContext(new_rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
// re-init glad-wgl
|
||||
if (make_current && !ReloadWGL(m_dc))
|
||||
return false;
|
||||
|
||||
wglDeleteContext(m_rc);
|
||||
}
|
||||
|
||||
m_rc = new_rc;
|
||||
return true;
|
||||
}
|
||||
} // namespace GL
|
||||
@@ -1,71 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/GL/Context.h"
|
||||
|
||||
#include "glad_wgl.h"
|
||||
#include "glad.h"
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <optional>
|
||||
|
||||
namespace GL
|
||||
{
|
||||
class ContextWGL final : public Context
|
||||
{
|
||||
public:
|
||||
ContextWGL(const WindowInfo& wi);
|
||||
~ContextWGL() override;
|
||||
|
||||
static std::unique_ptr<Context> Create(const WindowInfo& wi, gsl::span<const Version> versions_to_try);
|
||||
|
||||
void* GetProcAddress(const char* name) override;
|
||||
bool ChangeSurface(const WindowInfo& new_wi) override;
|
||||
void ResizeSurface(u32 new_surface_width = 0, u32 new_surface_height = 0) override;
|
||||
bool SwapBuffers() override;
|
||||
bool MakeCurrent() override;
|
||||
bool DoneCurrent() override;
|
||||
bool SetSwapInterval(s32 interval) override;
|
||||
std::unique_ptr<Context> CreateSharedContext(const WindowInfo& wi) override;
|
||||
|
||||
private:
|
||||
__fi HWND GetHWND() const { return static_cast<HWND>(m_wi.window_handle); }
|
||||
|
||||
HDC GetDCAndSetPixelFormat(HWND hwnd);
|
||||
|
||||
bool Initialize(gsl::span<const Version> versions_to_try);
|
||||
bool InitializeDC();
|
||||
void ReleaseDC();
|
||||
bool CreatePBuffer();
|
||||
bool CreateAnyContext(HGLRC share_context, bool make_current);
|
||||
bool CreateVersionContext(const Version& version, HGLRC share_context, bool make_current);
|
||||
|
||||
HDC m_dc = {};
|
||||
HGLRC m_rc = {};
|
||||
|
||||
// Can't change pixel format once it's set for a RC.
|
||||
std::optional<int> m_pixel_format;
|
||||
|
||||
// Dummy window for creating a PBuffer off when we're surfaceless.
|
||||
HWND m_dummy_window = {};
|
||||
HDC m_dummy_dc = {};
|
||||
HPBUFFERARB m_pbuffer = {};
|
||||
};
|
||||
} // namespace GL
|
||||
@@ -1,534 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/PrecompiledHeader.h"
|
||||
|
||||
#include "common/GL/Program.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
|
||||
namespace GL
|
||||
{
|
||||
GLuint Program::s_last_program_id = 0;
|
||||
static GLuint s_next_bad_shader_id = 1;
|
||||
|
||||
Program::Program() = default;
|
||||
|
||||
Program::Program(Program&& prog)
|
||||
{
|
||||
m_program_id = prog.m_program_id;
|
||||
prog.m_program_id = 0;
|
||||
m_vertex_shader_id = prog.m_vertex_shader_id;
|
||||
prog.m_vertex_shader_id = 0;
|
||||
m_fragment_shader_id = prog.m_fragment_shader_id;
|
||||
prog.m_fragment_shader_id = 0;
|
||||
m_uniform_locations = std::move(prog.m_uniform_locations);
|
||||
}
|
||||
|
||||
Program::~Program()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
GLuint Program::CompileShader(GLenum type, const std::string_view source)
|
||||
{
|
||||
GLuint id = glCreateShader(type);
|
||||
|
||||
std::array<const GLchar*, 1> sources = {{source.data()}};
|
||||
std::array<GLint, 1> source_lengths = {{static_cast<GLint>(source.size())}};
|
||||
glShaderSource(id, static_cast<GLsizei>(sources.size()), sources.data(), source_lengths.data());
|
||||
glCompileShader(id);
|
||||
|
||||
GLint status = GL_FALSE;
|
||||
glGetShaderiv(id, GL_COMPILE_STATUS, &status);
|
||||
|
||||
GLint info_log_length = 0;
|
||||
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &info_log_length);
|
||||
|
||||
// Log will create a new line when there are no warnings so let's set a minimum log length of 1.
|
||||
constexpr int info_log_min_length = 1;
|
||||
|
||||
if (status == GL_FALSE || info_log_length > info_log_min_length)
|
||||
{
|
||||
std::string info_log;
|
||||
info_log.resize(info_log_length + 1);
|
||||
glGetShaderInfoLog(id, info_log_length, &info_log_length, &info_log[0]);
|
||||
|
||||
if (status == GL_TRUE)
|
||||
{
|
||||
Console.Warning("Shader compiled with warnings:\n%s", info_log.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error("Shader failed to compile:\n%s", info_log.c_str());
|
||||
|
||||
std::ofstream ofs(StringUtil::StdStringFromFormat("pcsx2_bad_shader_%u.txt", s_next_bad_shader_id++).c_str(),
|
||||
std::ofstream::out | std::ofstream::binary);
|
||||
if (ofs.is_open())
|
||||
{
|
||||
ofs.write(sources[0], source_lengths[0]);
|
||||
ofs << "\n\nCompile failed, info log:\n";
|
||||
ofs << info_log;
|
||||
ofs.close();
|
||||
}
|
||||
|
||||
glDeleteShader(id);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void Program::ResetLastProgram()
|
||||
{
|
||||
s_last_program_id = 0;
|
||||
}
|
||||
|
||||
bool Program::Compile(const std::string_view vertex_shader, const std::string_view fragment_shader)
|
||||
{
|
||||
if (!vertex_shader.empty())
|
||||
{
|
||||
m_vertex_shader_id = CompileShader(GL_VERTEX_SHADER, vertex_shader);
|
||||
if (m_vertex_shader_id == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fragment_shader.empty())
|
||||
{
|
||||
m_fragment_shader_id = CompileShader(GL_FRAGMENT_SHADER, fragment_shader);
|
||||
if (m_fragment_shader_id == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
m_program_id = glCreateProgram();
|
||||
if (m_vertex_shader_id != 0)
|
||||
glAttachShader(m_program_id, m_vertex_shader_id);
|
||||
if (m_fragment_shader_id != 0)
|
||||
glAttachShader(m_program_id, m_fragment_shader_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Program::CompileCompute(const std::string_view glsl)
|
||||
{
|
||||
GLuint id = CompileShader(GL_COMPUTE_SHADER, glsl);
|
||||
if (id == 0)
|
||||
return false;
|
||||
|
||||
m_program_id = glCreateProgram();
|
||||
glAttachShader(m_program_id, id);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Program::CreateFromBinary(const void* data, u32 data_length, u32 data_format)
|
||||
{
|
||||
GLuint prog = glCreateProgram();
|
||||
glProgramBinary(prog, static_cast<GLenum>(data_format), data, data_length);
|
||||
|
||||
GLint link_status;
|
||||
glGetProgramiv(prog, GL_LINK_STATUS, &link_status);
|
||||
if (link_status != GL_TRUE)
|
||||
{
|
||||
Console.Error("Failed to create GL program from binary: status %d", link_status);
|
||||
glDeleteProgram(prog);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_program_id = prog;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Program::GetBinary(std::vector<u8>* out_data, u32* out_data_format)
|
||||
{
|
||||
GLint binary_size = 0;
|
||||
glGetProgramiv(m_program_id, GL_PROGRAM_BINARY_LENGTH, &binary_size);
|
||||
if (binary_size == 0)
|
||||
{
|
||||
Console.Warning("glGetProgramiv(GL_PROGRAM_BINARY_LENGTH) returned 0");
|
||||
return false;
|
||||
}
|
||||
|
||||
GLenum format = 0;
|
||||
out_data->resize(static_cast<size_t>(binary_size));
|
||||
glGetProgramBinary(m_program_id, binary_size, &binary_size, &format, out_data->data());
|
||||
if (binary_size == 0)
|
||||
{
|
||||
Console.Warning("glGetProgramBinary() failed");
|
||||
return false;
|
||||
}
|
||||
else if (static_cast<size_t>(binary_size) != out_data->size())
|
||||
{
|
||||
Console.Warning("Size changed from %zu to %d after glGetProgramBinary()", out_data->size(), binary_size);
|
||||
out_data->resize(static_cast<size_t>(binary_size));
|
||||
}
|
||||
|
||||
*out_data_format = static_cast<u32>(format);
|
||||
DevCon.WriteLn("Program binary retrieved, %zu bytes, format %u", out_data->size(), *out_data_format);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Program::SetBinaryRetrievableHint()
|
||||
{
|
||||
glProgramParameteri(m_program_id, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE);
|
||||
}
|
||||
|
||||
void Program::BindAttribute(GLuint index, const char* name)
|
||||
{
|
||||
glBindAttribLocation(m_program_id, index, name);
|
||||
}
|
||||
|
||||
void Program::BindDefaultAttributes()
|
||||
{
|
||||
BindAttribute(0, "a_position");
|
||||
BindAttribute(1, "a_texcoord");
|
||||
BindAttribute(2, "a_color");
|
||||
}
|
||||
|
||||
void Program::BindFragData(GLuint index /*= 0*/, const char* name /*= "o_col0"*/)
|
||||
{
|
||||
glBindFragDataLocation(m_program_id, index, name);
|
||||
}
|
||||
|
||||
void Program::BindFragDataIndexed(GLuint color_number /*= 0*/, const char* name /*= "o_col0"*/)
|
||||
{
|
||||
if (GLAD_GL_VERSION_3_3 || GLAD_GL_ARB_blend_func_extended)
|
||||
{
|
||||
glBindFragDataLocationIndexed(m_program_id, color_number, 0, name);
|
||||
return;
|
||||
}
|
||||
else if (GLAD_GL_EXT_blend_func_extended)
|
||||
{
|
||||
glBindFragDataLocationIndexedEXT(m_program_id, color_number, 0, name);
|
||||
return;
|
||||
}
|
||||
|
||||
Console.Error("BindFragDataIndexed() called without ARB or EXT extension, we'll probably crash.");
|
||||
glBindFragDataLocationIndexed(m_program_id, color_number, 0, name);
|
||||
}
|
||||
|
||||
bool Program::Link()
|
||||
{
|
||||
glLinkProgram(m_program_id);
|
||||
|
||||
if (m_vertex_shader_id != 0)
|
||||
glDeleteShader(m_vertex_shader_id);
|
||||
m_vertex_shader_id = 0;
|
||||
if (m_fragment_shader_id != 0)
|
||||
glDeleteShader(m_fragment_shader_id);
|
||||
m_fragment_shader_id = 0;
|
||||
|
||||
GLint status = GL_FALSE;
|
||||
glGetProgramiv(m_program_id, GL_LINK_STATUS, &status);
|
||||
|
||||
GLint info_log_length = 0;
|
||||
|
||||
// Log will create a new line when there are no warnings so let's set a minimum log length of 1.
|
||||
constexpr int info_log_min_length = 1;
|
||||
|
||||
glGetProgramiv(m_program_id, GL_INFO_LOG_LENGTH, &info_log_length);
|
||||
|
||||
if (status == GL_FALSE || info_log_length > info_log_min_length)
|
||||
{
|
||||
std::string info_log;
|
||||
info_log.resize(info_log_length + 1);
|
||||
glGetProgramInfoLog(m_program_id, info_log_length, &info_log_length, &info_log[0]);
|
||||
|
||||
if (status == GL_TRUE)
|
||||
{
|
||||
Console.Error("Program linked with warnings:\n%s", info_log.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Error("Program failed to link:\n%s", info_log.c_str());
|
||||
glDeleteProgram(m_program_id);
|
||||
m_program_id = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Program::Bind() const
|
||||
{
|
||||
if (s_last_program_id == m_program_id)
|
||||
return;
|
||||
|
||||
glUseProgram(m_program_id);
|
||||
s_last_program_id = m_program_id;
|
||||
}
|
||||
|
||||
void Program::Destroy()
|
||||
{
|
||||
if (m_vertex_shader_id != 0)
|
||||
{
|
||||
glDeleteShader(m_vertex_shader_id);
|
||||
m_vertex_shader_id = 0;
|
||||
}
|
||||
if (m_fragment_shader_id != 0)
|
||||
{
|
||||
glDeleteShader(m_fragment_shader_id);
|
||||
m_fragment_shader_id = 0;
|
||||
}
|
||||
if (m_program_id != 0)
|
||||
{
|
||||
glDeleteProgram(m_program_id);
|
||||
m_program_id = 0;
|
||||
}
|
||||
|
||||
m_uniform_locations.clear();
|
||||
}
|
||||
|
||||
int Program::RegisterUniform(const char* name)
|
||||
{
|
||||
int id = static_cast<int>(m_uniform_locations.size());
|
||||
m_uniform_locations.push_back(glGetUniformLocation(m_program_id, name));
|
||||
return id;
|
||||
}
|
||||
|
||||
void Program::Uniform1ui(int index, u32 x) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform1ui(location, x);
|
||||
}
|
||||
|
||||
void Program::Uniform2ui(int index, u32 x, u32 y) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform2ui(location, x, y);
|
||||
}
|
||||
|
||||
void Program::Uniform3ui(int index, u32 x, u32 y, u32 z) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform3ui(location, x, y, z);
|
||||
}
|
||||
|
||||
void Program::Uniform4ui(int index, u32 x, u32 y, u32 z, u32 w) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform4ui(location, x, y, z, w);
|
||||
}
|
||||
|
||||
void Program::Uniform1i(int index, s32 x) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform1i(location, x);
|
||||
}
|
||||
|
||||
void Program::Uniform2i(int index, s32 x, s32 y) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform2i(location, x, y);
|
||||
}
|
||||
|
||||
void Program::Uniform3i(int index, s32 x, s32 y, s32 z) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform3i(location, x, y, z);
|
||||
}
|
||||
|
||||
void Program::Uniform4i(int index, s32 x, s32 y, s32 z, s32 w) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform4i(location, x, y, z, w);
|
||||
}
|
||||
|
||||
void Program::Uniform1f(int index, float x) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform1f(location, x);
|
||||
}
|
||||
|
||||
void Program::Uniform2f(int index, float x, float y) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform2f(location, x, y);
|
||||
}
|
||||
|
||||
void Program::Uniform3f(int index, float x, float y, float z) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform3f(location, x, y, z);
|
||||
}
|
||||
|
||||
void Program::Uniform4f(int index, float x, float y, float z, float w) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform4f(location, x, y, z, w);
|
||||
}
|
||||
|
||||
void Program::Uniform2uiv(int index, const u32* v) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform2uiv(location, 1, v);
|
||||
}
|
||||
|
||||
void Program::Uniform3uiv(int index, const u32* v) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform3uiv(location, 1, v);
|
||||
}
|
||||
|
||||
void Program::Uniform4uiv(int index, const u32* v) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform4uiv(location, 1, v);
|
||||
}
|
||||
|
||||
void Program::Uniform2iv(int index, const s32* v) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform2iv(location, 1, v);
|
||||
}
|
||||
|
||||
void Program::Uniform3iv(int index, const s32* v) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform3iv(location, 1, v);
|
||||
}
|
||||
|
||||
void Program::Uniform4iv(int index, const s32* v) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform4iv(location, 1, v);
|
||||
}
|
||||
|
||||
void Program::Uniform2fv(int index, const float* v) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform2fv(location, 1, v);
|
||||
}
|
||||
|
||||
void Program::Uniform3fv(int index, const float* v) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform3fv(location, 1, v);
|
||||
}
|
||||
|
||||
void Program::Uniform4fv(int index, const float* v) const
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniform4fv(location, 1, v);
|
||||
}
|
||||
|
||||
void Program::UniformMatrix2fv(int index, const float* v)
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniformMatrix2fv(location, 1, GL_FALSE, v);
|
||||
}
|
||||
|
||||
void Program::UniformMatrix3fv(int index, const float* v)
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniformMatrix3fv(location, 1, GL_FALSE, v);
|
||||
}
|
||||
|
||||
void Program::UniformMatrix4fv(int index, const float* v)
|
||||
{
|
||||
pxAssert(static_cast<size_t>(index) < m_uniform_locations.size());
|
||||
const GLint location = m_uniform_locations[index];
|
||||
if (location >= 0)
|
||||
glUniformMatrix4fv(location, 1, GL_FALSE, v);
|
||||
}
|
||||
|
||||
void Program::BindUniformBlock(const char* name, u32 index)
|
||||
{
|
||||
const GLint location = glGetUniformBlockIndex(m_program_id, name);
|
||||
if (location >= 0)
|
||||
glUniformBlockBinding(m_program_id, location, index);
|
||||
}
|
||||
|
||||
void Program::SetName(const std::string_view& name)
|
||||
{
|
||||
if (name.empty())
|
||||
return;
|
||||
|
||||
#ifdef _DEBUG
|
||||
glObjectLabel(GL_PROGRAM, m_program_id, name.length(), name.data());
|
||||
#endif
|
||||
}
|
||||
|
||||
void Program::SetFormattedName(const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
std::string n = StringUtil::StdStringFromFormatV(format, ap);
|
||||
va_end(ap);
|
||||
SetName(n);
|
||||
}
|
||||
|
||||
Program& Program::operator=(Program&& prog)
|
||||
{
|
||||
Destroy();
|
||||
m_program_id = prog.m_program_id;
|
||||
prog.m_program_id = 0;
|
||||
m_vertex_shader_id = prog.m_vertex_shader_id;
|
||||
prog.m_vertex_shader_id = 0;
|
||||
m_fragment_shader_id = prog.m_fragment_shader_id;
|
||||
prog.m_fragment_shader_id = 0;
|
||||
m_uniform_locations = std::move(prog.m_uniform_locations);
|
||||
return *this;
|
||||
}
|
||||
} // namespace GL
|
||||
@@ -1,105 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "../Pcsx2Defs.h"
|
||||
#include "glad.h"
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace GL
|
||||
{
|
||||
class Program
|
||||
{
|
||||
public:
|
||||
Program();
|
||||
Program(const Program&) = delete;
|
||||
Program(Program&& prog);
|
||||
~Program();
|
||||
|
||||
static GLuint CompileShader(GLenum type, const std::string_view source);
|
||||
static void ResetLastProgram();
|
||||
|
||||
bool IsValid() const { return m_program_id != 0; }
|
||||
|
||||
bool Compile(const std::string_view vertex_shader, const std::string_view fragment_shader);
|
||||
|
||||
bool CompileCompute(const std::string_view glsl);
|
||||
|
||||
bool CreateFromBinary(const void* data, u32 data_length, u32 data_format);
|
||||
|
||||
bool GetBinary(std::vector<u8>* out_data, u32* out_data_format);
|
||||
void SetBinaryRetrievableHint();
|
||||
|
||||
void BindAttribute(GLuint index, const char* name);
|
||||
void BindDefaultAttributes();
|
||||
|
||||
void BindFragData(GLuint index = 0, const char* name = "o_col0");
|
||||
void BindFragDataIndexed(GLuint color_number = 0, const char* name = "o_col0");
|
||||
|
||||
bool Link();
|
||||
|
||||
void Bind() const;
|
||||
|
||||
void Destroy();
|
||||
|
||||
int RegisterUniform(const char* name);
|
||||
void Uniform1ui(int index, u32 x) const;
|
||||
void Uniform2ui(int index, u32 x, u32 y) const;
|
||||
void Uniform3ui(int index, u32 x, u32 y, u32 z) const;
|
||||
void Uniform4ui(int index, u32 x, u32 y, u32 z, u32 w) const;
|
||||
void Uniform1i(int index, s32 x) const;
|
||||
void Uniform2i(int index, s32 x, s32 y) const;
|
||||
void Uniform3i(int index, s32 x, s32 y, s32 z) const;
|
||||
void Uniform4i(int index, s32 x, s32 y, s32 z, s32 w) const;
|
||||
void Uniform1f(int index, float x) const;
|
||||
void Uniform2f(int index, float x, float y) const;
|
||||
void Uniform3f(int index, float x, float y, float z) const;
|
||||
void Uniform4f(int index, float x, float y, float z, float w) const;
|
||||
void Uniform2uiv(int index, const u32* v) const;
|
||||
void Uniform3uiv(int index, const u32* v) const;
|
||||
void Uniform4uiv(int index, const u32* v) const;
|
||||
void Uniform2iv(int index, const s32* v) const;
|
||||
void Uniform3iv(int index, const s32* v) const;
|
||||
void Uniform4iv(int index, const s32* v) const;
|
||||
void Uniform2fv(int index, const float* v) const;
|
||||
void Uniform3fv(int index, const float* v) const;
|
||||
void Uniform4fv(int index, const float* v) const;
|
||||
|
||||
void UniformMatrix2fv(int index, const float* v);
|
||||
void UniformMatrix3fv(int index, const float* v);
|
||||
void UniformMatrix4fv(int index, const float* v);
|
||||
|
||||
void BindUniformBlock(const char* name, u32 index);
|
||||
|
||||
void SetName(const std::string_view& name);
|
||||
void SetFormattedName(const char* format, ...);
|
||||
|
||||
Program& operator=(const Program&) = delete;
|
||||
Program& operator=(Program&& prog);
|
||||
|
||||
__fi bool operator==(const Program& rhs) const { return m_program_id == rhs.m_program_id; }
|
||||
__fi bool operator!=(const Program& rhs) const { return m_program_id != rhs.m_program_id; }
|
||||
|
||||
private:
|
||||
static u32 s_last_program_id;
|
||||
|
||||
GLuint m_program_id = 0;
|
||||
GLuint m_vertex_shader_id = 0;
|
||||
GLuint m_fragment_shader_id = 0;
|
||||
|
||||
std::vector<GLint> m_uniform_locations;
|
||||
};
|
||||
} // namespace GL
|
||||
@@ -1,547 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/GL/ShaderCache.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/MD5Digest.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/Timer.h"
|
||||
|
||||
namespace GL
|
||||
{
|
||||
#pragma pack(push, 1)
|
||||
struct CacheIndexEntry
|
||||
{
|
||||
u64 vertex_source_hash_low;
|
||||
u64 vertex_source_hash_high;
|
||||
u32 vertex_source_length;
|
||||
u64 fragment_source_hash_low;
|
||||
u64 fragment_source_hash_high;
|
||||
u32 fragment_source_length;
|
||||
u32 file_offset;
|
||||
u32 blob_size;
|
||||
u32 blob_format;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
ShaderCache::ShaderCache() = default;
|
||||
|
||||
ShaderCache::~ShaderCache()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
bool ShaderCache::CacheIndexKey::operator==(const CacheIndexKey& key) const
|
||||
{
|
||||
return (
|
||||
vertex_source_hash_low == key.vertex_source_hash_low && vertex_source_hash_high == key.vertex_source_hash_high &&
|
||||
vertex_source_length == key.vertex_source_length && fragment_source_hash_low == key.fragment_source_hash_low &&
|
||||
fragment_source_hash_high == key.fragment_source_hash_high && fragment_source_length == key.fragment_source_length);
|
||||
}
|
||||
|
||||
bool ShaderCache::CacheIndexKey::operator!=(const CacheIndexKey& key) const
|
||||
{
|
||||
return (
|
||||
vertex_source_hash_low != key.vertex_source_hash_low || vertex_source_hash_high != key.vertex_source_hash_high ||
|
||||
vertex_source_length != key.vertex_source_length || fragment_source_hash_low != key.fragment_source_hash_low ||
|
||||
fragment_source_hash_high != key.fragment_source_hash_high || fragment_source_length != key.fragment_source_length);
|
||||
}
|
||||
|
||||
bool ShaderCache::Open(bool is_gles, std::string_view base_path, u32 version)
|
||||
{
|
||||
m_base_path = base_path;
|
||||
m_version = version;
|
||||
m_program_binary_supported = is_gles || GLAD_GL_ARB_get_program_binary;
|
||||
if (m_program_binary_supported)
|
||||
{
|
||||
// check that there's at least one format and the extension isn't being "faked"
|
||||
GLint num_formats = 0;
|
||||
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &num_formats);
|
||||
Console.WriteLn("%u program binary formats supported by driver", num_formats);
|
||||
m_program_binary_supported = (num_formats > 0);
|
||||
}
|
||||
|
||||
if (!m_program_binary_supported)
|
||||
{
|
||||
Console.Warning("Your GL driver does not support program binaries. Hopefully it has a built-in cache.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!base_path.empty())
|
||||
{
|
||||
const std::string index_filename = GetIndexFileName();
|
||||
const std::string blob_filename = GetBlobFileName();
|
||||
|
||||
if (ReadExisting(index_filename, blob_filename))
|
||||
return true;
|
||||
|
||||
return CreateNew(index_filename, blob_filename);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShaderCache::CreateNew(const std::string& index_filename, const std::string& blob_filename)
|
||||
{
|
||||
if (FileSystem::FileExists(index_filename.c_str()))
|
||||
{
|
||||
Console.Warning("Removing existing index file '%s'", index_filename.c_str());
|
||||
FileSystem::DeleteFilePath(index_filename.c_str());
|
||||
}
|
||||
if (FileSystem::FileExists(blob_filename.c_str()))
|
||||
{
|
||||
Console.Warning("Removing existing blob file '%s'", blob_filename.c_str());
|
||||
FileSystem::DeleteFilePath(blob_filename.c_str());
|
||||
}
|
||||
|
||||
m_index_file = FileSystem::OpenCFile(index_filename.c_str(), "wb");
|
||||
if (!m_index_file)
|
||||
{
|
||||
Console.Error("Failed to open index file '%s' for writing", index_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
const u32 index_version = FILE_VERSION;
|
||||
if (std::fwrite(&index_version, sizeof(index_version), 1, m_index_file) != 1 ||
|
||||
std::fwrite(&m_version, sizeof(m_version), 1, m_index_file) != 1)
|
||||
{
|
||||
Console.Error("Failed to write version to index file '%s'", index_filename.c_str());
|
||||
std::fclose(m_index_file);
|
||||
m_index_file = nullptr;
|
||||
FileSystem::DeleteFilePath(index_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_blob_file = FileSystem::OpenCFile(blob_filename.c_str(), "w+b");
|
||||
if (!m_blob_file)
|
||||
{
|
||||
Console.Error("Failed to open blob file '%s' for writing", blob_filename.c_str());
|
||||
std::fclose(m_index_file);
|
||||
m_index_file = nullptr;
|
||||
FileSystem::DeleteFilePath(index_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShaderCache::ReadExisting(const std::string& index_filename, const std::string& blob_filename)
|
||||
{
|
||||
m_index_file = FileSystem::OpenCFile(index_filename.c_str(), "r+b");
|
||||
if (!m_index_file)
|
||||
{
|
||||
// special case here: when there's a sharing violation (i.e. two instances running),
|
||||
// we don't want to blow away the cache. so just continue without a cache.
|
||||
if (errno == EACCES)
|
||||
{
|
||||
Console.WriteLn("Failed to open shader cache index with EACCES, are you running two instances?");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 file_version = 0;
|
||||
u32 data_version = 0;
|
||||
if (std::fread(&file_version, sizeof(file_version), 1, m_index_file) != 1 || file_version != FILE_VERSION ||
|
||||
std::fread(&data_version, sizeof(data_version), 1, m_index_file) != 1 || data_version != m_version)
|
||||
{
|
||||
Console.Error("Bad file/data version in '%s'", index_filename.c_str());
|
||||
std::fclose(m_index_file);
|
||||
m_index_file = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_blob_file = FileSystem::OpenCFile(blob_filename.c_str(), "a+b");
|
||||
if (!m_blob_file)
|
||||
{
|
||||
Console.Error("Blob file '%s' is missing", blob_filename.c_str());
|
||||
std::fclose(m_index_file);
|
||||
m_index_file = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::fseek(m_blob_file, 0, SEEK_END);
|
||||
const u32 blob_file_size = static_cast<u32>(std::ftell(m_blob_file));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
CacheIndexEntry entry;
|
||||
if (std::fread(&entry, sizeof(entry), 1, m_index_file) != 1 ||
|
||||
(entry.file_offset + entry.blob_size) > blob_file_size)
|
||||
{
|
||||
if (std::feof(m_index_file))
|
||||
break;
|
||||
|
||||
Console.Error("Failed to read entry from '%s', corrupt file?", index_filename.c_str());
|
||||
m_index.clear();
|
||||
std::fclose(m_blob_file);
|
||||
m_blob_file = nullptr;
|
||||
std::fclose(m_index_file);
|
||||
m_index_file = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
const CacheIndexKey key{
|
||||
entry.vertex_source_hash_low, entry.vertex_source_hash_high, entry.vertex_source_length,
|
||||
entry.fragment_source_hash_low, entry.fragment_source_hash_high, entry.fragment_source_length};
|
||||
const CacheIndexData data{entry.file_offset, entry.blob_size, entry.blob_format};
|
||||
m_index.emplace(key, data);
|
||||
}
|
||||
|
||||
Console.WriteLn("Read %zu entries from '%s'", m_index.size(), index_filename.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderCache::Close()
|
||||
{
|
||||
m_index.clear();
|
||||
if (m_index_file)
|
||||
{
|
||||
std::fclose(m_index_file);
|
||||
m_index_file = nullptr;
|
||||
}
|
||||
if (m_blob_file)
|
||||
{
|
||||
std::fclose(m_blob_file);
|
||||
m_blob_file = nullptr;
|
||||
}
|
||||
|
||||
m_base_path = {};
|
||||
}
|
||||
|
||||
bool ShaderCache::Recreate()
|
||||
{
|
||||
Close();
|
||||
|
||||
const std::string index_filename = GetIndexFileName();
|
||||
const std::string blob_filename = GetBlobFileName();
|
||||
|
||||
return CreateNew(index_filename, blob_filename);
|
||||
}
|
||||
|
||||
ShaderCache::CacheIndexKey ShaderCache::GetCacheKey(const std::string_view& vertex_shader,
|
||||
const std::string_view& fragment_shader)
|
||||
{
|
||||
union ShaderHash
|
||||
{
|
||||
struct
|
||||
{
|
||||
u64 low;
|
||||
u64 high;
|
||||
};
|
||||
u8 bytes[16];
|
||||
};
|
||||
|
||||
ShaderHash vertex_hash = {};
|
||||
ShaderHash fragment_hash = {};
|
||||
|
||||
MD5Digest digest;
|
||||
if (!vertex_shader.empty())
|
||||
{
|
||||
digest.Update(vertex_shader.data(), static_cast<u32>(vertex_shader.length()));
|
||||
digest.Final(vertex_hash.bytes);
|
||||
}
|
||||
|
||||
if (!fragment_shader.empty())
|
||||
{
|
||||
digest.Reset();
|
||||
digest.Update(fragment_shader.data(), static_cast<u32>(fragment_shader.length()));
|
||||
digest.Final(fragment_hash.bytes);
|
||||
}
|
||||
|
||||
return CacheIndexKey{vertex_hash.low, vertex_hash.high, static_cast<u32>(vertex_shader.length()),
|
||||
fragment_hash.low, fragment_hash.high, static_cast<u32>(fragment_shader.length())};
|
||||
}
|
||||
|
||||
std::string ShaderCache::GetIndexFileName() const
|
||||
{
|
||||
return StringUtil::StdStringFromFormat("%s/gl_programs.idx", m_base_path.c_str());
|
||||
}
|
||||
|
||||
std::string ShaderCache::GetBlobFileName() const
|
||||
{
|
||||
return StringUtil::StdStringFromFormat("%s/gl_programs.bin", m_base_path.c_str());
|
||||
}
|
||||
|
||||
std::optional<Program> ShaderCache::GetProgram(const std::string_view vertex_shader,
|
||||
const std::string_view fragment_shader, const PreLinkCallback& callback)
|
||||
{
|
||||
if (!m_program_binary_supported || !m_blob_file)
|
||||
{
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
Common::Timer timer;
|
||||
#endif
|
||||
|
||||
std::optional<Program> res = CompileProgram(vertex_shader, fragment_shader, callback, false);
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
Console.WriteLn("Time to compile shader without caching: %.2fms", timer.GetTimeMilliseconds());
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
const auto key = GetCacheKey(vertex_shader, fragment_shader);
|
||||
auto iter = m_index.find(key);
|
||||
if (iter == m_index.end())
|
||||
return CompileAndAddProgram(key, vertex_shader, fragment_shader, callback);
|
||||
|
||||
std::vector<u8> data(iter->second.blob_size);
|
||||
if (std::fseek(m_blob_file, iter->second.file_offset, SEEK_SET) != 0 ||
|
||||
std::fread(data.data(), 1, iter->second.blob_size, m_blob_file) != iter->second.blob_size)
|
||||
{
|
||||
Console.Error("Read blob from file failed");
|
||||
return {};
|
||||
}
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
Common::Timer timer;
|
||||
#endif
|
||||
|
||||
Program prog;
|
||||
if (prog.CreateFromBinary(data.data(), static_cast<u32>(data.size()), iter->second.blob_format))
|
||||
{
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
Console.WriteLn("Time to create program from binary: %.2fms", timer.GetTimeMilliseconds());
|
||||
#endif
|
||||
|
||||
return std::optional<Program>(std::move(prog));
|
||||
}
|
||||
|
||||
Console.Warning(
|
||||
"Failed to create program from binary, this may be due to a driver or GPU Change. Recreating cache.");
|
||||
if (!Recreate())
|
||||
return CompileProgram(vertex_shader, fragment_shader, callback, false);
|
||||
else
|
||||
return CompileAndAddProgram(key, vertex_shader, fragment_shader, callback);
|
||||
}
|
||||
|
||||
bool ShaderCache::GetProgram(Program* out_program, const std::string_view vertex_shader,
|
||||
const std::string_view fragment_shader, const PreLinkCallback& callback /* = */)
|
||||
{
|
||||
auto prog = GetProgram(vertex_shader, fragment_shader, callback);
|
||||
if (!prog)
|
||||
return false;
|
||||
|
||||
*out_program = std::move(*prog);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShaderCache::WriteToBlobFile(const CacheIndexKey& key, const std::vector<u8>& prog_data, u32 prog_format)
|
||||
{
|
||||
if (!m_blob_file || std::fseek(m_blob_file, 0, SEEK_END) != 0)
|
||||
return false;
|
||||
|
||||
CacheIndexData data;
|
||||
data.file_offset = static_cast<u32>(std::ftell(m_blob_file));
|
||||
data.blob_size = static_cast<u32>(prog_data.size());
|
||||
data.blob_format = prog_format;
|
||||
|
||||
CacheIndexEntry entry = {};
|
||||
entry.vertex_source_hash_low = key.vertex_source_hash_low;
|
||||
entry.vertex_source_hash_high = key.vertex_source_hash_high;
|
||||
entry.vertex_source_length = key.vertex_source_length;
|
||||
entry.fragment_source_hash_low = key.fragment_source_hash_low;
|
||||
entry.fragment_source_hash_high = key.fragment_source_hash_high;
|
||||
entry.fragment_source_length = key.fragment_source_length;
|
||||
entry.file_offset = data.file_offset;
|
||||
entry.blob_size = data.blob_size;
|
||||
entry.blob_format = data.blob_format;
|
||||
|
||||
if (std::fwrite(prog_data.data(), 1, entry.blob_size, m_blob_file) != entry.blob_size ||
|
||||
std::fflush(m_blob_file) != 0 || std::fwrite(&entry, sizeof(entry), 1, m_index_file) != 1 ||
|
||||
std::fflush(m_index_file) != 0)
|
||||
{
|
||||
Console.Error("Failed to write shader blob to file");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_index.emplace(key, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<Program> ShaderCache::CompileProgram(const std::string_view& vertex_shader,
|
||||
const std::string_view& fragment_shader, const PreLinkCallback& callback, bool set_retrievable)
|
||||
{
|
||||
Program prog;
|
||||
if (!prog.Compile(vertex_shader, fragment_shader))
|
||||
return std::nullopt;
|
||||
|
||||
if (callback)
|
||||
callback(prog);
|
||||
|
||||
if (set_retrievable)
|
||||
prog.SetBinaryRetrievableHint();
|
||||
|
||||
if (!prog.Link())
|
||||
return std::nullopt;
|
||||
|
||||
return std::optional<Program>(std::move(prog));
|
||||
}
|
||||
|
||||
std::optional<Program> ShaderCache::CompileComputeProgram(const std::string_view& glsl,
|
||||
const PreLinkCallback& callback, bool set_retrievable)
|
||||
{
|
||||
Program prog;
|
||||
if (!prog.CompileCompute(glsl))
|
||||
return std::nullopt;
|
||||
|
||||
if (callback)
|
||||
callback(prog);
|
||||
|
||||
if (set_retrievable)
|
||||
prog.SetBinaryRetrievableHint();
|
||||
|
||||
if (!prog.Link())
|
||||
return std::nullopt;
|
||||
|
||||
return std::optional<Program>(std::move(prog));
|
||||
}
|
||||
|
||||
std::optional<Program> ShaderCache::CompileAndAddProgram(const CacheIndexKey& key,
|
||||
const std::string_view& vertex_shader, const std::string_view& fragment_shader,
|
||||
const PreLinkCallback& callback)
|
||||
{
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
Common::Timer timer;
|
||||
#endif
|
||||
|
||||
std::optional<Program> prog = CompileProgram(vertex_shader, fragment_shader, callback, true);
|
||||
if (!prog)
|
||||
return std::nullopt;
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
const float compile_time = timer.GetTimeMilliseconds();
|
||||
timer.Reset();
|
||||
#endif
|
||||
|
||||
std::vector<u8> prog_data;
|
||||
u32 prog_format = 0;
|
||||
if (!prog->GetBinary(&prog_data, &prog_format))
|
||||
return std::nullopt;
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
const float binary_time = timer.GetTimeMilliseconds();
|
||||
timer.Reset();
|
||||
#endif
|
||||
|
||||
WriteToBlobFile(key, prog_data, prog_format);
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
const float write_time = timer.GetTimeMilliseconds();
|
||||
Console.WriteLn("Compiled and cached shader: Compile: %.2fms, Binary: %.2fms, Write: %.2fms", compile_time, binary_time, write_time);
|
||||
#endif
|
||||
|
||||
return prog;
|
||||
}
|
||||
|
||||
std::optional<Program> ShaderCache::GetComputeProgram(const std::string_view glsl, const PreLinkCallback& callback)
|
||||
{
|
||||
if (!m_program_binary_supported || !m_blob_file)
|
||||
{
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
Common::Timer timer;
|
||||
#endif
|
||||
|
||||
std::optional<Program> res = CompileComputeProgram(glsl, callback, false);
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
Console.WriteLn("Time to compile shader without caching: %.2fms", timer.GetTimeMilliseconds());
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
const auto key = GetCacheKey(glsl, std::string_view());
|
||||
auto iter = m_index.find(key);
|
||||
if (iter == m_index.end())
|
||||
return CompileAndAddComputeProgram(key, glsl, callback);
|
||||
|
||||
std::vector<u8> data(iter->second.blob_size);
|
||||
if (std::fseek(m_blob_file, iter->second.file_offset, SEEK_SET) != 0 ||
|
||||
std::fread(data.data(), 1, iter->second.blob_size, m_blob_file) != iter->second.blob_size)
|
||||
{
|
||||
Console.Error("Read blob from file failed");
|
||||
return {};
|
||||
}
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
Common::Timer timer;
|
||||
#endif
|
||||
|
||||
Program prog;
|
||||
if (prog.CreateFromBinary(data.data(), static_cast<u32>(data.size()), iter->second.blob_format))
|
||||
{
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
Console.WriteLn("Time to create program from binary: %.2fms", timer.GetTimeMilliseconds());
|
||||
#endif
|
||||
|
||||
return std::optional<Program>(std::move(prog));
|
||||
}
|
||||
|
||||
Console.Warning(
|
||||
"Failed to create program from binary, this may be due to a driver or GPU Change. Recreating cache.");
|
||||
if (!Recreate())
|
||||
return CompileComputeProgram(glsl, callback, false);
|
||||
else
|
||||
return CompileAndAddComputeProgram(key, glsl, callback);
|
||||
}
|
||||
|
||||
bool ShaderCache::GetComputeProgram(Program* out_program, const std::string_view glsl, const PreLinkCallback& callback)
|
||||
{
|
||||
auto prog = GetComputeProgram(glsl, callback);
|
||||
if (!prog)
|
||||
return false;
|
||||
|
||||
*out_program = std::move(*prog);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<Program> ShaderCache::CompileAndAddComputeProgram(
|
||||
const CacheIndexKey& key, const std::string_view& glsl, const PreLinkCallback& callback)
|
||||
{
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
Common::Timer timer;
|
||||
#endif
|
||||
|
||||
std::optional<Program> prog = CompileComputeProgram(glsl, callback, true);
|
||||
if (!prog)
|
||||
return std::nullopt;
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
const float compile_time = timer.GetTimeMilliseconds();
|
||||
timer.Reset();
|
||||
#endif
|
||||
|
||||
std::vector<u8> prog_data;
|
||||
u32 prog_format = 0;
|
||||
if (!prog->GetBinary(&prog_data, &prog_format))
|
||||
return std::nullopt;
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
const float binary_time = timer.GetTimeMilliseconds();
|
||||
timer.Reset();
|
||||
#endif
|
||||
|
||||
WriteToBlobFile(key, prog_data, prog_format);
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
const float write_time = timer.GetTimeMilliseconds();
|
||||
Console.WriteLn("Compiled and cached compute shader: Compile: %.2fms, Binary: %.2fms, Write: %.2fms", compile_time, binary_time, write_time);
|
||||
#endif
|
||||
|
||||
return prog;
|
||||
}
|
||||
} // namespace GL
|
||||
@@ -1,112 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/HashCombine.h"
|
||||
#include "common/GL/Program.h"
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace GL
|
||||
{
|
||||
class ShaderCache
|
||||
{
|
||||
public:
|
||||
using PreLinkCallback = std::function<void(Program&)>;
|
||||
|
||||
ShaderCache();
|
||||
~ShaderCache();
|
||||
|
||||
bool Open(bool is_gles, std::string_view base_path, u32 version);
|
||||
void Close();
|
||||
|
||||
std::optional<Program> GetProgram(const std::string_view vertex_shader, const std::string_view fragment_shader, const PreLinkCallback& callback = {});
|
||||
bool GetProgram(Program* out_program, const std::string_view vertex_shader, const std::string_view fragment_shader, const PreLinkCallback& callback = {});
|
||||
|
||||
std::optional<Program> GetComputeProgram(const std::string_view glsl, const PreLinkCallback& callback = {});
|
||||
bool GetComputeProgram(Program* out_program, const std::string_view glsl, const PreLinkCallback& callback = {});
|
||||
|
||||
private:
|
||||
static constexpr u32 FILE_VERSION = 1;
|
||||
|
||||
struct CacheIndexKey
|
||||
{
|
||||
u64 vertex_source_hash_low;
|
||||
u64 vertex_source_hash_high;
|
||||
u32 vertex_source_length;
|
||||
u64 fragment_source_hash_low;
|
||||
u64 fragment_source_hash_high;
|
||||
u32 fragment_source_length;
|
||||
|
||||
bool operator==(const CacheIndexKey& key) const;
|
||||
bool operator!=(const CacheIndexKey& key) const;
|
||||
};
|
||||
|
||||
struct CacheIndexEntryHasher
|
||||
{
|
||||
std::size_t operator()(const CacheIndexKey& e) const noexcept
|
||||
{
|
||||
std::size_t h = 0;
|
||||
HashCombine(h,
|
||||
e.vertex_source_hash_low, e.vertex_source_hash_high, e.vertex_source_length,
|
||||
e.fragment_source_hash_low, e.fragment_source_hash_high, e.fragment_source_length);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
struct CacheIndexData
|
||||
{
|
||||
u32 file_offset;
|
||||
u32 blob_size;
|
||||
u32 blob_format;
|
||||
};
|
||||
|
||||
using CacheIndex = std::unordered_map<CacheIndexKey, CacheIndexData, CacheIndexEntryHasher>;
|
||||
|
||||
static CacheIndexKey GetCacheKey(const std::string_view& vertex_shader, const std::string_view& fragment_shader);
|
||||
|
||||
std::string GetIndexFileName() const;
|
||||
std::string GetBlobFileName() const;
|
||||
|
||||
bool CreateNew(const std::string& index_filename, const std::string& blob_filename);
|
||||
bool ReadExisting(const std::string& index_filename, const std::string& blob_filename);
|
||||
bool Recreate();
|
||||
|
||||
bool WriteToBlobFile(const CacheIndexKey& key, const std::vector<u8>& prog_data, u32 prog_format);
|
||||
|
||||
std::optional<Program> CompileProgram(const std::string_view& vertex_shader,
|
||||
const std::string_view& fragment_shader, const PreLinkCallback& callback,
|
||||
bool set_retrievable);
|
||||
std::optional<Program> CompileAndAddProgram(const CacheIndexKey& key, const std::string_view& vertex_shader,
|
||||
const std::string_view& fragment_shader, const PreLinkCallback& callback);
|
||||
|
||||
std::optional<Program> CompileComputeProgram(const std::string_view& glsl, const PreLinkCallback& callback, bool set_retrievable);
|
||||
std::optional<Program> CompileAndAddComputeProgram(const CacheIndexKey& key, const std::string_view& glsl, const PreLinkCallback& callback);
|
||||
|
||||
std::string m_base_path;
|
||||
std::FILE* m_index_file = nullptr;
|
||||
std::FILE* m_blob_file = nullptr;
|
||||
|
||||
CacheIndex m_index;
|
||||
u32 m_version = 0;
|
||||
bool m_program_binary_supported = false;
|
||||
};
|
||||
} // namespace GL
|
||||
@@ -1,357 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "StreamBuffer.h"
|
||||
#include "common/Align.h"
|
||||
#include "common/AlignedMalloc.h"
|
||||
#include "common/Assertions.h"
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
namespace GL
|
||||
{
|
||||
StreamBuffer::StreamBuffer(GLenum target, GLuint buffer_id, u32 size)
|
||||
: m_target(target)
|
||||
, m_buffer_id(buffer_id)
|
||||
, m_size(size)
|
||||
{
|
||||
}
|
||||
|
||||
StreamBuffer::~StreamBuffer()
|
||||
{
|
||||
glDeleteBuffers(1, &m_buffer_id);
|
||||
}
|
||||
|
||||
void StreamBuffer::Bind()
|
||||
{
|
||||
glBindBuffer(m_target, m_buffer_id);
|
||||
}
|
||||
|
||||
void StreamBuffer::Unbind()
|
||||
{
|
||||
glBindBuffer(m_target, 0);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// Uses glBufferSubData() to update. Preferred for drivers which don't support {ARB,EXT}_buffer_storage.
|
||||
class BufferSubDataStreamBuffer final : public StreamBuffer
|
||||
{
|
||||
public:
|
||||
~BufferSubDataStreamBuffer() override
|
||||
{
|
||||
_aligned_free(m_cpu_buffer);
|
||||
}
|
||||
|
||||
MappingResult Map(u32 alignment, u32 min_size) override
|
||||
{
|
||||
return MappingResult{static_cast<void*>(m_cpu_buffer), 0, 0, m_size / alignment};
|
||||
}
|
||||
|
||||
void Unmap(u32 used_size) override
|
||||
{
|
||||
if (used_size == 0)
|
||||
return;
|
||||
|
||||
glBindBuffer(m_target, m_buffer_id);
|
||||
glBufferSubData(m_target, 0, used_size, m_cpu_buffer);
|
||||
}
|
||||
|
||||
u32 GetChunkSize() const override
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
static std::unique_ptr<StreamBuffer> Create(GLenum target, u32 size)
|
||||
{
|
||||
glGetError();
|
||||
|
||||
GLuint buffer_id;
|
||||
glGenBuffers(1, &buffer_id);
|
||||
glBindBuffer(target, buffer_id);
|
||||
glBufferData(target, size, nullptr, GL_STREAM_DRAW);
|
||||
|
||||
GLenum err = glGetError();
|
||||
if (err != GL_NO_ERROR)
|
||||
{
|
||||
glBindBuffer(target, 0);
|
||||
glDeleteBuffers(1, &buffer_id);
|
||||
return {};
|
||||
}
|
||||
|
||||
return std::unique_ptr<StreamBuffer>(new BufferSubDataStreamBuffer(target, buffer_id, size));
|
||||
}
|
||||
|
||||
private:
|
||||
BufferSubDataStreamBuffer(GLenum target, GLuint buffer_id, u32 size)
|
||||
: StreamBuffer(target, buffer_id, size)
|
||||
{
|
||||
m_cpu_buffer = static_cast<u8*>(_aligned_malloc(size, 32));
|
||||
if (!m_cpu_buffer)
|
||||
pxFailRel("Failed to allocate CPU storage for GL buffer");
|
||||
}
|
||||
|
||||
u8* m_cpu_buffer;
|
||||
};
|
||||
|
||||
// Uses BufferData() to orphan the buffer after every update. Used on Mali where BufferSubData forces a sync.
|
||||
class BufferDataStreamBuffer final : public StreamBuffer
|
||||
{
|
||||
public:
|
||||
~BufferDataStreamBuffer() override
|
||||
{
|
||||
_aligned_free(m_cpu_buffer);
|
||||
}
|
||||
|
||||
MappingResult Map(u32 alignment, u32 min_size) override
|
||||
{
|
||||
return MappingResult{static_cast<void*>(m_cpu_buffer), 0, 0, m_size / alignment};
|
||||
}
|
||||
|
||||
void Unmap(u32 used_size) override
|
||||
{
|
||||
if (used_size == 0)
|
||||
return;
|
||||
|
||||
glBindBuffer(m_target, m_buffer_id);
|
||||
glBufferData(m_target, used_size, m_cpu_buffer, GL_STREAM_DRAW);
|
||||
}
|
||||
|
||||
u32 GetChunkSize() const override
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
static std::unique_ptr<StreamBuffer> Create(GLenum target, u32 size)
|
||||
{
|
||||
glGetError();
|
||||
|
||||
GLuint buffer_id;
|
||||
glGenBuffers(1, &buffer_id);
|
||||
glBindBuffer(target, buffer_id);
|
||||
glBufferData(target, size, nullptr, GL_STREAM_DRAW);
|
||||
|
||||
GLenum err = glGetError();
|
||||
if (err != GL_NO_ERROR)
|
||||
{
|
||||
glBindBuffer(target, 0);
|
||||
glDeleteBuffers(1, &buffer_id);
|
||||
return {};
|
||||
}
|
||||
|
||||
return std::unique_ptr<StreamBuffer>(new BufferDataStreamBuffer(target, buffer_id, size));
|
||||
}
|
||||
|
||||
private:
|
||||
BufferDataStreamBuffer(GLenum target, GLuint buffer_id, u32 size)
|
||||
: StreamBuffer(target, buffer_id, size)
|
||||
{
|
||||
m_cpu_buffer = static_cast<u8*>(_aligned_malloc(size, 32));
|
||||
if (!m_cpu_buffer)
|
||||
pxFailRel("Failed to allocate CPU storage for GL buffer");
|
||||
}
|
||||
|
||||
u8* m_cpu_buffer;
|
||||
};
|
||||
|
||||
// Base class for implementations which require syncing.
|
||||
class SyncingStreamBuffer : public StreamBuffer
|
||||
{
|
||||
public:
|
||||
enum : u32
|
||||
{
|
||||
NUM_SYNC_POINTS = 16
|
||||
};
|
||||
|
||||
virtual ~SyncingStreamBuffer() override
|
||||
{
|
||||
for (u32 i = m_available_block_index; i <= m_used_block_index; i++)
|
||||
{
|
||||
pxAssert(m_sync_objects[i]);
|
||||
glDeleteSync(m_sync_objects[i]);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
SyncingStreamBuffer(GLenum target, GLuint buffer_id, u32 size)
|
||||
: StreamBuffer(target, buffer_id, size)
|
||||
, m_bytes_per_block((size + (NUM_SYNC_POINTS)-1) / NUM_SYNC_POINTS)
|
||||
{
|
||||
}
|
||||
|
||||
__fi u32 GetSyncIndexForOffset(u32 offset) { return offset / m_bytes_per_block; }
|
||||
|
||||
__fi void AddSyncsForOffset(u32 offset)
|
||||
{
|
||||
const u32 end = GetSyncIndexForOffset(offset);
|
||||
for (; m_used_block_index < end; m_used_block_index++)
|
||||
{
|
||||
pxAssert(!m_sync_objects[m_used_block_index]);
|
||||
m_sync_objects[m_used_block_index] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
__fi void WaitForSync(GLsync& sync)
|
||||
{
|
||||
glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
|
||||
glDeleteSync(sync);
|
||||
sync = nullptr;
|
||||
}
|
||||
|
||||
__fi void EnsureSyncsWaitedForOffset(u32 offset)
|
||||
{
|
||||
const u32 end = std::min<u32>(GetSyncIndexForOffset(offset) + 1, NUM_SYNC_POINTS);
|
||||
for (; m_available_block_index < end; m_available_block_index++)
|
||||
{
|
||||
pxAssert(m_sync_objects[m_available_block_index]);
|
||||
WaitForSync(m_sync_objects[m_available_block_index]);
|
||||
}
|
||||
}
|
||||
|
||||
void AllocateSpace(u32 size)
|
||||
{
|
||||
// add sync objects for writes since the last allocation
|
||||
AddSyncsForOffset(m_position);
|
||||
|
||||
// wait for sync objects for the space we want to use
|
||||
EnsureSyncsWaitedForOffset(m_position + size);
|
||||
|
||||
// wrap-around?
|
||||
if ((m_position + size) > m_size)
|
||||
{
|
||||
// current position ... buffer end
|
||||
AddSyncsForOffset(m_size);
|
||||
|
||||
// rewind, and try again
|
||||
m_position = 0;
|
||||
|
||||
// wait for the sync at the start of the buffer
|
||||
WaitForSync(m_sync_objects[0]);
|
||||
m_available_block_index = 1;
|
||||
|
||||
// and however much more we need to satisfy the allocation
|
||||
EnsureSyncsWaitedForOffset(size);
|
||||
m_used_block_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
u32 GetChunkSize() const override
|
||||
{
|
||||
return m_size / NUM_SYNC_POINTS;
|
||||
}
|
||||
|
||||
u32 m_position = 0;
|
||||
u32 m_used_block_index = 0;
|
||||
u32 m_available_block_index = NUM_SYNC_POINTS;
|
||||
u32 m_bytes_per_block;
|
||||
std::array<GLsync, NUM_SYNC_POINTS> m_sync_objects{};
|
||||
};
|
||||
|
||||
class BufferStorageStreamBuffer : public SyncingStreamBuffer
|
||||
{
|
||||
public:
|
||||
~BufferStorageStreamBuffer() override
|
||||
{
|
||||
glBindBuffer(m_target, m_buffer_id);
|
||||
glUnmapBuffer(m_target);
|
||||
glBindBuffer(m_target, 0);
|
||||
}
|
||||
|
||||
MappingResult Map(u32 alignment, u32 min_size) override
|
||||
{
|
||||
if (m_position > 0)
|
||||
m_position = Common::AlignUp(m_position, alignment);
|
||||
|
||||
AllocateSpace(min_size);
|
||||
pxAssert((m_position + min_size) <= (m_available_block_index * m_bytes_per_block));
|
||||
|
||||
const u32 free_space_in_block = ((m_available_block_index * m_bytes_per_block) - m_position);
|
||||
return MappingResult{static_cast<void*>(m_mapped_ptr + m_position), m_position, m_position / alignment,
|
||||
free_space_in_block / alignment};
|
||||
}
|
||||
|
||||
void Unmap(u32 used_size) override
|
||||
{
|
||||
pxAssert((m_position + used_size) <= m_size);
|
||||
if (!m_coherent)
|
||||
{
|
||||
Bind();
|
||||
glFlushMappedBufferRange(m_target, m_position, used_size);
|
||||
}
|
||||
|
||||
m_position += used_size;
|
||||
}
|
||||
|
||||
static std::unique_ptr<StreamBuffer> Create(GLenum target, u32 size, bool coherent = true)
|
||||
{
|
||||
glGetError();
|
||||
|
||||
GLuint buffer_id;
|
||||
glGenBuffers(1, &buffer_id);
|
||||
glBindBuffer(target, buffer_id);
|
||||
|
||||
const u32 flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0);
|
||||
const u32 map_flags = GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT);
|
||||
if (GLAD_GL_VERSION_4_4 || GLAD_GL_ARB_buffer_storage)
|
||||
glBufferStorage(target, size, nullptr, flags);
|
||||
else if (GLAD_GL_EXT_buffer_storage)
|
||||
glBufferStorageEXT(target, size, nullptr, flags);
|
||||
|
||||
GLenum err = glGetError();
|
||||
if (err != GL_NO_ERROR)
|
||||
{
|
||||
glBindBuffer(target, 0);
|
||||
glDeleteBuffers(1, &buffer_id);
|
||||
return {};
|
||||
}
|
||||
|
||||
u8* mapped_ptr = static_cast<u8*>(glMapBufferRange(target, 0, size, map_flags));
|
||||
pxAssertRel(mapped_ptr, "Persistent buffer was mapped");
|
||||
|
||||
return std::unique_ptr<StreamBuffer>(new BufferStorageStreamBuffer(target, buffer_id, size, mapped_ptr, coherent));
|
||||
}
|
||||
|
||||
private:
|
||||
BufferStorageStreamBuffer(GLenum target, GLuint buffer_id, u32 size, u8* mapped_ptr, bool coherent)
|
||||
: SyncingStreamBuffer(target, buffer_id, size)
|
||||
, m_mapped_ptr(mapped_ptr)
|
||||
, m_coherent(coherent)
|
||||
{
|
||||
}
|
||||
|
||||
u8* m_mapped_ptr;
|
||||
bool m_coherent;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
std::unique_ptr<StreamBuffer> StreamBuffer::Create(GLenum target, u32 size)
|
||||
{
|
||||
std::unique_ptr<StreamBuffer> buf;
|
||||
if (GLAD_GL_VERSION_4_4 || GLAD_GL_ARB_buffer_storage || GLAD_GL_EXT_buffer_storage)
|
||||
{
|
||||
buf = detail::BufferStorageStreamBuffer::Create(target, size);
|
||||
if (buf)
|
||||
return buf;
|
||||
}
|
||||
|
||||
// BufferSubData is slower on all drivers except NVIDIA...
|
||||
const char* vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
|
||||
if (std::strstr(vendor, "NVIDIA"))
|
||||
return detail::BufferSubDataStreamBuffer::Create(target, size);
|
||||
else
|
||||
return detail::BufferDataStreamBuffer::Create(target, size);
|
||||
}
|
||||
} // namespace GL
|
||||
@@ -1,61 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "glad.h"
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace GL
|
||||
{
|
||||
/// Provides a buffer for streaming data to the GPU, ideally in write-combined memory.
|
||||
class StreamBuffer
|
||||
{
|
||||
public:
|
||||
virtual ~StreamBuffer();
|
||||
|
||||
__fi GLuint GetGLBufferId() const { return m_buffer_id; }
|
||||
__fi GLenum GetGLTarget() const { return m_target; }
|
||||
__fi u32 GetSize() const { return m_size; }
|
||||
|
||||
void Bind();
|
||||
void Unbind();
|
||||
|
||||
struct MappingResult
|
||||
{
|
||||
void* pointer;
|
||||
u32 buffer_offset;
|
||||
u32 index_aligned; // offset / alignment, suitable for base vertex
|
||||
u32 space_aligned; // remaining space / alignment
|
||||
};
|
||||
|
||||
virtual MappingResult Map(u32 alignment, u32 min_size) = 0;
|
||||
virtual void Unmap(u32 used_size) = 0;
|
||||
|
||||
/// Returns the minimum granularity of blocks which sync objects will be created around.
|
||||
virtual u32 GetChunkSize() const = 0;
|
||||
|
||||
static std::unique_ptr<StreamBuffer> Create(GLenum target, u32 size);
|
||||
|
||||
protected:
|
||||
StreamBuffer(GLenum target, GLuint buffer_id, u32 size);
|
||||
|
||||
GLenum m_target;
|
||||
GLuint m_buffer_id;
|
||||
u32 m_size;
|
||||
};
|
||||
} // namespace GL
|
||||
@@ -31,6 +31,10 @@
|
||||
#include "common/Threading.h"
|
||||
#include "common/WindowInfo.h"
|
||||
|
||||
#ifdef DBUS_API
|
||||
#include <dbus/dbus.h>
|
||||
#endif
|
||||
|
||||
// Returns 0 on failure (not supported by the operating system).
|
||||
u64 GetPhysicalMemory()
|
||||
{
|
||||
@@ -69,11 +73,77 @@ std::string GetOSVersionString()
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef X11_API
|
||||
#ifdef DBUS_API
|
||||
|
||||
bool ChangeScreenSaverStateDBus(const bool inhibit_requested, const char* program_name, const char* reason)
|
||||
{
|
||||
static dbus_uint32_t s_cookie;
|
||||
// "error_dbus" doesn't need to be cleared in the end with "dbus_message_unref" at least if there is
|
||||
// no error set, since calling "dbus_error_free" reinitializes it like "dbus_error_init" after freeing.
|
||||
DBusError error_dbus;
|
||||
dbus_error_init(&error_dbus);
|
||||
DBusConnection* connection = nullptr;
|
||||
DBusMessage* message = nullptr;
|
||||
DBusMessage* response = nullptr;
|
||||
// Initialized here because initializations should be before "goto" statements.
|
||||
const char* bus_method = (inhibit_requested) ? "Inhibit" : "UnInhibit";
|
||||
// "dbus_bus_get" gets a pointer to the same connection in libdbus, if exists, without creating a new connection.
|
||||
// this doesn't need to be deleted, except if there's an error then calling "dbus_connection_unref", to free it,
|
||||
// might be better so a new connection is established on the next try.
|
||||
if (!(connection = dbus_bus_get(DBUS_BUS_SESSION, &error_dbus)) || (dbus_error_is_set(&error_dbus)))
|
||||
goto cleanup;
|
||||
if (!(message = dbus_message_new_method_call("org.freedesktop.ScreenSaver", "/org/freedesktop/ScreenSaver", "org.freedesktop.ScreenSaver", bus_method)))
|
||||
goto cleanup;
|
||||
// Initialize an append iterator for the message, gets freed with the message.
|
||||
DBusMessageIter message_itr;
|
||||
dbus_message_iter_init_append(message, &message_itr);
|
||||
if (inhibit_requested)
|
||||
{
|
||||
// Append process/window name.
|
||||
if (!dbus_message_iter_append_basic(&message_itr, DBUS_TYPE_STRING, &program_name))
|
||||
goto cleanup;
|
||||
// Append reason for inhibiting the screensaver.
|
||||
if (!dbus_message_iter_append_basic(&message_itr, DBUS_TYPE_STRING, &reason))
|
||||
goto cleanup;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only Append the cookie.
|
||||
if (!dbus_message_iter_append_basic(&message_itr, DBUS_TYPE_UINT32, &s_cookie))
|
||||
goto cleanup;
|
||||
}
|
||||
// Send message and get response.
|
||||
if (!(response = dbus_connection_send_with_reply_and_block(connection, message, DBUS_TIMEOUT_USE_DEFAULT, &error_dbus))
|
||||
|| dbus_error_is_set(&error_dbus))
|
||||
goto cleanup;
|
||||
if (inhibit_requested)
|
||||
{
|
||||
// Get the cookie from the response message.
|
||||
if (!dbus_message_get_args(response, &error_dbus, DBUS_TYPE_UINT32, &s_cookie, DBUS_TYPE_INVALID))
|
||||
goto cleanup;
|
||||
}
|
||||
dbus_message_unref(message);
|
||||
dbus_message_unref(response);
|
||||
return true;
|
||||
cleanup:
|
||||
if (dbus_error_is_set(&error_dbus))
|
||||
dbus_error_free(&error_dbus);
|
||||
if (connection)
|
||||
dbus_connection_unref(connection);
|
||||
if (message)
|
||||
dbus_message_unref(message);
|
||||
if (response)
|
||||
dbus_message_unref(response);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(DBUS_API) && defined(X11_API)
|
||||
|
||||
static bool SetScreensaverInhibitX11(const WindowInfo& wi, bool inhibit)
|
||||
{
|
||||
extern char **environ;
|
||||
extern char** environ;
|
||||
|
||||
const char* command = "xdg-screensaver";
|
||||
const char* operation = inhibit ? "suspend" : "resume";
|
||||
@@ -88,8 +158,6 @@ static bool SetScreensaverInhibitX11(const WindowInfo& wi, bool inhibit)
|
||||
return (res == 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static bool SetScreensaverInhibit(const WindowInfo& wi, bool inhibit)
|
||||
{
|
||||
switch (wi.type)
|
||||
@@ -106,8 +174,18 @@ static bool SetScreensaverInhibit(const WindowInfo& wi, bool inhibit)
|
||||
|
||||
static std::optional<WindowInfo> s_inhibit_window_info;
|
||||
|
||||
#endif
|
||||
|
||||
bool WindowInfo::InhibitScreensaver(const WindowInfo& wi, bool inhibit)
|
||||
{
|
||||
|
||||
#ifdef DBUS_API
|
||||
|
||||
return ChangeScreenSaverStateDBus(inhibit, "PCSX2", "PCSX2 VM is running.");
|
||||
|
||||
#else
|
||||
|
||||
//ChangeScreenSaverStateDBus
|
||||
if (s_inhibit_window_info.has_value())
|
||||
{
|
||||
// Bit of extra logic here, because wx spams it and we don't want to
|
||||
@@ -132,6 +210,9 @@ bool WindowInfo::InhibitScreensaver(const WindowInfo& wi, bool inhibit)
|
||||
|
||||
s_inhibit_window_info = wi;
|
||||
return true;
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
bool Common::PlaySoundAsync(const char* path)
|
||||
|
||||
+164
-154
@@ -15,197 +15,207 @@
|
||||
|
||||
#include "common/Perf.h"
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#ifdef __unix__
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include "common/Assertions.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#ifdef ENABLE_VTUNE
|
||||
#include "jitprofiling.h"
|
||||
#endif
|
||||
|
||||
#include <string> // std::string
|
||||
#include <cstring> // strncpy
|
||||
#include <algorithm> // std::remove_if
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <atomic>
|
||||
#include <ctime>
|
||||
#include <mutex>
|
||||
#include <elf.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
//#define ProfileWithPerf
|
||||
#define MERGE_BLOCK_RESULT
|
||||
//#define ProfileWithPerfJitDump
|
||||
|
||||
#ifdef ENABLE_VTUNE
|
||||
#ifdef _WIN32
|
||||
#if defined(ENABLE_VTUNE) && defined(_WIN32)
|
||||
#pragma comment(lib, "jitprofiling.lib")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace Perf
|
||||
{
|
||||
// Warning object aren't thread safe
|
||||
InfoVector any("");
|
||||
InfoVector ee("EE");
|
||||
InfoVector iop("IOP");
|
||||
InfoVector vu("VU");
|
||||
InfoVector vif("VIF");
|
||||
Group any("");
|
||||
Group ee("EE");
|
||||
Group iop("IOP");
|
||||
Group vu0("VU0");
|
||||
Group vu1("VU1");
|
||||
Group vif("VIF");
|
||||
|
||||
// Perf is only supported on linux
|
||||
#if defined(__linux__) && (defined(ProfileWithPerf) || defined(ENABLE_VTUNE))
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Implementation of the Info object
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Info::Info(uptr x86, u32 size, const char* symbol)
|
||||
: m_x86(x86)
|
||||
, m_size(size)
|
||||
, m_dynamic(false)
|
||||
#if defined(__linux__) && defined(ProfileWithPerf)
|
||||
static std::FILE* s_map_file = nullptr;
|
||||
static bool s_map_file_opened = false;
|
||||
static std::mutex s_mutex;
|
||||
static void RegisterMethod(const void* ptr, size_t size, const char* symbol)
|
||||
{
|
||||
strncpy(m_symbol, symbol, sizeof(m_symbol));
|
||||
}
|
||||
std::unique_lock lock(s_mutex);
|
||||
|
||||
Info::Info(uptr x86, u32 size, const char* symbol, u32 pc)
|
||||
: m_x86(x86)
|
||||
, m_size(size)
|
||||
, m_dynamic(true)
|
||||
{
|
||||
snprintf(m_symbol, sizeof(m_symbol), "%s_0x%08x", symbol, pc);
|
||||
}
|
||||
|
||||
void Info::Print(FILE* fp)
|
||||
{
|
||||
fprintf(fp, "%x %x %s\n", m_x86, m_size, m_symbol);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Implementation of the InfoVector object
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
InfoVector::InfoVector(const char* prefix)
|
||||
{
|
||||
strncpy(m_prefix, prefix, sizeof(m_prefix));
|
||||
#ifdef ENABLE_VTUNE
|
||||
m_vtune_id = iJIT_GetNewMethodID();
|
||||
#else
|
||||
m_vtune_id = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void InfoVector::print(FILE* fp)
|
||||
{
|
||||
for (auto&& it : m_v)
|
||||
it.Print(fp);
|
||||
}
|
||||
|
||||
void InfoVector::map(uptr x86, u32 size, const char* symbol)
|
||||
{
|
||||
// This function is typically used for dispatcher and recompiler.
|
||||
// Dispatchers are on a page and must always be kept.
|
||||
// Recompilers are much bigger (TODO check VIF) and are only
|
||||
// useful when MERGE_BLOCK_RESULT is defined
|
||||
#if defined(ENABLE_VTUNE) || !defined(MERGE_BLOCK_RESULT)
|
||||
u32 max_code_size = 16 * _1kb;
|
||||
#else
|
||||
u32 max_code_size = _1gb;
|
||||
#endif
|
||||
|
||||
if (size < max_code_size)
|
||||
if (!s_map_file)
|
||||
{
|
||||
m_v.emplace_back(x86, size, symbol);
|
||||
if (s_map_file_opened)
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_VTUNE
|
||||
std::string name = std::string(symbol);
|
||||
|
||||
iJIT_Method_Load ml;
|
||||
|
||||
memset(&ml, 0, sizeof(ml));
|
||||
|
||||
ml.method_id = iJIT_GetNewMethodID();
|
||||
ml.method_name = (char*)name.c_str();
|
||||
ml.method_load_address = (void*)x86;
|
||||
ml.method_size = size;
|
||||
|
||||
iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, &ml);
|
||||
|
||||
//fprintf(stderr, "mapF %s: %p size %dKB\n", ml.method_name, ml.method_load_address, ml.method_size / 1024u);
|
||||
#endif
|
||||
char file[256];
|
||||
snprintf(file, std::size(file), "/tmp/perf-%d.map", getpid());
|
||||
s_map_file = std::fopen(file, "wb");
|
||||
s_map_file_opened = true;
|
||||
if (!s_map_file)
|
||||
return;
|
||||
}
|
||||
|
||||
std::fprintf(s_map_file, "%" PRIx64 " %zx %s\n", static_cast<u64>(reinterpret_cast<uintptr_t>(ptr)), size, symbol);
|
||||
std::fflush(s_map_file);
|
||||
}
|
||||
#elif defined(__linux__) && defined(ProfileWithPerfJitDump)
|
||||
enum : u32
|
||||
{
|
||||
JIT_CODE_LOAD = 0,
|
||||
JIT_CODE_MOVE = 1,
|
||||
JIT_CODE_DEBUG_INFO = 2,
|
||||
JIT_CODE_CLOSE = 3,
|
||||
JIT_CODE_UNWINDING_INFO = 4
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct JITDUMP_HEADER
|
||||
{
|
||||
u32 magic = 0x4A695444; // JiTD
|
||||
u32 version = 1;
|
||||
u32 header_size = sizeof(JITDUMP_HEADER);
|
||||
u32 elf_mach;
|
||||
u32 pad1 = 0;
|
||||
u32 pid;
|
||||
u64 timestamp;
|
||||
u64 flags = 0;
|
||||
};
|
||||
struct JITDUMP_RECORD_HEADER
|
||||
{
|
||||
u32 id;
|
||||
u32 total_size;
|
||||
u64 timestamp;
|
||||
};
|
||||
struct JITDUMP_CODE_LOAD
|
||||
{
|
||||
JITDUMP_RECORD_HEADER header;
|
||||
u32 pid;
|
||||
u32 tid;
|
||||
u64 vma;
|
||||
u64 code_addr;
|
||||
u64 code_size;
|
||||
u64 code_index;
|
||||
// name
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static u64 JitDumpTimestamp()
|
||||
{
|
||||
struct timespec ts = {};
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (static_cast<u64>(ts.tv_sec) * 1000000000ULL) + static_cast<u64>(ts.tv_nsec);
|
||||
}
|
||||
|
||||
void InfoVector::map(uptr x86, u32 size, u32 pc)
|
||||
static FILE* s_jitdump_file = nullptr;
|
||||
static bool s_jitdump_file_opened = false;
|
||||
static std::mutex s_jitdump_mutex;
|
||||
static u32 s_jitdump_record_id;
|
||||
|
||||
static void RegisterMethod(const void* ptr, size_t size, const char* symbol)
|
||||
{
|
||||
#ifndef MERGE_BLOCK_RESULT
|
||||
m_v.emplace_back(x86, size, m_prefix, pc);
|
||||
#endif
|
||||
const u32 namelen = std::strlen(symbol) + 1;
|
||||
|
||||
#ifdef ENABLE_VTUNE
|
||||
iJIT_Method_Load_V2 ml;
|
||||
std::unique_lock lock(s_jitdump_mutex);
|
||||
if (!s_jitdump_file)
|
||||
{
|
||||
if (!s_jitdump_file_opened)
|
||||
{
|
||||
char file[256];
|
||||
snprintf(file, std::size(file), "jit-%d.dump", getpid());
|
||||
s_jitdump_file = fopen(file, "w+b");
|
||||
s_jitdump_file_opened = true;
|
||||
if (!s_jitdump_file)
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&ml, 0, sizeof(ml));
|
||||
void* perf_marker = mmap(nullptr, 4096, PROT_READ | PROT_EXEC, MAP_PRIVATE, fileno(s_jitdump_file), 0);
|
||||
pxAssertRel(perf_marker != MAP_FAILED, "Map perf marker");
|
||||
|
||||
#ifdef MERGE_BLOCK_RESULT
|
||||
ml.method_id = m_vtune_id;
|
||||
ml.method_name = m_prefix;
|
||||
#else
|
||||
std::string name = std::string(m_prefix) + "_" + std::to_string(pc);
|
||||
JITDUMP_HEADER jh = {};
|
||||
jh.elf_mach = EM_X86_64;
|
||||
jh.pid = getpid();
|
||||
jh.timestamp = JitDumpTimestamp();
|
||||
std::fwrite(&jh, sizeof(jh), 1, s_jitdump_file);
|
||||
}
|
||||
|
||||
JITDUMP_CODE_LOAD cl = {};
|
||||
cl.header.id = JIT_CODE_LOAD;
|
||||
cl.header.total_size = sizeof(cl) + namelen + static_cast<u32>(size);
|
||||
cl.header.timestamp = JitDumpTimestamp();
|
||||
cl.pid = getpid();
|
||||
cl.tid = syscall(SYS_gettid);
|
||||
cl.vma = 0;
|
||||
cl.code_addr = static_cast<u64>(reinterpret_cast<uintptr_t>(ptr));
|
||||
cl.code_size = static_cast<u64>(size);
|
||||
cl.code_index = s_jitdump_record_id++;
|
||||
std::fwrite(&cl, sizeof(cl), 1, s_jitdump_file);
|
||||
std::fwrite(symbol, namelen, 1, s_jitdump_file);
|
||||
std::fwrite(ptr, size, 1, s_jitdump_file);
|
||||
std::fflush(s_jitdump_file);
|
||||
}
|
||||
#elif defined(ENABLE_VTUNE)
|
||||
static void RegisterMethod(const void* ptr, size_t size, const char* symbol)
|
||||
{
|
||||
iJIT_Method_Load_V2 ml = {};
|
||||
ml.method_id = iJIT_GetNewMethodID();
|
||||
ml.method_name = (char*)name.c_str();
|
||||
#endif
|
||||
ml.method_load_address = (void*)x86;
|
||||
ml.method_size = size;
|
||||
|
||||
ml.method_name = const_cast<char*>(symbol);
|
||||
ml.method_load_address = const_cast<void*>(ptr);
|
||||
ml.method_size = static_cast<unsigned int>(size);
|
||||
iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED_V2, &ml);
|
||||
|
||||
//fprintf(stderr, "mapB %s: %p size %d\n", ml.method_name, ml.method_load_address, ml.method_size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void InfoVector::reset()
|
||||
#if (defined(__linux__) && (defined(ProfileWithPerf) || defined(ProfileWithPerfJitDump))) || defined(ENABLE_VTUNE)
|
||||
void Group::Register(const void* ptr, size_t size, const char* symbol)
|
||||
{
|
||||
auto dynamic = std::remove_if(m_v.begin(), m_v.end(), [](Info i) { return i.m_dynamic; });
|
||||
m_v.erase(dynamic, m_v.end());
|
||||
char full_symbol[128];
|
||||
if (HasPrefix())
|
||||
std::snprintf(full_symbol, std::size(full_symbol), "%s_%s", m_prefix, symbol);
|
||||
else
|
||||
StringUtil::Strlcpy(full_symbol, symbol, std::size(full_symbol));
|
||||
RegisterMethod(ptr, size, full_symbol);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Global function
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void dump()
|
||||
void Group::RegisterPC(const void* ptr, size_t size, u32 pc)
|
||||
{
|
||||
char file[256];
|
||||
snprintf(file, 250, "/tmp/perf-%d.map", getpid());
|
||||
FILE* fp = fopen(file, "w");
|
||||
|
||||
any.print(fp);
|
||||
ee.print(fp);
|
||||
iop.print(fp);
|
||||
vu.print(fp);
|
||||
|
||||
if (fp)
|
||||
fclose(fp);
|
||||
char full_symbol[128];
|
||||
if (HasPrefix())
|
||||
std::snprintf(full_symbol, std::size(full_symbol), "%s_%08X", m_prefix, pc);
|
||||
else
|
||||
std::snprintf(full_symbol, std::size(full_symbol), "%08X", pc);
|
||||
RegisterMethod(ptr, size, full_symbol);
|
||||
}
|
||||
|
||||
void dump_and_reset()
|
||||
void Group::RegisterKey(const void* ptr, size_t size, const char* prefix, u64 key)
|
||||
{
|
||||
dump();
|
||||
|
||||
any.reset();
|
||||
ee.reset();
|
||||
iop.reset();
|
||||
vu.reset();
|
||||
char full_symbol[128];
|
||||
if (HasPrefix())
|
||||
std::snprintf(full_symbol, std::size(full_symbol), "%s_%s%016" PRIX64, m_prefix, prefix, key);
|
||||
else
|
||||
std::snprintf(full_symbol, std::size(full_symbol), "%s%016" PRIX64, prefix, key);
|
||||
RegisterMethod(ptr, size, full_symbol);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Dummy implementation
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
InfoVector::InfoVector(const char* prefix)
|
||||
: m_vtune_id(0)
|
||||
{
|
||||
}
|
||||
void InfoVector::map(uptr x86, u32 size, const char* symbol) {}
|
||||
void InfoVector::map(uptr x86, u32 size, u32 pc) {}
|
||||
void InfoVector::reset() {}
|
||||
|
||||
void dump() {}
|
||||
void dump_and_reset() {}
|
||||
|
||||
void Group::Register(const void* ptr, size_t size, const char* symbol) {}
|
||||
void Group::RegisterPC(const void* ptr, size_t size, u32 pc) {}
|
||||
void Group::RegisterKey(const void* ptr, size_t size, const char* prefix, u64 key) {}
|
||||
#endif
|
||||
} // namespace Perf
|
||||
|
||||
+13
-32
@@ -21,42 +21,23 @@
|
||||
|
||||
namespace Perf
|
||||
{
|
||||
|
||||
struct Info
|
||||
class Group
|
||||
{
|
||||
uptr m_x86;
|
||||
u32 m_size;
|
||||
char m_symbol[20];
|
||||
// The idea is to keep static zones that are set only
|
||||
// once.
|
||||
bool m_dynamic;
|
||||
|
||||
Info(uptr x86, u32 size, const char* symbol);
|
||||
Info(uptr x86, u32 size, const char* symbol, u32 pc);
|
||||
void Print(FILE* fp);
|
||||
};
|
||||
|
||||
class InfoVector
|
||||
{
|
||||
std::vector<Info> m_v;
|
||||
char m_prefix[20];
|
||||
unsigned int m_vtune_id;
|
||||
const char* m_prefix;
|
||||
|
||||
public:
|
||||
InfoVector(const char* prefix);
|
||||
constexpr Group(const char* prefix) : m_prefix(prefix) {}
|
||||
bool HasPrefix() const { return (m_prefix && m_prefix[0]); }
|
||||
|
||||
void print(FILE* fp);
|
||||
void map(uptr x86, u32 size, const char* symbol);
|
||||
void map(uptr x86, u32 size, u32 pc);
|
||||
void reset();
|
||||
void Register(const void* ptr, size_t size, const char* symbol);
|
||||
void RegisterPC(const void* ptr, size_t size, u32 pc);
|
||||
void RegisterKey(const void* ptr, size_t size, const char* prefix, u64 key);
|
||||
};
|
||||
|
||||
void dump();
|
||||
void dump_and_reset();
|
||||
|
||||
extern InfoVector any;
|
||||
extern InfoVector ee;
|
||||
extern InfoVector iop;
|
||||
extern InfoVector vu;
|
||||
extern InfoVector vif;
|
||||
extern Group any;
|
||||
extern Group ee;
|
||||
extern Group iop;
|
||||
extern Group vu0;
|
||||
extern Group vu1;
|
||||
extern Group vif;
|
||||
} // namespace Perf
|
||||
|
||||
+2
-8
@@ -100,10 +100,7 @@ namespace StringUtil
|
||||
return std::nullopt;
|
||||
|
||||
if (endptr)
|
||||
{
|
||||
const size_t remaining_len = end - ptr - 1;
|
||||
*endptr = (remaining_len > 0) ? std::string_view(result.ptr, remaining_len) : std::string_view();
|
||||
}
|
||||
*endptr = (result.ptr < end) ? std::string_view(result.ptr, end - result.ptr) : std::string_view();
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -131,10 +128,7 @@ namespace StringUtil
|
||||
return std::nullopt;
|
||||
|
||||
if (endptr)
|
||||
{
|
||||
const size_t remaining_len = end - ptr - 1;
|
||||
*endptr = (remaining_len > 0) ? std::string_view(result.ptr, remaining_len) : std::string_view();
|
||||
}
|
||||
*endptr = (result.ptr < end) ? std::string_view(result.ptr, end - result.ptr) : std::string_view();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -1,951 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/Vulkan/Builders.h"
|
||||
#include "common/Vulkan/Util.h"
|
||||
#include "common/Assertions.h"
|
||||
#include <limits>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
DescriptorSetLayoutBuilder::DescriptorSetLayoutBuilder() { Clear(); }
|
||||
|
||||
void DescriptorSetLayoutBuilder::Clear()
|
||||
{
|
||||
m_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
m_ci.pNext = nullptr;
|
||||
m_ci.flags = 0;
|
||||
m_ci.pBindings = nullptr;
|
||||
m_ci.bindingCount = 0;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayout DescriptorSetLayoutBuilder::Create(VkDevice device)
|
||||
{
|
||||
VkDescriptorSetLayout layout;
|
||||
VkResult res = vkCreateDescriptorSetLayout(device, &m_ci, nullptr, &layout);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateDescriptorSetLayout() failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
Clear();
|
||||
return layout;
|
||||
}
|
||||
|
||||
void DescriptorSetLayoutBuilder::AddBinding(
|
||||
u32 binding, VkDescriptorType dtype, u32 dcount, VkShaderStageFlags stages)
|
||||
{
|
||||
pxAssert(m_ci.bindingCount < MAX_BINDINGS);
|
||||
|
||||
VkDescriptorSetLayoutBinding& b = m_bindings[m_ci.bindingCount];
|
||||
b.binding = binding;
|
||||
b.descriptorType = dtype;
|
||||
b.descriptorCount = dcount;
|
||||
b.stageFlags = stages;
|
||||
b.pImmutableSamplers = nullptr;
|
||||
|
||||
m_ci.pBindings = m_bindings.data();
|
||||
m_ci.bindingCount++;
|
||||
}
|
||||
|
||||
PipelineLayoutBuilder::PipelineLayoutBuilder() { Clear(); }
|
||||
|
||||
void PipelineLayoutBuilder::Clear()
|
||||
{
|
||||
m_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
m_ci.pNext = nullptr;
|
||||
m_ci.flags = 0;
|
||||
m_ci.pSetLayouts = nullptr;
|
||||
m_ci.setLayoutCount = 0;
|
||||
m_ci.pPushConstantRanges = nullptr;
|
||||
m_ci.pushConstantRangeCount = 0;
|
||||
}
|
||||
|
||||
VkPipelineLayout PipelineLayoutBuilder::Create(VkDevice device)
|
||||
{
|
||||
VkPipelineLayout layout;
|
||||
VkResult res = vkCreatePipelineLayout(device, &m_ci, nullptr, &layout);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreatePipelineLayout() failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
Clear();
|
||||
return layout;
|
||||
}
|
||||
|
||||
void PipelineLayoutBuilder::AddDescriptorSet(VkDescriptorSetLayout layout)
|
||||
{
|
||||
pxAssert(m_ci.setLayoutCount < MAX_SETS);
|
||||
|
||||
m_sets[m_ci.setLayoutCount] = layout;
|
||||
|
||||
m_ci.setLayoutCount++;
|
||||
m_ci.pSetLayouts = m_sets.data();
|
||||
}
|
||||
|
||||
void PipelineLayoutBuilder::AddPushConstants(VkShaderStageFlags stages, u32 offset, u32 size)
|
||||
{
|
||||
pxAssert(m_ci.pushConstantRangeCount < MAX_PUSH_CONSTANTS);
|
||||
|
||||
VkPushConstantRange& r = m_push_constants[m_ci.pushConstantRangeCount];
|
||||
r.stageFlags = stages;
|
||||
r.offset = offset;
|
||||
r.size = size;
|
||||
|
||||
m_ci.pushConstantRangeCount++;
|
||||
m_ci.pPushConstantRanges = m_push_constants.data();
|
||||
}
|
||||
|
||||
GraphicsPipelineBuilder::GraphicsPipelineBuilder() { Clear(); }
|
||||
|
||||
void GraphicsPipelineBuilder::Clear()
|
||||
{
|
||||
m_ci = {};
|
||||
m_ci.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
|
||||
m_shader_stages = {};
|
||||
|
||||
m_vertex_input_state = {};
|
||||
m_vertex_input_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
m_ci.pVertexInputState = &m_vertex_input_state;
|
||||
m_vertex_attributes = {};
|
||||
m_vertex_buffers = {};
|
||||
|
||||
m_input_assembly = {};
|
||||
m_input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
|
||||
m_rasterization_state = {};
|
||||
m_rasterization_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
m_rasterization_state.lineWidth = 1.0f;
|
||||
m_depth_state = {};
|
||||
m_depth_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
||||
m_blend_state = {};
|
||||
m_blend_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
m_blend_attachments = {};
|
||||
|
||||
m_viewport_state = {};
|
||||
m_viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
m_viewport = {};
|
||||
m_scissor = {};
|
||||
|
||||
m_dynamic_state = {};
|
||||
m_dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||
m_dynamic_state_values = {};
|
||||
|
||||
m_multisample_state = {};
|
||||
m_multisample_state.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
|
||||
m_provoking_vertex = {};
|
||||
m_provoking_vertex.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT;
|
||||
|
||||
m_line_rasterization_state = {};
|
||||
m_line_rasterization_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT;
|
||||
|
||||
// set defaults
|
||||
SetNoCullRasterizationState();
|
||||
SetNoDepthTestState();
|
||||
SetNoBlendingState();
|
||||
SetPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
|
||||
|
||||
// have to be specified even if dynamic
|
||||
SetViewport(0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f);
|
||||
SetScissorRect(0, 0, 1, 1);
|
||||
SetMultisamples(VK_SAMPLE_COUNT_1_BIT);
|
||||
}
|
||||
|
||||
VkPipeline GraphicsPipelineBuilder::Create(VkDevice device, VkPipelineCache pipeline_cache, bool clear /* = true */)
|
||||
{
|
||||
VkPipeline pipeline;
|
||||
VkResult res = vkCreateGraphicsPipelines(device, pipeline_cache, 1, &m_ci, nullptr, &pipeline);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateGraphicsPipelines() failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (clear)
|
||||
Clear();
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetShaderStage(
|
||||
VkShaderStageFlagBits stage, VkShaderModule module, const char* entry_point)
|
||||
{
|
||||
pxAssert(m_ci.stageCount < MAX_SHADER_STAGES);
|
||||
|
||||
u32 index = 0;
|
||||
for (; index < m_ci.stageCount; index++)
|
||||
{
|
||||
if (m_shader_stages[index].stage == stage)
|
||||
break;
|
||||
}
|
||||
if (index == m_ci.stageCount)
|
||||
{
|
||||
m_ci.stageCount++;
|
||||
m_ci.pStages = m_shader_stages.data();
|
||||
}
|
||||
|
||||
VkPipelineShaderStageCreateInfo& s = m_shader_stages[index];
|
||||
s.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
s.stage = stage;
|
||||
s.module = module;
|
||||
s.pName = entry_point;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::AddVertexBuffer(
|
||||
u32 binding, u32 stride, VkVertexInputRate input_rate /*= VK_VERTEX_INPUT_RATE_VERTEX*/)
|
||||
{
|
||||
pxAssert(m_vertex_input_state.vertexAttributeDescriptionCount < MAX_VERTEX_BUFFERS);
|
||||
|
||||
VkVertexInputBindingDescription& b = m_vertex_buffers[m_vertex_input_state.vertexBindingDescriptionCount];
|
||||
b.binding = binding;
|
||||
b.stride = stride;
|
||||
b.inputRate = input_rate;
|
||||
|
||||
m_vertex_input_state.vertexBindingDescriptionCount++;
|
||||
m_vertex_input_state.pVertexBindingDescriptions = m_vertex_buffers.data();
|
||||
m_ci.pVertexInputState = &m_vertex_input_state;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::AddVertexAttribute(u32 location, u32 binding, VkFormat format, u32 offset)
|
||||
{
|
||||
pxAssert(m_vertex_input_state.vertexAttributeDescriptionCount < MAX_VERTEX_BUFFERS);
|
||||
|
||||
VkVertexInputAttributeDescription& a =
|
||||
m_vertex_attributes[m_vertex_input_state.vertexAttributeDescriptionCount];
|
||||
a.location = location;
|
||||
a.binding = binding;
|
||||
a.format = format;
|
||||
a.offset = offset;
|
||||
|
||||
m_vertex_input_state.vertexAttributeDescriptionCount++;
|
||||
m_vertex_input_state.pVertexAttributeDescriptions = m_vertex_attributes.data();
|
||||
m_ci.pVertexInputState = &m_vertex_input_state;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetPrimitiveTopology(
|
||||
VkPrimitiveTopology topology, bool enable_primitive_restart /*= false*/)
|
||||
{
|
||||
m_input_assembly.topology = topology;
|
||||
m_input_assembly.primitiveRestartEnable = enable_primitive_restart;
|
||||
|
||||
m_ci.pInputAssemblyState = &m_input_assembly;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetRasterizationState(
|
||||
VkPolygonMode polygon_mode, VkCullModeFlags cull_mode, VkFrontFace front_face)
|
||||
{
|
||||
m_rasterization_state.polygonMode = polygon_mode;
|
||||
m_rasterization_state.cullMode = cull_mode;
|
||||
m_rasterization_state.frontFace = front_face;
|
||||
|
||||
m_ci.pRasterizationState = &m_rasterization_state;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetLineWidth(float width)
|
||||
{
|
||||
m_rasterization_state.lineWidth = width;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetLineRasterizationMode(VkLineRasterizationModeEXT mode)
|
||||
{
|
||||
Util::AddPointerToChain(&m_rasterization_state, &m_line_rasterization_state);
|
||||
|
||||
m_line_rasterization_state.lineRasterizationMode = mode;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetMultisamples(u32 multisamples, bool per_sample_shading)
|
||||
{
|
||||
m_multisample_state.rasterizationSamples = static_cast<VkSampleCountFlagBits>(multisamples);
|
||||
m_multisample_state.sampleShadingEnable = per_sample_shading;
|
||||
m_multisample_state.minSampleShading = (multisamples > 1) ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetNoCullRasterizationState()
|
||||
{
|
||||
SetRasterizationState(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE);
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetDepthState(bool depth_test, bool depth_write, VkCompareOp compare_op)
|
||||
{
|
||||
m_depth_state.depthTestEnable = depth_test;
|
||||
m_depth_state.depthWriteEnable = depth_write;
|
||||
m_depth_state.depthCompareOp = compare_op;
|
||||
|
||||
m_ci.pDepthStencilState = &m_depth_state;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetStencilState(
|
||||
bool stencil_test, const VkStencilOpState& front, const VkStencilOpState& back)
|
||||
{
|
||||
m_depth_state.stencilTestEnable = stencil_test;
|
||||
m_depth_state.front = front;
|
||||
m_depth_state.back = back;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetNoStencilState()
|
||||
{
|
||||
m_depth_state.stencilTestEnable = VK_FALSE;
|
||||
m_depth_state.front = {};
|
||||
m_depth_state.back = {};
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetNoDepthTestState() { SetDepthState(false, false, VK_COMPARE_OP_ALWAYS); }
|
||||
|
||||
void GraphicsPipelineBuilder::SetBlendConstants(float r, float g, float b, float a)
|
||||
{
|
||||
m_blend_state.blendConstants[0] = r;
|
||||
m_blend_state.blendConstants[1] = g;
|
||||
m_blend_state.blendConstants[2] = b;
|
||||
m_blend_state.blendConstants[3] = a;
|
||||
m_ci.pColorBlendState = &m_blend_state;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::AddBlendAttachment(bool blend_enable, VkBlendFactor src_factor,
|
||||
VkBlendFactor dst_factor, VkBlendOp op, VkBlendFactor alpha_src_factor, VkBlendFactor alpha_dst_factor,
|
||||
VkBlendOp alpha_op,
|
||||
VkColorComponentFlags
|
||||
write_mask /* = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT */)
|
||||
{
|
||||
pxAssert(m_blend_state.attachmentCount < MAX_ATTACHMENTS);
|
||||
|
||||
VkPipelineColorBlendAttachmentState& bs = m_blend_attachments[m_blend_state.attachmentCount];
|
||||
bs.blendEnable = blend_enable;
|
||||
bs.srcColorBlendFactor = src_factor;
|
||||
bs.dstColorBlendFactor = dst_factor;
|
||||
bs.colorBlendOp = op;
|
||||
bs.srcAlphaBlendFactor = alpha_src_factor;
|
||||
bs.dstAlphaBlendFactor = alpha_dst_factor;
|
||||
bs.alphaBlendOp = alpha_op;
|
||||
bs.colorWriteMask = write_mask;
|
||||
|
||||
m_blend_state.attachmentCount++;
|
||||
m_blend_state.pAttachments = m_blend_attachments.data();
|
||||
m_ci.pColorBlendState = &m_blend_state;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetBlendAttachment(u32 attachment, bool blend_enable, VkBlendFactor src_factor,
|
||||
VkBlendFactor dst_factor, VkBlendOp op, VkBlendFactor alpha_src_factor, VkBlendFactor alpha_dst_factor,
|
||||
VkBlendOp alpha_op,
|
||||
VkColorComponentFlags
|
||||
write_mask /*= VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT*/)
|
||||
{
|
||||
pxAssert(attachment < MAX_ATTACHMENTS);
|
||||
|
||||
VkPipelineColorBlendAttachmentState& bs = m_blend_attachments[attachment];
|
||||
bs.blendEnable = blend_enable;
|
||||
bs.srcColorBlendFactor = src_factor;
|
||||
bs.dstColorBlendFactor = dst_factor;
|
||||
bs.colorBlendOp = op;
|
||||
bs.srcAlphaBlendFactor = alpha_src_factor;
|
||||
bs.dstAlphaBlendFactor = alpha_dst_factor;
|
||||
bs.alphaBlendOp = alpha_op;
|
||||
bs.colorWriteMask = write_mask;
|
||||
|
||||
if (attachment >= m_blend_state.attachmentCount)
|
||||
{
|
||||
m_blend_state.attachmentCount = attachment + 1u;
|
||||
m_blend_state.pAttachments = m_blend_attachments.data();
|
||||
m_ci.pColorBlendState = &m_blend_state;
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::AddBlendFlags(u32 flags)
|
||||
{
|
||||
m_blend_state.flags |= flags;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::ClearBlendAttachments()
|
||||
{
|
||||
m_blend_attachments = {};
|
||||
m_blend_state.attachmentCount = 0;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetNoBlendingState()
|
||||
{
|
||||
ClearBlendAttachments();
|
||||
SetBlendAttachment(0, false, VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD, VK_BLEND_FACTOR_ONE,
|
||||
VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD,
|
||||
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT);
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::AddDynamicState(VkDynamicState state)
|
||||
{
|
||||
pxAssert(m_dynamic_state.dynamicStateCount < MAX_DYNAMIC_STATE);
|
||||
|
||||
m_dynamic_state_values[m_dynamic_state.dynamicStateCount] = state;
|
||||
m_dynamic_state.dynamicStateCount++;
|
||||
m_dynamic_state.pDynamicStates = m_dynamic_state_values.data();
|
||||
m_ci.pDynamicState = &m_dynamic_state;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetDynamicViewportAndScissorState()
|
||||
{
|
||||
AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT);
|
||||
AddDynamicState(VK_DYNAMIC_STATE_SCISSOR);
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetViewport(
|
||||
float x, float y, float width, float height, float min_depth, float max_depth)
|
||||
{
|
||||
m_viewport.x = x;
|
||||
m_viewport.y = y;
|
||||
m_viewport.width = width;
|
||||
m_viewport.height = height;
|
||||
m_viewport.minDepth = min_depth;
|
||||
m_viewport.maxDepth = max_depth;
|
||||
|
||||
m_viewport_state.pViewports = &m_viewport;
|
||||
m_viewport_state.viewportCount = 1u;
|
||||
m_ci.pViewportState = &m_viewport_state;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetScissorRect(s32 x, s32 y, u32 width, u32 height)
|
||||
{
|
||||
m_scissor.offset.x = x;
|
||||
m_scissor.offset.y = y;
|
||||
m_scissor.extent.width = width;
|
||||
m_scissor.extent.height = height;
|
||||
|
||||
m_viewport_state.pScissors = &m_scissor;
|
||||
m_viewport_state.scissorCount = 1u;
|
||||
m_ci.pViewportState = &m_viewport_state;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetMultisamples(VkSampleCountFlagBits samples)
|
||||
{
|
||||
m_multisample_state.rasterizationSamples = samples;
|
||||
m_ci.pMultisampleState = &m_multisample_state;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetPipelineLayout(VkPipelineLayout layout) { m_ci.layout = layout; }
|
||||
|
||||
void GraphicsPipelineBuilder::SetRenderPass(VkRenderPass render_pass, u32 subpass)
|
||||
{
|
||||
m_ci.renderPass = render_pass;
|
||||
m_ci.subpass = subpass;
|
||||
}
|
||||
|
||||
void GraphicsPipelineBuilder::SetProvokingVertex(VkProvokingVertexModeEXT mode)
|
||||
{
|
||||
Util::AddPointerToChain(&m_rasterization_state, &m_provoking_vertex);
|
||||
|
||||
m_provoking_vertex.provokingVertexMode = mode;
|
||||
}
|
||||
|
||||
ComputePipelineBuilder::ComputePipelineBuilder() { Clear(); }
|
||||
|
||||
void ComputePipelineBuilder::Clear()
|
||||
{
|
||||
m_ci = {};
|
||||
m_ci.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
||||
m_si = {};
|
||||
m_smap_entries = {};
|
||||
m_smap_constants = {};
|
||||
}
|
||||
|
||||
VkPipeline ComputePipelineBuilder::Create(VkDevice device, VkPipelineCache pipeline_cache /*= VK_NULL_HANDLE*/, bool clear /*= true*/)
|
||||
{
|
||||
VkPipeline pipeline;
|
||||
VkResult res = vkCreateComputePipelines(device, pipeline_cache, 1, &m_ci, nullptr, &pipeline);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateComputePipelines() failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (clear)
|
||||
Clear();
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
void ComputePipelineBuilder::SetShader(VkShaderModule module, const char* entry_point)
|
||||
{
|
||||
m_ci.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
m_ci.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
||||
m_ci.stage.module = module;
|
||||
m_ci.stage.pName = entry_point;
|
||||
}
|
||||
|
||||
void ComputePipelineBuilder::SetPipelineLayout(VkPipelineLayout layout)
|
||||
{
|
||||
m_ci.layout = layout;
|
||||
}
|
||||
|
||||
void ComputePipelineBuilder::SetSpecializationBool(u32 index, bool value)
|
||||
{
|
||||
const u32 u32_value = static_cast<u32>(value);
|
||||
SetSpecializationValue(index, u32_value);
|
||||
}
|
||||
|
||||
void ComputePipelineBuilder::SetSpecializationValue(u32 index, u32 value)
|
||||
{
|
||||
if (m_si.mapEntryCount == 0)
|
||||
{
|
||||
m_si.pMapEntries = m_smap_entries.data();
|
||||
m_si.pData = m_smap_constants.data();
|
||||
m_ci.stage.pSpecializationInfo = &m_si;
|
||||
}
|
||||
|
||||
m_smap_entries[m_si.mapEntryCount++] = {index, index * SPECIALIZATION_CONSTANT_SIZE, SPECIALIZATION_CONSTANT_SIZE};
|
||||
m_si.dataSize += SPECIALIZATION_CONSTANT_SIZE;
|
||||
}
|
||||
|
||||
SamplerBuilder::SamplerBuilder() { Clear(); }
|
||||
|
||||
void SamplerBuilder::Clear()
|
||||
{
|
||||
m_ci = {};
|
||||
m_ci.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||
}
|
||||
|
||||
VkSampler SamplerBuilder::Create(VkDevice device, bool clear /* = true */)
|
||||
{
|
||||
VkSampler sampler;
|
||||
VkResult res = vkCreateSampler(device, &m_ci, nullptr, &sampler);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateSampler() failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return sampler;
|
||||
}
|
||||
|
||||
void SamplerBuilder::SetFilter(VkFilter mag_filter, VkFilter min_filter, VkSamplerMipmapMode mip_filter)
|
||||
{
|
||||
m_ci.magFilter = mag_filter;
|
||||
m_ci.minFilter = min_filter;
|
||||
m_ci.mipmapMode = mip_filter;
|
||||
}
|
||||
|
||||
void SamplerBuilder::SetAddressMode(VkSamplerAddressMode u, VkSamplerAddressMode v, VkSamplerAddressMode w)
|
||||
{
|
||||
m_ci.addressModeU = u;
|
||||
m_ci.addressModeV = v;
|
||||
m_ci.addressModeW = w;
|
||||
}
|
||||
|
||||
void SamplerBuilder::SetPointSampler(
|
||||
VkSamplerAddressMode address_mode /* = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER */)
|
||||
{
|
||||
Clear();
|
||||
SetFilter(VK_FILTER_NEAREST, VK_FILTER_NEAREST, VK_SAMPLER_MIPMAP_MODE_NEAREST);
|
||||
SetAddressMode(address_mode, address_mode, address_mode);
|
||||
}
|
||||
|
||||
void SamplerBuilder::SetLinearSampler(
|
||||
bool mipmaps, VkSamplerAddressMode address_mode /* = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER */)
|
||||
{
|
||||
Clear();
|
||||
SetFilter(VK_FILTER_LINEAR, VK_FILTER_LINEAR,
|
||||
mipmaps ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST);
|
||||
SetAddressMode(address_mode, address_mode, address_mode);
|
||||
|
||||
if (mipmaps)
|
||||
{
|
||||
m_ci.minLod = std::numeric_limits<float>::min();
|
||||
m_ci.maxLod = std::numeric_limits<float>::max();
|
||||
}
|
||||
}
|
||||
|
||||
DescriptorSetUpdateBuilder::DescriptorSetUpdateBuilder() { Clear(); }
|
||||
|
||||
void DescriptorSetUpdateBuilder::Clear()
|
||||
{
|
||||
m_writes = {};
|
||||
m_num_writes = 0;
|
||||
}
|
||||
|
||||
void DescriptorSetUpdateBuilder::Update(VkDevice device, bool clear /*= true*/)
|
||||
{
|
||||
pxAssert(m_num_writes > 0);
|
||||
|
||||
vkUpdateDescriptorSets(device, m_num_writes, (m_num_writes > 0) ? m_writes.data() : nullptr, 0, nullptr);
|
||||
|
||||
if (clear)
|
||||
Clear();
|
||||
}
|
||||
|
||||
void DescriptorSetUpdateBuilder::AddImageDescriptorWrite(VkDescriptorSet set, u32 binding, VkImageView view,
|
||||
VkImageLayout layout /*= VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL*/)
|
||||
{
|
||||
pxAssert(m_num_writes < MAX_WRITES && m_num_image_infos < MAX_IMAGE_INFOS);
|
||||
|
||||
VkDescriptorImageInfo& ii = m_image_infos[m_num_image_infos++];
|
||||
ii.imageView = view;
|
||||
ii.imageLayout = layout;
|
||||
ii.sampler = VK_NULL_HANDLE;
|
||||
|
||||
VkWriteDescriptorSet& dw = m_writes[m_num_writes++];
|
||||
dw.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
dw.dstSet = set;
|
||||
dw.dstBinding = binding;
|
||||
dw.descriptorCount = 1;
|
||||
dw.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
||||
dw.pImageInfo = ⅈ
|
||||
}
|
||||
|
||||
|
||||
void DescriptorSetUpdateBuilder::AddImageDescriptorWrites(VkDescriptorSet set, u32 binding,
|
||||
const VkImageView* views, u32 num_views, VkImageLayout layout /*= VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL*/)
|
||||
{
|
||||
pxAssert(m_num_writes < MAX_WRITES && (m_num_image_infos + num_views) < MAX_IMAGE_INFOS);
|
||||
|
||||
#if 1
|
||||
// NOTE: This is deliberately split up - updating multiple descriptors in one write is broken on Adreno.
|
||||
for (u32 i = 0; i < num_views; i++)
|
||||
AddImageDescriptorWrite(set, binding + i, views[i], layout);
|
||||
#else
|
||||
VkWriteDescriptorSet& dw = m_writes[m_num_writes++];
|
||||
dw.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
dw.dstSet = set;
|
||||
dw.dstBinding = binding;
|
||||
dw.descriptorCount = num_views;
|
||||
dw.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
||||
dw.pImageInfo = &m_image_infos[m_num_image_infos];
|
||||
|
||||
for (u32 i = 0; i < num_views; i++)
|
||||
{
|
||||
VkDescriptorImageInfo& ii = m_image_infos[m_num_image_infos++];
|
||||
ii.imageView = views[i];
|
||||
ii.imageLayout = layout;
|
||||
ii.sampler = VK_NULL_HANDLE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void DescriptorSetUpdateBuilder::AddSamplerDescriptorWrite(VkDescriptorSet set, u32 binding, VkSampler sampler)
|
||||
{
|
||||
pxAssert(m_num_writes < MAX_WRITES && m_num_image_infos < MAX_IMAGE_INFOS);
|
||||
|
||||
VkDescriptorImageInfo& ii = m_image_infos[m_num_image_infos++];
|
||||
ii.imageView = VK_NULL_HANDLE;
|
||||
ii.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
ii.sampler = sampler;
|
||||
|
||||
VkWriteDescriptorSet& dw = m_writes[m_num_writes++];
|
||||
dw.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
dw.dstSet = set;
|
||||
dw.dstBinding = binding;
|
||||
dw.descriptorCount = 1;
|
||||
dw.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
|
||||
dw.pImageInfo = ⅈ
|
||||
}
|
||||
|
||||
|
||||
void DescriptorSetUpdateBuilder::AddSamplerDescriptorWrites(
|
||||
VkDescriptorSet set, u32 binding, const VkSampler* samplers, u32 num_samplers)
|
||||
{
|
||||
pxAssert(m_num_writes < MAX_WRITES && (m_num_image_infos + num_samplers) < MAX_IMAGE_INFOS);
|
||||
|
||||
VkWriteDescriptorSet& dw = m_writes[m_num_writes++];
|
||||
dw.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
dw.dstSet = set;
|
||||
dw.dstBinding = binding;
|
||||
dw.descriptorCount = num_samplers;
|
||||
dw.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
|
||||
dw.pImageInfo = &m_image_infos[m_num_image_infos];
|
||||
|
||||
for (u32 i = 0; i < num_samplers; i++)
|
||||
{
|
||||
VkDescriptorImageInfo& ii = m_image_infos[m_num_image_infos++];
|
||||
ii.imageView = VK_NULL_HANDLE;
|
||||
ii.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
ii.sampler = samplers[i];
|
||||
}
|
||||
}
|
||||
|
||||
void DescriptorSetUpdateBuilder::AddCombinedImageSamplerDescriptorWrite(VkDescriptorSet set, u32 binding,
|
||||
VkImageView view, VkSampler sampler, VkImageLayout layout /*= VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL*/)
|
||||
{
|
||||
pxAssert(m_num_writes < MAX_WRITES && m_num_image_infos < MAX_IMAGE_INFOS);
|
||||
|
||||
VkDescriptorImageInfo& ii = m_image_infos[m_num_image_infos++];
|
||||
ii.imageView = view;
|
||||
ii.imageLayout = layout;
|
||||
ii.sampler = sampler;
|
||||
|
||||
VkWriteDescriptorSet& dw = m_writes[m_num_writes++];
|
||||
dw.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
dw.dstSet = set;
|
||||
dw.dstBinding = binding;
|
||||
dw.descriptorCount = 1;
|
||||
dw.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
dw.pImageInfo = ⅈ
|
||||
}
|
||||
|
||||
void DescriptorSetUpdateBuilder::AddCombinedImageSamplerDescriptorWrites(VkDescriptorSet set, u32 binding,
|
||||
const VkImageView* views, const VkSampler* samplers, u32 num_views,
|
||||
VkImageLayout layout /* = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL */)
|
||||
{
|
||||
pxAssert(m_num_writes < MAX_WRITES && (m_num_image_infos + num_views) < MAX_IMAGE_INFOS);
|
||||
|
||||
VkWriteDescriptorSet& dw = m_writes[m_num_writes++];
|
||||
dw.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
dw.dstSet = set;
|
||||
dw.dstBinding = binding;
|
||||
dw.descriptorCount = num_views;
|
||||
dw.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
dw.pImageInfo = &m_image_infos[m_num_image_infos];
|
||||
|
||||
for (u32 i = 0; i < num_views; i++)
|
||||
{
|
||||
VkDescriptorImageInfo& ii = m_image_infos[m_num_image_infos++];
|
||||
ii.imageView = views[i];
|
||||
ii.sampler = samplers[i];
|
||||
ii.imageLayout = layout;
|
||||
}
|
||||
}
|
||||
|
||||
void DescriptorSetUpdateBuilder::AddBufferDescriptorWrite(
|
||||
VkDescriptorSet set, u32 binding, VkDescriptorType dtype, VkBuffer buffer, u32 offset, u32 size)
|
||||
{
|
||||
pxAssert(m_num_writes < MAX_WRITES && m_num_buffer_infos < MAX_BUFFER_INFOS);
|
||||
|
||||
VkDescriptorBufferInfo& bi = m_buffer_infos[m_num_buffer_infos++];
|
||||
bi.buffer = buffer;
|
||||
bi.offset = offset;
|
||||
bi.range = size;
|
||||
|
||||
VkWriteDescriptorSet& dw = m_writes[m_num_writes++];
|
||||
dw.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
dw.dstSet = set;
|
||||
dw.dstBinding = binding;
|
||||
dw.descriptorCount = 1;
|
||||
dw.descriptorType = dtype;
|
||||
dw.pBufferInfo = &bi;
|
||||
}
|
||||
|
||||
void DescriptorSetUpdateBuilder::AddBufferViewDescriptorWrite(
|
||||
VkDescriptorSet set, u32 binding, VkDescriptorType dtype, VkBufferView view)
|
||||
{
|
||||
pxAssert(m_num_writes < MAX_WRITES && m_num_views < MAX_VIEWS);
|
||||
|
||||
VkBufferView& bi = m_views[m_num_views++];
|
||||
bi = view;
|
||||
|
||||
VkWriteDescriptorSet& dw = m_writes[m_num_writes++];
|
||||
dw.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
dw.dstSet = set;
|
||||
dw.dstBinding = binding;
|
||||
dw.descriptorCount = 1;
|
||||
dw.descriptorType = dtype;
|
||||
dw.pTexelBufferView = &bi;
|
||||
}
|
||||
|
||||
void DescriptorSetUpdateBuilder::AddInputAttachmentDescriptorWrite(
|
||||
VkDescriptorSet set, u32 binding, VkImageView view, VkImageLayout layout /*= VK_IMAGE_LAYOUT_GENERAL*/)
|
||||
{
|
||||
pxAssert(m_num_writes < MAX_WRITES && m_num_image_infos < MAX_IMAGE_INFOS);
|
||||
|
||||
VkWriteDescriptorSet& dw = m_writes[m_num_writes++];
|
||||
dw.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
dw.dstSet = set;
|
||||
dw.dstBinding = binding;
|
||||
dw.descriptorCount = 1;
|
||||
dw.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
|
||||
dw.pImageInfo = &m_image_infos[m_num_image_infos];
|
||||
|
||||
VkDescriptorImageInfo& ii = m_image_infos[m_num_image_infos++];
|
||||
ii.imageView = view;
|
||||
ii.imageLayout = layout;
|
||||
ii.sampler = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void DescriptorSetUpdateBuilder::AddStorageImageDescriptorWrite(
|
||||
VkDescriptorSet set, u32 binding, VkImageView view, VkImageLayout layout /*= VK_IMAGE_LAYOUT_GENERAL*/)
|
||||
{
|
||||
pxAssert(m_num_writes < MAX_WRITES && m_num_image_infos < MAX_IMAGE_INFOS);
|
||||
|
||||
VkWriteDescriptorSet& dw = m_writes[m_num_writes++];
|
||||
dw.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
dw.dstSet = set;
|
||||
dw.dstBinding = binding;
|
||||
dw.descriptorCount = 1;
|
||||
dw.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
||||
dw.pImageInfo = &m_image_infos[m_num_image_infos];
|
||||
|
||||
VkDescriptorImageInfo& ii = m_image_infos[m_num_image_infos++];
|
||||
ii.imageView = view;
|
||||
ii.imageLayout = layout;
|
||||
ii.sampler = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
FramebufferBuilder::FramebufferBuilder() { Clear(); }
|
||||
|
||||
void FramebufferBuilder::Clear()
|
||||
{
|
||||
m_ci = {};
|
||||
m_ci.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||||
m_images = {};
|
||||
}
|
||||
|
||||
VkFramebuffer FramebufferBuilder::Create(VkDevice device, bool clear /*= true*/)
|
||||
{
|
||||
VkFramebuffer fb;
|
||||
VkResult res = vkCreateFramebuffer(device, &m_ci, nullptr, &fb);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateFramebuffer() failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (clear)
|
||||
Clear();
|
||||
|
||||
return fb;
|
||||
}
|
||||
|
||||
void FramebufferBuilder::AddAttachment(VkImageView image)
|
||||
{
|
||||
pxAssert(m_ci.attachmentCount < MAX_ATTACHMENTS);
|
||||
|
||||
m_images[m_ci.attachmentCount] = image;
|
||||
|
||||
m_ci.attachmentCount++;
|
||||
m_ci.pAttachments = m_images.data();
|
||||
}
|
||||
|
||||
void FramebufferBuilder::SetSize(u32 width, u32 height, u32 layers)
|
||||
{
|
||||
m_ci.width = width;
|
||||
m_ci.height = height;
|
||||
m_ci.layers = layers;
|
||||
}
|
||||
|
||||
void FramebufferBuilder::SetRenderPass(VkRenderPass render_pass) { m_ci.renderPass = render_pass; }
|
||||
|
||||
RenderPassBuilder::RenderPassBuilder() { Clear(); }
|
||||
|
||||
void RenderPassBuilder::Clear()
|
||||
{
|
||||
m_ci = {};
|
||||
m_ci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
m_attachments = {};
|
||||
m_attachment_references = {};
|
||||
m_num_attachment_references = 0;
|
||||
m_subpasses = {};
|
||||
}
|
||||
|
||||
VkRenderPass RenderPassBuilder::Create(VkDevice device, bool clear /*= true*/)
|
||||
{
|
||||
VkRenderPass rp;
|
||||
VkResult res = vkCreateRenderPass(device, &m_ci, nullptr, &rp);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateRenderPass() failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return rp;
|
||||
}
|
||||
|
||||
u32 RenderPassBuilder::AddAttachment(VkFormat format, VkSampleCountFlagBits samples, VkAttachmentLoadOp load_op,
|
||||
VkAttachmentStoreOp store_op, VkImageLayout initial_layout, VkImageLayout final_layout)
|
||||
{
|
||||
pxAssert(m_ci.attachmentCount < MAX_ATTACHMENTS);
|
||||
|
||||
const u32 index = m_ci.attachmentCount;
|
||||
VkAttachmentDescription& ad = m_attachments[index];
|
||||
ad.format = format;
|
||||
ad.samples = samples;
|
||||
ad.loadOp = load_op;
|
||||
ad.storeOp = store_op;
|
||||
ad.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
ad.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
ad.initialLayout = initial_layout;
|
||||
ad.finalLayout = final_layout;
|
||||
|
||||
m_ci.attachmentCount++;
|
||||
m_ci.pAttachments = m_attachments.data();
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
u32 RenderPassBuilder::AddSubpass()
|
||||
{
|
||||
pxAssert(m_ci.subpassCount < MAX_SUBPASSES);
|
||||
|
||||
const u32 index = m_ci.subpassCount;
|
||||
VkSubpassDescription& sp = m_subpasses[index];
|
||||
sp.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
|
||||
m_ci.subpassCount++;
|
||||
m_ci.pSubpasses = m_subpasses.data();
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
void RenderPassBuilder::AddSubpassColorAttachment(u32 subpass, u32 attachment, VkImageLayout layout)
|
||||
{
|
||||
pxAssert(subpass < m_ci.subpassCount && m_num_attachment_references < MAX_ATTACHMENT_REFERENCES);
|
||||
|
||||
VkAttachmentReference& ar = m_attachment_references[m_num_attachment_references++];
|
||||
ar.attachment = attachment;
|
||||
ar.layout = layout;
|
||||
|
||||
VkSubpassDescription& sp = m_subpasses[subpass];
|
||||
if (sp.colorAttachmentCount == 0)
|
||||
sp.pColorAttachments = &ar;
|
||||
sp.colorAttachmentCount++;
|
||||
}
|
||||
|
||||
void RenderPassBuilder::AddSubpassDepthAttachment(u32 subpass, u32 attachment, VkImageLayout layout)
|
||||
{
|
||||
pxAssert(subpass < m_ci.subpassCount && m_num_attachment_references < MAX_ATTACHMENT_REFERENCES);
|
||||
|
||||
VkAttachmentReference& ar = m_attachment_references[m_num_attachment_references++];
|
||||
ar.attachment = attachment;
|
||||
ar.layout = layout;
|
||||
|
||||
VkSubpassDescription& sp = m_subpasses[subpass];
|
||||
sp.pDepthStencilAttachment = &ar;
|
||||
}
|
||||
|
||||
BufferViewBuilder::BufferViewBuilder() { Clear(); }
|
||||
|
||||
void BufferViewBuilder::Clear()
|
||||
{
|
||||
m_ci = {};
|
||||
m_ci.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
|
||||
}
|
||||
|
||||
VkBufferView BufferViewBuilder::Create(VkDevice device, bool clear /*= true*/)
|
||||
{
|
||||
VkBufferView bv;
|
||||
VkResult res = vkCreateBufferView(device, &m_ci, nullptr, &bv);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateBufferView() failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return bv;
|
||||
}
|
||||
|
||||
void BufferViewBuilder::Set(VkBuffer buffer, VkFormat format, u32 offset, u32 size)
|
||||
{
|
||||
m_ci.buffer = buffer;
|
||||
m_ci.format = format;
|
||||
m_ci.offset = offset;
|
||||
m_ci.range = size;
|
||||
}
|
||||
} // namespace Vulkan
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,414 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
|
||||
#include "common/ReadbackSpinManager.h"
|
||||
#include "common/Vulkan/Loader.h"
|
||||
#include "common/Vulkan/StreamBuffer.h"
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
struct WindowInfo;
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class SwapChain;
|
||||
|
||||
class Context
|
||||
{
|
||||
public:
|
||||
enum : u32
|
||||
{
|
||||
NUM_COMMAND_BUFFERS = 3,
|
||||
TEXTURE_BUFFER_SIZE = 64 * 1024 * 1024,
|
||||
};
|
||||
|
||||
struct OptionalExtensions
|
||||
{
|
||||
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_khr_driver_properties : 1;
|
||||
bool vk_khr_fragment_shader_barycentric : 1;
|
||||
bool vk_khr_shader_draw_parameters : 1;
|
||||
};
|
||||
|
||||
~Context();
|
||||
|
||||
// Helper method to create a Vulkan instance.
|
||||
static VkInstance CreateVulkanInstance(
|
||||
const WindowInfo& wi, bool enable_debug_utils, bool enable_validation_layer);
|
||||
|
||||
// Returns a list of Vulkan-compatible GPUs.
|
||||
using GPUList = std::vector<std::pair<VkPhysicalDevice, std::string>>;
|
||||
static GPUList EnumerateGPUs(VkInstance instance);
|
||||
|
||||
// Creates a new context and sets it up as global.
|
||||
static bool Create(VkInstance instance, VkSurfaceKHR surface, VkPhysicalDevice physical_device,
|
||||
bool threaded_presentation, bool enable_debug_utils, bool enable_validation_layer);
|
||||
|
||||
// Destroys context.
|
||||
static void Destroy();
|
||||
|
||||
// Enable/disable debug message runtime.
|
||||
bool EnableDebugUtils();
|
||||
void DisableDebugUtils();
|
||||
|
||||
// Global state accessors
|
||||
__fi VkInstance GetVulkanInstance() const { return m_instance; }
|
||||
__fi VkPhysicalDevice GetPhysicalDevice() const { return m_physical_device; }
|
||||
__fi VkDevice GetDevice() const { return m_device; }
|
||||
__fi VmaAllocator GetAllocator() const { return m_allocator; }
|
||||
__fi VkQueue GetGraphicsQueue() const { return m_graphics_queue; }
|
||||
__fi u32 GetGraphicsQueueFamilyIndex() const { return m_graphics_queue_family_index; }
|
||||
__fi VkQueue GetPresentQueue() const { return m_present_queue; }
|
||||
__fi u32 GetPresentQueueFamilyIndex() const { return m_present_queue_family_index; }
|
||||
__fi const VkQueueFamilyProperties& GetGraphicsQueueProperties() const { return m_graphics_queue_properties; }
|
||||
__fi const VkPhysicalDeviceMemoryProperties& GetDeviceMemoryProperties() const
|
||||
{
|
||||
return m_device_memory_properties;
|
||||
}
|
||||
__fi const VkPhysicalDeviceProperties& GetDeviceProperties() const { return m_device_properties; }
|
||||
__fi const VkPhysicalDeviceFeatures& GetDeviceFeatures() const { return m_device_features; }
|
||||
__fi const VkPhysicalDeviceLimits& GetDeviceLimits() const { return m_device_properties.limits; }
|
||||
__fi const VkPhysicalDeviceDriverProperties& GetDeviceDriverProperties() const { return m_device_driver_properties; }
|
||||
__fi const OptionalExtensions& GetOptionalExtensions() const { return m_optional_extensions; }
|
||||
|
||||
// Helpers for getting constants
|
||||
__fi u32 GetUniformBufferAlignment() const
|
||||
{
|
||||
return static_cast<u32>(m_device_properties.limits.minUniformBufferOffsetAlignment);
|
||||
}
|
||||
__fi u32 GetTexelBufferAlignment() const
|
||||
{
|
||||
return static_cast<u32>(m_device_properties.limits.minTexelBufferOffsetAlignment);
|
||||
}
|
||||
__fi u32 GetStorageBufferAlignment() const
|
||||
{
|
||||
return static_cast<u32>(m_device_properties.limits.minStorageBufferOffsetAlignment);
|
||||
}
|
||||
__fi u32 GetBufferImageGranularity() const
|
||||
{
|
||||
return static_cast<u32>(m_device_properties.limits.bufferImageGranularity);
|
||||
}
|
||||
__fi u32 GetBufferCopyOffsetAlignment() const
|
||||
{
|
||||
return static_cast<u32>(m_device_properties.limits.optimalBufferCopyOffsetAlignment);
|
||||
}
|
||||
__fi u32 GetBufferCopyRowPitchAlignment() const
|
||||
{
|
||||
return static_cast<u32>(m_device_properties.limits.optimalBufferCopyRowPitchAlignment);
|
||||
}
|
||||
__fi u32 GetMaxImageDimension2D() const
|
||||
{
|
||||
return m_device_properties.limits.maxImageDimension2D;
|
||||
}
|
||||
|
||||
// Creates a simple render pass.
|
||||
__ri VkRenderPass GetRenderPass(VkFormat color_format, VkFormat depth_format,
|
||||
VkAttachmentLoadOp color_load_op = VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||
VkAttachmentStoreOp color_store_op = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
VkAttachmentLoadOp depth_load_op = VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||
VkAttachmentStoreOp depth_store_op = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
VkAttachmentLoadOp stencil_load_op = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
VkAttachmentStoreOp stencil_store_op = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
bool color_feedback_loop = false, bool depth_sampling = false)
|
||||
{
|
||||
RenderPassCacheKey key = {};
|
||||
key.color_format = color_format;
|
||||
key.depth_format = depth_format;
|
||||
key.color_load_op = color_load_op;
|
||||
key.color_store_op = color_store_op;
|
||||
key.depth_load_op = depth_load_op;
|
||||
key.depth_store_op = depth_store_op;
|
||||
key.stencil_load_op = stencil_load_op;
|
||||
key.stencil_store_op = stencil_store_op;
|
||||
key.color_feedback_loop = color_feedback_loop;
|
||||
key.depth_sampling = depth_sampling;
|
||||
|
||||
auto it = m_render_pass_cache.find(key.key);
|
||||
if (it != m_render_pass_cache.end())
|
||||
return it->second;
|
||||
|
||||
return CreateCachedRenderPass(key);
|
||||
}
|
||||
|
||||
// These command buffers are allocated per-frame. They are valid until the command buffer
|
||||
// is submitted, after that you should call these functions again.
|
||||
__fi u32 GetCurrentCommandBufferIndex() const { return m_current_frame; }
|
||||
__fi VkDescriptorPool GetGlobalDescriptorPool() const { return m_global_descriptor_pool; }
|
||||
__fi VkCommandBuffer GetCurrentCommandBuffer() const { return m_current_command_buffer; }
|
||||
__fi StreamBuffer& GetTextureUploadBuffer() { return m_texture_upload_buffer; }
|
||||
__fi VkDescriptorPool GetCurrentDescriptorPool() const
|
||||
{
|
||||
return m_frame_resources[m_current_frame].descriptor_pool;
|
||||
}
|
||||
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);
|
||||
|
||||
/// Frees a descriptor set allocated from the global pool.
|
||||
void FreeGlobalDescriptorSet(VkDescriptorSet set);
|
||||
|
||||
// Gets the fence that will be signaled when the currently executing command buffer is
|
||||
// queued and executed. Do not wait for this fence before the buffer is executed.
|
||||
__fi VkFence GetCurrentCommandBufferFence() const { return m_frame_resources[m_current_frame].fence; }
|
||||
|
||||
// Fence "counters" are used to track which commands have been completed by the GPU.
|
||||
// If the last completed fence counter is greater or equal to N, it means that the work
|
||||
// associated counter N has been completed by the GPU. The value of N to associate with
|
||||
// commands can be retreived by calling GetCurrentFenceCounter().
|
||||
u64 GetCompletedFenceCounter() const { return m_completed_fence_counter; }
|
||||
|
||||
// Gets the fence that will be signaled when the currently executing command buffer is
|
||||
// queued and executed. Do not wait for this fence before the buffer is executed.
|
||||
u64 GetCurrentFenceCounter() const { return m_frame_resources[m_current_frame].fence_counter; }
|
||||
|
||||
void SubmitCommandBuffer(SwapChain* present_swap_chain = nullptr, bool submit_on_thread = false);
|
||||
void MoveToNextCommandBuffer();
|
||||
|
||||
enum class WaitType
|
||||
{
|
||||
None,
|
||||
Sleep,
|
||||
Spin,
|
||||
};
|
||||
|
||||
void ExecuteCommandBuffer(WaitType wait_for_completion);
|
||||
void WaitForPresentComplete();
|
||||
|
||||
// Was the last present submitted to the queue a failure? If so, we must recreate our swapchain.
|
||||
bool CheckLastPresentFail();
|
||||
bool CheckLastSubmitFail();
|
||||
|
||||
// Schedule a vulkan resource for destruction later on. This will occur when the command buffer
|
||||
// is next re-used, and the GPU has finished working with the specified resource.
|
||||
void DeferBufferDestruction(VkBuffer object);
|
||||
void DeferBufferDestruction(VkBuffer object, VmaAllocation allocation);
|
||||
void DeferBufferViewDestruction(VkBufferView object);
|
||||
void DeferDeviceMemoryDestruction(VkDeviceMemory object);
|
||||
void DeferFramebufferDestruction(VkFramebuffer object);
|
||||
void DeferImageDestruction(VkImage object);
|
||||
void DeferImageDestruction(VkImage object, VmaAllocation allocation);
|
||||
void DeferImageViewDestruction(VkImageView object);
|
||||
void DeferPipelineDestruction(VkPipeline pipeline);
|
||||
void DeferSamplerDestruction(VkSampler sampler);
|
||||
|
||||
// Wait for a fence to be completed.
|
||||
// Also invokes callbacks for completion.
|
||||
void WaitForFenceCounter(u64 fence_counter);
|
||||
|
||||
void WaitForGPUIdle();
|
||||
|
||||
float GetAndResetAccumulatedGPUTime();
|
||||
bool SetEnableGPUTiming(bool enabled);
|
||||
|
||||
void CountRenderPass() { m_command_buffer_render_passes++; }
|
||||
void NotifyOfReadback();
|
||||
|
||||
// Allocates a temporary CPU staging buffer, fires the callback with it to populate, then copies to a GPU buffer.
|
||||
bool AllocatePreinitializedGPUBuffer(u32 size, VkBuffer* gpu_buffer, VmaAllocation* gpu_allocation,
|
||||
VkBufferUsageFlags gpu_usage, const std::function<void(void*)>& fill_callback);
|
||||
|
||||
private:
|
||||
Context(VkInstance instance, VkPhysicalDevice physical_device);
|
||||
|
||||
union RenderPassCacheKey
|
||||
{
|
||||
struct
|
||||
{
|
||||
u32 color_format : 8;
|
||||
u32 depth_format : 8;
|
||||
u32 color_load_op : 2;
|
||||
u32 color_store_op : 1;
|
||||
u32 depth_load_op : 2;
|
||||
u32 depth_store_op : 1;
|
||||
u32 stencil_load_op : 2;
|
||||
u32 stencil_store_op : 1;
|
||||
u32 color_feedback_loop : 1;
|
||||
u32 depth_sampling : 1;
|
||||
};
|
||||
|
||||
u32 key;
|
||||
};
|
||||
|
||||
using ExtensionList = std::vector<const char*>;
|
||||
static bool SelectInstanceExtensions(
|
||||
ExtensionList* extension_list, const WindowInfo& wi, bool enable_debug_utils);
|
||||
bool SelectDeviceExtensions(ExtensionList* extension_list, bool enable_surface);
|
||||
bool SelectDeviceFeatures(const VkPhysicalDeviceFeatures* required_features);
|
||||
bool CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer, const char** required_device_extensions,
|
||||
u32 num_required_device_extensions, const char** required_device_layers, u32 num_required_device_layers,
|
||||
const VkPhysicalDeviceFeatures* required_features);
|
||||
void ProcessDeviceExtensions();
|
||||
|
||||
bool CreateAllocator();
|
||||
void DestroyAllocator();
|
||||
bool CreateCommandBuffers();
|
||||
void DestroyCommandBuffers();
|
||||
bool CreateGlobalDescriptorPool();
|
||||
void DestroyGlobalDescriptorPool();
|
||||
bool CreateTextureStreamBuffer();
|
||||
|
||||
VkRenderPass CreateCachedRenderPass(RenderPassCacheKey key);
|
||||
void DestroyRenderPassCache();
|
||||
|
||||
void CommandBufferCompleted(u32 index);
|
||||
void ActivateCommandBuffer(u32 index);
|
||||
void ScanForCommandBufferCompletion();
|
||||
void WaitForCommandBufferCompletion(u32 index);
|
||||
|
||||
void DoSubmitCommandBuffer(u32 index, SwapChain* present_swap_chain, u32 spin_cycles);
|
||||
void DoPresent(SwapChain* present_swap_chain);
|
||||
void WaitForPresentComplete(std::unique_lock<std::mutex>& lock);
|
||||
void PresentThread();
|
||||
void StartPresentThread();
|
||||
void StopPresentThread();
|
||||
|
||||
bool InitSpinResources();
|
||||
void DestroySpinResources();
|
||||
void WaitForSpinCompletion(u32 index);
|
||||
void SpinCommandCompleted(u32 index);
|
||||
void SubmitSpinCommand(u32 index, u32 cycles);
|
||||
void CalibrateSpinTimestamp();
|
||||
u64 GetCPUTimestamp();
|
||||
|
||||
struct FrameResources
|
||||
{
|
||||
// [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;
|
||||
u32 submit_timestamp = 0;
|
||||
bool init_buffer_used = false;
|
||||
bool needs_fence_wait = false;
|
||||
bool timestamp_written = false;
|
||||
|
||||
std::vector<std::function<void()>> cleanup_resources;
|
||||
};
|
||||
|
||||
struct SpinResources
|
||||
{
|
||||
VkCommandPool command_pool = VK_NULL_HANDLE;
|
||||
VkCommandBuffer command_buffer = VK_NULL_HANDLE;
|
||||
VkSemaphore semaphore = VK_NULL_HANDLE;
|
||||
VkFence fence = VK_NULL_HANDLE;
|
||||
u32 cycles = 0;
|
||||
bool in_progress = false;
|
||||
};
|
||||
|
||||
VkInstance m_instance = VK_NULL_HANDLE;
|
||||
VkPhysicalDevice m_physical_device = VK_NULL_HANDLE;
|
||||
VkDevice m_device = VK_NULL_HANDLE;
|
||||
VmaAllocator m_allocator = VK_NULL_HANDLE;
|
||||
|
||||
VkCommandBuffer m_current_command_buffer = VK_NULL_HANDLE;
|
||||
|
||||
VkDescriptorPool m_global_descriptor_pool = VK_NULL_HANDLE;
|
||||
|
||||
VkQueue m_graphics_queue = VK_NULL_HANDLE;
|
||||
VkQueue m_present_queue = VK_NULL_HANDLE;
|
||||
u32 m_graphics_queue_family_index = 0;
|
||||
u32 m_present_queue_family_index = 0;
|
||||
|
||||
ReadbackSpinManager m_spin_manager;
|
||||
VkQueue m_spin_queue = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout m_spin_descriptor_set_layout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_spin_pipeline_layout = VK_NULL_HANDLE;
|
||||
VkPipeline m_spin_pipeline = VK_NULL_HANDLE;
|
||||
VkBuffer m_spin_buffer = VK_NULL_HANDLE;
|
||||
VmaAllocation m_spin_buffer_allocation = VK_NULL_HANDLE;
|
||||
VkDescriptorSet m_spin_descriptor_set = VK_NULL_HANDLE;
|
||||
std::array<SpinResources, NUM_COMMAND_BUFFERS> m_spin_resources;
|
||||
#ifdef _WIN32
|
||||
double m_queryperfcounter_to_ns = 0;
|
||||
#endif
|
||||
double m_spin_timestamp_scale = 0;
|
||||
double m_spin_timestamp_offset = 0;
|
||||
u32 m_spin_queue_family_index = 0;
|
||||
u32 m_command_buffer_render_passes = 0;
|
||||
u32 m_spin_timer = 0;
|
||||
bool m_spinning_supported = false;
|
||||
bool m_spin_queue_is_graphics_queue = false;
|
||||
bool m_spin_buffer_initialized = false;
|
||||
|
||||
VkQueryPool m_timestamp_query_pool = VK_NULL_HANDLE;
|
||||
float m_accumulated_gpu_time = 0.0f;
|
||||
bool m_gpu_timing_enabled = false;
|
||||
bool m_gpu_timing_supported = false;
|
||||
bool m_wants_new_timestamp_calibration = false;
|
||||
VkTimeDomainEXT m_calibrated_timestamp_type = VK_TIME_DOMAIN_DEVICE_EXT;
|
||||
|
||||
std::array<FrameResources, NUM_COMMAND_BUFFERS> m_frame_resources;
|
||||
u64 m_next_fence_counter = 1;
|
||||
u64 m_completed_fence_counter = 0;
|
||||
u32 m_current_frame = 0;
|
||||
|
||||
StreamBuffer m_texture_upload_buffer;
|
||||
|
||||
std::atomic_bool m_last_submit_failed{false};
|
||||
std::atomic_bool m_last_present_failed{false};
|
||||
std::atomic_bool m_present_done{true};
|
||||
std::mutex m_present_mutex;
|
||||
std::condition_variable m_present_queued_cv;
|
||||
std::condition_variable m_present_done_cv;
|
||||
std::thread m_present_thread;
|
||||
std::atomic_bool m_present_thread_done{false};
|
||||
|
||||
struct QueuedPresent
|
||||
{
|
||||
SwapChain* swap_chain;
|
||||
u32 command_buffer_index;
|
||||
u32 spin_cycles;
|
||||
};
|
||||
|
||||
QueuedPresent m_queued_present = {};
|
||||
|
||||
std::map<u32, VkRenderPass> m_render_pass_cache;
|
||||
|
||||
VkDebugUtilsMessengerEXT m_debug_messenger_callback = VK_NULL_HANDLE;
|
||||
|
||||
VkQueueFamilyProperties m_graphics_queue_properties = {};
|
||||
VkPhysicalDeviceFeatures m_device_features = {};
|
||||
VkPhysicalDeviceProperties m_device_properties = {};
|
||||
VkPhysicalDeviceMemoryProperties m_device_memory_properties = {};
|
||||
VkPhysicalDeviceDriverPropertiesKHR m_device_driver_properties = {};
|
||||
OptionalExtensions m_optional_extensions = {};
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
extern std::unique_ptr<Vulkan::Context> g_vulkan_context;
|
||||
@@ -1,237 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// We abuse the preprocessor here to only need to specify function names once.
|
||||
// Function names are prefixed so to not conflict with system symbols at runtime.
|
||||
#define VULKAN_MODULE_ENTRY_POINT(name, required) extern PFN_##name pcsx2_##name;
|
||||
#define VULKAN_INSTANCE_ENTRY_POINT(name, required) extern PFN_##name pcsx2_##name;
|
||||
#define VULKAN_DEVICE_ENTRY_POINT(name, required) extern PFN_##name pcsx2_##name;
|
||||
#define VULKAN_DEFINE_NAME_PREFIX pcsx2_
|
||||
#include "EntryPoints.inl"
|
||||
#undef VULKAN_DEFINE_NAME_PREFIX
|
||||
#undef VULKAN_DEVICE_ENTRY_POINT
|
||||
#undef VULKAN_INSTANCE_ENTRY_POINT
|
||||
#undef VULKAN_MODULE_ENTRY_POINT
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#define vkCreateInstance pcsx2_vkCreateInstance
|
||||
#define vkGetInstanceProcAddr pcsx2_vkGetInstanceProcAddr
|
||||
#define vkEnumerateInstanceExtensionProperties pcsx2_vkEnumerateInstanceExtensionProperties
|
||||
#define vkEnumerateInstanceLayerProperties pcsx2_vkEnumerateInstanceLayerProperties
|
||||
#define vkEnumerateInstanceVersion pcsx2_vkEnumerateInstanceVersion
|
||||
|
||||
#define vkGetDeviceProcAddr pcsx2_vkGetDeviceProcAddr
|
||||
#define vkDestroyInstance pcsx2_vkDestroyInstance
|
||||
#define vkEnumeratePhysicalDevices pcsx2_vkEnumeratePhysicalDevices
|
||||
#define vkGetPhysicalDeviceFeatures pcsx2_vkGetPhysicalDeviceFeatures
|
||||
#define vkGetPhysicalDeviceFormatProperties pcsx2_vkGetPhysicalDeviceFormatProperties
|
||||
#define vkGetPhysicalDeviceImageFormatProperties pcsx2_vkGetPhysicalDeviceImageFormatProperties
|
||||
#define vkGetPhysicalDeviceProperties pcsx2_vkGetPhysicalDeviceProperties
|
||||
#define vkGetPhysicalDeviceQueueFamilyProperties pcsx2_vkGetPhysicalDeviceQueueFamilyProperties
|
||||
#define vkGetPhysicalDeviceMemoryProperties pcsx2_vkGetPhysicalDeviceMemoryProperties
|
||||
#define vkCreateDevice pcsx2_vkCreateDevice
|
||||
#define vkEnumerateDeviceExtensionProperties pcsx2_vkEnumerateDeviceExtensionProperties
|
||||
#define vkEnumerateDeviceLayerProperties pcsx2_vkEnumerateDeviceLayerProperties
|
||||
#define vkGetPhysicalDeviceSparseImageFormatProperties pcsx2_vkGetPhysicalDeviceSparseImageFormatProperties
|
||||
#define vkDestroySurfaceKHR pcsx2_vkDestroySurfaceKHR
|
||||
#define vkGetPhysicalDeviceSurfaceSupportKHR pcsx2_vkGetPhysicalDeviceSurfaceSupportKHR
|
||||
#define vkGetPhysicalDeviceSurfaceCapabilitiesKHR pcsx2_vkGetPhysicalDeviceSurfaceCapabilitiesKHR
|
||||
#define vkGetPhysicalDeviceSurfaceFormatsKHR pcsx2_vkGetPhysicalDeviceSurfaceFormatsKHR
|
||||
#define vkGetPhysicalDeviceSurfacePresentModesKHR pcsx2_vkGetPhysicalDeviceSurfacePresentModesKHR
|
||||
#define vkCreateWin32SurfaceKHR pcsx2_vkCreateWin32SurfaceKHR
|
||||
#define vkGetPhysicalDeviceWin32PresentationSupportKHR pcsx2_vkGetPhysicalDeviceWin32PresentationSupportKHR
|
||||
#define vkCreateXlibSurfaceKHR pcsx2_vkCreateXlibSurfaceKHR
|
||||
#define vkGetPhysicalDeviceXlibPresentationSupportKHR pcsx2_vkGetPhysicalDeviceXlibPresentationSupportKHR
|
||||
#define vkCreateWaylandSurfaceKHR pcsx2_vkCreateWaylandSurfaceKHR
|
||||
#define vkCreateAndroidSurfaceKHR pcsx2_vkCreateAndroidSurfaceKHR
|
||||
#define vkCreateMacOSSurfaceMVK pcsx2_vkCreateMacOSSurfaceMVK
|
||||
#define vkCreateMetalSurfaceEXT pcsx2_vkCreateMetalSurfaceEXT
|
||||
|
||||
// VK_EXT_debug_utils
|
||||
#define vkCmdBeginDebugUtilsLabelEXT pcsx2_vkCmdBeginDebugUtilsLabelEXT
|
||||
#define vkCmdEndDebugUtilsLabelEXT pcsx2_vkCmdEndDebugUtilsLabelEXT
|
||||
#define vkCmdInsertDebugUtilsLabelEXT pcsx2_vkCmdInsertDebugUtilsLabelEXT
|
||||
#define vkCreateDebugUtilsMessengerEXT pcsx2_vkCreateDebugUtilsMessengerEXT
|
||||
#define vkDestroyDebugUtilsMessengerEXT pcsx2_vkDestroyDebugUtilsMessengerEXT
|
||||
#define vkQueueBeginDebugUtilsLabelEXT pcsx2_vkQueueBeginDebugUtilsLabelEXT
|
||||
#define vkQueueEndDebugUtilsLabelEXT pcsx2_vkQueueEndDebugUtilsLabelEXT
|
||||
#define vkQueueInsertDebugUtilsLabelEXT pcsx2_vkQueueInsertDebugUtilsLabelEXT
|
||||
#define vkSetDebugUtilsObjectNameEXT pcsx2_vkSetDebugUtilsObjectNameEXT
|
||||
#define vkSetDebugUtilsObjectTagEXT pcsx2_vkSetDebugUtilsObjectTagEXT
|
||||
#define vkSubmitDebugUtilsMessageEXT pcsx2_vkSubmitDebugUtilsMessageEXT
|
||||
|
||||
#define vkGetPhysicalDeviceSurfaceCapabilities2KHR pcsx2_vkGetPhysicalDeviceSurfaceCapabilities2KHR
|
||||
#define vkGetPhysicalDeviceDisplayPropertiesKHR pcsx2_vkGetPhysicalDeviceDisplayPropertiesKHR
|
||||
#define vkGetPhysicalDeviceDisplayPlanePropertiesKHR pcsx2_vkGetPhysicalDeviceDisplayPlanePropertiesKHR
|
||||
#define vkGetDisplayPlaneSupportedDisplaysKHR pcsx2_vkGetDisplayPlaneSupportedDisplaysKHR
|
||||
#define vkGetDisplayModePropertiesKHR pcsx2_vkGetDisplayModePropertiesKHR
|
||||
#define vkCreateDisplayModeKHR pcsx2_vkCreateDisplayModeKHR
|
||||
#define vkGetDisplayPlaneCapabilitiesKHR pcsx2_vkGetDisplayPlaneCapabilitiesKHR
|
||||
#define vkCreateDisplayPlaneSurfaceKHR pcsx2_vkCreateDisplayPlaneSurfaceKHR
|
||||
|
||||
// Vulkan 1.1 functions.
|
||||
#define vkGetPhysicalDeviceFeatures2 pcsx2_vkGetPhysicalDeviceFeatures2
|
||||
#define vkGetPhysicalDeviceProperties2 pcsx2_vkGetPhysicalDeviceProperties2
|
||||
#define vkGetPhysicalDeviceMemoryProperties2 pcsx2_vkGetPhysicalDeviceMemoryProperties2
|
||||
|
||||
#define vkDestroyDevice pcsx2_vkDestroyDevice
|
||||
#define vkGetDeviceQueue pcsx2_vkGetDeviceQueue
|
||||
#define vkQueueSubmit pcsx2_vkQueueSubmit
|
||||
#define vkQueueWaitIdle pcsx2_vkQueueWaitIdle
|
||||
#define vkDeviceWaitIdle pcsx2_vkDeviceWaitIdle
|
||||
#define vkAllocateMemory pcsx2_vkAllocateMemory
|
||||
#define vkFreeMemory pcsx2_vkFreeMemory
|
||||
#define vkMapMemory pcsx2_vkMapMemory
|
||||
#define vkUnmapMemory pcsx2_vkUnmapMemory
|
||||
#define vkFlushMappedMemoryRanges pcsx2_vkFlushMappedMemoryRanges
|
||||
#define vkInvalidateMappedMemoryRanges pcsx2_vkInvalidateMappedMemoryRanges
|
||||
#define vkGetDeviceMemoryCommitment pcsx2_vkGetDeviceMemoryCommitment
|
||||
#define vkBindBufferMemory pcsx2_vkBindBufferMemory
|
||||
#define vkBindImageMemory pcsx2_vkBindImageMemory
|
||||
#define vkGetBufferMemoryRequirements pcsx2_vkGetBufferMemoryRequirements
|
||||
#define vkGetImageMemoryRequirements pcsx2_vkGetImageMemoryRequirements
|
||||
#define vkGetImageSparseMemoryRequirements pcsx2_vkGetImageSparseMemoryRequirements
|
||||
#define vkQueueBindSparse pcsx2_vkQueueBindSparse
|
||||
#define vkCreateFence pcsx2_vkCreateFence
|
||||
#define vkDestroyFence pcsx2_vkDestroyFence
|
||||
#define vkResetFences pcsx2_vkResetFences
|
||||
#define vkGetFenceStatus pcsx2_vkGetFenceStatus
|
||||
#define vkWaitForFences pcsx2_vkWaitForFences
|
||||
#define vkCreateSemaphore pcsx2_vkCreateSemaphore
|
||||
#define vkDestroySemaphore pcsx2_vkDestroySemaphore
|
||||
#define vkCreateEvent pcsx2_vkCreateEvent
|
||||
#define vkDestroyEvent pcsx2_vkDestroyEvent
|
||||
#define vkGetEventStatus pcsx2_vkGetEventStatus
|
||||
#define vkSetEvent pcsx2_vkSetEvent
|
||||
#define vkResetEvent pcsx2_vkResetEvent
|
||||
#define vkCreateQueryPool pcsx2_vkCreateQueryPool
|
||||
#define vkDestroyQueryPool pcsx2_vkDestroyQueryPool
|
||||
#define vkGetQueryPoolResults pcsx2_vkGetQueryPoolResults
|
||||
#define vkCreateBuffer pcsx2_vkCreateBuffer
|
||||
#define vkDestroyBuffer pcsx2_vkDestroyBuffer
|
||||
#define vkCreateBufferView pcsx2_vkCreateBufferView
|
||||
#define vkDestroyBufferView pcsx2_vkDestroyBufferView
|
||||
#define vkCreateImage pcsx2_vkCreateImage
|
||||
#define vkDestroyImage pcsx2_vkDestroyImage
|
||||
#define vkGetImageSubresourceLayout pcsx2_vkGetImageSubresourceLayout
|
||||
#define vkCreateImageView pcsx2_vkCreateImageView
|
||||
#define vkDestroyImageView pcsx2_vkDestroyImageView
|
||||
#define vkCreateShaderModule pcsx2_vkCreateShaderModule
|
||||
#define vkDestroyShaderModule pcsx2_vkDestroyShaderModule
|
||||
#define vkCreatePipelineCache pcsx2_vkCreatePipelineCache
|
||||
#define vkDestroyPipelineCache pcsx2_vkDestroyPipelineCache
|
||||
#define vkGetPipelineCacheData pcsx2_vkGetPipelineCacheData
|
||||
#define vkMergePipelineCaches pcsx2_vkMergePipelineCaches
|
||||
#define vkCreateGraphicsPipelines pcsx2_vkCreateGraphicsPipelines
|
||||
#define vkCreateComputePipelines pcsx2_vkCreateComputePipelines
|
||||
#define vkDestroyPipeline pcsx2_vkDestroyPipeline
|
||||
#define vkCreatePipelineLayout pcsx2_vkCreatePipelineLayout
|
||||
#define vkDestroyPipelineLayout pcsx2_vkDestroyPipelineLayout
|
||||
#define vkCreateSampler pcsx2_vkCreateSampler
|
||||
#define vkDestroySampler pcsx2_vkDestroySampler
|
||||
#define vkCreateDescriptorSetLayout pcsx2_vkCreateDescriptorSetLayout
|
||||
#define vkDestroyDescriptorSetLayout pcsx2_vkDestroyDescriptorSetLayout
|
||||
#define vkCreateDescriptorPool pcsx2_vkCreateDescriptorPool
|
||||
#define vkDestroyDescriptorPool pcsx2_vkDestroyDescriptorPool
|
||||
#define vkResetDescriptorPool pcsx2_vkResetDescriptorPool
|
||||
#define vkAllocateDescriptorSets pcsx2_vkAllocateDescriptorSets
|
||||
#define vkFreeDescriptorSets pcsx2_vkFreeDescriptorSets
|
||||
#define vkUpdateDescriptorSets pcsx2_vkUpdateDescriptorSets
|
||||
#define vkCreateFramebuffer pcsx2_vkCreateFramebuffer
|
||||
#define vkDestroyFramebuffer pcsx2_vkDestroyFramebuffer
|
||||
#define vkCreateRenderPass pcsx2_vkCreateRenderPass
|
||||
#define vkDestroyRenderPass pcsx2_vkDestroyRenderPass
|
||||
#define vkGetRenderAreaGranularity pcsx2_vkGetRenderAreaGranularity
|
||||
#define vkCreateCommandPool pcsx2_vkCreateCommandPool
|
||||
#define vkDestroyCommandPool pcsx2_vkDestroyCommandPool
|
||||
#define vkResetCommandPool pcsx2_vkResetCommandPool
|
||||
#define vkAllocateCommandBuffers pcsx2_vkAllocateCommandBuffers
|
||||
#define vkFreeCommandBuffers pcsx2_vkFreeCommandBuffers
|
||||
#define vkBeginCommandBuffer pcsx2_vkBeginCommandBuffer
|
||||
#define vkEndCommandBuffer pcsx2_vkEndCommandBuffer
|
||||
#define vkResetCommandBuffer pcsx2_vkResetCommandBuffer
|
||||
#define vkCmdBindPipeline pcsx2_vkCmdBindPipeline
|
||||
#define vkCmdSetViewport pcsx2_vkCmdSetViewport
|
||||
#define vkCmdSetScissor pcsx2_vkCmdSetScissor
|
||||
#define vkCmdSetLineWidth pcsx2_vkCmdSetLineWidth
|
||||
#define vkCmdSetDepthBias pcsx2_vkCmdSetDepthBias
|
||||
#define vkCmdSetBlendConstants pcsx2_vkCmdSetBlendConstants
|
||||
#define vkCmdSetDepthBounds pcsx2_vkCmdSetDepthBounds
|
||||
#define vkCmdSetStencilCompareMask pcsx2_vkCmdSetStencilCompareMask
|
||||
#define vkCmdSetStencilWriteMask pcsx2_vkCmdSetStencilWriteMask
|
||||
#define vkCmdSetStencilReference pcsx2_vkCmdSetStencilReference
|
||||
#define vkCmdBindDescriptorSets pcsx2_vkCmdBindDescriptorSets
|
||||
#define vkCmdBindIndexBuffer pcsx2_vkCmdBindIndexBuffer
|
||||
#define vkCmdBindVertexBuffers pcsx2_vkCmdBindVertexBuffers
|
||||
#define vkCmdDraw pcsx2_vkCmdDraw
|
||||
#define vkCmdDrawIndexed pcsx2_vkCmdDrawIndexed
|
||||
#define vkCmdDrawIndirect pcsx2_vkCmdDrawIndirect
|
||||
#define vkCmdDrawIndexedIndirect pcsx2_vkCmdDrawIndexedIndirect
|
||||
#define vkCmdDispatch pcsx2_vkCmdDispatch
|
||||
#define vkCmdDispatchIndirect pcsx2_vkCmdDispatchIndirect
|
||||
#define vkCmdCopyBuffer pcsx2_vkCmdCopyBuffer
|
||||
#define vkCmdCopyImage pcsx2_vkCmdCopyImage
|
||||
#define vkCmdBlitImage pcsx2_vkCmdBlitImage
|
||||
#define vkCmdCopyBufferToImage pcsx2_vkCmdCopyBufferToImage
|
||||
#define vkCmdCopyImageToBuffer pcsx2_vkCmdCopyImageToBuffer
|
||||
#define vkCmdUpdateBuffer pcsx2_vkCmdUpdateBuffer
|
||||
#define vkCmdFillBuffer pcsx2_vkCmdFillBuffer
|
||||
#define vkCmdClearColorImage pcsx2_vkCmdClearColorImage
|
||||
#define vkCmdClearDepthStencilImage pcsx2_vkCmdClearDepthStencilImage
|
||||
#define vkCmdClearAttachments pcsx2_vkCmdClearAttachments
|
||||
#define vkCmdResolveImage pcsx2_vkCmdResolveImage
|
||||
#define vkCmdSetEvent pcsx2_vkCmdSetEvent
|
||||
#define vkCmdResetEvent pcsx2_vkCmdResetEvent
|
||||
#define vkCmdWaitEvents pcsx2_vkCmdWaitEvents
|
||||
#define vkCmdPipelineBarrier pcsx2_vkCmdPipelineBarrier
|
||||
#define vkCmdBeginQuery pcsx2_vkCmdBeginQuery
|
||||
#define vkCmdEndQuery pcsx2_vkCmdEndQuery
|
||||
#define vkCmdResetQueryPool pcsx2_vkCmdResetQueryPool
|
||||
#define vkCmdWriteTimestamp pcsx2_vkCmdWriteTimestamp
|
||||
#define vkCmdCopyQueryPoolResults pcsx2_vkCmdCopyQueryPoolResults
|
||||
#define vkCmdPushConstants pcsx2_vkCmdPushConstants
|
||||
#define vkCmdBeginRenderPass pcsx2_vkCmdBeginRenderPass
|
||||
#define vkCmdNextSubpass pcsx2_vkCmdNextSubpass
|
||||
#define vkCmdEndRenderPass pcsx2_vkCmdEndRenderPass
|
||||
#define vkCmdExecuteCommands pcsx2_vkCmdExecuteCommands
|
||||
#define vkCreateSwapchainKHR pcsx2_vkCreateSwapchainKHR
|
||||
#define vkDestroySwapchainKHR pcsx2_vkDestroySwapchainKHR
|
||||
#define vkGetSwapchainImagesKHR pcsx2_vkGetSwapchainImagesKHR
|
||||
#define vkAcquireNextImageKHR pcsx2_vkAcquireNextImageKHR
|
||||
#define vkQueuePresentKHR pcsx2_vkQueuePresentKHR
|
||||
|
||||
// Vulkan 1.1 functions.
|
||||
#define vkGetBufferMemoryRequirements2 pcsx2_vkGetBufferMemoryRequirements2
|
||||
#define vkGetImageMemoryRequirements2 pcsx2_vkGetImageMemoryRequirements2
|
||||
#define vkBindBufferMemory2 pcsx2_vkBindBufferMemory2
|
||||
#define vkBindImageMemory2 pcsx2_vkBindImageMemory2
|
||||
|
||||
#ifdef SUPPORTS_VULKAN_EXCLUSIVE_FULLSCREEN
|
||||
#define vkAcquireFullScreenExclusiveModeEXT pcsx2_vkAcquireFullScreenExclusiveModeEXT
|
||||
#define vkReleaseFullScreenExclusiveModeEXT pcsx2_vkReleaseFullScreenExclusiveModeEXT
|
||||
#endif
|
||||
|
||||
// VK_EXT_calibrated_timestamps
|
||||
#define vkGetCalibratedTimestampsEXT pcsx2_vkGetCalibratedTimestampsEXT
|
||||
#define vkGetPhysicalDeviceCalibrateableTimeDomainsEXT pcsx2_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT
|
||||
@@ -1,260 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "Loader.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <mach-o/dyld.h>
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
|
||||
#define VULKAN_MODULE_ENTRY_POINT(name, required) PFN_##name pcsx2_##name;
|
||||
#define VULKAN_INSTANCE_ENTRY_POINT(name, required) PFN_##name pcsx2_##name;
|
||||
#define VULKAN_DEVICE_ENTRY_POINT(name, required) PFN_##name pcsx2_##name;
|
||||
#include "EntryPoints.inl"
|
||||
#undef VULKAN_DEVICE_ENTRY_POINT
|
||||
#undef VULKAN_INSTANCE_ENTRY_POINT
|
||||
#undef VULKAN_MODULE_ENTRY_POINT
|
||||
|
||||
}
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
void ResetVulkanLibraryFunctionPointers()
|
||||
{
|
||||
#define VULKAN_MODULE_ENTRY_POINT(name, required) pcsx2_##name = nullptr;
|
||||
#define VULKAN_INSTANCE_ENTRY_POINT(name, required) pcsx2_##name = nullptr;
|
||||
#define VULKAN_DEVICE_ENTRY_POINT(name, required) pcsx2_##name = nullptr;
|
||||
#include "EntryPoints.inl"
|
||||
#undef VULKAN_DEVICE_ENTRY_POINT
|
||||
#undef VULKAN_INSTANCE_ENTRY_POINT
|
||||
#undef VULKAN_MODULE_ENTRY_POINT
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
static HMODULE vulkan_module;
|
||||
static std::atomic_int vulkan_module_ref_count = {0};
|
||||
|
||||
bool LoadVulkanLibrary()
|
||||
{
|
||||
// Not thread safe if a second thread calls the loader whilst the first is still in-progress.
|
||||
if (vulkan_module)
|
||||
{
|
||||
vulkan_module_ref_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
vulkan_module = LoadLibraryA("vulkan-1.dll");
|
||||
if (!vulkan_module)
|
||||
{
|
||||
std::fprintf(stderr, "Failed to load vulkan-1.dll\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool required_functions_missing = false;
|
||||
auto LoadFunction = [&](FARPROC* func_ptr, const char* name, bool is_required) {
|
||||
*func_ptr = GetProcAddress(vulkan_module, name);
|
||||
if (!(*func_ptr) && is_required)
|
||||
{
|
||||
std::fprintf(stderr, "Vulkan: Failed to load required module function %s\n", name);
|
||||
required_functions_missing = true;
|
||||
}
|
||||
};
|
||||
|
||||
#define VULKAN_MODULE_ENTRY_POINT(name, required) LoadFunction(reinterpret_cast<FARPROC*>(&name), #name, required);
|
||||
#include "EntryPoints.inl"
|
||||
#undef VULKAN_MODULE_ENTRY_POINT
|
||||
|
||||
if (required_functions_missing)
|
||||
{
|
||||
ResetVulkanLibraryFunctionPointers();
|
||||
FreeLibrary(vulkan_module);
|
||||
vulkan_module = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
vulkan_module_ref_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnloadVulkanLibrary()
|
||||
{
|
||||
if ((--vulkan_module_ref_count) > 0)
|
||||
return;
|
||||
|
||||
ResetVulkanLibraryFunctionPointers();
|
||||
FreeLibrary(vulkan_module);
|
||||
vulkan_module = nullptr;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void* vulkan_module;
|
||||
static std::atomic_int vulkan_module_ref_count = {0};
|
||||
|
||||
bool LoadVulkanLibrary()
|
||||
{
|
||||
// Not thread safe if a second thread calls the loader whilst the first is still in-progress.
|
||||
if (vulkan_module)
|
||||
{
|
||||
vulkan_module_ref_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
// Check if a path to a specific Vulkan library has been specified.
|
||||
char* libvulkan_env = getenv("LIBVULKAN_PATH");
|
||||
if (libvulkan_env)
|
||||
vulkan_module = dlopen(libvulkan_env, RTLD_NOW);
|
||||
if (!vulkan_module)
|
||||
{
|
||||
unsigned path_size = 0;
|
||||
_NSGetExecutablePath(nullptr, &path_size);
|
||||
std::string path;
|
||||
path.resize(path_size);
|
||||
if (_NSGetExecutablePath(path.data(), &path_size) == 0)
|
||||
{
|
||||
path[path_size] = 0;
|
||||
|
||||
size_t pos = path.rfind('/');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
path.erase(pos);
|
||||
path += "/../Frameworks/libvulkan.dylib";
|
||||
vulkan_module = dlopen(path.c_str(), RTLD_NOW);
|
||||
if (!vulkan_module)
|
||||
{
|
||||
path.erase(pos);
|
||||
path += "/../Frameworks/libMoltenVK.dylib";
|
||||
vulkan_module = dlopen(path.c_str(), RTLD_NOW);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!vulkan_module)
|
||||
{
|
||||
vulkan_module = dlopen("libvulkan.dylib", RTLD_NOW);
|
||||
if (!vulkan_module)
|
||||
vulkan_module = dlopen("libMoltenVK.dylib", RTLD_NOW);
|
||||
}
|
||||
#else
|
||||
// Names of libraries to search. Desktop should use libvulkan.so.1 or libvulkan.so.
|
||||
static const char* search_lib_names[] = {"libvulkan.so.1", "libvulkan.so"};
|
||||
for (size_t i = 0; i < sizeof(search_lib_names) / sizeof(search_lib_names[0]); i++)
|
||||
{
|
||||
vulkan_module = dlopen(search_lib_names[i], RTLD_NOW);
|
||||
if (vulkan_module)
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!vulkan_module)
|
||||
{
|
||||
std::fprintf(stderr, "Failed to load or locate libvulkan.so\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool required_functions_missing = false;
|
||||
auto LoadFunction = [&](void** func_ptr, const char* name, bool is_required) {
|
||||
*func_ptr = dlsym(vulkan_module, name);
|
||||
if (!(*func_ptr) && is_required)
|
||||
{
|
||||
std::fprintf(stderr, "Vulkan: Failed to load required module function %s\n", name);
|
||||
required_functions_missing = true;
|
||||
}
|
||||
};
|
||||
|
||||
#define VULKAN_MODULE_ENTRY_POINT(name, required) LoadFunction(reinterpret_cast<void**>(&name), #name, required);
|
||||
#include "EntryPoints.inl"
|
||||
#undef VULKAN_MODULE_ENTRY_POINT
|
||||
|
||||
if (required_functions_missing)
|
||||
{
|
||||
ResetVulkanLibraryFunctionPointers();
|
||||
dlclose(vulkan_module);
|
||||
vulkan_module = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
vulkan_module_ref_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnloadVulkanLibrary()
|
||||
{
|
||||
if ((--vulkan_module_ref_count) > 0)
|
||||
return;
|
||||
|
||||
ResetVulkanLibraryFunctionPointers();
|
||||
dlclose(vulkan_module);
|
||||
vulkan_module = nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool LoadVulkanInstanceFunctions(VkInstance instance)
|
||||
{
|
||||
bool required_functions_missing = false;
|
||||
auto LoadFunction = [&](PFN_vkVoidFunction* func_ptr, const char* name, bool is_required) {
|
||||
*func_ptr = vkGetInstanceProcAddr(instance, name);
|
||||
if (!(*func_ptr) && is_required)
|
||||
{
|
||||
std::fprintf(stderr, "Vulkan: Failed to load required instance function %s\n", name);
|
||||
required_functions_missing = true;
|
||||
}
|
||||
};
|
||||
|
||||
#define VULKAN_INSTANCE_ENTRY_POINT(name, required) \
|
||||
LoadFunction(reinterpret_cast<PFN_vkVoidFunction*>(&name), #name, required);
|
||||
#include "EntryPoints.inl"
|
||||
#undef VULKAN_INSTANCE_ENTRY_POINT
|
||||
|
||||
return !required_functions_missing;
|
||||
}
|
||||
|
||||
bool LoadVulkanDeviceFunctions(VkDevice device)
|
||||
{
|
||||
bool required_functions_missing = false;
|
||||
auto LoadFunction = [&](PFN_vkVoidFunction* func_ptr, const char* name, bool is_required) {
|
||||
*func_ptr = vkGetDeviceProcAddr(device, name);
|
||||
if (!(*func_ptr) && is_required)
|
||||
{
|
||||
std::fprintf(stderr, "Vulkan: Failed to load required device function %s\n", name);
|
||||
required_functions_missing = true;
|
||||
}
|
||||
};
|
||||
|
||||
#define VULKAN_DEVICE_ENTRY_POINT(name, required) \
|
||||
LoadFunction(reinterpret_cast<PFN_vkVoidFunction*>(&name), #name, required);
|
||||
#include "EntryPoints.inl"
|
||||
#undef VULKAN_DEVICE_ENTRY_POINT
|
||||
|
||||
return !required_functions_missing;
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
@@ -1,539 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/Vulkan/ShaderCache.h"
|
||||
#include "common/Vulkan/ShaderCompiler.h"
|
||||
#include "common/Vulkan/Context.h"
|
||||
#include "common/Vulkan/Util.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/MD5Digest.h"
|
||||
|
||||
// TODO: store the driver version and stuff in the shader header
|
||||
|
||||
std::unique_ptr<Vulkan::ShaderCache> g_vulkan_shader_cache;
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
using ShaderCompiler::SPIRVCodeType;
|
||||
using ShaderCompiler::SPIRVCodeVector;
|
||||
|
||||
#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 CacheIndexEntry
|
||||
{
|
||||
u64 source_hash_low;
|
||||
u64 source_hash_high;
|
||||
u32 source_length;
|
||||
u32 shader_type;
|
||||
u32 file_offset;
|
||||
u32 blob_size;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static bool ValidatePipelineCacheHeader(const VK_PIPELINE_CACHE_HEADER& header)
|
||||
{
|
||||
if (header.header_length < sizeof(VK_PIPELINE_CACHE_HEADER))
|
||||
{
|
||||
Console.Error("Pipeline cache failed validation: Invalid header length");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.header_version != VK_PIPELINE_CACHE_HEADER_VERSION_ONE)
|
||||
{
|
||||
Console.Error("Pipeline cache failed validation: Invalid header version");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.vendor_id != g_vulkan_context->GetDeviceProperties().vendorID)
|
||||
{
|
||||
Console.Error("Pipeline cache failed validation: Incorrect vendor ID (file: 0x%X, device: 0x%X)",
|
||||
header.vendor_id, g_vulkan_context->GetDeviceProperties().vendorID);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.device_id != g_vulkan_context->GetDeviceProperties().deviceID)
|
||||
{
|
||||
Console.Error("Pipeline cache failed validation: Incorrect device ID (file: 0x%X, device: 0x%X)",
|
||||
header.device_id, g_vulkan_context->GetDeviceProperties().deviceID);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::memcmp(header.uuid, g_vulkan_context->GetDeviceProperties().pipelineCacheUUID, VK_UUID_SIZE) != 0)
|
||||
{
|
||||
Console.Error("Pipeline cache failed validation: Incorrect UUID");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void FillPipelineCacheHeader(VK_PIPELINE_CACHE_HEADER* header)
|
||||
{
|
||||
header->header_length = sizeof(VK_PIPELINE_CACHE_HEADER);
|
||||
header->header_version = VK_PIPELINE_CACHE_HEADER_VERSION_ONE;
|
||||
header->vendor_id = g_vulkan_context->GetDeviceProperties().vendorID;
|
||||
header->device_id = g_vulkan_context->GetDeviceProperties().deviceID;
|
||||
std::memcpy(header->uuid, g_vulkan_context->GetDeviceProperties().pipelineCacheUUID, VK_UUID_SIZE);
|
||||
}
|
||||
|
||||
ShaderCache::ShaderCache() = default;
|
||||
|
||||
ShaderCache::~ShaderCache()
|
||||
{
|
||||
CloseShaderCache();
|
||||
FlushPipelineCache();
|
||||
ClosePipelineCache();
|
||||
}
|
||||
|
||||
bool ShaderCache::CacheIndexKey::operator==(const CacheIndexKey& key) const
|
||||
{
|
||||
return (source_hash_low == key.source_hash_low && source_hash_high == key.source_hash_high &&
|
||||
source_length == key.source_length && shader_type == key.shader_type);
|
||||
}
|
||||
|
||||
bool ShaderCache::CacheIndexKey::operator!=(const CacheIndexKey& key) const
|
||||
{
|
||||
return (source_hash_low != key.source_hash_low || source_hash_high != key.source_hash_high ||
|
||||
source_length != key.source_length || shader_type != key.shader_type);
|
||||
}
|
||||
|
||||
void ShaderCache::Create(std::string_view base_path, u32 version, bool debug)
|
||||
{
|
||||
pxAssert(!g_vulkan_shader_cache);
|
||||
g_vulkan_shader_cache.reset(new ShaderCache());
|
||||
g_vulkan_shader_cache->Open(base_path, version, debug);
|
||||
}
|
||||
|
||||
void ShaderCache::Destroy() { g_vulkan_shader_cache.reset(); }
|
||||
|
||||
void ShaderCache::Open(std::string_view directory, u32 version, bool debug)
|
||||
{
|
||||
m_version = version;
|
||||
m_debug = debug;
|
||||
|
||||
if (!directory.empty())
|
||||
{
|
||||
m_pipeline_cache_filename = GetPipelineCacheBaseFileName(directory, debug);
|
||||
|
||||
const std::string base_filename = GetShaderCacheBaseFileName(directory, debug);
|
||||
const std::string index_filename = base_filename + ".idx";
|
||||
const std::string blob_filename = base_filename + ".bin";
|
||||
|
||||
if (!ReadExistingShaderCache(index_filename, blob_filename))
|
||||
CreateNewShaderCache(index_filename, blob_filename);
|
||||
|
||||
if (!ReadExistingPipelineCache())
|
||||
CreateNewPipelineCache();
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateNewPipelineCache();
|
||||
}
|
||||
}
|
||||
|
||||
VkPipelineCache ShaderCache::GetPipelineCache(bool set_dirty /*= true*/)
|
||||
{
|
||||
if (m_pipeline_cache == VK_NULL_HANDLE)
|
||||
return VK_NULL_HANDLE;
|
||||
|
||||
m_pipeline_cache_dirty |= set_dirty;
|
||||
return m_pipeline_cache;
|
||||
}
|
||||
|
||||
bool ShaderCache::CreateNewShaderCache(const std::string& index_filename, const std::string& blob_filename)
|
||||
{
|
||||
if (FileSystem::FileExists(index_filename.c_str()))
|
||||
{
|
||||
Console.Warning("Removing existing index file '%s'", index_filename.c_str());
|
||||
FileSystem::DeleteFilePath(index_filename.c_str());
|
||||
}
|
||||
if (FileSystem::FileExists(blob_filename.c_str()))
|
||||
{
|
||||
Console.Warning("Removing existing blob file '%s'", blob_filename.c_str());
|
||||
FileSystem::DeleteFilePath(blob_filename.c_str());
|
||||
}
|
||||
|
||||
m_index_file = FileSystem::OpenCFile(index_filename.c_str(), "wb");
|
||||
if (!m_index_file)
|
||||
{
|
||||
Console.Error("Failed to open index file '%s' for writing", index_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
const u32 index_version = FILE_VERSION;
|
||||
VK_PIPELINE_CACHE_HEADER header;
|
||||
FillPipelineCacheHeader(&header);
|
||||
|
||||
if (std::fwrite(&index_version, sizeof(index_version), 1, m_index_file) != 1 ||
|
||||
std::fwrite(&m_version, sizeof(m_version), 1, m_index_file) != 1 ||
|
||||
std::fwrite(&header, sizeof(header), 1, m_index_file) != 1)
|
||||
{
|
||||
Console.Error("Failed to write header to index file '%s'", index_filename.c_str());
|
||||
std::fclose(m_index_file);
|
||||
m_index_file = nullptr;
|
||||
FileSystem::DeleteFilePath(index_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_blob_file = FileSystem::OpenCFile(blob_filename.c_str(), "w+b");
|
||||
if (!m_blob_file)
|
||||
{
|
||||
Console.Error("Failed to open blob file '%s' for writing", blob_filename.c_str());
|
||||
std::fclose(m_index_file);
|
||||
m_index_file = nullptr;
|
||||
FileSystem::DeleteFilePath(index_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShaderCache::ReadExistingShaderCache(const std::string& index_filename, const std::string& blob_filename)
|
||||
{
|
||||
m_index_file = FileSystem::OpenCFile(index_filename.c_str(), "r+b");
|
||||
if (!m_index_file)
|
||||
{
|
||||
// special case here: when there's a sharing violation (i.e. two instances running),
|
||||
// we don't want to blow away the cache. so just continue without a cache.
|
||||
if (errno == EACCES)
|
||||
{
|
||||
Console.WriteLn("Failed to open shader cache index with EACCES, are you running two instances?");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 file_version = 0;
|
||||
u32 data_version = 0;
|
||||
if (std::fread(&file_version, sizeof(file_version), 1, m_index_file) != 1 || file_version != FILE_VERSION ||
|
||||
std::fread(&data_version, sizeof(data_version), 1, m_index_file) != 1 || data_version != m_version)
|
||||
{
|
||||
Console.Error("Bad file/data version in '%s'", index_filename.c_str());
|
||||
std::fclose(m_index_file);
|
||||
m_index_file = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
VK_PIPELINE_CACHE_HEADER header;
|
||||
if (std::fread(&header, sizeof(header), 1, m_index_file) != 1 || !ValidatePipelineCacheHeader(header))
|
||||
{
|
||||
Console.Error("Mismatched pipeline cache header in '%s' (GPU/driver changed?)", index_filename.c_str());
|
||||
std::fclose(m_index_file);
|
||||
m_index_file = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_blob_file = FileSystem::OpenCFile(blob_filename.c_str(), "a+b");
|
||||
if (!m_blob_file)
|
||||
{
|
||||
Console.Error("Blob file '%s' is missing", blob_filename.c_str());
|
||||
std::fclose(m_index_file);
|
||||
m_index_file = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::fseek(m_blob_file, 0, SEEK_END);
|
||||
const u32 blob_file_size = static_cast<u32>(std::ftell(m_blob_file));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
CacheIndexEntry entry;
|
||||
if (std::fread(&entry, sizeof(entry), 1, m_index_file) != 1 ||
|
||||
(entry.file_offset + entry.blob_size) > blob_file_size)
|
||||
{
|
||||
if (std::feof(m_index_file))
|
||||
break;
|
||||
|
||||
Console.Error("Failed to read entry from '%s', corrupt file?", index_filename.c_str());
|
||||
m_index.clear();
|
||||
std::fclose(m_blob_file);
|
||||
m_blob_file = nullptr;
|
||||
std::fclose(m_index_file);
|
||||
m_index_file = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
const CacheIndexKey key{entry.source_hash_low, entry.source_hash_high, entry.source_length,
|
||||
static_cast<ShaderCompiler::Type>(entry.shader_type)};
|
||||
const CacheIndexData data{entry.file_offset, entry.blob_size};
|
||||
m_index.emplace(key, data);
|
||||
}
|
||||
|
||||
// ensure we don't write before seeking
|
||||
std::fseek(m_index_file, 0, SEEK_END);
|
||||
|
||||
Console.WriteLn("Read %zu entries from '%s'", m_index.size(), index_filename.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderCache::CloseShaderCache()
|
||||
{
|
||||
if (m_index_file)
|
||||
{
|
||||
std::fclose(m_index_file);
|
||||
m_index_file = nullptr;
|
||||
}
|
||||
if (m_blob_file)
|
||||
{
|
||||
std::fclose(m_blob_file);
|
||||
m_blob_file = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool ShaderCache::CreateNewPipelineCache()
|
||||
{
|
||||
if (!m_pipeline_cache_filename.empty() && FileSystem::FileExists(m_pipeline_cache_filename.c_str()))
|
||||
{
|
||||
Console.Warning("Removing existing pipeline cache '%s'", m_pipeline_cache_filename.c_str());
|
||||
FileSystem::DeleteFilePath(m_pipeline_cache_filename.c_str());
|
||||
}
|
||||
|
||||
const VkPipelineCacheCreateInfo ci{VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, nullptr, 0, 0, nullptr};
|
||||
VkResult res = vkCreatePipelineCache(g_vulkan_context->GetDevice(), &ci, nullptr, &m_pipeline_cache);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreatePipelineCache() failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pipeline_cache_dirty = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShaderCache::ReadExistingPipelineCache()
|
||||
{
|
||||
std::optional<std::vector<u8>> data = FileSystem::ReadBinaryFile(m_pipeline_cache_filename.c_str());
|
||||
if (!data.has_value())
|
||||
return false;
|
||||
|
||||
if (data->size() < sizeof(VK_PIPELINE_CACHE_HEADER))
|
||||
{
|
||||
Console.Error("Pipeline cache at '%s' is too small", m_pipeline_cache_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
VK_PIPELINE_CACHE_HEADER header;
|
||||
std::memcpy(&header, data->data(), sizeof(header));
|
||||
if (!ValidatePipelineCacheHeader(header))
|
||||
return false;
|
||||
|
||||
const VkPipelineCacheCreateInfo ci{
|
||||
VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, nullptr, 0, data->size(), data->data()};
|
||||
VkResult res = vkCreatePipelineCache(g_vulkan_context->GetDevice(), &ci, nullptr, &m_pipeline_cache);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreatePipelineCache() failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShaderCache::FlushPipelineCache()
|
||||
{
|
||||
if (m_pipeline_cache == VK_NULL_HANDLE || !m_pipeline_cache_dirty || m_pipeline_cache_filename.empty())
|
||||
return false;
|
||||
|
||||
size_t data_size;
|
||||
VkResult res = vkGetPipelineCacheData(g_vulkan_context->GetDevice(), m_pipeline_cache, &data_size, nullptr);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPipelineCacheData() failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<u8> data(data_size);
|
||||
res = vkGetPipelineCacheData(g_vulkan_context->GetDevice(), m_pipeline_cache, &data_size, data.data());
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPipelineCacheData() (2) failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
data.resize(data_size);
|
||||
|
||||
// Save disk writes if it hasn't changed, think of the poor SSDs.
|
||||
FILESYSTEM_STAT_DATA sd;
|
||||
if (!FileSystem::StatFile(m_pipeline_cache_filename.c_str(), &sd) || sd.Size != static_cast<s64>(data_size))
|
||||
{
|
||||
Console.WriteLn("Writing %zu bytes to '%s'", data_size, m_pipeline_cache_filename.c_str());
|
||||
if (!FileSystem::WriteBinaryFile(m_pipeline_cache_filename.c_str(), data.data(), data.size()))
|
||||
{
|
||||
Console.Error("Failed to write pipeline cache to '%s'", m_pipeline_cache_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLn(
|
||||
"Skipping updating pipeline cache '%s' due to no changes.", m_pipeline_cache_filename.c_str());
|
||||
}
|
||||
|
||||
m_pipeline_cache_dirty = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShaderCache::ClosePipelineCache()
|
||||
{
|
||||
if (m_pipeline_cache == VK_NULL_HANDLE)
|
||||
return;
|
||||
|
||||
vkDestroyPipelineCache(g_vulkan_context->GetDevice(), m_pipeline_cache, nullptr);
|
||||
m_pipeline_cache = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
std::string ShaderCache::GetShaderCacheBaseFileName(const std::string_view& base_path, bool debug)
|
||||
{
|
||||
std::string base_filename(base_path);
|
||||
base_filename += FS_OSPATH_SEPARATOR_STR "vulkan_shaders";
|
||||
|
||||
if (debug)
|
||||
base_filename += "_debug";
|
||||
|
||||
return base_filename;
|
||||
}
|
||||
|
||||
std::string ShaderCache::GetPipelineCacheBaseFileName(const std::string_view& base_path, bool debug)
|
||||
{
|
||||
std::string base_filename(base_path);
|
||||
base_filename += FS_OSPATH_SEPARATOR_STR "vulkan_pipelines";
|
||||
|
||||
if (debug)
|
||||
base_filename += "_debug";
|
||||
|
||||
base_filename += ".bin";
|
||||
return base_filename;
|
||||
}
|
||||
|
||||
ShaderCache::CacheIndexKey ShaderCache::GetCacheKey(ShaderCompiler::Type type, const std::string_view& shader_code)
|
||||
{
|
||||
union HashParts
|
||||
{
|
||||
struct
|
||||
{
|
||||
u64 hash_low;
|
||||
u64 hash_high;
|
||||
};
|
||||
u8 hash[16];
|
||||
};
|
||||
HashParts h;
|
||||
|
||||
MD5Digest digest;
|
||||
digest.Update(shader_code.data(), static_cast<u32>(shader_code.length()));
|
||||
digest.Final(h.hash);
|
||||
|
||||
return CacheIndexKey{h.hash_low, h.hash_high, static_cast<u32>(shader_code.length()), type};
|
||||
}
|
||||
|
||||
std::optional<ShaderCompiler::SPIRVCodeVector> ShaderCache::GetShaderSPV(
|
||||
ShaderCompiler::Type type, std::string_view shader_code)
|
||||
{
|
||||
const auto key = GetCacheKey(type, shader_code);
|
||||
auto iter = m_index.find(key);
|
||||
if (iter == m_index.end())
|
||||
return CompileAndAddShaderSPV(key, shader_code);
|
||||
|
||||
SPIRVCodeVector spv(iter->second.blob_size);
|
||||
if (std::fseek(m_blob_file, iter->second.file_offset, SEEK_SET) != 0 ||
|
||||
std::fread(spv.data(), sizeof(SPIRVCodeType), iter->second.blob_size, m_blob_file) !=
|
||||
iter->second.blob_size)
|
||||
{
|
||||
Console.Error("Read blob from file failed, recompiling");
|
||||
return ShaderCompiler::CompileShader(type, shader_code, m_debug);
|
||||
}
|
||||
|
||||
return spv;
|
||||
}
|
||||
|
||||
VkShaderModule ShaderCache::GetShaderModule(ShaderCompiler::Type type, std::string_view shader_code)
|
||||
{
|
||||
std::optional<SPIRVCodeVector> spv = GetShaderSPV(type, shader_code);
|
||||
if (!spv.has_value())
|
||||
return VK_NULL_HANDLE;
|
||||
|
||||
const VkShaderModuleCreateInfo ci{
|
||||
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, nullptr, 0, spv->size() * sizeof(SPIRVCodeType), spv->data()};
|
||||
|
||||
VkShaderModule mod;
|
||||
VkResult res = vkCreateShaderModule(g_vulkan_context->GetDevice(), &ci, nullptr, &mod);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateShaderModule() failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
VkShaderModule ShaderCache::GetVertexShader(std::string_view shader_code)
|
||||
{
|
||||
return GetShaderModule(ShaderCompiler::Type::Vertex, std::move(shader_code));
|
||||
}
|
||||
|
||||
VkShaderModule ShaderCache::GetFragmentShader(std::string_view shader_code)
|
||||
{
|
||||
return GetShaderModule(ShaderCompiler::Type::Fragment, std::move(shader_code));
|
||||
}
|
||||
|
||||
VkShaderModule ShaderCache::GetComputeShader(std::string_view shader_code)
|
||||
{
|
||||
return GetShaderModule(ShaderCompiler::Type::Compute, std::move(shader_code));
|
||||
}
|
||||
|
||||
std::optional<ShaderCompiler::SPIRVCodeVector> ShaderCache::CompileAndAddShaderSPV(
|
||||
const CacheIndexKey& key, std::string_view shader_code)
|
||||
{
|
||||
std::optional<SPIRVCodeVector> spv = ShaderCompiler::CompileShader(key.shader_type, shader_code, m_debug);
|
||||
if (!spv.has_value())
|
||||
return {};
|
||||
|
||||
if (!m_blob_file || std::fseek(m_blob_file, 0, SEEK_END) != 0)
|
||||
return spv;
|
||||
|
||||
CacheIndexData data;
|
||||
data.file_offset = static_cast<u32>(std::ftell(m_blob_file));
|
||||
data.blob_size = static_cast<u32>(spv->size());
|
||||
|
||||
CacheIndexEntry entry = {};
|
||||
entry.source_hash_low = key.source_hash_low;
|
||||
entry.source_hash_high = key.source_hash_high;
|
||||
entry.source_length = key.source_length;
|
||||
entry.shader_type = static_cast<u32>(key.shader_type);
|
||||
entry.blob_size = data.blob_size;
|
||||
entry.file_offset = data.file_offset;
|
||||
|
||||
if (std::fwrite(spv->data(), sizeof(SPIRVCodeType), entry.blob_size, m_blob_file) != entry.blob_size ||
|
||||
std::fflush(m_blob_file) != 0 || std::fwrite(&entry, sizeof(entry), 1, m_index_file) != 1 ||
|
||||
std::fflush(m_index_file) != 0)
|
||||
{
|
||||
Console.Error("Failed to write shader blob to file");
|
||||
return spv;
|
||||
}
|
||||
|
||||
m_index.emplace(key, data);
|
||||
return spv;
|
||||
}
|
||||
} // namespace Vulkan
|
||||
@@ -1,117 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "common/Vulkan/ShaderCompiler.h"
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/HashCombine.h"
|
||||
#include "common/Vulkan/Loader.h"
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class ShaderCache
|
||||
{
|
||||
public:
|
||||
~ShaderCache();
|
||||
|
||||
static void Create(std::string_view directory, u32 version, bool debug);
|
||||
static void Destroy();
|
||||
|
||||
/// Returns a handle to the pipeline cache. Set set_dirty to true if you are planning on writing to it externally.
|
||||
VkPipelineCache GetPipelineCache(bool set_dirty = true);
|
||||
|
||||
/// Writes pipeline cache to file, saving all newly compiled pipelines.
|
||||
bool FlushPipelineCache();
|
||||
|
||||
std::optional<ShaderCompiler::SPIRVCodeVector> GetShaderSPV(
|
||||
ShaderCompiler::Type type, std::string_view shader_code);
|
||||
VkShaderModule GetShaderModule(ShaderCompiler::Type type, std::string_view shader_code);
|
||||
|
||||
VkShaderModule GetVertexShader(std::string_view shader_code);
|
||||
VkShaderModule GetFragmentShader(std::string_view shader_code);
|
||||
VkShaderModule GetComputeShader(std::string_view shader_code);
|
||||
|
||||
private:
|
||||
static constexpr u32 FILE_VERSION = 2;
|
||||
|
||||
struct CacheIndexKey
|
||||
{
|
||||
u64 source_hash_low;
|
||||
u64 source_hash_high;
|
||||
u32 source_length;
|
||||
ShaderCompiler::Type shader_type;
|
||||
|
||||
bool operator==(const CacheIndexKey& key) const;
|
||||
bool operator!=(const CacheIndexKey& key) const;
|
||||
};
|
||||
|
||||
struct CacheIndexEntryHasher
|
||||
{
|
||||
std::size_t operator()(const CacheIndexKey& e) const noexcept
|
||||
{
|
||||
std::size_t h = 0;
|
||||
HashCombine(h, e.source_hash_low, e.source_hash_high, e.source_length, e.shader_type);
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
struct CacheIndexData
|
||||
{
|
||||
u32 file_offset;
|
||||
u32 blob_size;
|
||||
};
|
||||
|
||||
using CacheIndex = std::unordered_map<CacheIndexKey, CacheIndexData, CacheIndexEntryHasher>;
|
||||
|
||||
ShaderCache();
|
||||
|
||||
static std::string GetShaderCacheBaseFileName(const std::string_view& base_path, bool debug);
|
||||
static std::string GetPipelineCacheBaseFileName(const std::string_view& base_path, bool debug);
|
||||
static CacheIndexKey GetCacheKey(ShaderCompiler::Type type, const std::string_view& shader_code);
|
||||
|
||||
void Open(std::string_view base_path, u32 version, bool debug);
|
||||
|
||||
bool CreateNewShaderCache(const std::string& index_filename, const std::string& blob_filename);
|
||||
bool ReadExistingShaderCache(const std::string& index_filename, const std::string& blob_filename);
|
||||
void CloseShaderCache();
|
||||
|
||||
bool CreateNewPipelineCache();
|
||||
bool ReadExistingPipelineCache();
|
||||
void ClosePipelineCache();
|
||||
|
||||
std::optional<ShaderCompiler::SPIRVCodeVector> CompileAndAddShaderSPV(
|
||||
const CacheIndexKey& key, std::string_view shader_code);
|
||||
|
||||
std::FILE* m_index_file = nullptr;
|
||||
std::FILE* m_blob_file = nullptr;
|
||||
std::string m_pipeline_cache_filename;
|
||||
|
||||
CacheIndex m_index;
|
||||
|
||||
VkPipelineCache m_pipeline_cache = VK_NULL_HANDLE;
|
||||
u32 m_version = 0;
|
||||
bool m_debug = false;
|
||||
bool m_pipeline_cache_dirty = false;
|
||||
};
|
||||
} // namespace Vulkan
|
||||
|
||||
extern std::unique_ptr<Vulkan::ShaderCache> g_vulkan_shader_cache;
|
||||
@@ -1,184 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/Vulkan/ShaderCompiler.h"
|
||||
#include "common/Vulkan/Util.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
|
||||
// glslang includes
|
||||
#include "SPIRV/GlslangToSpv.h"
|
||||
#include "StandAlone/ResourceLimits.h"
|
||||
#include "glslang/Public/ShaderLang.h"
|
||||
|
||||
namespace Vulkan::ShaderCompiler
|
||||
{
|
||||
// Registers itself for cleanup via atexit
|
||||
bool InitializeGlslang();
|
||||
|
||||
static unsigned s_next_bad_shader_id = 1;
|
||||
|
||||
static bool glslang_initialized = false;
|
||||
|
||||
static std::optional<SPIRVCodeVector> CompileShaderToSPV(
|
||||
EShLanguage stage, const char* stage_filename, std::string_view source, bool debug)
|
||||
{
|
||||
if (!InitializeGlslang())
|
||||
return std::nullopt;
|
||||
|
||||
std::unique_ptr<glslang::TShader> shader = std::make_unique<glslang::TShader>(stage);
|
||||
std::unique_ptr<glslang::TProgram> program;
|
||||
glslang::TShader::ForbidIncluder includer;
|
||||
const EProfile profile = ECoreProfile;
|
||||
const EShMessages messages = static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules | (debug ? EShMsgDebugInfo : 0));
|
||||
const int default_version = 450;
|
||||
|
||||
std::string full_source_code;
|
||||
const char* pass_source_code = source.data();
|
||||
int pass_source_code_length = static_cast<int>(source.size());
|
||||
shader->setStringsWithLengths(&pass_source_code, &pass_source_code_length, 1);
|
||||
|
||||
auto DumpBadShader = [&](const char* msg) {
|
||||
std::string filename = StringUtil::StdStringFromFormat("pcsx2_bad_shader_%u.txt", s_next_bad_shader_id++);
|
||||
Console.Error("CompileShaderToSPV: %s, writing to %s", msg, filename.c_str());
|
||||
|
||||
std::ofstream ofs(filename.c_str(), std::ofstream::out | std::ofstream::binary);
|
||||
if (ofs.is_open())
|
||||
{
|
||||
ofs << source;
|
||||
ofs << "\n";
|
||||
|
||||
ofs << msg << std::endl;
|
||||
ofs << "Shader Info Log:" << std::endl;
|
||||
ofs << shader->getInfoLog() << std::endl;
|
||||
ofs << shader->getInfoDebugLog() << std::endl;
|
||||
if (program)
|
||||
{
|
||||
ofs << "Program Info Log:" << std::endl;
|
||||
ofs << program->getInfoLog() << std::endl;
|
||||
ofs << program->getInfoDebugLog() << std::endl;
|
||||
}
|
||||
|
||||
ofs.close();
|
||||
}
|
||||
};
|
||||
|
||||
if (!shader->parse(
|
||||
&glslang::DefaultTBuiltInResource, default_version, profile, false, true, messages, includer))
|
||||
{
|
||||
DumpBadShader("Failed to parse shader");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Even though there's only a single shader, we still need to link it to generate SPV
|
||||
program = std::make_unique<glslang::TProgram>();
|
||||
program->addShader(shader.get());
|
||||
if (!program->link(messages))
|
||||
{
|
||||
DumpBadShader("Failed to link program");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
glslang::TIntermediate* intermediate = program->getIntermediate(stage);
|
||||
if (!intermediate)
|
||||
{
|
||||
DumpBadShader("Failed to generate SPIR-V");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
SPIRVCodeVector out_code;
|
||||
spv::SpvBuildLogger logger;
|
||||
glslang::SpvOptions options;
|
||||
options.generateDebugInfo = debug;
|
||||
glslang::GlslangToSpv(*intermediate, out_code, &logger, &options);
|
||||
|
||||
// Write out messages
|
||||
if (std::strlen(shader->getInfoLog()) > 0)
|
||||
Console.Warning("Shader info log: %s", shader->getInfoLog());
|
||||
if (std::strlen(shader->getInfoDebugLog()) > 0)
|
||||
Console.Warning("Shader debug info log: %s", shader->getInfoDebugLog());
|
||||
if (std::strlen(program->getInfoLog()) > 0)
|
||||
Console.Warning("Program info log: %s", program->getInfoLog());
|
||||
if (std::strlen(program->getInfoDebugLog()) > 0)
|
||||
Console.Warning("Program debug info log: %s", program->getInfoDebugLog());
|
||||
std::string spv_messages = logger.getAllMessages();
|
||||
if (!spv_messages.empty())
|
||||
Console.Warning("SPIR-V conversion messages: %s", spv_messages.c_str());
|
||||
|
||||
return out_code;
|
||||
}
|
||||
|
||||
bool InitializeGlslang()
|
||||
{
|
||||
if (glslang_initialized)
|
||||
return true;
|
||||
|
||||
if (!glslang::InitializeProcess())
|
||||
{
|
||||
pxFailRel("Failed to initialize glslang shader compiler");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::atexit(DeinitializeGlslang);
|
||||
glslang_initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeinitializeGlslang()
|
||||
{
|
||||
if (!glslang_initialized)
|
||||
return;
|
||||
|
||||
glslang::FinalizeProcess();
|
||||
glslang_initialized = false;
|
||||
}
|
||||
|
||||
std::optional<SPIRVCodeVector> CompileVertexShader(std::string_view source_code, bool debug)
|
||||
{
|
||||
return CompileShaderToSPV(EShLangVertex, "vs", source_code, debug);
|
||||
}
|
||||
|
||||
std::optional<SPIRVCodeVector> CompileFragmentShader(std::string_view source_code, bool debug)
|
||||
{
|
||||
return CompileShaderToSPV(EShLangFragment, "ps", source_code, debug);
|
||||
}
|
||||
|
||||
std::optional<SPIRVCodeVector> CompileComputeShader(std::string_view source_code, bool debug)
|
||||
{
|
||||
return CompileShaderToSPV(EShLangCompute, "cs", source_code, debug);
|
||||
}
|
||||
|
||||
std::optional<ShaderCompiler::SPIRVCodeVector> CompileShader(Type type, std::string_view source_code, bool debug)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Type::Vertex:
|
||||
return CompileShaderToSPV(EShLangVertex, "vs", source_code, debug);
|
||||
|
||||
case Type::Fragment:
|
||||
return CompileShaderToSPV(EShLangFragment, "ps", source_code, debug);
|
||||
|
||||
case Type::Compute:
|
||||
return CompileShaderToSPV(EShLangCompute, "cs", source_code, debug);
|
||||
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
} // namespace Vulkan::ShaderCompiler
|
||||
@@ -1,49 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace Vulkan::ShaderCompiler
|
||||
{
|
||||
// Shader types
|
||||
enum class Type
|
||||
{
|
||||
Vertex,
|
||||
Fragment,
|
||||
Compute
|
||||
};
|
||||
|
||||
void DeinitializeGlslang();
|
||||
|
||||
// SPIR-V compiled code type
|
||||
using SPIRVCodeType = u32;
|
||||
using SPIRVCodeVector = std::vector<SPIRVCodeType>;
|
||||
|
||||
// Compile a vertex shader to SPIR-V.
|
||||
std::optional<SPIRVCodeVector> CompileVertexShader(std::string_view source_code, bool debug);
|
||||
|
||||
// Compile a fragment shader to SPIR-V.
|
||||
std::optional<SPIRVCodeVector> CompileFragmentShader(std::string_view source_code, bool debug);
|
||||
|
||||
// Compile a compute shader to SPIR-V.
|
||||
std::optional<SPIRVCodeVector> CompileComputeShader(std::string_view source_code, bool debug);
|
||||
|
||||
std::optional<SPIRVCodeVector> CompileShader(Type type, std::string_view source_code, bool debug);
|
||||
} // namespace Vulkan::ShaderCompiler
|
||||
@@ -1,323 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/Vulkan/StreamBuffer.h"
|
||||
#include "common/Vulkan/Context.h"
|
||||
#include "common/Vulkan/Util.h"
|
||||
#include "common/Align.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
StreamBuffer::StreamBuffer() = default;
|
||||
|
||||
StreamBuffer::StreamBuffer(StreamBuffer&& move)
|
||||
: m_size(move.m_size)
|
||||
, m_current_offset(move.m_current_offset)
|
||||
, m_current_space(move.m_current_space)
|
||||
, m_current_gpu_position(move.m_current_gpu_position)
|
||||
, m_allocation(move.m_allocation)
|
||||
, m_buffer(move.m_buffer)
|
||||
, m_host_pointer(move.m_host_pointer)
|
||||
, m_tracked_fences(std::move(move.m_tracked_fences))
|
||||
{
|
||||
move.m_size = 0;
|
||||
move.m_current_offset = 0;
|
||||
move.m_current_space = 0;
|
||||
move.m_current_gpu_position = 0;
|
||||
move.m_allocation = VK_NULL_HANDLE;
|
||||
move.m_buffer = VK_NULL_HANDLE;
|
||||
move.m_host_pointer = nullptr;
|
||||
}
|
||||
|
||||
StreamBuffer::~StreamBuffer()
|
||||
{
|
||||
if (IsValid())
|
||||
Destroy(true);
|
||||
}
|
||||
|
||||
StreamBuffer& StreamBuffer::operator=(StreamBuffer&& move)
|
||||
{
|
||||
if (IsValid())
|
||||
Destroy(true);
|
||||
|
||||
std::swap(m_size, move.m_size);
|
||||
std::swap(m_current_offset, move.m_current_offset);
|
||||
std::swap(m_current_space, move.m_current_space);
|
||||
std::swap(m_current_gpu_position, move.m_current_gpu_position);
|
||||
std::swap(m_buffer, move.m_buffer);
|
||||
std::swap(m_host_pointer, move.m_host_pointer);
|
||||
std::swap(m_tracked_fences, move.m_tracked_fences);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool StreamBuffer::Create(VkBufferUsageFlags usage, u32 size)
|
||||
{
|
||||
const VkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, nullptr, 0,
|
||||
static_cast<VkDeviceSize>(size), usage, VK_SHARING_MODE_EXCLUSIVE, 0, nullptr};
|
||||
|
||||
VmaAllocationCreateInfo aci = {};
|
||||
aci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
||||
aci.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
|
||||
aci.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||
|
||||
VmaAllocationInfo ai = {};
|
||||
VkBuffer new_buffer = VK_NULL_HANDLE;
|
||||
VmaAllocation new_allocation = VK_NULL_HANDLE;
|
||||
VkResult res = vmaCreateBuffer(g_vulkan_context->GetAllocator(), &bci, &aci, &new_buffer, &new_allocation, &ai);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateBuffer failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsValid())
|
||||
Destroy(true);
|
||||
|
||||
// Replace with the new buffer
|
||||
m_size = size;
|
||||
m_current_offset = 0;
|
||||
m_current_gpu_position = 0;
|
||||
m_tracked_fences.clear();
|
||||
m_allocation = new_allocation;
|
||||
m_buffer = new_buffer;
|
||||
m_host_pointer = static_cast<u8*>(ai.pMappedData);
|
||||
return true;
|
||||
}
|
||||
|
||||
void StreamBuffer::Destroy(bool defer)
|
||||
{
|
||||
if (m_buffer != VK_NULL_HANDLE)
|
||||
{
|
||||
if (defer)
|
||||
g_vulkan_context->DeferBufferDestruction(m_buffer, m_allocation);
|
||||
else
|
||||
vmaDestroyBuffer(g_vulkan_context->GetAllocator(), m_buffer, m_allocation);
|
||||
}
|
||||
|
||||
m_size = 0;
|
||||
m_current_offset = 0;
|
||||
m_current_gpu_position = 0;
|
||||
m_tracked_fences.clear();
|
||||
m_buffer = VK_NULL_HANDLE;
|
||||
m_allocation = VK_NULL_HANDLE;
|
||||
m_host_pointer = nullptr;
|
||||
}
|
||||
|
||||
bool StreamBuffer::ReserveMemory(u32 num_bytes, u32 alignment)
|
||||
{
|
||||
const u32 required_bytes = num_bytes + alignment;
|
||||
|
||||
// Check for sane allocations
|
||||
if (required_bytes > m_size)
|
||||
{
|
||||
Console.Error("Attempting to allocate %u bytes from a %u byte stream buffer", static_cast<u32>(num_bytes),
|
||||
static_cast<u32>(m_size));
|
||||
pxFailRel("Stream buffer overflow");
|
||||
return false;
|
||||
}
|
||||
|
||||
UpdateGPUPosition();
|
||||
|
||||
// Is the GPU behind or up to date with our current offset?
|
||||
if (m_current_offset >= m_current_gpu_position)
|
||||
{
|
||||
const u32 remaining_bytes = m_size - m_current_offset;
|
||||
if (required_bytes <= remaining_bytes)
|
||||
{
|
||||
// Place at the current position, after the GPU position.
|
||||
m_current_offset = Common::AlignUp(m_current_offset, alignment);
|
||||
m_current_space = m_size - m_current_offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for space at the start of the buffer
|
||||
// We use < here because we don't want to have the case of m_current_offset ==
|
||||
// m_current_gpu_position. That would mean the code above would assume the
|
||||
// GPU has caught up to us, which it hasn't.
|
||||
if (required_bytes < m_current_gpu_position)
|
||||
{
|
||||
// Reset offset to zero, since we're allocating behind the gpu now
|
||||
m_current_offset = 0;
|
||||
m_current_space = m_current_gpu_position - 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Is the GPU ahead of our current offset?
|
||||
if (m_current_offset < m_current_gpu_position)
|
||||
{
|
||||
// We have from m_current_offset..m_current_gpu_position space to use.
|
||||
const u32 remaining_bytes = m_current_gpu_position - m_current_offset;
|
||||
if (required_bytes < remaining_bytes)
|
||||
{
|
||||
// Place at the current position, since this is still behind the GPU.
|
||||
m_current_offset = Common::AlignUp(m_current_offset, alignment);
|
||||
m_current_space = m_current_gpu_position - m_current_offset - 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Can we find a fence to wait on that will give us enough memory?
|
||||
if (WaitForClearSpace(required_bytes))
|
||||
{
|
||||
const u32 align_diff = Common::AlignUp(m_current_offset, alignment) - m_current_offset;
|
||||
m_current_offset += align_diff;
|
||||
m_current_space -= align_diff;
|
||||
return true;
|
||||
}
|
||||
|
||||
// We tried everything we could, and still couldn't get anything. This means that too much space
|
||||
// in the buffer is being used by the command buffer currently being recorded. Therefore, the
|
||||
// only option is to execute it, and wait until it's done.
|
||||
return false;
|
||||
}
|
||||
|
||||
void StreamBuffer::CommitMemory(u32 final_num_bytes)
|
||||
{
|
||||
pxAssert((m_current_offset + final_num_bytes) <= m_size);
|
||||
pxAssert(final_num_bytes <= m_current_space);
|
||||
|
||||
// For non-coherent mappings, flush the memory range
|
||||
vmaFlushAllocation(g_vulkan_context->GetAllocator(), m_allocation, m_current_offset, final_num_bytes);
|
||||
|
||||
m_current_offset += final_num_bytes;
|
||||
m_current_space -= final_num_bytes;
|
||||
UpdateCurrentFencePosition();
|
||||
}
|
||||
|
||||
void StreamBuffer::UpdateCurrentFencePosition()
|
||||
{
|
||||
// Has the offset changed since the last fence?
|
||||
const u64 counter = g_vulkan_context->GetCurrentFenceCounter();
|
||||
if (!m_tracked_fences.empty() && m_tracked_fences.back().first == counter)
|
||||
{
|
||||
// Still haven't executed a command buffer, so just update the offset.
|
||||
m_tracked_fences.back().second = m_current_offset;
|
||||
return;
|
||||
}
|
||||
|
||||
// New buffer, so update the GPU position while we're at it.
|
||||
m_tracked_fences.emplace_back(counter, m_current_offset);
|
||||
}
|
||||
|
||||
void StreamBuffer::UpdateGPUPosition()
|
||||
{
|
||||
auto start = m_tracked_fences.begin();
|
||||
auto end = start;
|
||||
|
||||
const u64 completed_counter = g_vulkan_context->GetCompletedFenceCounter();
|
||||
while (end != m_tracked_fences.end() && completed_counter >= end->first)
|
||||
{
|
||||
m_current_gpu_position = end->second;
|
||||
++end;
|
||||
}
|
||||
|
||||
if (start != end)
|
||||
{
|
||||
m_tracked_fences.erase(start, end);
|
||||
if (m_current_offset == m_current_gpu_position)
|
||||
{
|
||||
// GPU is all caught up now.
|
||||
m_current_offset = 0;
|
||||
m_current_gpu_position = 0;
|
||||
m_current_space = m_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool StreamBuffer::WaitForClearSpace(u32 num_bytes)
|
||||
{
|
||||
u32 new_offset = 0;
|
||||
u32 new_space = 0;
|
||||
u32 new_gpu_position = 0;
|
||||
|
||||
auto iter = m_tracked_fences.begin();
|
||||
for (; iter != m_tracked_fences.end(); ++iter)
|
||||
{
|
||||
// Would this fence bring us in line with the GPU?
|
||||
// This is the "last resort" case, where a command buffer execution has been forced
|
||||
// after no additional data has been written to it, so we can assume that after the
|
||||
// fence has been signaled the entire buffer is now consumed.
|
||||
u32 gpu_position = iter->second;
|
||||
if (m_current_offset == gpu_position)
|
||||
{
|
||||
new_offset = 0;
|
||||
new_space = m_size;
|
||||
new_gpu_position = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Assuming that we wait for this fence, are we allocating in front of the GPU?
|
||||
if (m_current_offset > gpu_position)
|
||||
{
|
||||
// This would suggest the GPU has now followed us and wrapped around, so we have from
|
||||
// m_current_position..m_size free, as well as and 0..gpu_position.
|
||||
const u32 remaining_space_after_offset = m_size - m_current_offset;
|
||||
if (remaining_space_after_offset >= num_bytes)
|
||||
{
|
||||
// Switch to allocating in front of the GPU, using the remainder of the buffer.
|
||||
new_offset = m_current_offset;
|
||||
new_space = m_size - m_current_offset;
|
||||
new_gpu_position = gpu_position;
|
||||
break;
|
||||
}
|
||||
|
||||
// We can wrap around to the start, behind the GPU, if there is enough space.
|
||||
// We use > here because otherwise we'd end up lining up with the GPU, and then the
|
||||
// allocator would assume that the GPU has consumed what we just wrote.
|
||||
if (gpu_position > num_bytes)
|
||||
{
|
||||
new_offset = 0;
|
||||
new_space = gpu_position - 1;
|
||||
new_gpu_position = gpu_position;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're currently allocating behind the GPU. This would give us between the current
|
||||
// offset and the GPU position worth of space to work with. Again, > because we can't
|
||||
// align the GPU position with the buffer offset.
|
||||
u32 available_space_inbetween = gpu_position - m_current_offset;
|
||||
if (available_space_inbetween > num_bytes)
|
||||
{
|
||||
// Leave the offset as-is, but update the GPU position.
|
||||
new_offset = m_current_offset;
|
||||
new_space = available_space_inbetween - 1;
|
||||
new_gpu_position = gpu_position;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Did any fences satisfy this condition?
|
||||
// Has the command buffer been executed yet? If not, the caller should execute it.
|
||||
if (iter == m_tracked_fences.end() || iter->first == g_vulkan_context->GetCurrentFenceCounter())
|
||||
return false;
|
||||
|
||||
// Wait until this fence is signaled. This will fire the callback, updating the GPU position.
|
||||
g_vulkan_context->WaitForFenceCounter(iter->first);
|
||||
m_tracked_fences.erase(
|
||||
m_tracked_fences.begin(), m_current_offset == iter->second ? m_tracked_fences.end() : ++iter);
|
||||
m_current_offset = new_offset;
|
||||
m_current_space = new_space;
|
||||
m_current_gpu_position = new_gpu_position;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
@@ -1,71 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/Vulkan/Loader.h"
|
||||
#include "vk_mem_alloc.h"
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class StreamBuffer
|
||||
{
|
||||
public:
|
||||
StreamBuffer();
|
||||
StreamBuffer(StreamBuffer&& move);
|
||||
StreamBuffer(const StreamBuffer&) = delete;
|
||||
~StreamBuffer();
|
||||
|
||||
StreamBuffer& operator=(StreamBuffer&& move);
|
||||
StreamBuffer& operator=(const StreamBuffer&) = delete;
|
||||
|
||||
__fi bool IsValid() const { return (m_buffer != VK_NULL_HANDLE); }
|
||||
__fi VkBuffer GetBuffer() 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; }
|
||||
__fi u32 GetCurrentSpace() const { return m_current_space; }
|
||||
__fi u32 GetCurrentOffset() const { return m_current_offset; }
|
||||
|
||||
bool Create(VkBufferUsageFlags usage, u32 size);
|
||||
void Destroy(bool defer);
|
||||
|
||||
bool ReserveMemory(u32 num_bytes, u32 alignment);
|
||||
void CommitMemory(u32 final_num_bytes);
|
||||
|
||||
private:
|
||||
bool AllocateBuffer(VkBufferUsageFlags usage, u32 size);
|
||||
void UpdateCurrentFencePosition();
|
||||
void UpdateGPUPosition();
|
||||
|
||||
// Waits for as many fences as needed to allocate num_bytes bytes from the buffer.
|
||||
bool WaitForClearSpace(u32 num_bytes);
|
||||
|
||||
u32 m_size = 0;
|
||||
u32 m_current_offset = 0;
|
||||
u32 m_current_space = 0;
|
||||
u32 m_current_gpu_position = 0;
|
||||
|
||||
VmaAllocation m_allocation = VK_NULL_HANDLE;
|
||||
VkBuffer m_buffer = VK_NULL_HANDLE;
|
||||
u8* m_host_pointer = nullptr;
|
||||
|
||||
// List of fences and the corresponding positions in the buffer
|
||||
std::deque<std::pair<u64, u32>> m_tracked_fences;
|
||||
};
|
||||
} // namespace Vulkan
|
||||
@@ -1,841 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/Vulkan/SwapChain.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/CocoaTools.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/Vulkan/Context.h"
|
||||
#include "common/Vulkan/Util.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
|
||||
#if defined(VK_USE_PLATFORM_XLIB_KHR)
|
||||
#include <X11/Xlib.h>
|
||||
#endif
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
SwapChain::SwapChain(const WindowInfo& wi, VkSurfaceKHR surface, VkPresentModeKHR preferred_present_mode)
|
||||
: m_window_info(wi)
|
||||
, m_surface(surface)
|
||||
, m_preferred_present_mode(preferred_present_mode)
|
||||
{
|
||||
}
|
||||
|
||||
SwapChain::~SwapChain()
|
||||
{
|
||||
DestroySwapChainImages();
|
||||
DestroySwapChain();
|
||||
DestroySurface();
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Unused for now, can be used for nogui
|
||||
static VkSurfaceKHR CreateDisplaySurface(VkInstance instance, VkPhysicalDevice physical_device, WindowInfo* wi)
|
||||
{
|
||||
Console.WriteLn("Trying to create a VK_KHR_display surface of %ux%u", wi->surface_width, wi->surface_height);
|
||||
|
||||
u32 num_displays;
|
||||
VkResult res = vkGetPhysicalDeviceDisplayPropertiesKHR(physical_device, &num_displays, nullptr);
|
||||
if (res != VK_SUCCESS || num_displays == 0)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceDisplayPropertiesKHR() failed:");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<VkDisplayPropertiesKHR> displays(num_displays);
|
||||
res = vkGetPhysicalDeviceDisplayPropertiesKHR(physical_device, &num_displays, displays.data());
|
||||
if (res != VK_SUCCESS || num_displays != displays.size())
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceDisplayPropertiesKHR() failed:");
|
||||
return {};
|
||||
}
|
||||
|
||||
for (u32 display_index = 0; display_index < num_displays; display_index++)
|
||||
{
|
||||
const VkDisplayPropertiesKHR& props = displays[display_index];
|
||||
DevCon.WriteLn("Testing display '%s'", props.displayName);
|
||||
|
||||
u32 num_modes;
|
||||
res = vkGetDisplayModePropertiesKHR(physical_device, props.display, &num_modes, nullptr);
|
||||
if (res != VK_SUCCESS || num_modes == 0)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetDisplayModePropertiesKHR() failed:");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<VkDisplayModePropertiesKHR> modes(num_modes);
|
||||
res = vkGetDisplayModePropertiesKHR(physical_device, props.display, &num_modes, modes.data());
|
||||
if (res != VK_SUCCESS || num_modes != modes.size())
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetDisplayModePropertiesKHR() failed:");
|
||||
continue;
|
||||
}
|
||||
|
||||
const VkDisplayModePropertiesKHR* matched_mode = nullptr;
|
||||
for (const VkDisplayModePropertiesKHR& mode : modes)
|
||||
{
|
||||
const float refresh_rate = static_cast<float>(mode.parameters.refreshRate) / 1000.0f;
|
||||
DevCon.WriteLn(" Mode %ux%u @ %f", mode.parameters.visibleRegion.width,
|
||||
mode.parameters.visibleRegion.height, refresh_rate);
|
||||
|
||||
if (!matched_mode && ((wi->surface_width == 0 && wi->surface_height == 0) ||
|
||||
(mode.parameters.visibleRegion.width == wi->surface_width &&
|
||||
mode.parameters.visibleRegion.height == wi->surface_height &&
|
||||
(wi->surface_refresh_rate == 0.0f ||
|
||||
std::abs(refresh_rate - wi->surface_refresh_rate) < 0.1f))))
|
||||
{
|
||||
matched_mode = &mode;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matched_mode)
|
||||
{
|
||||
DevCon.WriteLn("No modes matched on '%s'", props.displayName);
|
||||
continue;
|
||||
}
|
||||
|
||||
u32 num_planes;
|
||||
res = vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physical_device, &num_planes, nullptr);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR() failed:");
|
||||
continue;
|
||||
}
|
||||
if (num_planes == 0)
|
||||
continue;
|
||||
|
||||
std::vector<VkDisplayPlanePropertiesKHR> planes(num_planes);
|
||||
res = vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physical_device, &num_planes, planes.data());
|
||||
if (res != VK_SUCCESS || num_planes != planes.size())
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR() failed:");
|
||||
continue;
|
||||
}
|
||||
|
||||
u32 plane_index = 0;
|
||||
for (; plane_index < num_planes; plane_index++)
|
||||
{
|
||||
u32 supported_display_count;
|
||||
res = vkGetDisplayPlaneSupportedDisplaysKHR(
|
||||
physical_device, plane_index, &supported_display_count, nullptr);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetDisplayPlaneSupportedDisplaysKHR() failed:");
|
||||
continue;
|
||||
}
|
||||
if (supported_display_count == 0)
|
||||
continue;
|
||||
|
||||
std::vector<VkDisplayKHR> supported_displays(supported_display_count);
|
||||
res = vkGetDisplayPlaneSupportedDisplaysKHR(
|
||||
physical_device, plane_index, &supported_display_count, supported_displays.data());
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetDisplayPlaneSupportedDisplaysKHR() failed:");
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool is_supported = std::find(supported_displays.begin(), supported_displays.end(),
|
||||
props.display) != supported_displays.end();
|
||||
if (!is_supported)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (plane_index == num_planes)
|
||||
{
|
||||
DevCon.WriteLn("No planes matched on '%s'", props.displayName);
|
||||
continue;
|
||||
}
|
||||
|
||||
VkDisplaySurfaceCreateInfoKHR info = {};
|
||||
info.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
|
||||
info.displayMode = matched_mode->displayMode;
|
||||
info.planeIndex = plane_index;
|
||||
info.planeStackIndex = planes[plane_index].currentStackIndex;
|
||||
info.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
info.globalAlpha = 1.0f;
|
||||
info.alphaMode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
|
||||
info.imageExtent = matched_mode->parameters.visibleRegion;
|
||||
|
||||
VkSurfaceKHR surface;
|
||||
res = vkCreateDisplayPlaneSurfaceKHR(instance, &info, nullptr, &surface);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateDisplayPlaneSurfaceKHR() failed: ");
|
||||
continue;
|
||||
}
|
||||
|
||||
wi->surface_refresh_rate = static_cast<float>(matched_mode->parameters.refreshRate) / 1000.0f;
|
||||
return surface;
|
||||
}
|
||||
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
static std::vector<SwapChain::FullscreenModeInfo> GetDisplayModes(
|
||||
VkInstance instance, VkPhysicalDevice physical_device, const WindowInfo& wi)
|
||||
{
|
||||
u32 num_displays;
|
||||
VkResult res = vkGetPhysicalDeviceDisplayPropertiesKHR(physical_device, &num_displays, nullptr);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceDisplayPropertiesKHR() failed:");
|
||||
return {};
|
||||
}
|
||||
if (num_displays == 0)
|
||||
{
|
||||
Console.Error("No displays were returned");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<VkDisplayPropertiesKHR> displays(num_displays);
|
||||
res = vkGetPhysicalDeviceDisplayPropertiesKHR(physical_device, &num_displays, displays.data());
|
||||
if (res != VK_SUCCESS || num_displays != displays.size())
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceDisplayPropertiesKHR() failed:");
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<SwapChain::FullscreenModeInfo> result;
|
||||
for (u32 display_index = 0; display_index < num_displays; display_index++)
|
||||
{
|
||||
const VkDisplayPropertiesKHR& props = displays[display_index];
|
||||
|
||||
u32 num_modes;
|
||||
res = vkGetDisplayModePropertiesKHR(physical_device, props.display, &num_modes, nullptr);
|
||||
if (res != VK_SUCCESS || num_modes == 0)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetDisplayModePropertiesKHR() failed:");
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<VkDisplayModePropertiesKHR> modes(num_modes);
|
||||
res = vkGetDisplayModePropertiesKHR(physical_device, props.display, &num_modes, modes.data());
|
||||
if (res != VK_SUCCESS || num_modes != modes.size())
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetDisplayModePropertiesKHR() failed:");
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const VkDisplayModePropertiesKHR& mode : modes)
|
||||
{
|
||||
const float refresh_rate = static_cast<float>(mode.parameters.refreshRate) / 1000.0f;
|
||||
if (std::find_if(
|
||||
result.begin(), result.end(), [&mode, refresh_rate](const SwapChain::FullscreenModeInfo& mi) {
|
||||
return (mi.width == mode.parameters.visibleRegion.width &&
|
||||
mi.height == mode.parameters.visibleRegion.height &&
|
||||
mode.parameters.refreshRate == refresh_rate);
|
||||
}) != result.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push_back(SwapChain::FullscreenModeInfo{static_cast<u32>(mode.parameters.visibleRegion.width),
|
||||
static_cast<u32>(mode.parameters.visibleRegion.height), refresh_rate});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, VkPhysicalDevice physical_device, WindowInfo* wi)
|
||||
{
|
||||
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||
if (wi->type == WindowInfo::Type::Win32)
|
||||
{
|
||||
VkWin32SurfaceCreateInfoKHR surface_create_info = {
|
||||
VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, // VkStructureType sType
|
||||
nullptr, // const void* pNext
|
||||
0, // VkWin32SurfaceCreateFlagsKHR flags
|
||||
nullptr, // HINSTANCE hinstance
|
||||
reinterpret_cast<HWND>(wi->window_handle) // HWND hwnd
|
||||
};
|
||||
|
||||
VkSurfaceKHR surface;
|
||||
VkResult res = vkCreateWin32SurfaceKHR(instance, &surface_create_info, nullptr, &surface);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateWin32SurfaceKHR failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(VK_USE_PLATFORM_XLIB_KHR)
|
||||
if (wi->type == WindowInfo::Type::X11)
|
||||
{
|
||||
VkXlibSurfaceCreateInfoKHR surface_create_info = {
|
||||
VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, // VkStructureType sType
|
||||
nullptr, // const void* pNext
|
||||
0, // VkXlibSurfaceCreateFlagsKHR flags
|
||||
static_cast<Display*>(wi->display_connection), // Display* dpy
|
||||
reinterpret_cast<Window>(wi->window_handle) // Window window
|
||||
};
|
||||
|
||||
VkSurfaceKHR surface;
|
||||
VkResult res = vkCreateXlibSurfaceKHR(instance, &surface_create_info, nullptr, &surface);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateXlibSurfaceKHR failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
if (wi->type == WindowInfo::Type::Wayland)
|
||||
{
|
||||
VkWaylandSurfaceCreateInfoKHR surface_create_info = {VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR,
|
||||
nullptr, 0, static_cast<struct wl_display*>(wi->display_connection),
|
||||
static_cast<struct wl_surface*>(wi->window_handle)};
|
||||
|
||||
VkSurfaceKHR surface;
|
||||
VkResult res = vkCreateWaylandSurfaceKHR(instance, &surface_create_info, nullptr, &surface);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateWaylandSurfaceEXT failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
|
||||
if (wi->type == WindowInfo::Type::Android)
|
||||
{
|
||||
VkAndroidSurfaceCreateInfoKHR surface_create_info = {
|
||||
VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, // VkStructureType sType
|
||||
nullptr, // const void* pNext
|
||||
0, // VkAndroidSurfaceCreateFlagsKHR flags
|
||||
reinterpret_cast<ANativeWindow*>(wi->window_handle) // ANativeWindow* window
|
||||
};
|
||||
|
||||
VkSurfaceKHR surface;
|
||||
VkResult res = vkCreateAndroidSurfaceKHR(instance, &surface_create_info, nullptr, &surface);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateAndroidSurfaceKHR failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(VK_USE_PLATFORM_METAL_EXT)
|
||||
if (wi->type == WindowInfo::Type::MacOS)
|
||||
{
|
||||
if (!wi->surface_handle && !CocoaTools::CreateMetalLayer(wi))
|
||||
return VK_NULL_HANDLE;
|
||||
|
||||
VkMetalSurfaceCreateInfoEXT surface_create_info = {VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT, nullptr,
|
||||
0, static_cast<const CAMetalLayer*>(wi->surface_handle)};
|
||||
|
||||
VkSurfaceKHR surface;
|
||||
VkResult res = vkCreateMetalSurfaceEXT(instance, &surface_create_info, nullptr, &surface);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateMetalSurfaceEXT failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
#elif defined(VK_USE_PLATFORM_MACOS_MVK)
|
||||
if (wi->type == WindowInfo::Type::MacOS)
|
||||
{
|
||||
VkMacOSSurfaceCreateInfoMVK surface_create_info = {
|
||||
VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK, nullptr, 0, wi->window_handle};
|
||||
|
||||
VkSurfaceKHR surface;
|
||||
VkResult res = vkCreateMacOSSurfaceMVK(instance, &surface_create_info, nullptr, &surface);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateMacOSSurfaceMVK failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
if (wi->type == WindowInfo::Type::Display)
|
||||
return CreateDisplaySurface(instance, physical_device, wi);
|
||||
#endif
|
||||
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void SwapChain::DestroyVulkanSurface(VkInstance instance, WindowInfo* wi, VkSurfaceKHR surface)
|
||||
{
|
||||
vkDestroySurfaceKHR(g_vulkan_context->GetVulkanInstance(), surface, nullptr);
|
||||
|
||||
#if defined(__APPLE__)
|
||||
if (wi->type == WindowInfo::Type::MacOS && wi->surface_handle)
|
||||
CocoaTools::DestroyMetalLayer(wi);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<SwapChain::FullscreenModeInfo> SwapChain::GetSurfaceFullscreenModes(
|
||||
VkInstance instance, VkPhysicalDevice physical_device, const WindowInfo& wi)
|
||||
{
|
||||
#if 0
|
||||
if (wi.type == WindowInfo::Type::Display)
|
||||
return GetDisplayModes(instance, physical_device, wi);
|
||||
#endif
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<SwapChain> SwapChain::Create(const WindowInfo& wi, VkSurfaceKHR surface,
|
||||
VkPresentModeKHR preferred_present_mode)
|
||||
{
|
||||
std::unique_ptr<SwapChain> swap_chain = std::make_unique<SwapChain>(wi, surface, preferred_present_mode);
|
||||
if (!swap_chain->CreateSwapChain() || !swap_chain->SetupSwapChainImages())
|
||||
return nullptr;
|
||||
|
||||
return swap_chain;
|
||||
}
|
||||
|
||||
bool SwapChain::SelectSurfaceFormat()
|
||||
{
|
||||
u32 format_count;
|
||||
VkResult res = vkGetPhysicalDeviceSurfaceFormatsKHR(
|
||||
g_vulkan_context->GetPhysicalDevice(), m_surface, &format_count, nullptr);
|
||||
if (res != VK_SUCCESS || format_count == 0)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceFormatsKHR failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<VkSurfaceFormatKHR> surface_formats(format_count);
|
||||
res = vkGetPhysicalDeviceSurfaceFormatsKHR(
|
||||
g_vulkan_context->GetPhysicalDevice(), m_surface, &format_count, surface_formats.data());
|
||||
pxAssert(res == VK_SUCCESS);
|
||||
|
||||
// If there is a single undefined surface format, the device doesn't care, so we'll just use RGBA
|
||||
if (surface_formats[0].format == VK_FORMAT_UNDEFINED)
|
||||
{
|
||||
m_surface_format.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
m_surface_format.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try to find a suitable format.
|
||||
for (const VkSurfaceFormatKHR& surface_format : surface_formats)
|
||||
{
|
||||
// Some drivers seem to return a SRGB format here (Intel Mesa).
|
||||
// This results in gamma correction when presenting to the screen, which we don't want.
|
||||
// Use a linear format instead, if this is the case.
|
||||
m_surface_format.format = Util::GetLinearFormat(surface_format.format);
|
||||
m_surface_format.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||
return true;
|
||||
}
|
||||
|
||||
pxFailRel("Failed to find a suitable format for swap chain buffers.");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SwapChain::SelectPresentMode()
|
||||
{
|
||||
VkResult res;
|
||||
u32 mode_count;
|
||||
res = vkGetPhysicalDeviceSurfacePresentModesKHR(
|
||||
g_vulkan_context->GetPhysicalDevice(), m_surface, &mode_count, nullptr);
|
||||
if (res != VK_SUCCESS || mode_count == 0)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceFormatsKHR failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<VkPresentModeKHR> present_modes(mode_count);
|
||||
res = vkGetPhysicalDeviceSurfacePresentModesKHR(
|
||||
g_vulkan_context->GetPhysicalDevice(), m_surface, &mode_count, present_modes.data());
|
||||
pxAssert(res == VK_SUCCESS);
|
||||
|
||||
// Checks if a particular mode is supported, if it is, returns that mode.
|
||||
auto CheckForMode = [&present_modes](VkPresentModeKHR check_mode) {
|
||||
auto it = std::find_if(present_modes.begin(), present_modes.end(),
|
||||
[check_mode](VkPresentModeKHR mode) { return check_mode == mode; });
|
||||
return it != present_modes.end();
|
||||
};
|
||||
|
||||
// Use preferred mode if available.
|
||||
if (CheckForMode(m_preferred_present_mode))
|
||||
{
|
||||
m_present_mode = m_preferred_present_mode;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Prefer mailbox over fifo for adaptive vsync/no-vsync.
|
||||
if ((m_preferred_present_mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR ||
|
||||
m_preferred_present_mode == VK_PRESENT_MODE_IMMEDIATE_KHR) &&
|
||||
CheckForMode(VK_PRESENT_MODE_MAILBOX_KHR))
|
||||
{
|
||||
m_present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fallback to FIFO if we're using any kind of vsync.
|
||||
if (m_preferred_present_mode == VK_PRESENT_MODE_FIFO_KHR || m_preferred_present_mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR)
|
||||
{
|
||||
// This should never fail, FIFO is mandated.
|
||||
if (CheckForMode(VK_PRESENT_MODE_FIFO_KHR))
|
||||
{
|
||||
m_present_mode = VK_PRESENT_MODE_FIFO_KHR;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to whatever is available.
|
||||
m_present_mode = present_modes[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SwapChain::CreateSwapChain()
|
||||
{
|
||||
// Look up surface properties to determine image count and dimensions
|
||||
VkSurfaceCapabilitiesKHR surface_capabilities;
|
||||
VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
|
||||
g_vulkan_context->GetPhysicalDevice(), m_surface, &surface_capabilities);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Select swap chain format and present mode
|
||||
if (!SelectSurfaceFormat() || !SelectPresentMode())
|
||||
return false;
|
||||
|
||||
DevCon.WriteLn("(SwapChain) Preferred present mode: %s, selected: %s",
|
||||
Util::PresentModeToString(m_preferred_present_mode), Util::PresentModeToString(m_present_mode));
|
||||
|
||||
// Select number of images in swap chain, we prefer one buffer in the background to work on
|
||||
u32 image_count = std::max(surface_capabilities.minImageCount + 1u, 2u);
|
||||
|
||||
// maxImageCount can be zero, in which case there isn't an upper limit on the number of buffers.
|
||||
if (surface_capabilities.maxImageCount > 0)
|
||||
image_count = std::min(image_count, surface_capabilities.maxImageCount);
|
||||
|
||||
// Determine the dimensions of the swap chain. Values of -1 indicate the size we specify here
|
||||
// determines window size?
|
||||
VkExtent2D size = surface_capabilities.currentExtent;
|
||||
if (size.width == UINT32_MAX)
|
||||
{
|
||||
size.width = m_window_info.surface_width;
|
||||
size.height = m_window_info.surface_height;
|
||||
}
|
||||
size.width = std::clamp(
|
||||
size.width, surface_capabilities.minImageExtent.width, surface_capabilities.maxImageExtent.width);
|
||||
size.height = std::clamp(
|
||||
size.height, surface_capabilities.minImageExtent.height, surface_capabilities.maxImageExtent.height);
|
||||
|
||||
// Prefer identity transform if possible
|
||||
VkSurfaceTransformFlagBitsKHR transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
if (!(surface_capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR))
|
||||
transform = surface_capabilities.currentTransform;
|
||||
|
||||
VkCompositeAlphaFlagBitsKHR alpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
if (!(surface_capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR))
|
||||
{
|
||||
// If we only support pre-multiplied/post-multiplied... :/
|
||||
if (surface_capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
|
||||
alpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
|
||||
}
|
||||
|
||||
// Select swap chain flags, we only need a colour attachment
|
||||
VkImageUsageFlags image_usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
if ((surface_capabilities.supportedUsageFlags & image_usage) != image_usage)
|
||||
{
|
||||
Console.Error("Vulkan: Swap chain does not support usage as color attachment");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store the old/current swap chain when recreating for resize
|
||||
VkSwapchainKHR old_swap_chain = m_swap_chain;
|
||||
m_swap_chain = VK_NULL_HANDLE;
|
||||
|
||||
// Now we can actually create the swap chain
|
||||
VkSwapchainCreateInfoKHR swap_chain_info = {VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, nullptr, 0, m_surface,
|
||||
image_count, m_surface_format.format, m_surface_format.colorSpace, size, 1u, image_usage,
|
||||
VK_SHARING_MODE_EXCLUSIVE, 0, nullptr, transform, alpha, m_present_mode,
|
||||
VK_TRUE, old_swap_chain};
|
||||
std::array<uint32_t, 2> indices = {{
|
||||
g_vulkan_context->GetGraphicsQueueFamilyIndex(),
|
||||
g_vulkan_context->GetPresentQueueFamilyIndex(),
|
||||
}};
|
||||
if (g_vulkan_context->GetGraphicsQueueFamilyIndex() != g_vulkan_context->GetPresentQueueFamilyIndex())
|
||||
{
|
||||
swap_chain_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
||||
swap_chain_info.queueFamilyIndexCount = 2;
|
||||
swap_chain_info.pQueueFamilyIndices = indices.data();
|
||||
}
|
||||
|
||||
if (m_swap_chain == VK_NULL_HANDLE)
|
||||
{
|
||||
res = vkCreateSwapchainKHR(g_vulkan_context->GetDevice(), &swap_chain_info, nullptr, &m_swap_chain);
|
||||
}
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateSwapchainKHR failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now destroy the old swap chain, since it's been recreated.
|
||||
// We can do this immediately since all work should have been completed before calling resize.
|
||||
if (old_swap_chain != VK_NULL_HANDLE)
|
||||
vkDestroySwapchainKHR(g_vulkan_context->GetDevice(), old_swap_chain, nullptr);
|
||||
|
||||
m_window_info.surface_width = std::max(1u, size.width);
|
||||
m_window_info.surface_height = std::max(1u, size.height);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SwapChain::SetupSwapChainImages()
|
||||
{
|
||||
pxAssert(m_images.empty());
|
||||
|
||||
u32 image_count;
|
||||
VkResult res = vkGetSwapchainImagesKHR(g_vulkan_context->GetDevice(), m_swap_chain, &image_count, nullptr);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetSwapchainImagesKHR failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<VkImage> images(image_count);
|
||||
res = vkGetSwapchainImagesKHR(g_vulkan_context->GetDevice(), m_swap_chain, &image_count, images.data());
|
||||
pxAssert(res == VK_SUCCESS);
|
||||
|
||||
m_load_render_pass =
|
||||
g_vulkan_context->GetRenderPass(m_surface_format.format, VK_FORMAT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_LOAD);
|
||||
m_clear_render_pass =
|
||||
g_vulkan_context->GetRenderPass(m_surface_format.format, VK_FORMAT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_CLEAR);
|
||||
if (m_load_render_pass == VK_NULL_HANDLE || m_clear_render_pass == VK_NULL_HANDLE)
|
||||
{
|
||||
pxFailRel("Failed to get swap chain render passes.");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_images.reserve(image_count);
|
||||
m_current_image = 0;
|
||||
for (u32 i = 0; i < image_count; i++)
|
||||
{
|
||||
SwapChainImage image;
|
||||
image.image = images[i];
|
||||
|
||||
// Create texture object, which creates a view of the backbuffer
|
||||
if (!image.texture.Adopt(image.image, VK_IMAGE_VIEW_TYPE_2D, m_window_info.surface_width,
|
||||
m_window_info.surface_height, 1, 1, m_surface_format.format, VK_SAMPLE_COUNT_1_BIT))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
image.framebuffer = image.texture.CreateFramebuffer(m_load_render_pass);
|
||||
if (image.framebuffer == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
m_images.emplace_back(std::move(image));
|
||||
}
|
||||
|
||||
m_semaphores.reserve(image_count);
|
||||
m_current_semaphore = (image_count - 1);
|
||||
for (u32 i = 0; i < image_count; i++)
|
||||
{
|
||||
ImageSemaphores sema;
|
||||
|
||||
const VkSemaphoreCreateInfo semaphore_info = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0};
|
||||
res = vkCreateSemaphore(g_vulkan_context->GetDevice(), &semaphore_info, nullptr, &sema.available_semaphore);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateSemaphore failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
res = vkCreateSemaphore(g_vulkan_context->GetDevice(), &semaphore_info, nullptr, &sema.rendering_finished_semaphore);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateSemaphore failed: ");
|
||||
vkDestroySemaphore(g_vulkan_context->GetDevice(), sema.available_semaphore, nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_semaphores.push_back(sema);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SwapChain::DestroySwapChainImages()
|
||||
{
|
||||
for (auto& it : m_images)
|
||||
{
|
||||
// Images themselves are cleaned up by the swap chain object
|
||||
vkDestroyFramebuffer(g_vulkan_context->GetDevice(), it.framebuffer, nullptr);
|
||||
}
|
||||
m_images.clear();
|
||||
for (auto& it : m_semaphores)
|
||||
{
|
||||
vkDestroySemaphore(g_vulkan_context->GetDevice(), it.rendering_finished_semaphore, nullptr);
|
||||
vkDestroySemaphore(g_vulkan_context->GetDevice(), it.available_semaphore, nullptr);
|
||||
}
|
||||
m_semaphores.clear();
|
||||
|
||||
m_image_acquire_result.reset();
|
||||
}
|
||||
|
||||
void SwapChain::DestroySwapChain()
|
||||
{
|
||||
if (m_swap_chain == VK_NULL_HANDLE)
|
||||
return;
|
||||
|
||||
vkDestroySwapchainKHR(g_vulkan_context->GetDevice(), m_swap_chain, nullptr);
|
||||
m_swap_chain = VK_NULL_HANDLE;
|
||||
m_window_info.surface_width = 0;
|
||||
m_window_info.surface_height = 0;
|
||||
}
|
||||
|
||||
VkResult SwapChain::AcquireNextImage()
|
||||
{
|
||||
if (m_image_acquire_result.has_value())
|
||||
return m_image_acquire_result.value();
|
||||
|
||||
if (!m_swap_chain)
|
||||
return VK_ERROR_SURFACE_LOST_KHR;
|
||||
|
||||
// Use a different semaphore for each image.
|
||||
m_current_semaphore = (m_current_semaphore + 1) % static_cast<u32>(m_semaphores.size());
|
||||
|
||||
const VkResult res = vkAcquireNextImageKHR(g_vulkan_context->GetDevice(), m_swap_chain, UINT64_MAX,
|
||||
m_semaphores[m_current_semaphore].available_semaphore, VK_NULL_HANDLE, &m_current_image);
|
||||
m_image_acquire_result = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
void SwapChain::ReleaseCurrentImage()
|
||||
{
|
||||
m_image_acquire_result.reset();
|
||||
}
|
||||
|
||||
bool SwapChain::ResizeSwapChain(u32 new_width, u32 new_height, float new_scale)
|
||||
{
|
||||
DestroySwapChainImages();
|
||||
|
||||
if (new_width != 0 && new_height != 0)
|
||||
{
|
||||
m_window_info.surface_width = new_width;
|
||||
m_window_info.surface_height = new_height;
|
||||
}
|
||||
|
||||
m_window_info.surface_scale = new_scale;
|
||||
|
||||
if (!CreateSwapChain() || !SetupSwapChainImages())
|
||||
{
|
||||
DestroySwapChainImages();
|
||||
DestroySwapChain();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SwapChain::RecreateSwapChain()
|
||||
{
|
||||
DestroySwapChainImages();
|
||||
|
||||
if (!CreateSwapChain() || !SetupSwapChainImages())
|
||||
{
|
||||
DestroySwapChainImages();
|
||||
DestroySwapChain();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SwapChain::SetVSync(VkPresentModeKHR preferred_mode)
|
||||
{
|
||||
if (m_preferred_present_mode == preferred_mode)
|
||||
return true;
|
||||
|
||||
// Recreate the swap chain with the new present mode.
|
||||
m_preferred_present_mode = preferred_mode;
|
||||
return RecreateSwapChain();
|
||||
}
|
||||
|
||||
bool SwapChain::RecreateSurface(const WindowInfo& new_wi)
|
||||
{
|
||||
// Destroy the old swap chain, images, and surface.
|
||||
DestroySwapChainImages();
|
||||
DestroySwapChain();
|
||||
DestroySurface();
|
||||
|
||||
// Re-create the surface with the new native handle
|
||||
m_window_info = new_wi;
|
||||
m_surface = CreateVulkanSurface(
|
||||
g_vulkan_context->GetVulkanInstance(), g_vulkan_context->GetPhysicalDevice(), &m_window_info);
|
||||
if (m_surface == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
// The validation layers get angry at us if we don't call this before creating the swapchain.
|
||||
VkBool32 present_supported = VK_TRUE;
|
||||
VkResult res = vkGetPhysicalDeviceSurfaceSupportKHR(g_vulkan_context->GetPhysicalDevice(),
|
||||
g_vulkan_context->GetPresentQueueFamilyIndex(), m_surface, &present_supported);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceSupportKHR failed: ");
|
||||
return false;
|
||||
}
|
||||
if (!present_supported)
|
||||
{
|
||||
pxFailRel("Recreated surface does not support presenting.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finally re-create the swap chain
|
||||
if (!CreateSwapChain())
|
||||
return false;
|
||||
if (!SetupSwapChainImages())
|
||||
{
|
||||
DestroySwapChain();
|
||||
DestroySurface();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SwapChain::DestroySurface()
|
||||
{
|
||||
if (m_surface == VK_NULL_HANDLE)
|
||||
return;
|
||||
|
||||
DestroyVulkanSurface(g_vulkan_context->GetVulkanInstance(), &m_window_info, m_surface);
|
||||
m_surface = VK_NULL_HANDLE;
|
||||
}
|
||||
} // namespace Vulkan
|
||||
@@ -1,135 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/WindowInfo.h"
|
||||
#include "common/Vulkan/Texture.h"
|
||||
#include "common/Vulkan/Loader.h"
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class SwapChain
|
||||
{
|
||||
public:
|
||||
SwapChain(const WindowInfo& wi, VkSurfaceKHR surface, VkPresentModeKHR preferred_present_mode);
|
||||
~SwapChain();
|
||||
|
||||
// Creates a vulkan-renderable surface for the specified window handle.
|
||||
static VkSurfaceKHR CreateVulkanSurface(VkInstance instance, VkPhysicalDevice physical_device, WindowInfo* wi);
|
||||
|
||||
// Destroys a previously-created surface.
|
||||
static void DestroyVulkanSurface(VkInstance instance, WindowInfo* wi, VkSurfaceKHR surface);
|
||||
|
||||
// Enumerates fullscreen modes for window info.
|
||||
struct FullscreenModeInfo
|
||||
{
|
||||
u32 width;
|
||||
u32 height;
|
||||
float refresh_rate;
|
||||
};
|
||||
static std::vector<FullscreenModeInfo> GetSurfaceFullscreenModes(
|
||||
VkInstance instance, VkPhysicalDevice physical_device, const WindowInfo& wi);
|
||||
|
||||
// Create a new swap chain from a pre-existing surface.
|
||||
static std::unique_ptr<SwapChain> Create(const WindowInfo& wi, VkSurfaceKHR surface,
|
||||
VkPresentModeKHR preferred_present_mode);
|
||||
|
||||
__fi VkSurfaceKHR GetSurface() const { return m_surface; }
|
||||
__fi VkSurfaceFormatKHR GetSurfaceFormat() const { return m_surface_format; }
|
||||
__fi VkFormat GetTextureFormat() const { return m_surface_format.format; }
|
||||
__fi VkPresentModeKHR GetPreferredPresentMode() const { return m_preferred_present_mode; }
|
||||
__fi VkSwapchainKHR GetSwapChain() const { return m_swap_chain; }
|
||||
__fi const VkSwapchainKHR* GetSwapChainPtr() const { return &m_swap_chain; }
|
||||
__fi const WindowInfo& GetWindowInfo() const { return m_window_info; }
|
||||
__fi u32 GetWidth() const { return m_window_info.surface_width; }
|
||||
__fi u32 GetHeight() const { return m_window_info.surface_height; }
|
||||
__fi float GetScale() const { return m_window_info.surface_scale; }
|
||||
__fi u32 GetCurrentImageIndex() const { return m_current_image; }
|
||||
__fi const u32* GetCurrentImageIndexPtr() const { return &m_current_image; }
|
||||
__fi u32 GetImageCount() const { return static_cast<u32>(m_images.size()); }
|
||||
__fi VkImage GetCurrentImage() const { return m_images[m_current_image].image; }
|
||||
__fi const Texture& GetCurrentTexture() const { return m_images[m_current_image].texture; }
|
||||
__fi Texture& GetCurrentTexture() { return m_images[m_current_image].texture; }
|
||||
__fi VkFramebuffer GetCurrentFramebuffer() const { return m_images[m_current_image].framebuffer; }
|
||||
__fi VkRenderPass GetLoadRenderPass() const { return m_load_render_pass; }
|
||||
__fi VkRenderPass GetClearRenderPass() const { return m_clear_render_pass; }
|
||||
__fi VkSemaphore GetImageAvailableSemaphore() const { return m_semaphores[m_current_semaphore].available_semaphore; }
|
||||
__fi const VkSemaphore* GetImageAvailableSemaphorePtr() const { return &m_semaphores[m_current_semaphore].available_semaphore; }
|
||||
__fi VkSemaphore GetRenderingFinishedSemaphore() const { return m_semaphores[m_current_semaphore].rendering_finished_semaphore; }
|
||||
__fi const VkSemaphore* GetRenderingFinishedSemaphorePtr() const { return &m_semaphores[m_current_semaphore].rendering_finished_semaphore; }
|
||||
VkResult AcquireNextImage();
|
||||
void ReleaseCurrentImage();
|
||||
|
||||
bool RecreateSurface(const WindowInfo& new_wi);
|
||||
bool ResizeSwapChain(u32 new_width = 0, u32 new_height = 0, float new_scale = 1.0f);
|
||||
bool RecreateSwapChain();
|
||||
|
||||
// Change vsync enabled state. This may fail as it causes a swapchain recreation.
|
||||
bool SetVSync(VkPresentModeKHR preferred_mode);
|
||||
|
||||
// Returns true if the current present mode is synchronizing (adaptive or hard).
|
||||
bool IsPresentModeSynchronizing() const
|
||||
{
|
||||
return (m_present_mode == VK_PRESENT_MODE_FIFO_KHR || m_present_mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR);
|
||||
}
|
||||
|
||||
private:
|
||||
bool SelectSurfaceFormat();
|
||||
bool SelectPresentMode();
|
||||
|
||||
bool CreateSwapChain();
|
||||
void DestroySwapChain();
|
||||
|
||||
bool SetupSwapChainImages();
|
||||
void DestroySwapChainImages();
|
||||
|
||||
void DestroySurface();
|
||||
|
||||
struct SwapChainImage
|
||||
{
|
||||
VkImage image;
|
||||
Texture texture;
|
||||
VkFramebuffer framebuffer;
|
||||
};
|
||||
|
||||
struct ImageSemaphores
|
||||
{
|
||||
VkSemaphore available_semaphore;
|
||||
VkSemaphore rendering_finished_semaphore;
|
||||
};
|
||||
|
||||
WindowInfo m_window_info;
|
||||
|
||||
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
|
||||
VkSurfaceFormatKHR m_surface_format = {};
|
||||
VkPresentModeKHR m_preferred_present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||
VkPresentModeKHR m_present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
|
||||
|
||||
VkRenderPass m_load_render_pass = VK_NULL_HANDLE;
|
||||
VkRenderPass m_clear_render_pass = VK_NULL_HANDLE;
|
||||
|
||||
VkSwapchainKHR m_swap_chain = VK_NULL_HANDLE;
|
||||
std::vector<SwapChainImage> m_images;
|
||||
std::vector<ImageSemaphores> m_semaphores;
|
||||
u32 m_current_image = 0;
|
||||
u32 m_current_semaphore = 0;
|
||||
std::optional<VkResult> m_image_acquire_result;
|
||||
};
|
||||
} // namespace Vulkan
|
||||
@@ -1,395 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/Vulkan/Texture.h"
|
||||
#include "common/Vulkan/Context.h"
|
||||
#include "common/Vulkan/Util.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
#include <algorithm>
|
||||
|
||||
static constexpr VkComponentMapping s_identity_swizzle{VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
Texture::Texture() = default;
|
||||
|
||||
Texture::Texture(Texture&& move)
|
||||
: m_width(move.m_width)
|
||||
, m_height(move.m_height)
|
||||
, m_levels(move.m_levels)
|
||||
, m_layers(move.m_layers)
|
||||
, m_format(move.m_format)
|
||||
, m_samples(move.m_samples)
|
||||
, m_view_type(move.m_view_type)
|
||||
, m_layout(move.m_layout)
|
||||
, m_image(move.m_image)
|
||||
, m_allocation(move.m_allocation)
|
||||
, m_view(move.m_view)
|
||||
{
|
||||
move.m_width = 0;
|
||||
move.m_height = 0;
|
||||
move.m_levels = 0;
|
||||
move.m_layers = 0;
|
||||
move.m_format = VK_FORMAT_UNDEFINED;
|
||||
move.m_samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
move.m_view_type = VK_IMAGE_VIEW_TYPE_2D;
|
||||
move.m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
move.m_image = VK_NULL_HANDLE;
|
||||
move.m_allocation = VK_NULL_HANDLE;
|
||||
move.m_view = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
if (IsValid())
|
||||
Destroy(true);
|
||||
}
|
||||
|
||||
Vulkan::Texture& Texture::operator=(Texture&& move)
|
||||
{
|
||||
if (IsValid())
|
||||
Destroy(true);
|
||||
|
||||
std::swap(m_width, move.m_width);
|
||||
std::swap(m_height, move.m_height);
|
||||
std::swap(m_levels, move.m_levels);
|
||||
std::swap(m_layers, move.m_layers);
|
||||
std::swap(m_format, move.m_format);
|
||||
std::swap(m_samples, move.m_samples);
|
||||
std::swap(m_view_type, move.m_view_type);
|
||||
std::swap(m_layout, move.m_layout);
|
||||
std::swap(m_image, move.m_image);
|
||||
std::swap(m_allocation, move.m_allocation);
|
||||
std::swap(m_view, move.m_view);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Texture::Create(u32 width, u32 height, u32 levels, u32 layers, VkFormat format, VkSampleCountFlagBits samples,
|
||||
VkImageViewType view_type, VkImageTiling tiling, VkImageUsageFlags usage,
|
||||
const VkComponentMapping* swizzle /* = nullptr*/)
|
||||
{
|
||||
const VkImageCreateInfo image_info = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, nullptr, 0, VK_IMAGE_TYPE_2D, format,
|
||||
{width, height, 1}, levels, layers, samples, tiling, usage, VK_SHARING_MODE_EXCLUSIVE, 0, nullptr,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED};
|
||||
|
||||
VmaAllocationCreateInfo aci = {};
|
||||
aci.usage = VMA_MEMORY_USAGE_GPU_ONLY;
|
||||
aci.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT;
|
||||
aci.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
VmaAllocation allocation = VK_NULL_HANDLE;
|
||||
VkResult res =
|
||||
vmaCreateImage(g_vulkan_context->GetAllocator(), &image_info, &aci, &image, &allocation, nullptr);
|
||||
if (res == VK_ERROR_OUT_OF_DEVICE_MEMORY)
|
||||
{
|
||||
DevCon.WriteLn("Failed to allocate device memory for %ux%u texture", width, height);
|
||||
return false;
|
||||
}
|
||||
else if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vmaCreateImage failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
const VkImageViewCreateInfo view_info = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, nullptr, 0, image, view_type,
|
||||
format, swizzle ? *swizzle : s_identity_swizzle,
|
||||
{Util::IsDepthFormat(format) ? static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT) :
|
||||
static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_COLOR_BIT),
|
||||
0, levels, 0, layers}};
|
||||
|
||||
VkImageView view = VK_NULL_HANDLE;
|
||||
res = vkCreateImageView(g_vulkan_context->GetDevice(), &view_info, nullptr, &view);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateImageView failed: ");
|
||||
vmaDestroyImage(g_vulkan_context->GetAllocator(), image, allocation);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsValid())
|
||||
Destroy(true);
|
||||
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_levels = levels;
|
||||
m_layers = layers;
|
||||
m_format = format;
|
||||
m_samples = samples;
|
||||
m_view_type = view_type;
|
||||
m_image = image;
|
||||
m_allocation = allocation;
|
||||
m_view = view;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Texture::Adopt(VkImage existing_image, VkImageViewType view_type, u32 width, u32 height, u32 levels,
|
||||
u32 layers, VkFormat format, VkSampleCountFlagBits samples, const VkComponentMapping* swizzle /* = nullptr*/)
|
||||
{
|
||||
// Only need to create the image view, this is mainly for swap chains.
|
||||
const VkImageViewCreateInfo view_info = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, nullptr, 0, existing_image,
|
||||
view_type, format, swizzle ? *swizzle : s_identity_swizzle,
|
||||
{Util::IsDepthFormat(format) ? static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT) :
|
||||
static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_COLOR_BIT),
|
||||
0, levels, 0, layers}};
|
||||
|
||||
// Memory is managed by the owner of the image.
|
||||
VkImageView view = VK_NULL_HANDLE;
|
||||
VkResult res = vkCreateImageView(g_vulkan_context->GetDevice(), &view_info, nullptr, &view);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateImageView failed: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsValid())
|
||||
Destroy(true);
|
||||
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_levels = levels;
|
||||
m_layers = layers;
|
||||
m_format = format;
|
||||
m_samples = samples;
|
||||
m_view_type = view_type;
|
||||
m_image = existing_image;
|
||||
m_view = view;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Texture::Destroy(bool defer /* = true */)
|
||||
{
|
||||
if (m_view != VK_NULL_HANDLE)
|
||||
{
|
||||
if (defer)
|
||||
g_vulkan_context->DeferImageViewDestruction(m_view);
|
||||
else
|
||||
vkDestroyImageView(g_vulkan_context->GetDevice(), m_view, nullptr);
|
||||
m_view = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
// If we don't have device memory allocated, the image is not owned by us (e.g. swapchain)
|
||||
if (m_allocation != VK_NULL_HANDLE)
|
||||
{
|
||||
pxAssert(m_image != VK_NULL_HANDLE);
|
||||
if (defer)
|
||||
g_vulkan_context->DeferImageDestruction(m_image, m_allocation);
|
||||
else
|
||||
vmaDestroyImage(g_vulkan_context->GetAllocator(), m_image, m_allocation);
|
||||
m_image = VK_NULL_HANDLE;
|
||||
m_allocation = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
m_width = 0;
|
||||
m_height = 0;
|
||||
m_levels = 0;
|
||||
m_layers = 0;
|
||||
m_format = VK_FORMAT_UNDEFINED;
|
||||
m_samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
m_view_type = VK_IMAGE_VIEW_TYPE_2D;
|
||||
m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
}
|
||||
|
||||
void Texture::OverrideImageLayout(VkImageLayout new_layout) { m_layout = new_layout; }
|
||||
|
||||
void Texture::TransitionToLayout(VkCommandBuffer command_buffer, VkImageLayout new_layout)
|
||||
{
|
||||
if (m_layout == new_layout)
|
||||
return;
|
||||
|
||||
TransitionSubresourcesToLayout(command_buffer, 0, m_levels, 0, m_layers, m_layout, new_layout);
|
||||
|
||||
m_layout = new_layout;
|
||||
}
|
||||
|
||||
void Texture::TransitionSubresourcesToLayout(VkCommandBuffer command_buffer, u32 start_level, u32 num_levels,
|
||||
u32 start_layer, u32 num_layers, VkImageLayout old_layout, VkImageLayout new_layout)
|
||||
{
|
||||
VkImageAspectFlags aspect;
|
||||
if (Util::IsDepthStencilFormat(m_format))
|
||||
aspect = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
else if (Util::IsDepthFormat(m_format))
|
||||
aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
else
|
||||
aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
|
||||
VkImageMemoryBarrier barrier = {
|
||||
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType
|
||||
nullptr, // const void* pNext
|
||||
0, // VkAccessFlags srcAccessMask
|
||||
0, // VkAccessFlags dstAccessMask
|
||||
old_layout, // VkImageLayout oldLayout
|
||||
new_layout, // VkImageLayout newLayout
|
||||
VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex
|
||||
VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex
|
||||
m_image, // VkImage image
|
||||
{aspect, start_level, num_levels, start_layer, num_layers} // VkImageSubresourceRange subresourceRange
|
||||
};
|
||||
|
||||
// srcStageMask -> Stages that must complete before the barrier
|
||||
// dstStageMask -> Stages that must wait for after the barrier before beginning
|
||||
VkPipelineStageFlags srcStageMask, dstStageMask;
|
||||
switch (old_layout)
|
||||
{
|
||||
case VK_IMAGE_LAYOUT_UNDEFINED:
|
||||
// Layout undefined therefore contents undefined, and we don't care what happens to it.
|
||||
barrier.srcAccessMask = 0;
|
||||
srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_PREINITIALIZED:
|
||||
// Image has been pre-initialized by the host, so ensure all writes have completed.
|
||||
barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
|
||||
srcStageMask = VK_PIPELINE_STAGE_HOST_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
|
||||
// Image was being used as a color attachment, so ensure all writes have completed.
|
||||
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
|
||||
// Image was being used as a depthstencil attachment, so ensure all writes have completed.
|
||||
barrier.srcAccessMask =
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
||||
// Image was being used as a shader resource, make sure all reads have finished.
|
||||
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
|
||||
// Image was being used as a copy source, ensure all reads have finished.
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
|
||||
// Image was being used as a copy destination, ensure all writes have finished.
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_GENERAL:
|
||||
// General is used for feedback loops.
|
||||
barrier.srcAccessMask = (aspect == VK_IMAGE_ASPECT_COLOR_BIT) ?
|
||||
(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT) :
|
||||
(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT);
|
||||
srcStageMask = (aspect == VK_IMAGE_ASPECT_COLOR_BIT) ?
|
||||
(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT) :
|
||||
(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
|
||||
break;
|
||||
|
||||
default:
|
||||
srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (new_layout)
|
||||
{
|
||||
case VK_IMAGE_LAYOUT_UNDEFINED:
|
||||
barrier.dstAccessMask = 0;
|
||||
dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
|
||||
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
|
||||
barrier.dstAccessMask =
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
||||
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
|
||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
|
||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
|
||||
srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
|
||||
dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
break;
|
||||
|
||||
case VK_IMAGE_LAYOUT_GENERAL:
|
||||
// General is used for feedback loops.
|
||||
barrier.dstAccessMask = (aspect == VK_IMAGE_ASPECT_COLOR_BIT) ?
|
||||
(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT) :
|
||||
(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT);
|
||||
dstStageMask = (aspect == VK_IMAGE_ASPECT_COLOR_BIT) ?
|
||||
(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT) :
|
||||
(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
|
||||
break;
|
||||
|
||||
default:
|
||||
dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
break;
|
||||
}
|
||||
vkCmdPipelineBarrier(command_buffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
}
|
||||
|
||||
VkFramebuffer Texture::CreateFramebuffer(VkRenderPass render_pass)
|
||||
{
|
||||
const VkFramebufferCreateInfo ci = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, 0u, render_pass, 1,
|
||||
&m_view, m_width, m_height, m_layers};
|
||||
VkFramebuffer fb = VK_NULL_HANDLE;
|
||||
VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &ci, nullptr, &fb);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkCreateFramebuffer() failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
return fb;
|
||||
}
|
||||
|
||||
void Texture::UpdateFromBuffer(VkCommandBuffer cmdbuf, u32 level, u32 layer, u32 x, u32 y, u32 width, u32 height,
|
||||
u32 buffer_height, u32 row_length, VkBuffer buffer, u32 buffer_offset)
|
||||
{
|
||||
const VkImageLayout old_layout = m_layout;
|
||||
if (old_layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
|
||||
TransitionSubresourcesToLayout(
|
||||
cmdbuf, level, 1, layer, 1, old_layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
|
||||
const VkBufferImageCopy bic = {static_cast<VkDeviceSize>(buffer_offset), row_length, buffer_height,
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, level, layer, 1u}, {static_cast<int32_t>(x), static_cast<int32_t>(y), 0},
|
||||
{width, height, 1u}};
|
||||
|
||||
vkCmdCopyBufferToImage(cmdbuf, buffer, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bic);
|
||||
|
||||
if (old_layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
|
||||
TransitionSubresourcesToLayout(
|
||||
cmdbuf, level, 1, layer, 1, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, old_layout);
|
||||
}
|
||||
} // namespace Vulkan
|
||||
@@ -1,92 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include "common/Vulkan/Loader.h"
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
class Texture
|
||||
{
|
||||
public:
|
||||
Texture();
|
||||
Texture(Texture&& move);
|
||||
Texture(const Texture&) = delete;
|
||||
~Texture();
|
||||
|
||||
Texture& operator=(Texture&& move);
|
||||
Texture& operator=(const Texture&) = delete;
|
||||
|
||||
__fi bool IsValid() const { return (m_image != VK_NULL_HANDLE); }
|
||||
|
||||
/// An image is considered owned/managed if we control the memory.
|
||||
__fi bool IsOwned() const { return (m_allocation != VK_NULL_HANDLE); }
|
||||
|
||||
__fi u32 GetWidth() const { return m_width; }
|
||||
__fi u32 GetHeight() const { return m_height; }
|
||||
__fi u32 GetLevels() const { return m_levels; }
|
||||
__fi u32 GetLayers() const { return m_layers; }
|
||||
__fi u32 GetMipWidth(u32 level) const { return std::max<u32>(m_width >> level, 1u); }
|
||||
__fi u32 GetMipHeight(u32 level) const { return std::max<u32>(m_height >> level, 1u); }
|
||||
__fi VkFormat GetFormat() const { return m_format; }
|
||||
__fi VkSampleCountFlagBits GetSamples() const { return m_samples; }
|
||||
__fi VkImageLayout GetLayout() const { return m_layout; }
|
||||
__fi VkImageViewType GetViewType() const { return m_view_type; }
|
||||
__fi VkImage GetImage() const { return m_image; }
|
||||
__fi VmaAllocation GetAllocation() const { return m_allocation; }
|
||||
__fi VkImageView GetView() const { return m_view; }
|
||||
|
||||
bool Create(u32 width, u32 height, u32 levels, u32 layers, VkFormat format, VkSampleCountFlagBits samples,
|
||||
VkImageViewType view_type, VkImageTiling tiling, VkImageUsageFlags usage,
|
||||
const VkComponentMapping* swizzle = nullptr);
|
||||
|
||||
bool Adopt(VkImage existing_image, VkImageViewType view_type, u32 width, u32 height, u32 levels, u32 layers,
|
||||
VkFormat format, VkSampleCountFlagBits samples, const VkComponentMapping* swizzle = nullptr);
|
||||
|
||||
void Destroy(bool defer = true);
|
||||
|
||||
// Used when the render pass is changing the image layout, or to force it to
|
||||
// VK_IMAGE_LAYOUT_UNDEFINED, if the existing contents of the image is
|
||||
// irrelevant and will not be loaded.
|
||||
void OverrideImageLayout(VkImageLayout new_layout);
|
||||
|
||||
void TransitionToLayout(VkCommandBuffer command_buffer, VkImageLayout new_layout);
|
||||
void TransitionSubresourcesToLayout(VkCommandBuffer command_buffer, u32 start_level, u32 num_levels,
|
||||
u32 start_layer, u32 num_layers, VkImageLayout old_layout, VkImageLayout new_layout);
|
||||
|
||||
VkFramebuffer CreateFramebuffer(VkRenderPass render_pass);
|
||||
|
||||
void UpdateFromBuffer(VkCommandBuffer cmdbuf, u32 level, u32 layer, u32 x, u32 y, u32 width, u32 height,
|
||||
u32 buffer_height, u32 row_length, VkBuffer buffer, u32 buffer_offset);
|
||||
|
||||
private:
|
||||
u32 m_width = 0;
|
||||
u32 m_height = 0;
|
||||
u32 m_levels = 0;
|
||||
u32 m_layers = 0;
|
||||
VkFormat m_format = VK_FORMAT_UNDEFINED;
|
||||
VkSampleCountFlagBits m_samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
VkImageViewType m_view_type = VK_IMAGE_VIEW_TYPE_2D;
|
||||
VkImageLayout m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
VkImage m_image = VK_NULL_HANDLE;
|
||||
VmaAllocation m_allocation = VK_NULL_HANDLE;
|
||||
VkImageView m_view = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
@@ -1,393 +0,0 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2021 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common/Vulkan/Util.h"
|
||||
#include "common/Vulkan/Context.h"
|
||||
#include "common/Vulkan/ShaderCompiler.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
namespace Util
|
||||
{
|
||||
bool IsDepthFormat(VkFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case VK_FORMAT_D16_UNORM:
|
||||
case VK_FORMAT_D16_UNORM_S8_UINT:
|
||||
case VK_FORMAT_D24_UNORM_S8_UINT:
|
||||
case VK_FORMAT_D32_SFLOAT:
|
||||
case VK_FORMAT_D32_SFLOAT_S8_UINT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsDepthStencilFormat(VkFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case VK_FORMAT_D16_UNORM_S8_UINT:
|
||||
case VK_FORMAT_D24_UNORM_S8_UINT:
|
||||
case VK_FORMAT_D32_SFLOAT_S8_UINT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
VkFormat GetLinearFormat(VkFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case VK_FORMAT_R8_SRGB:
|
||||
return VK_FORMAT_R8_UNORM;
|
||||
case VK_FORMAT_R8G8_SRGB:
|
||||
return VK_FORMAT_R8G8_UNORM;
|
||||
case VK_FORMAT_R8G8B8_SRGB:
|
||||
return VK_FORMAT_R8G8B8_UNORM;
|
||||
case VK_FORMAT_R8G8B8A8_SRGB:
|
||||
return VK_FORMAT_R8G8B8A8_UNORM;
|
||||
case VK_FORMAT_B8G8R8_SRGB:
|
||||
return VK_FORMAT_B8G8R8_UNORM;
|
||||
case VK_FORMAT_B8G8R8A8_SRGB:
|
||||
return VK_FORMAT_B8G8R8A8_UNORM;
|
||||
default:
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
u32 GetTexelSize(VkFormat format)
|
||||
{
|
||||
// Only contains pixel formats we use.
|
||||
switch (format)
|
||||
{
|
||||
case VK_FORMAT_R8_UNORM:
|
||||
return 1;
|
||||
|
||||
case VK_FORMAT_R5G5B5A1_UNORM_PACK16:
|
||||
case VK_FORMAT_A1R5G5B5_UNORM_PACK16:
|
||||
case VK_FORMAT_R5G6B5_UNORM_PACK16:
|
||||
case VK_FORMAT_B5G6R5_UNORM_PACK16:
|
||||
case VK_FORMAT_R16_UINT:
|
||||
return 2;
|
||||
|
||||
case VK_FORMAT_R8G8B8A8_UNORM:
|
||||
case VK_FORMAT_B8G8R8A8_UNORM:
|
||||
case VK_FORMAT_R32_UINT:
|
||||
case VK_FORMAT_R32_SFLOAT:
|
||||
case VK_FORMAT_D32_SFLOAT:
|
||||
return 4;
|
||||
|
||||
case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
|
||||
return 8;
|
||||
|
||||
case VK_FORMAT_BC2_UNORM_BLOCK:
|
||||
case VK_FORMAT_BC3_UNORM_BLOCK:
|
||||
case VK_FORMAT_BC7_UNORM_BLOCK:
|
||||
return 16;
|
||||
|
||||
default:
|
||||
pxFailRel("Unhandled pixel format");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
VkBlendFactor GetAlphaBlendFactor(VkBlendFactor factor)
|
||||
{
|
||||
switch (factor)
|
||||
{
|
||||
case VK_BLEND_FACTOR_SRC_COLOR:
|
||||
return VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
case VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR:
|
||||
return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
case VK_BLEND_FACTOR_DST_COLOR:
|
||||
return VK_BLEND_FACTOR_DST_ALPHA;
|
||||
case VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR:
|
||||
return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
|
||||
default:
|
||||
return factor;
|
||||
}
|
||||
}
|
||||
|
||||
void SetViewport(VkCommandBuffer command_buffer, int x, int y, int width, int height,
|
||||
float min_depth /*= 0.0f*/, float max_depth /*= 1.0f*/)
|
||||
{
|
||||
const VkViewport vp{static_cast<float>(x), static_cast<float>(y), static_cast<float>(width),
|
||||
static_cast<float>(height), min_depth, max_depth};
|
||||
vkCmdSetViewport(command_buffer, 0, 1, &vp);
|
||||
}
|
||||
|
||||
void SetScissor(VkCommandBuffer command_buffer, int x, int y, int width, int height)
|
||||
{
|
||||
const VkRect2D scissor{{x, y}, {static_cast<u32>(width), static_cast<u32>(height)}};
|
||||
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
|
||||
}
|
||||
|
||||
void SetViewportAndScissor(VkCommandBuffer command_buffer, int x, int y, int width, int height,
|
||||
float min_depth /* = 0.0f */, float max_depth /* = 1.0f */)
|
||||
{
|
||||
}
|
||||
|
||||
void SafeDestroyFramebuffer(VkFramebuffer& fb)
|
||||
{
|
||||
if (fb != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyFramebuffer(g_vulkan_context->GetDevice(), fb, nullptr);
|
||||
fb = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void SafeDestroyShaderModule(VkShaderModule& sm)
|
||||
{
|
||||
if (sm != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), sm, nullptr);
|
||||
sm = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void SafeDestroyPipeline(VkPipeline& p)
|
||||
{
|
||||
if (p != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyPipeline(g_vulkan_context->GetDevice(), p, nullptr);
|
||||
p = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void SafeDestroyPipelineLayout(VkPipelineLayout& pl)
|
||||
{
|
||||
if (pl != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), pl, nullptr);
|
||||
pl = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void SafeDestroyDescriptorSetLayout(VkDescriptorSetLayout& dsl)
|
||||
{
|
||||
if (dsl != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyDescriptorSetLayout(g_vulkan_context->GetDevice(), dsl, nullptr);
|
||||
dsl = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void SafeDestroyBufferView(VkBufferView& bv)
|
||||
{
|
||||
if (bv != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyBufferView(g_vulkan_context->GetDevice(), bv, nullptr);
|
||||
bv = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void SafeDestroyImageView(VkImageView& iv)
|
||||
{
|
||||
if (iv != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyImageView(g_vulkan_context->GetDevice(), iv, nullptr);
|
||||
iv = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void SafeDestroySampler(VkSampler& samp)
|
||||
{
|
||||
if (samp != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroySampler(g_vulkan_context->GetDevice(), samp, nullptr);
|
||||
samp = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void SafeDestroySemaphore(VkSemaphore& sem)
|
||||
{
|
||||
if (sem != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroySemaphore(g_vulkan_context->GetDevice(), sem, nullptr);
|
||||
sem = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void SafeFreeGlobalDescriptorSet(VkDescriptorSet& ds)
|
||||
{
|
||||
if (ds != VK_NULL_HANDLE)
|
||||
{
|
||||
g_vulkan_context->FreeGlobalDescriptorSet(ds);
|
||||
ds = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void BufferMemoryBarrier(VkCommandBuffer command_buffer, VkBuffer buffer, VkAccessFlags src_access_mask,
|
||||
VkAccessFlags dst_access_mask, VkDeviceSize offset, VkDeviceSize size, VkPipelineStageFlags src_stage_mask,
|
||||
VkPipelineStageFlags dst_stage_mask)
|
||||
{
|
||||
VkBufferMemoryBarrier buffer_info = {
|
||||
VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // VkStructureType sType
|
||||
nullptr, // const void* pNext
|
||||
src_access_mask, // VkAccessFlags srcAccessMask
|
||||
dst_access_mask, // VkAccessFlags dstAccessMask
|
||||
VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex
|
||||
VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex
|
||||
buffer, // VkBuffer buffer
|
||||
offset, // VkDeviceSize offset
|
||||
size // VkDeviceSize size
|
||||
};
|
||||
|
||||
vkCmdPipelineBarrier(
|
||||
command_buffer, src_stage_mask, dst_stage_mask, 0, 0, nullptr, 1, &buffer_info, 0, nullptr);
|
||||
}
|
||||
|
||||
void AddPointerToChain(void* head, const void* ptr)
|
||||
{
|
||||
VkBaseInStructure* last_st = static_cast<VkBaseInStructure*>(head);
|
||||
while (last_st->pNext)
|
||||
{
|
||||
if (last_st->pNext == ptr)
|
||||
return;
|
||||
|
||||
last_st = const_cast<VkBaseInStructure*>(last_st->pNext);
|
||||
}
|
||||
|
||||
last_st->pNext = static_cast<const VkBaseInStructure*>(ptr);
|
||||
}
|
||||
|
||||
const char* VkResultToString(VkResult res)
|
||||
{
|
||||
switch (res)
|
||||
{
|
||||
case VK_SUCCESS:
|
||||
return "VK_SUCCESS";
|
||||
|
||||
case VK_NOT_READY:
|
||||
return "VK_NOT_READY";
|
||||
|
||||
case VK_TIMEOUT:
|
||||
return "VK_TIMEOUT";
|
||||
|
||||
case VK_EVENT_SET:
|
||||
return "VK_EVENT_SET";
|
||||
|
||||
case VK_EVENT_RESET:
|
||||
return "VK_EVENT_RESET";
|
||||
|
||||
case VK_INCOMPLETE:
|
||||
return "VK_INCOMPLETE";
|
||||
|
||||
case VK_ERROR_OUT_OF_HOST_MEMORY:
|
||||
return "VK_ERROR_OUT_OF_HOST_MEMORY";
|
||||
|
||||
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
|
||||
return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
|
||||
|
||||
case VK_ERROR_INITIALIZATION_FAILED:
|
||||
return "VK_ERROR_INITIALIZATION_FAILED";
|
||||
|
||||
case VK_ERROR_DEVICE_LOST:
|
||||
return "VK_ERROR_DEVICE_LOST";
|
||||
|
||||
case VK_ERROR_MEMORY_MAP_FAILED:
|
||||
return "VK_ERROR_MEMORY_MAP_FAILED";
|
||||
|
||||
case VK_ERROR_LAYER_NOT_PRESENT:
|
||||
return "VK_ERROR_LAYER_NOT_PRESENT";
|
||||
|
||||
case VK_ERROR_EXTENSION_NOT_PRESENT:
|
||||
return "VK_ERROR_EXTENSION_NOT_PRESENT";
|
||||
|
||||
case VK_ERROR_FEATURE_NOT_PRESENT:
|
||||
return "VK_ERROR_FEATURE_NOT_PRESENT";
|
||||
|
||||
case VK_ERROR_INCOMPATIBLE_DRIVER:
|
||||
return "VK_ERROR_INCOMPATIBLE_DRIVER";
|
||||
|
||||
case VK_ERROR_TOO_MANY_OBJECTS:
|
||||
return "VK_ERROR_TOO_MANY_OBJECTS";
|
||||
|
||||
case VK_ERROR_FORMAT_NOT_SUPPORTED:
|
||||
return "VK_ERROR_FORMAT_NOT_SUPPORTED";
|
||||
|
||||
case VK_ERROR_SURFACE_LOST_KHR:
|
||||
return "VK_ERROR_SURFACE_LOST_KHR";
|
||||
|
||||
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
|
||||
return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
|
||||
|
||||
case VK_SUBOPTIMAL_KHR:
|
||||
return "VK_SUBOPTIMAL_KHR";
|
||||
|
||||
case VK_ERROR_OUT_OF_DATE_KHR:
|
||||
return "VK_ERROR_OUT_OF_DATE_KHR";
|
||||
|
||||
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
|
||||
return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
|
||||
|
||||
case VK_ERROR_VALIDATION_FAILED_EXT:
|
||||
return "VK_ERROR_VALIDATION_FAILED_EXT";
|
||||
|
||||
case VK_ERROR_INVALID_SHADER_NV:
|
||||
return "VK_ERROR_INVALID_SHADER_NV";
|
||||
|
||||
default:
|
||||
return "UNKNOWN_VK_RESULT";
|
||||
}
|
||||
}
|
||||
|
||||
const char* PresentModeToString(VkPresentModeKHR mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case VK_PRESENT_MODE_IMMEDIATE_KHR:
|
||||
return "VK_PRESENT_MODE_IMMEDIATE_KHR";
|
||||
|
||||
case VK_PRESENT_MODE_MAILBOX_KHR:
|
||||
return "VK_PRESENT_MODE_MAILBOX_KHR";
|
||||
|
||||
case VK_PRESENT_MODE_FIFO_KHR:
|
||||
return "VK_PRESENT_MODE_FIFO_KHR";
|
||||
|
||||
case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
|
||||
return "VK_PRESENT_MODE_FIFO_RELAXED_KHR";
|
||||
|
||||
case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR:
|
||||
return "VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR";
|
||||
|
||||
case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR:
|
||||
return "VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR";
|
||||
|
||||
default:
|
||||
return "UNKNOWN_VK_PRESENT_MODE";
|
||||
}
|
||||
}
|
||||
|
||||
void LogVulkanResult(const char* func_name, VkResult res, const char* msg, ...)
|
||||
{
|
||||
std::va_list ap;
|
||||
va_start(ap, msg);
|
||||
std::string real_msg = StringUtil::StdStringFromFormatV(msg, ap);
|
||||
va_end(ap);
|
||||
|
||||
Console.Error(
|
||||
"(%s) %s (%d: %s)", func_name, real_msg.c_str(), static_cast<int>(res), VkResultToString(res));
|
||||
}
|
||||
} // namespace Util
|
||||
} // namespace Vulkan
|
||||
+4
-70
@@ -22,7 +22,7 @@
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="vsprops\CommonLibrary.props" />
|
||||
<Import Project="vsprops\3rdpartyDeps.props" />
|
||||
<Import Project="vsprops\common.props" />
|
||||
<Import Condition="$(Configuration.Contains(Debug))" Project="vsprops\CodeGen_Debug.props" />
|
||||
<Import Condition="$(Configuration.Contains(Devel))" Project="vsprops\CodeGen_Devel.props" />
|
||||
<Import Condition="$(Configuration.Contains(Release))" Project="vsprops\CodeGen_Release.props" />
|
||||
@@ -34,21 +34,13 @@
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\d3d12memalloc\include;$(SolutionDir)3rdparty\glad\include;$(SolutionDir)3rdparty\glslang\glslang;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\rapidyaml\rapidyaml\ext\c4core\src\c4\ext\fast_float\include</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\libpng</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\jpgd</AdditionalIncludeDirectories>
|
||||
<ExceptionHandling>Async</ExceptionHandling>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\fmt\fmt\include</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<ForcedIncludeFiles>PrecompiledHeader.h</ForcedIncludeFiles>
|
||||
<PrecompiledHeaderFile>PrecompiledHeader.h</PrecompiledHeaderFile>
|
||||
<PreprocessorDefinitions>WIN32_LEAN_AND_MEAN;NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="!$(Configuration.Contains(AVX2))">_M_SSE=0x401;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="$(Configuration.Contains(AVX2))">_M_SSE=0x501;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<EnableEnhancedInstructionSet Condition="!$(Configuration.Contains(AVX2)) Or $(Configuration.Contains(Clang))">NotSet</EnableEnhancedInstructionSet>
|
||||
<EnableEnhancedInstructionSet Condition="$(Configuration.Contains(AVX2)) And !$(Configuration.Contains(Clang))">AdvancedVectorExtensions2</EnableEnhancedInstructionSet>
|
||||
<AdditionalOptions Condition="$(Configuration.Contains(Clang)) And !$(Configuration.Contains(AVX2))"> -march=nehalem %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalOptions Condition="$(Configuration.Contains(Clang)) And $(Configuration.Contains(AVX2))"> -march=haswell %(AdditionalOptions)</AdditionalOptions>
|
||||
<ObjectFileName>$(IntDir)%(RelativeDir)</ObjectFileName>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
@@ -56,27 +48,13 @@
|
||||
<ClCompile Include="AlignedMalloc.cpp" />
|
||||
<ClCompile Include="Console.cpp" />
|
||||
<ClCompile Include="CrashHandler.cpp" />
|
||||
<ClCompile Include="D3D11\ShaderCache.cpp" />
|
||||
<ClCompile Include="D3D11\ShaderCompiler.cpp" />
|
||||
<ClCompile Include="D3D12\Builders.cpp" />
|
||||
<ClCompile Include="D3D12\Context.cpp" />
|
||||
<ClCompile Include="D3D12\DescriptorHeapManager.cpp" />
|
||||
<ClCompile Include="D3D12\ShaderCache.cpp" />
|
||||
<ClCompile Include="D3D12\StreamBuffer.cpp" />
|
||||
<ClCompile Include="D3D12\Texture.cpp" />
|
||||
<ClCompile Include="D3D12\Util.cpp" />
|
||||
<ClCompile Include="DynamicLibrary.cpp" />
|
||||
<ClCompile Include="Exceptions.cpp" />
|
||||
<ClCompile Include="FastJmp.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="General.cpp" />
|
||||
<ClCompile Include="GL\Context.cpp" />
|
||||
<ClCompile Include="GL\ContextWGL.cpp" />
|
||||
<ClCompile Include="GL\Program.cpp" />
|
||||
<ClCompile Include="GL\ShaderCache.cpp" />
|
||||
<ClCompile Include="GL\StreamBuffer.cpp" />
|
||||
<ClCompile Include="FileSystem.cpp" />
|
||||
<ClCompile Include="General.cpp" />
|
||||
<ClCompile Include="Image.cpp" />
|
||||
<ClCompile Include="HTTPDownloader.cpp" />
|
||||
<ClCompile Include="HTTPDownloaderCurl.cpp">
|
||||
@@ -91,16 +69,6 @@
|
||||
<ClCompile Include="StringUtil.cpp" />
|
||||
<ClCompile Include="SettingsWrapper.cpp" />
|
||||
<ClCompile Include="Timer.cpp" />
|
||||
<ClCompile Include="Vulkan\vk_mem_alloc.cpp" />
|
||||
<ClCompile Include="Vulkan\Builders.cpp" />
|
||||
<ClCompile Include="Vulkan\Context.cpp" />
|
||||
<ClCompile Include="Vulkan\Loader.cpp" />
|
||||
<ClCompile Include="Vulkan\ShaderCache.cpp" />
|
||||
<ClCompile Include="Vulkan\ShaderCompiler.cpp" />
|
||||
<ClCompile Include="Vulkan\StreamBuffer.cpp" />
|
||||
<ClCompile Include="Vulkan\SwapChain.cpp" />
|
||||
<ClCompile Include="Vulkan\Texture.cpp" />
|
||||
<ClCompile Include="Vulkan\Util.cpp" />
|
||||
<ClCompile Include="WAVWriter.cpp" />
|
||||
<ClCompile Include="WindowInfo.cpp" />
|
||||
<ClCompile Include="Perf.cpp" />
|
||||
@@ -131,31 +99,16 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<MASM Include="FastJmp.asm" />
|
||||
<None Include="Vulkan\EntryPoints.inl" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Align.h" />
|
||||
<ClInclude Include="AlignedMalloc.h" />
|
||||
<ClInclude Include="BitCast.h" />
|
||||
<ClInclude Include="CrashHandler.h" />
|
||||
<ClInclude Include="D3D11\ShaderCache.h" />
|
||||
<ClInclude Include="D3D11\ShaderCompiler.h" />
|
||||
<ClInclude Include="D3D12\Builders.h" />
|
||||
<ClInclude Include="D3D12\Context.h" />
|
||||
<ClInclude Include="D3D12\DescriptorHeapManager.h" />
|
||||
<ClInclude Include="D3D12\ShaderCache.h" />
|
||||
<ClInclude Include="D3D12\StreamBuffer.h" />
|
||||
<ClInclude Include="D3D12\Texture.h" />
|
||||
<ClInclude Include="D3D12\Util.h" />
|
||||
<ClInclude Include="DynamicLibrary.h" />
|
||||
<ClInclude Include="Easing.h" />
|
||||
<ClInclude Include="boost_spsc_queue.hpp" />
|
||||
<ClInclude Include="FastJmp.h" />
|
||||
<ClInclude Include="GL\Context.h" />
|
||||
<ClInclude Include="GL\ContextWGL.h" />
|
||||
<ClInclude Include="GL\Program.h" />
|
||||
<ClInclude Include="GL\ShaderCache.h" />
|
||||
<ClInclude Include="GL\StreamBuffer.h" />
|
||||
<ClInclude Include="FileSystem.h" />
|
||||
<ClInclude Include="HashCombine.h" />
|
||||
<ClInclude Include="HeapArray.h" />
|
||||
@@ -188,16 +141,6 @@
|
||||
<ClInclude Include="SafeArray.h" />
|
||||
<ClInclude Include="ThreadPool.h" />
|
||||
<ClInclude Include="Timer.h" />
|
||||
<ClInclude Include="Vulkan\Builders.h" />
|
||||
<ClInclude Include="Vulkan\Context.h" />
|
||||
<ClInclude Include="Vulkan\EntryPoints.h" />
|
||||
<ClInclude Include="Vulkan\Loader.h" />
|
||||
<ClInclude Include="Vulkan\ShaderCache.h" />
|
||||
<ClInclude Include="Vulkan\ShaderCompiler.h" />
|
||||
<ClInclude Include="Vulkan\StreamBuffer.h" />
|
||||
<ClInclude Include="Vulkan\SwapChain.h" />
|
||||
<ClInclude Include="Vulkan\Texture.h" />
|
||||
<ClInclude Include="Vulkan\Util.h" />
|
||||
<ClInclude Include="WAVWriter.h" />
|
||||
<ClInclude Include="WindowInfo.h" />
|
||||
<ClInclude Include="Threading.h" />
|
||||
@@ -230,15 +173,6 @@
|
||||
<ClInclude Include="ZipHelpers.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\3rdparty\d3d12memalloc\d3d12memalloc.vcxproj">
|
||||
<Project>{d45cec7a-3171-40dd-975d-e1544cf16139}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\3rdparty\glad\glad.vcxproj">
|
||||
<Project>{c0293b32-5acf-40f0-aa6c-e6da6f3bf33a}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\3rdparty\glslang\glslang.vcxproj">
|
||||
<Project>{ef6834a9-11f3-4331-bc34-21b325abb180}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\3rdparty\jpgd\jpgd.vcxproj">
|
||||
<Project>{ed2f21fd-0a36-4a8f-9b90-e7d92a2acb63}</Project>
|
||||
</ProjectReference>
|
||||
@@ -247,4 +181,4 @@
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -94,18 +94,6 @@
|
||||
<ClCompile Include="WindowInfo.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GL\ContextWGL.cpp">
|
||||
<Filter>Source Files\GL</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GL\Context.cpp">
|
||||
<Filter>Source Files\GL</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GL\StreamBuffer.cpp">
|
||||
<Filter>Source Files\GL</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FileSystem.cpp">
|
||||
<Filter>Source Files\GL</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StringUtil.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -115,72 +103,9 @@
|
||||
<ClCompile Include="ProgressCallback.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Vulkan\Builders.cpp">
|
||||
<Filter>Source Files\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Vulkan\Context.cpp">
|
||||
<Filter>Source Files\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Vulkan\Loader.cpp">
|
||||
<Filter>Source Files\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Vulkan\ShaderCache.cpp">
|
||||
<Filter>Source Files\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Vulkan\ShaderCompiler.cpp">
|
||||
<Filter>Source Files\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Vulkan\StreamBuffer.cpp">
|
||||
<Filter>Source Files\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Vulkan\SwapChain.cpp">
|
||||
<Filter>Source Files\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Vulkan\Texture.cpp">
|
||||
<Filter>Source Files\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Vulkan\Util.cpp">
|
||||
<Filter>Source Files\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MD5Digest.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GL\Program.cpp">
|
||||
<Filter>Source Files\GL</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="GL\ShaderCache.cpp">
|
||||
<Filter>Source Files\GL</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Vulkan\vk_mem_alloc.cpp">
|
||||
<Filter>Source Files\Vulkan</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="D3D11\ShaderCache.cpp">
|
||||
<Filter>Source Files\D3D11</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="D3D11\ShaderCompiler.cpp">
|
||||
<Filter>Source Files\D3D11</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="D3D12\Context.cpp">
|
||||
<Filter>Source Files\D3D12</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="D3D12\DescriptorHeapManager.cpp">
|
||||
<Filter>Source Files\D3D12</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="D3D12\ShaderCache.cpp">
|
||||
<Filter>Source Files\D3D12</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="D3D12\StreamBuffer.cpp">
|
||||
<Filter>Source Files\D3D12</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="D3D12\Texture.cpp">
|
||||
<Filter>Source Files\D3D12</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="D3D12\Util.cpp">
|
||||
<Filter>Source Files\D3D12</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="D3D12\Builders.cpp">
|
||||
<Filter>Source Files\D3D12</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StackWalker.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -208,7 +133,12 @@
|
||||
<ClCompile Include="WAVWriter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="General.cpp" />
|
||||
<ClCompile Include="FileSystem.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="General.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AlignedMalloc.h">
|
||||
@@ -352,18 +282,6 @@
|
||||
<ClInclude Include="BitCast.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GL\Context.h">
|
||||
<Filter>Header Files\GL</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GL\ContextWGL.h">
|
||||
<Filter>Header Files\GL</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GL\StreamBuffer.h">
|
||||
<Filter>Header Files\GL</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FileSystem.h">
|
||||
<Filter>Header Files\GL</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StringUtil.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -382,75 +300,12 @@
|
||||
<ClInclude Include="HashCombine.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Vulkan\ShaderCompiler.h">
|
||||
<Filter>Header Files\Vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Vulkan\StreamBuffer.h">
|
||||
<Filter>Header Files\Vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Vulkan\SwapChain.h">
|
||||
<Filter>Header Files\Vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Vulkan\Texture.h">
|
||||
<Filter>Header Files\Vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Vulkan\Util.h">
|
||||
<Filter>Header Files\Vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Vulkan\Builders.h">
|
||||
<Filter>Header Files\Vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Vulkan\Context.h">
|
||||
<Filter>Header Files\Vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Vulkan\EntryPoints.h">
|
||||
<Filter>Header Files\Vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Vulkan\Loader.h">
|
||||
<Filter>Header Files\Vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Vulkan\ShaderCache.h">
|
||||
<Filter>Header Files\Vulkan</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MD5Digest.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GL\Program.h">
|
||||
<Filter>Header Files\GL</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GL\ShaderCache.h">
|
||||
<Filter>Header Files\GL</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="D3D11\ShaderCache.h">
|
||||
<Filter>Header Files\D3D11</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="D3D11\ShaderCompiler.h">
|
||||
<Filter>Header Files\D3D11</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ZipHelpers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="D3D12\Context.h">
|
||||
<Filter>Header Files\D3D12</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="D3D12\DescriptorHeapManager.h">
|
||||
<Filter>Header Files\D3D12</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="D3D12\ShaderCache.h">
|
||||
<Filter>Header Files\D3D12</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="D3D12\StreamBuffer.h">
|
||||
<Filter>Header Files\D3D12</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="D3D12\Texture.h">
|
||||
<Filter>Header Files\D3D12</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="D3D12\Util.h">
|
||||
<Filter>Header Files\D3D12</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="D3D12\Builders.h">
|
||||
<Filter>Header Files\D3D12</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CrashHandler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -493,6 +348,9 @@
|
||||
<ClInclude Include="WAVWriter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FileSystem.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
@@ -501,35 +359,6 @@
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{eef579af-e6a8-4d3b-a88e-c0e4cad9e5d8}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\GL">
|
||||
<UniqueIdentifier>{92d1bb2d-d172-4356-b9f4-a8fcc8e7fcce}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\GL">
|
||||
<UniqueIdentifier>{5e76b340-cb0e-4946-83ec-7d72e397cac8}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Vulkan">
|
||||
<UniqueIdentifier>{94154238-8b02-44f8-a7b8-3612e7bfa33c}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\Vulkan">
|
||||
<UniqueIdentifier>{46f36c68-0e0e-4acd-a621-3365e3167c4f}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\D3D11">
|
||||
<UniqueIdentifier>{f428aac0-c9c5-4b66-b218-9829dce45d13}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\D3D11">
|
||||
<UniqueIdentifier>{764ddf71-77a6-41b8-bc65-1eef94b6997b}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\D3D12">
|
||||
<UniqueIdentifier>{96f78eb9-089b-4166-a23e-78c4e7d90b64}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\D3D12">
|
||||
<UniqueIdentifier>{cf37623d-bb05-4c54-8c72-1c20b5331c69}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Vulkan\EntryPoints.inl">
|
||||
<Filter>Source Files\Vulkan</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<MASM Include="FastJmp.asm">
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion>
|
||||
<_PropertySheetDisplayName>Pcsx3rdpartyDependencies</_PropertySheetDisplayName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\;$(SolutionDir)3rdparty\soundtouch\soundtouch\;$(SolutionDir)3rdparty\rapidyaml\rapidyaml\src;$(SolutionDir)3rdparty\rapidyaml\rapidyaml\ext\c4core\src;$(SolutionDir)3rdparty\fmt\fmt\include\;$(SolutionDir)3rdparty\libchdr\libchdr\include;$(SolutionDir)3rdparty\wil\include;$(SolutionDir)3rdparty\Vulkan-Headers\include\;$(SolutionDir)3rdparty\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIL_SUPPRESS_EXCEPTIONS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>$(SolutionDir)deps\$(Platform)\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
</Project>
|
||||
@@ -1,38 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion>
|
||||
<_PropertySheetDisplayName>PcsxBaseProperties</_PropertySheetDisplayName>
|
||||
<OutDir>$(SolutionDir)bin\$(PcsxSubsection)\</OutDir>
|
||||
<OutDir>$(SolutionDir)bin\</OutDir>
|
||||
<IntDir>$(PlatformName)\$(Configuration)\</IntDir>
|
||||
<ExtensionsToDeleteOnClean>*.bsc;*.idb;*.sbr;*.res;*.pch;*.pdb;*.obj;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;$(TargetPath);$(ExtensionsToDeleteOnClean)</ExtensionsToDeleteOnClean>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>__WIN32__;WIN32;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;WINVER=0x0A00;_WIN32_WINNT=0x0A00;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CompileAs>Default</CompileAs>
|
||||
<DisableSpecificWarnings>4063;4100;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
||||
<!-- Force ThinLTO for Release builds, MSVC doesn't seem to do it otherwise. -->
|
||||
<!-- Also due to include order, needs to be set here, rather than in CodeGen_Release.props -->
|
||||
<AdditionalOptions Condition="$(Configuration.Contains(Clang)) And $(Configuration.Contains(Release))"> -flto=thin %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
<PreBuildEvent>
|
||||
<Command>"$(SolutionDir)common\vsprops\preBuild.cmd"</Command>
|
||||
</PreBuildEvent>
|
||||
<ResourceCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ResourceCompile>
|
||||
</ItemDefinitionGroup>
|
||||
</Project>
|
||||
@@ -1,22 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion>
|
||||
<OutDir>$(SolutionDir)deps\$(PlatformName)\$(Configuration)\</OutDir>
|
||||
<IntDir>$(PlatformName)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)\3rdparty;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>__WIN32__;WIN32;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="'$(Platform)'=='x64'">_ARCH_64=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CompileAs>Default</CompileAs>
|
||||
<DisableSpecificWarnings>4063;4100;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemDefinitionGroup>
|
||||
<!-- This sucks. Need a better solution. -->
|
||||
<PreBuildEvent>
|
||||
<Command>"$(SolutionDir)common\vsprops\preBuild.cmd"</Command>
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
</Project>
|
||||
+33
-29
@@ -1,10 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Label="UserMacros">
|
||||
<ProjectRootDir>$(ProjectDir)\..\..</ProjectRootDir>
|
||||
<SvnRootDir>$(ProjectRootDir)\..</SvnRootDir>
|
||||
<SvnCommonDir>$(SvnRootDir)\common</SvnCommonDir>
|
||||
<PcsxSubsection />
|
||||
<SSEString />
|
||||
<SSEString Condition="$(Configuration.Contains(AVX2))">-avx2</SSEString>
|
||||
<ArchString />
|
||||
@@ -17,42 +13,50 @@
|
||||
<EXEString>$(ProjectName)$(ArchString)$(SSEString)$(ToolkitString)$(BuildString)</EXEString>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>./;$(ProjectDir);$(ProjectDir)x86;$(ProjectDir)x86\ix86-32;$(ProjectDir)IPU;$(ProjectDir)System;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>_M_X86;TIXML_USE_STL;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir);$(ProjectDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>__WIN32__;WIN32;_WINDOWS;WIN32_LEAN_AND_MEAN;NOMINMAX;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SCL_SECURE_NO_WARNINGS;WINVER=0x0A00;_WIN32_WINNT=0x0A00;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="$(Configuration.Contains(Debug))">PCSX2_DEBUG;PCSX2_DEVBUILD;_SECURE_SCL_=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="$(Configuration.Contains(Devel))">PCSX2_DEVEL;PCSX2_DEVBUILD;NDEBUG;_SECURE_SCL_=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="$(Configuration.Contains(Release))">NDEBUG;_SECURE_SCL_=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pcsx2.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<CompileAs>Default</CompileAs>
|
||||
<DisableSpecificWarnings>4063;4100;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<PreprocessorDefinitions Condition="'$(Platform)'=='x64'">_ARCH_64=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ExceptionHandling>Async</ExceptionHandling>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<ObjectFileName>$(IntDir)%(RelativeDir)</ObjectFileName>
|
||||
|
||||
<!-- SH 2/3 flashlight, explicitly set here don't change -->
|
||||
<!-- https://github.com/PCSX2/pcsx2/commit/16431653e4d92fda4069031897e24fbe4688d36a -->
|
||||
<FloatingPointModel>Precise</FloatingPointModel>
|
||||
|
||||
<!-- MSVC automatically adds __AVX__ and __AVX2__ appropriately -->
|
||||
<PreprocessorDefinitions>_M_X86;__SSE4_1__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<EnableEnhancedInstructionSet Condition="!$(Configuration.Contains(AVX2)) Or $(Configuration.Contains(Clang))">NotSet</EnableEnhancedInstructionSet>
|
||||
<EnableEnhancedInstructionSet Condition="$(Configuration.Contains(AVX2)) And !$(Configuration.Contains(Clang))">AdvancedVectorExtensions2</EnableEnhancedInstructionSet>
|
||||
<AdditionalOptions Condition="$(Configuration.Contains(Clang)) And !$(Configuration.Contains(AVX2))"> -march=nehalem %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalOptions Condition="$(Configuration.Contains(Clang)) And $(Configuration.Contains(AVX2))"> -march=haswell %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalOptions Condition="!$(Configuration.Contains(Clang))">%(AdditionalOptions) /Zc:externConstexpr /Zc:__cplusplus /Zo /utf-8</AdditionalOptions>
|
||||
|
||||
<!-- Force ThinLTO for Release builds, MSVC doesn't seem to do it otherwise. -->
|
||||
<!-- Also due to include order, needs to be set here, rather than in CodeGen_Release.props -->
|
||||
<AdditionalOptions Condition="$(Configuration.Contains(Clang)) And $(Configuration.Contains(Release))"> -flto=thin %(AdditionalOptions)</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>setupapi.lib;comctl32.lib;ws2_32.lib;shlwapi.lib;winmm.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\;..\libs;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>comctl32.lib;ws2_32.lib;shlwapi.lib;winmm.lib;rpcrt4.lib;iphlpapi.lib;dsound.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>dxguid.lib;dinput8.lib;hid.lib;PowrProf.lib;d3dcompiler.lib;d3d11.lib;d3d12.lib;dxgi.lib;strmiids.lib;opengl32.lib;comsuppw.lib;OneCore.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
<PreBuildEvent>
|
||||
<Command>
|
||||
</Command>
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<BuildMacro Include="ProjectRootDir">
|
||||
<Value>$(ProjectRootDir)</Value>
|
||||
</BuildMacro>
|
||||
<BuildMacro Include="SvnRootDir">
|
||||
<Value>$(SvnRootDir)</Value>
|
||||
</BuildMacro>
|
||||
<BuildMacro Include="SvnCommonDir">
|
||||
<Value>$(SvnCommonDir)</Value>
|
||||
</BuildMacro>
|
||||
<BuildMacro Include="PcsxSubsection">
|
||||
<Value>$(PcsxSubsection)</Value>
|
||||
</BuildMacro>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Label="UserMacros">
|
||||
<PcsxSubsection>plugins</PcsxSubsection>
|
||||
<ProjectRootDir>$(ProjectDir)..</ProjectRootDir>
|
||||
<SvnRootDir>$(ProjectRootDir)\..\..\..</SvnRootDir>
|
||||
<SvnCommonDir>$(SvnRootDir)\common</SvnCommonDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion>
|
||||
<_PropertySheetDisplayName>PluginSvnRoot</_PropertySheetDisplayName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)\..\3rdparty;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<BuildMacro Include="PcsxSubsection">
|
||||
<Value>$(PcsxSubsection)</Value>
|
||||
</BuildMacro>
|
||||
<BuildMacro Include="ProjectRootDir">
|
||||
<Value>$(ProjectRootDir)</Value>
|
||||
</BuildMacro>
|
||||
<BuildMacro Include="SvnRootDir">
|
||||
<Value>$(SvnRootDir)</Value>
|
||||
</BuildMacro>
|
||||
<BuildMacro Include="SvnCommonDir">
|
||||
<Value>$(SvnCommonDir)</Value>
|
||||
</BuildMacro>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
+13
-26
@@ -37,25 +37,20 @@
|
||||
|
||||
#include "pcsx2/PrecompiledHeader.h"
|
||||
|
||||
#include "pcsx2/Achievements.h"
|
||||
#include "pcsx2/CDVD/CDVD.h"
|
||||
#include "pcsx2/Frontend/CommonHost.h"
|
||||
#include "pcsx2/Frontend/InputManager.h"
|
||||
#include "pcsx2/Frontend/ImGuiManager.h"
|
||||
#include "pcsx2/Frontend/LogSink.h"
|
||||
#include "pcsx2/GS.h"
|
||||
#include "pcsx2/GS/GS.h"
|
||||
#include "pcsx2/GSDumpReplayer.h"
|
||||
#include "pcsx2/Host.h"
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/INISettingsInterface.h"
|
||||
#include "pcsx2/ImGui/ImGuiManager.h"
|
||||
#include "pcsx2/Input/InputManager.h"
|
||||
#include "pcsx2/LogSink.h"
|
||||
#include "pcsx2/PAD/Host/PAD.h"
|
||||
#include "pcsx2/PerformanceMetrics.h"
|
||||
#include "pcsx2/VMManager.h"
|
||||
|
||||
#ifdef ENABLE_ACHIEVEMENTS
|
||||
#include "pcsx2/Frontend/Achievements.h"
|
||||
#endif
|
||||
|
||||
#include "svnrev.h"
|
||||
|
||||
namespace GSRunner
|
||||
@@ -87,14 +82,14 @@ static u32 s_loop_number = s_loop_count;
|
||||
|
||||
bool GSRunner::InitializeConfig()
|
||||
{
|
||||
if (!CommonHost::InitializeCriticalFolders())
|
||||
if (!EmuFolders::InitializeCriticalFolders())
|
||||
return false;
|
||||
|
||||
// don't provide an ini path, or bother loading. we'll store everything in memory.
|
||||
MemorySettingsInterface& si = s_settings_interface;
|
||||
Host::Internal::SetBaseSettingsLayer(&si);
|
||||
|
||||
CommonHost::SetDefaultSettings(si, true, true, true, true, true);
|
||||
VMManager::SetDefaultSettings(si, true, true, true, true, true);
|
||||
|
||||
// complete as quickly as possible
|
||||
si.SetBoolValue("EmuCore/GS", "FrameLimitEnable", false);
|
||||
@@ -131,7 +126,7 @@ bool GSRunner::InitializeConfig()
|
||||
si.SetStringValue("MemoryCards", fmt::format("Slot{}_Filename", i + 1).c_str(), "");
|
||||
}
|
||||
|
||||
CommonHost::LoadStartupSettings();
|
||||
VMManager::Internal::LoadStartupSettings();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -142,12 +137,10 @@ void Host::CommitBaseSettingChanges()
|
||||
|
||||
void Host::LoadSettings(SettingsInterface& si, std::unique_lock<std::mutex>& lock)
|
||||
{
|
||||
CommonHost::LoadSettings(si, lock);
|
||||
}
|
||||
|
||||
void Host::CheckForSettingsChanges(const Pcsx2Config& old_config)
|
||||
{
|
||||
CommonHost::CheckForSettingsChanges(old_config);
|
||||
}
|
||||
|
||||
bool Host::RequestResetSettings(bool folders, bool core, bool controllers, bool hotkeys, bool ui)
|
||||
@@ -414,7 +407,7 @@ void GSRunner::InitializeConsole()
|
||||
const char* var = std::getenv("PCSX2_NOCONSOLE");
|
||||
s_no_console = (var && StringUtil::FromChars<bool>(var).value_or(false));
|
||||
if (!s_no_console)
|
||||
CommonHost::InitializeEarlyConsole();
|
||||
LogSink::InitializeEarlyConsole();
|
||||
}
|
||||
|
||||
bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& params)
|
||||
@@ -540,7 +533,7 @@ bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& pa
|
||||
{
|
||||
// disable timestamps, since we want to be able to diff the logs
|
||||
Console.WriteLn("Logging to %s...", logfile);
|
||||
CommonHost::SetFileLogPath(logfile);
|
||||
LogSink::SetFileLogPath(logfile);
|
||||
s_settings_interface.SetBoolValue("Logging", "EnableFileLogging", true);
|
||||
s_settings_interface.SetBoolValue("Logging", "EnableTimestamps", false);
|
||||
}
|
||||
@@ -626,12 +619,8 @@ int main(int argc, char* argv[])
|
||||
if (!GSRunner::ParseCommandLineArgs(argc, argv, params))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
PerformanceMetrics::SetCPUThread(Threading::ThreadHandle::GetForCallingThread());
|
||||
if (!VMManager::Internal::InitializeGlobals() || !VMManager::Internal::InitializeMemory())
|
||||
{
|
||||
Console.Error("Failed to allocate globals/memory.");
|
||||
if (!VMManager::Internal::CPUThreadInitialize())
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (s_use_window.value_or(true) && !GSRunner::CreatePlatformWindow())
|
||||
{
|
||||
@@ -653,16 +642,14 @@ int main(int argc, char* argv[])
|
||||
VMManager::Shutdown(false);
|
||||
}
|
||||
|
||||
InputManager::CloseSources();
|
||||
VMManager::Internal::ReleaseMemory();
|
||||
VMManager::Internal::ReleaseGlobals();
|
||||
PerformanceMetrics::SetCPUThread(Threading::ThreadHandle());
|
||||
VMManager::Internal::CPUThreadShutdown();
|
||||
GSRunner::DestroyPlatformWindow();
|
||||
LogSink::CloseFileLog();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
void Host::CPUThreadVSync()
|
||||
void Host::VSyncOnCPUThread()
|
||||
{
|
||||
// update GS thread copy of frame number
|
||||
GetMTGS().RunOnGSThread([frame_number = GSDumpReplayer::GetFrameNumber()]() { s_dump_frame_number = frame_number; });
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(SolutionDir)common\vsprops\common.props" />
|
||||
<Import Project="$(SolutionDir)common\vsprops\BaseProperties.props" />
|
||||
<Import Project="$(SolutionDir)common\vsprops\3rdpartyDeps.props" />
|
||||
<Import Project="$(SolutionDir)common\vsprops\GenerateSCMVersion.props" />
|
||||
<Import Condition="$(Configuration.Contains(Debug))" Project="$(SolutionDir)common\vsprops\CodeGen_Debug.props" />
|
||||
<Import Condition="$(Configuration.Contains(Devel))" Project="$(SolutionDir)common\vsprops\CodeGen_Devel.props" />
|
||||
<Import Condition="$(Configuration.Contains(Release))" Project="$(SolutionDir)common\vsprops\CodeGen_Release.props" />
|
||||
@@ -34,84 +34,28 @@
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\xbyak;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\freetype\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\xz\xz\src\liblzma\api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\libpng;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\glad\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\simpleini\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\rapidyaml\rapidyaml\ext\c4core\src\c4\ext\fast_float\include;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(ProjectDir);$(SolutionDir)pcsx2;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<!-- Needed for moc pch -->
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(ProjectDir)\Settings;$(ProjectDir)\GameList</AdditionalIncludeDirectories>
|
||||
<ExceptionHandling>Async</ExceptionHandling>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\fmt\fmt\include</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\include</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\rapidyaml\rapidyaml\ext\c4core\src\c4\ext\fast_float\include;</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\simpleini\include</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)pcsx2</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<EnableEnhancedInstructionSet>NoExtensions</EnableEnhancedInstructionSet>
|
||||
<PreprocessorDefinitions>WIN32_LEAN_AND_MEAN;LZMA_API_STATIC;BUILD_DX=1;ENABLE_RAINTEGRATION;ENABLE_ACHIEVEMENTS;ENABLE_DISCORD_PRESENCE;ENABLE_OPENGL;ENABLE_VULKAN;DIRECTINPUT_VERSION=0x0800;PCSX2_CORE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="$(Configuration.Contains(Debug))">PCSX2_DEBUG;PCSX2_DEVBUILD;_SECURE_SCL_=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="$(Configuration.Contains(Devel))">PCSX2_DEVEL;PCSX2_DEVBUILD;NDEBUG;_SECURE_SCL_=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="$(Configuration.Contains(Release))">NDEBUG;_SECURE_SCL_=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="!$(Configuration.Contains(AVX2))">_M_SSE=0x401;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions Condition="$(Configuration.Contains(AVX2))">_M_SSE=0x501;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<EnableEnhancedInstructionSet Condition="!$(Configuration.Contains(AVX2)) Or $(Configuration.Contains(Clang))">NotSet</EnableEnhancedInstructionSet>
|
||||
<EnableEnhancedInstructionSet Condition="$(Configuration.Contains(AVX2)) And !$(Configuration.Contains(Clang))">AdvancedVectorExtensions2</EnableEnhancedInstructionSet>
|
||||
<AdditionalOptions Condition="$(Configuration.Contains(Clang)) And !$(Configuration.Contains(AVX2))"> -march=nehalem %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalOptions Condition="$(Configuration.Contains(Clang)) And $(Configuration.Contains(AVX2))"> -march=haswell %(AdditionalOptions)</AdditionalOptions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<ObjectFileName>$(IntDir)%(RelativeDir)</ObjectFileName>
|
||||
<!-- SH 2/3 flashlight, explicitly set here don't change -->
|
||||
<!-- https://github.com/PCSX2/pcsx2/commit/16431653e4d92fda4069031897e24fbe4688d36a -->
|
||||
<FloatingPointModel>Precise</FloatingPointModel>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalOptions>/Zc:__cplusplus /Zo /utf-8%(AdditionalOptions)</AdditionalOptions>
|
||||
<PreprocessorDefinitions>WIN32_LEAN_AND_MEAN;LZMA_API_STATIC;ENABLE_RAINTEGRATION;ENABLE_ACHIEVEMENTS;ENABLE_DISCORD_PRESENCE;ENABLE_OPENGL;ENABLE_VULKAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<LargeAddressAware>Yes</LargeAddressAware>
|
||||
<AdditionalDependencies>comctl32.lib;ws2_32.lib;shlwapi.lib;winmm.lib;rpcrt4.lib;iphlpapi.lib;dsound.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>dxguid.lib;dinput8.lib;hid.lib;PowrProf.lib;d3dcompiler.lib;d3d11.lib;dxgi.lib;strmiids.lib;opengl32.lib;comsuppw.lib;OneCore.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(SolutionDir)3rdparty\fmt\fmt.vcxproj">
|
||||
<Project>{449ad25e-424a-4714-babc-68706cdcc33b}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)3rdparty\libjpeg\libjpeg.vcxproj">
|
||||
<Project>{bc236261-77e8-4567-8d09-45cd02965eb6}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)3rdparty\libpng\projects\vstudio\libpng\libpng.vcxproj">
|
||||
<Project>{d6973076-9317-4ef2-a0b8-b7a18ac0713e}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)3rdparty\soundtouch\SoundTouch.vcxproj">
|
||||
<Project>{e9b51944-7e6d-4bcd-83f2-7bbd5a46182d}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)3rdparty\xz\liblzma.vcxproj">
|
||||
<Project>{12728250-16ec-4dc6-94d7-e21dd88947f8}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)3rdparty\libchdr\libchdr.vcxproj">
|
||||
<Project>{a0d2b3ad-1f72-4ee3-8b5c-f2c358da35f0}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)3rdparty\zlib\zlib.vcxproj">
|
||||
<Project>{2f6c0388-20cb-4242-9f6c-a6ebb6a83f47}</Project>
|
||||
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)3rdparty\jpgd\jpgd.vcxproj">
|
||||
<Project>{ed2f21fd-0a36-4a8f-9b90-e7d92a2acb63}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)3rdparty\imgui\imgui.vcxproj">
|
||||
<Project>{88fb34ec-845e-4f21-a552-f1573b9ed167}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)common\common.vcxproj">
|
||||
<Project>{4639972e-424e-4e13-8b07-ca403c481346}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)pcsx2\pcsx2core.vcxproj">
|
||||
<ProjectReference Include="$(SolutionDir)pcsx2\pcsx2.vcxproj">
|
||||
<Project>{6c7986c4-3e4d-4dcc-b3c6-6bb12b238995}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="$(SolutionDir)3rdparty\glad\glad.vcxproj">
|
||||
<Project>{c0293b32-5acf-40f0-aa6c-e6da6f3bf33a}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Main.cpp" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "QtHost.h"
|
||||
#include "QtUtils.h"
|
||||
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/Host.h"
|
||||
#include "pcsx2/SysForwardDefs.h"
|
||||
#include "svnrev.h"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
@@ -15,12 +15,12 @@
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
|
||||
#include "pcsx2/Frontend/GameList.h"
|
||||
|
||||
#include "CoverDownloadDialog.h"
|
||||
|
||||
#include "pcsx2/GameList.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
|
||||
CoverDownloadDialog::CoverDownloadDialog(QWidget* parent /*= nullptr*/)
|
||||
: QDialog(parent)
|
||||
{
|
||||
|
||||
@@ -319,8 +319,12 @@ bool BreakpointModel::insertBreakpointRows(int row, int count, std::vector<Break
|
||||
if (breakpoints.size() != static_cast<size_t>(count))
|
||||
return false;
|
||||
|
||||
beginInsertRows(index, row, row + count);
|
||||
beginInsertRows(index, row, row + (count - 1));
|
||||
|
||||
// After endInsertRows, Qt will try and validate our new rows
|
||||
// Because we add the breakpoints off of the UI thread, our new rows may not be visible yet
|
||||
// To prevent the (seemingly harmless?) warning emitted by enderInsertRows, add the breakpoints manually here as well
|
||||
m_breakpoints.insert(m_breakpoints.begin(), breakpoints.begin(), breakpoints.end());
|
||||
for (const auto& bp_mc : breakpoints)
|
||||
{
|
||||
if (const auto* bp = std::get_if<BreakPoint>(&bp_mc))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
@@ -15,16 +15,15 @@
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
|
||||
#include "pcsx2/Frontend/ImGuiManager.h"
|
||||
|
||||
#include "DisplayWidget.h"
|
||||
#include "MainWindow.h"
|
||||
#include "QtHost.h"
|
||||
#include "QtUtils.h"
|
||||
|
||||
#include "pcsx2/GS/GSIntrin.h" // _BitScanForward
|
||||
#include "pcsx2/ImGui/ImGuiManager.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
@@ -14,8 +14,11 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "pcsx2/Frontend/GameList.h"
|
||||
|
||||
#include "pcsx2/GameList.h"
|
||||
|
||||
#include "common/LRUCache.h"
|
||||
|
||||
#include <QtCore/QAbstractTableModel>
|
||||
#include <QtGui/QPixmap>
|
||||
#include <algorithm>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
@@ -15,11 +15,12 @@
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "GameListRefreshThread.h"
|
||||
|
||||
#include "pcsx2/GameList.h"
|
||||
|
||||
#include "common/ProgressCallback.h"
|
||||
#include "common/Timer.h"
|
||||
#include "pcsx2/Frontend/GameList.h"
|
||||
|
||||
#include "GameListRefreshThread.h"
|
||||
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
@@ -64,7 +65,9 @@ void AsyncRefreshProgressCallback::SetProgressValue(u32 value)
|
||||
fireUpdate();
|
||||
}
|
||||
|
||||
void AsyncRefreshProgressCallback::SetTitle(const char* title) {}
|
||||
void AsyncRefreshProgressCallback::SetTitle(const char* title)
|
||||
{
|
||||
}
|
||||
|
||||
void AsyncRefreshProgressCallback::DisplayError(const char* message)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
@@ -15,11 +15,19 @@
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "GameListModel.h"
|
||||
#include "GameListRefreshThread.h"
|
||||
#include "GameListWidget.h"
|
||||
#include "QtHost.h"
|
||||
#include "QtUtils.h"
|
||||
|
||||
#include "pcsx2/GameList.h"
|
||||
#include "pcsx2/Host.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "pcsx2/Frontend/GameList.h"
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include <QtCore/QSortFilterProxyModel>
|
||||
#include <QtGui/QPixmap>
|
||||
@@ -29,16 +37,9 @@
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QScrollBar>
|
||||
|
||||
#include "GameListModel.h"
|
||||
#include "GameListRefreshThread.h"
|
||||
#include "GameListWidget.h"
|
||||
#include "QtHost.h"
|
||||
#include "QtUtils.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
static const char* SUPPORTED_FORMATS_STRING = QT_TRANSLATE_NOOP(GameListWidget,
|
||||
".bin/.iso (ISO Disc Images)\n"
|
||||
".mdf (Media Descriptor File)\n"
|
||||
".chd (Compressed Hunks of Data)\n"
|
||||
".cso (Compressed ISO)\n"
|
||||
".gz (Gzip Compressed ISO)");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
@@ -14,9 +14,12 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "pcsx2/Frontend/GameList.h"
|
||||
|
||||
#include "ui_EmptyGameListWidget.h"
|
||||
#include "ui_GameListWidget.h"
|
||||
|
||||
#include "pcsx2/GameList.h"
|
||||
|
||||
#include <QtWidgets/QListView>
|
||||
#include <QtWidgets/QTableView>
|
||||
|
||||
|
||||
+32
-33
@@ -15,31 +15,6 @@
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtGui/QCloseEvent>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QInputDialog>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QProgressBar>
|
||||
#include <QtWidgets/QStyle>
|
||||
#include <QtWidgets/QStyleFactory>
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/CocoaTools.h"
|
||||
#include "common/FileSystem.h"
|
||||
|
||||
#include "pcsx2/CDVD/CDVDcommon.h"
|
||||
#include "pcsx2/CDVD/CDVDdiscReader.h"
|
||||
#include "pcsx2/Frontend/GameList.h"
|
||||
#include "pcsx2/Frontend/LogSink.h"
|
||||
#include "pcsx2/GS.h"
|
||||
#include "pcsx2/GS/GS.h"
|
||||
#include "pcsx2/GSDumpReplayer.h"
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/PerformanceMetrics.h"
|
||||
#include "pcsx2/Recording/InputRecording.h"
|
||||
#include "pcsx2/Recording/InputRecordingControls.h"
|
||||
|
||||
#include "AboutDialog.h"
|
||||
#include "AutoUpdaterDialog.h"
|
||||
#include "CoverDownloadDialog.h"
|
||||
@@ -49,27 +24,50 @@
|
||||
#include "MainWindow.h"
|
||||
#include "QtHost.h"
|
||||
#include "QtUtils.h"
|
||||
#include "SettingWidgetBinder.h"
|
||||
#include "Settings/ControllerSettingsDialog.h"
|
||||
#include "Settings/GameListSettingsWidget.h"
|
||||
#include "Settings/InterfaceSettingsWidget.h"
|
||||
#include "SettingWidgetBinder.h"
|
||||
#include "svnrev.h"
|
||||
#include "Tools/InputRecording/InputRecordingViewer.h"
|
||||
#include "Tools/InputRecording/NewInputRecordingDlg.h"
|
||||
#include "svnrev.h"
|
||||
|
||||
#include "pcsx2/Achievements.h"
|
||||
#include "pcsx2/CDVD/CDVDcommon.h"
|
||||
#include "pcsx2/CDVD/CDVDdiscReader.h"
|
||||
#include "pcsx2/GS.h"
|
||||
#include "pcsx2/GS/GS.h"
|
||||
#include "pcsx2/GSDumpReplayer.h"
|
||||
#include "pcsx2/GameList.h"
|
||||
#include "pcsx2/Host.h"
|
||||
#include "pcsx2/LogSink.h"
|
||||
#include "pcsx2/PerformanceMetrics.h"
|
||||
#include "pcsx2/Recording/InputRecording.h"
|
||||
#include "pcsx2/Recording/InputRecordingControls.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/CocoaTools.h"
|
||||
#include "common/FileSystem.h"
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtGui/QCloseEvent>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QInputDialog>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QProgressBar>
|
||||
#include <QtWidgets/QStyle>
|
||||
#include <QtWidgets/QStyleFactory>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "common/RedtapeWindows.h"
|
||||
#include <Dbt.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_RAINTEGRATION
|
||||
#include "pcsx2/Frontend/Achievements.h"
|
||||
#endif
|
||||
|
||||
const char* MainWindow::OPEN_FILE_FILTER =
|
||||
QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.chd *.cso *.gz *.elf *.irx *.gs *.gs.xz *.gs.zst *.dump);;"
|
||||
QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.mdf *.chd *.cso *.gz *.elf *.irx *.gs *.gs.xz *.gs.zst *.dump);;"
|
||||
"Single-Track Raw Images (*.bin *.iso);;"
|
||||
"Cue Sheets (*.cue);;"
|
||||
"Media Descriptor File (*.mdf);;"
|
||||
"MAME CHD Images (*.chd);;"
|
||||
"CSO Images (*.cso);;"
|
||||
"GZ Images (*.gz);;"
|
||||
@@ -78,9 +76,10 @@ const char* MainWindow::OPEN_FILE_FILTER =
|
||||
"GS Dumps (*.gs *.gs.xz *.gs.zst);;"
|
||||
"Block Dumps (*.dump)");
|
||||
|
||||
const char* MainWindow::DISC_IMAGE_FILTER = QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.chd *.cso *.gz *.dump);;"
|
||||
const char* MainWindow::DISC_IMAGE_FILTER = QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.mdf *.chd *.cso *.gz *.dump);;"
|
||||
"Single-Track Raw Images (*.bin *.iso);;"
|
||||
"Cue Sheets (*.cue);;"
|
||||
"Media Descriptor File (*.mdf);;"
|
||||
"MAME CHD Images (*.chd);;"
|
||||
"CSO Images (*.cso);;"
|
||||
"GZ Images (*.gz);;"
|
||||
|
||||
+52
-62
@@ -1,5 +1,5 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
@@ -15,8 +15,32 @@
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <csignal>
|
||||
#include "AutoUpdaterDialog.h"
|
||||
#include "DisplayWidget.h"
|
||||
#include "GameList/GameListWidget.h"
|
||||
#include "MainWindow.h"
|
||||
#include "QtHost.h"
|
||||
#include "QtUtils.h"
|
||||
#include "svnrev.h"
|
||||
|
||||
#include "pcsx2/CDVD/CDVDcommon.h"
|
||||
#include "pcsx2/Achievements.h"
|
||||
#include "pcsx2/CDVD/CDVD.h"
|
||||
#include "pcsx2/Counters.h"
|
||||
#include "pcsx2/DebugTools/Debug.h"
|
||||
#include "pcsx2/GS.h"
|
||||
#include "pcsx2/GS/GS.h"
|
||||
#include "pcsx2/GSDumpReplayer.h"
|
||||
#include "pcsx2/GameList.h"
|
||||
#include "pcsx2/Host.h"
|
||||
#include "pcsx2/INISettingsInterface.h"
|
||||
#include "pcsx2/ImGui/FullscreenUI.h"
|
||||
#include "pcsx2/ImGui/ImGuiManager.h"
|
||||
#include "pcsx2/Input/InputManager.h"
|
||||
#include "pcsx2/LogSink.h"
|
||||
#include "pcsx2/PAD/Host/PAD.h"
|
||||
#include "pcsx2/PerformanceMetrics.h"
|
||||
#include "pcsx2/VMManager.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
@@ -28,25 +52,6 @@
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/Timer.h"
|
||||
|
||||
#include "pcsx2/CDVD/CDVD.h"
|
||||
#include "pcsx2/CDVD/CDVDcommon.h"
|
||||
#include "pcsx2/Counters.h"
|
||||
#include "pcsx2/DebugTools/Debug.h"
|
||||
#include "pcsx2/Frontend/CommonHost.h"
|
||||
#include "pcsx2/Frontend/FullscreenUI.h"
|
||||
#include "pcsx2/Frontend/GameList.h"
|
||||
#include "pcsx2/Frontend/InputManager.h"
|
||||
#include "pcsx2/Frontend/ImGuiManager.h"
|
||||
#include "pcsx2/Frontend/LogSink.h"
|
||||
#include "pcsx2/GS.h"
|
||||
#include "pcsx2/GS/GS.h"
|
||||
#include "pcsx2/GSDumpReplayer.h"
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/INISettingsInterface.h"
|
||||
#include "pcsx2/PAD/Host/PAD.h"
|
||||
#include "pcsx2/PerformanceMetrics.h"
|
||||
#include "pcsx2/VMManager.h"
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
@@ -55,17 +60,8 @@
|
||||
|
||||
#include "fmt/core.h"
|
||||
|
||||
#include "AutoUpdaterDialog.h"
|
||||
#include "DisplayWidget.h"
|
||||
#include "GameList/GameListWidget.h"
|
||||
#include "MainWindow.h"
|
||||
#include "QtHost.h"
|
||||
#include "QtUtils.h"
|
||||
#include "svnrev.h"
|
||||
|
||||
#ifdef ENABLE_ACHIEVEMENTS
|
||||
#include "Frontend/Achievements.h"
|
||||
#endif
|
||||
#include <cmath>
|
||||
#include <csignal>
|
||||
|
||||
static constexpr u32 SETTINGS_SAVE_DELAY = 1000;
|
||||
|
||||
@@ -374,7 +370,12 @@ void EmuThread::run()
|
||||
connectSignals();
|
||||
|
||||
// Common host initialization (VM setup, etc).
|
||||
CommonHost::CPUThreadInitialize();
|
||||
if (!VMManager::Internal::CPUThreadInitialize())
|
||||
{
|
||||
VMManager::Internal::CPUThreadShutdown();
|
||||
QMetaObject::invokeMethod(qApp, &QCoreApplication::quit, Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start background polling because the VM won't do it for us.
|
||||
createBackgroundControllerPollTimer();
|
||||
@@ -395,7 +396,7 @@ void EmuThread::run()
|
||||
// Teardown in reverse order.
|
||||
stopBackgroundControllerPollTimer();
|
||||
destroyBackgroundControllerPollTimer();
|
||||
CommonHost::CPUThreadShutdown();
|
||||
VMManager::Internal::CPUThreadShutdown();
|
||||
|
||||
// Move back to the UI thread, since we're no longer running.
|
||||
moveToThread(m_ui_thread);
|
||||
@@ -569,7 +570,7 @@ void EmuThread::updateEmuFolders()
|
||||
return;
|
||||
}
|
||||
|
||||
Host::Internal::UpdateEmuFolders();
|
||||
VMManager::Internal::UpdateEmuFolders();
|
||||
}
|
||||
|
||||
void EmuThread::connectSignals()
|
||||
@@ -585,7 +586,6 @@ void EmuThread::loadSettings(SettingsInterface& si, std::unique_lock<std::mutex>
|
||||
|
||||
void Host::LoadSettings(SettingsInterface& si, std::unique_lock<std::mutex>& lock)
|
||||
{
|
||||
CommonHost::LoadSettings(si, lock);
|
||||
g_emu_thread->loadSettings(si, lock);
|
||||
}
|
||||
|
||||
@@ -609,7 +609,6 @@ void EmuThread::checkForSettingChanges(const Pcsx2Config& old_config)
|
||||
|
||||
void Host::CheckForSettingsChanges(const Pcsx2Config& old_config)
|
||||
{
|
||||
CommonHost::CheckForSettingsChanges(old_config);
|
||||
g_emu_thread->checkForSettingChanges(old_config);
|
||||
}
|
||||
|
||||
@@ -931,35 +930,29 @@ void Host::RequestResizeHostDisplay(s32 width, s32 height)
|
||||
|
||||
void Host::OnVMStarting()
|
||||
{
|
||||
CommonHost::OnVMStarting();
|
||||
g_emu_thread->stopBackgroundControllerPollTimer();
|
||||
emit g_emu_thread->onVMStarting();
|
||||
}
|
||||
|
||||
void Host::OnVMStarted()
|
||||
{
|
||||
CommonHost::OnVMStarted();
|
||||
emit g_emu_thread->onVMStarted();
|
||||
}
|
||||
|
||||
void Host::OnVMDestroyed()
|
||||
{
|
||||
CommonHost::OnVMDestroyed();
|
||||
emit g_emu_thread->onVMStopped();
|
||||
g_emu_thread->startBackgroundControllerPollTimer();
|
||||
}
|
||||
|
||||
void Host::OnVMPaused()
|
||||
{
|
||||
CommonHost::OnVMPaused();
|
||||
g_emu_thread->startBackgroundControllerPollTimer();
|
||||
emit g_emu_thread->onVMPaused();
|
||||
}
|
||||
|
||||
void Host::OnVMResumed()
|
||||
{
|
||||
CommonHost::OnVMResumed();
|
||||
|
||||
// exit the event loop when we eventually return
|
||||
g_emu_thread->getEventLoop()->quit();
|
||||
g_emu_thread->stopBackgroundControllerPollTimer();
|
||||
@@ -974,7 +967,6 @@ void Host::OnVMResumed()
|
||||
void Host::OnGameChanged(const std::string& disc_path, const std::string& elf_override, const std::string& game_serial,
|
||||
const std::string& game_name, u32 game_crc)
|
||||
{
|
||||
CommonHost::OnGameChanged(disc_path, elf_override, game_serial, game_name, game_crc);
|
||||
emit g_emu_thread->onGameChanged(QString::fromStdString(disc_path), QString::fromStdString(elf_override),
|
||||
QString::fromStdString(game_serial), QString::fromStdString(game_name), game_crc);
|
||||
}
|
||||
@@ -1121,10 +1113,9 @@ void Host::OnAchievementsRefreshed()
|
||||
}
|
||||
#endif
|
||||
|
||||
void Host::CPUThreadVSync()
|
||||
void Host::VSyncOnCPUThread()
|
||||
{
|
||||
g_emu_thread->getEventLoop()->processEvents(QEventLoop::AllEvents);
|
||||
CommonHost::CPUThreadVSync();
|
||||
}
|
||||
|
||||
void Host::RunOnCPUThread(std::function<void()> function, bool block /* = false */)
|
||||
@@ -1200,19 +1191,22 @@ SysMtgsThread& GetMTGS()
|
||||
|
||||
bool QtHost::InitializeConfig()
|
||||
{
|
||||
if (!CommonHost::InitializeCriticalFolders())
|
||||
if (!EmuFolders::InitializeCriticalFolders())
|
||||
{
|
||||
QMessageBox::critical(nullptr, QStringLiteral("PCSX2"),
|
||||
QStringLiteral("One or more critical directories are missing, your installation may be incomplete."));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write crash dumps to the data directory, since that'll be accessible for certain.
|
||||
CrashHandler::SetWriteDirectory(EmuFolders::DataRoot);
|
||||
|
||||
const std::string path(Path::Combine(EmuFolders::Settings, "PCSX2.ini"));
|
||||
Console.WriteLn("Loading config from %s.", path.c_str());
|
||||
|
||||
s_base_settings_interface = std::make_unique<INISettingsInterface>(std::move(path));
|
||||
Host::Internal::SetBaseSettingsLayer(s_base_settings_interface.get());
|
||||
if (!s_base_settings_interface->Load() || !CommonHost::CheckSettingsVersion())
|
||||
if (!s_base_settings_interface->Load() || !VMManager::Internal::CheckSettingsVersion())
|
||||
{
|
||||
// If the config file doesn't exist, assume this is a new install and don't prompt to overwrite.
|
||||
if (FileSystem::FileExists(s_base_settings_interface->GetFileName().c_str()) &&
|
||||
@@ -1223,12 +1217,12 @@ bool QtHost::InitializeConfig()
|
||||
return false;
|
||||
}
|
||||
|
||||
CommonHost::SetDefaultSettings(*s_base_settings_interface, true, true, true, true, true);
|
||||
VMManager::SetDefaultSettings(*s_base_settings_interface, true, true, true, true, true);
|
||||
SaveSettings();
|
||||
}
|
||||
|
||||
CommonHost::SetBlockSystemConsole(QtHost::InNoGUIMode());
|
||||
CommonHost::LoadStartupSettings();
|
||||
LogSink::SetBlockSystemConsole(QtHost::InNoGUIMode());
|
||||
VMManager::Internal::LoadStartupSettings();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1311,7 +1305,7 @@ bool Host::RequestResetSettings(bool folders, bool core, bool controllers, bool
|
||||
{
|
||||
{
|
||||
auto lock = Host::GetSettingsLock();
|
||||
CommonHost::SetDefaultSettings(*s_base_settings_interface.get(), folders, core, controllers, hotkeys, ui);
|
||||
VMManager::SetDefaultSettings(*s_base_settings_interface.get(), folders, core, controllers, hotkeys, ui);
|
||||
}
|
||||
Host::CommitBaseSettingChanges();
|
||||
|
||||
@@ -1516,7 +1510,7 @@ void QtHost::HookSignals()
|
||||
|
||||
void QtHost::PrintCommandLineVersion()
|
||||
{
|
||||
CommonHost::InitializeEarlyConsole();
|
||||
LogSink::InitializeEarlyConsole();
|
||||
std::fprintf(stderr, "%s\n", (GetAppNameAndVersion() + GetAppConfigSuffix()).toUtf8().constData());
|
||||
std::fprintf(stderr, "https://pcsx2.net/\n");
|
||||
std::fprintf(stderr, "\n");
|
||||
@@ -1632,7 +1626,7 @@ bool QtHost::ParseCommandLineOptions(const QStringList& args, std::shared_ptr<VM
|
||||
}
|
||||
else if (CHECK_ARG_PARAM(QStringLiteral("-logfile")))
|
||||
{
|
||||
CommonHost::SetFileLogPath((++it)->toStdString());
|
||||
LogSink::SetFileLogPath((++it)->toStdString());
|
||||
continue;
|
||||
}
|
||||
else if (CHECK_ARG(QStringLiteral("-bios")))
|
||||
@@ -1653,7 +1647,7 @@ bool QtHost::ParseCommandLineOptions(const QStringList& args, std::shared_ptr<VM
|
||||
}
|
||||
else if (CHECK_ARG(QStringLiteral("-earlyconsolelog")))
|
||||
{
|
||||
CommonHost::InitializeEarlyConsole();
|
||||
LogSink::InitializeEarlyConsole();
|
||||
continue;
|
||||
}
|
||||
else if (CHECK_ARG(QStringLiteral("-bigpicture")))
|
||||
@@ -1710,7 +1704,7 @@ bool QtHost::ParseCommandLineOptions(const QStringList& args, std::shared_ptr<VM
|
||||
// or disc, we don't want to actually start.
|
||||
if (autoboot && !autoboot->source_type.has_value() && autoboot->filename.empty() && autoboot->elf_override.empty())
|
||||
{
|
||||
CommonHost::InitializeEarlyConsole();
|
||||
LogSink::InitializeEarlyConsole();
|
||||
Console.Warning("Skipping autoboot due to no boot parameters.");
|
||||
autoboot.reset();
|
||||
}
|
||||
@@ -1843,11 +1837,7 @@ int main(int argc, char* argv[])
|
||||
s_base_settings_interface->Save();
|
||||
|
||||
// Ensure emulog is flushed.
|
||||
if (emuLog)
|
||||
{
|
||||
std::fclose(emuLog);
|
||||
emuLog = nullptr;
|
||||
}
|
||||
LogSink::CloseFileLog();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
@@ -21,8 +21,7 @@
|
||||
#include <optional>
|
||||
|
||||
#include "pcsx2/Host.h"
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/Frontend/InputManager.h"
|
||||
#include "pcsx2/Input/InputManager.h"
|
||||
#include "pcsx2/VMManager.h"
|
||||
|
||||
#include <QtCore/QList>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#include "QtUtils.h"
|
||||
|
||||
#include "pcsx2/Frontend/InputManager.h"
|
||||
#include "pcsx2/Input/InputManager.h"
|
||||
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
#include "common/Path.h"
|
||||
|
||||
#include "pcsx2/Config.h"
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/Host.h"
|
||||
|
||||
#include "QtHost.h"
|
||||
#include "QtUtils.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
@@ -14,9 +14,12 @@
|
||||
*/
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "AchievementLoginDialog.h"
|
||||
#include "pcsx2/Frontend/Achievements.h"
|
||||
#include "QtHost.h"
|
||||
|
||||
#include "pcsx2/Achievements.h"
|
||||
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
AchievementLoginDialog::AchievementLoginDialog(QWidget* parent)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
@@ -22,8 +22,8 @@
|
||||
#include "SettingWidgetBinder.h"
|
||||
#include "QtUtils.h"
|
||||
|
||||
#include "pcsx2/Frontend/Achievements.h"
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/Achievements.h"
|
||||
#include "pcsx2/Host.h"
|
||||
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
|
||||
@@ -67,6 +67,9 @@ AdvancedSettingsWidget::AdvancedSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.ntscFrameRate, "EmuCore/GS", "FramerateNTSC", 59.94f);
|
||||
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.palFrameRate, "EmuCore/GS", "FrameratePAL", 50.00f);
|
||||
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pineEnable, "EmuCore", "EnablePINE", false);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.pineSlot, "EmuCore", "PINESlot", 28011);
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.eeRoundingMode, tr("Rounding Mode"), tr("Chop / Zero (Default)"), tr(""));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.eeClampMode, tr("Clamping Mode"), tr("Normal (Default)"), tr(""));
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>809</width>
|
||||
<height>647</height>
|
||||
<height>725</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -37,7 +37,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>807</width>
|
||||
<height>645</height>
|
||||
<height>723</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
@@ -417,6 +417,45 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_5">
|
||||
<property name="title">
|
||||
<string>PINE Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_8">
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="pineSlot">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Slot:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="pineEnable">
|
||||
<property name="text">
|
||||
<string>Enable</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <algorithm>
|
||||
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/Host.h"
|
||||
#include "pcsx2/ps2/BiosTools.h"
|
||||
|
||||
#include "BIOSSettingsWidget.h"
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/Host.h"
|
||||
#include "pcsx2/PAD/Host/PAD.h"
|
||||
|
||||
#include "Settings/ControllerBindingWidgets.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
@@ -15,15 +15,16 @@
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "Frontend/InputManager.h"
|
||||
#include "Settings/ControllerGlobalSettingsWidget.h"
|
||||
#include "Settings/ControllerSettingsDialog.h"
|
||||
#include "Settings/ControllerSettingWidgetBinder.h"
|
||||
#include "QtUtils.h"
|
||||
#include "SettingWidgetBinder.h"
|
||||
|
||||
#include "pcsx2/Input/InputManager.h"
|
||||
|
||||
#ifdef SDL_BUILD
|
||||
#include "pcsx2/Frontend/SDLInputSource.h"
|
||||
#include "pcsx2/Input/SDLInputSource.h"
|
||||
#endif
|
||||
|
||||
ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, ControllerSettingsDialog* dialog)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
@@ -15,6 +15,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pcsx2/Host.h"
|
||||
|
||||
#include "QtHost.h"
|
||||
#include "SettingWidgetBinder.h"
|
||||
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
|
||||
@@ -27,11 +32,6 @@
|
||||
#include <QtWidgets/QSlider>
|
||||
#include <QtWidgets/QSpinBox>
|
||||
|
||||
#include "pcsx2/HostSettings.h"
|
||||
|
||||
#include "QtHost.h"
|
||||
#include "SettingWidgetBinder.h"
|
||||
|
||||
/// This nastyness is required because input profiles aren't overlaid settings like the rest of them, it's
|
||||
/// input profile *or* global, not both.
|
||||
namespace ControllerSettingWidgetBinder
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "Settings/ControllerBindingWidgets.h"
|
||||
#include "Settings/HotkeySettingsWidget.h"
|
||||
|
||||
#include "pcsx2/Frontend/CommonHost.h"
|
||||
#include "pcsx2/INISettingsInterface.h"
|
||||
#include "pcsx2/PAD/Host/PAD.h"
|
||||
#include "pcsx2/Sio.h"
|
||||
@@ -208,7 +207,7 @@ void ControllerSettingsDialog::onRestoreDefaultsClicked()
|
||||
// actually restore it
|
||||
{
|
||||
auto lock = Host::GetSettingsLock();
|
||||
CommonHost::SetDefaultSettings(*Host::Internal::GetBaseSettingsLayer(), false, false, true, true, false);
|
||||
VMManager::SetDefaultSettings(*Host::Internal::GetBaseSettingsLayer(), false, false, true, true, false);
|
||||
}
|
||||
Host::CommitBaseSettingChanges();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
@@ -15,8 +15,10 @@
|
||||
|
||||
#pragma once
|
||||
#include "ui_ControllerSettingsDialog.h"
|
||||
#include "pcsx2/Frontend/InputManager.h"
|
||||
|
||||
#include "pcsx2/Input/InputManager.h"
|
||||
#include "pcsx2/USB/USB.h"
|
||||
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QPair>
|
||||
#include <QtCore/QString>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "common/Path.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/Host.h"
|
||||
#include "pcsx2/INISettingsInterface.h"
|
||||
|
||||
#include "DEV9SettingsWidget.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
@@ -19,9 +19,10 @@
|
||||
#include "QtUtils.h"
|
||||
#include "SettingWidgetBinder.h"
|
||||
#include "SettingsDialog.h"
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/Host.h"
|
||||
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
DebugSettingsWidget::DebugSettingsWidget(SettingsDialog* dialog, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <limits>
|
||||
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/Host.h"
|
||||
|
||||
#include "EmulationSettingsWidget.h"
|
||||
#include "QtUtils.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
* Copyright (C) 2002-2023 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
@@ -15,22 +15,22 @@
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "Frontend/GameList.h"
|
||||
#include "PAD/Host/PAD.h"
|
||||
|
||||
#include "GameSummaryWidget.h"
|
||||
#include "SettingsDialog.h"
|
||||
#include "MainWindow.h"
|
||||
#include "QtHost.h"
|
||||
#include "QtUtils.h"
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include "pcsx2/GameList.h"
|
||||
#include "pcsx2/PAD/Host/PAD.h"
|
||||
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include <QtCore/QDir>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
|
||||
GameSummaryWidget::GameSummaryWidget(const GameList::Entry* entry, SettingsDialog* dialog, QWidget* parent)
|
||||
: m_dialog(dialog)
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "SettingsDialog.h"
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
#include "pcsx2/HostSettings.h"
|
||||
#include "pcsx2/Host.h"
|
||||
#include "pcsx2/GS/GS.h"
|
||||
#include "pcsx2/GS/GSCapture.h"
|
||||
#include "pcsx2/GS/GSUtil.h"
|
||||
@@ -181,7 +181,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// HW Renderer Fixes
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.crcFixLevel, "EmuCore/GS", "crc_hack_level", static_cast<int>(CRCHackLevel::Automatic), -1);
|
||||
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);
|
||||
@@ -193,19 +192,21 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.frameBufferConversion, "EmuCore/GS", "UserHacks_CPU_FB_Conversion", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableDepthEmulation, "EmuCore/GS", "UserHacks_DisableDepthSupport", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableSafeFeatures, "EmuCore/GS", "UserHacks_Disable_Safe_Features", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableRenderFixes, "EmuCore/GS", "UserHacks_DisableRenderFixes", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.preloadFrameData, "EmuCore/GS", "preload_frame_with_gs_data", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(
|
||||
sif, m_ui.disablePartialInvalidation, "EmuCore/GS", "UserHacks_DisablePartialInvalidation", false);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(
|
||||
sif, m_ui.textureInsideRt, "EmuCore/GS", "UserHacks_TextureInsideRt", static_cast<int>(GSTextureInRtMode::Disabled));
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.readTCOnClose, "EmuCore/GS", "UserHacks_ReadTCOnClose", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.targetPartialInvalidation, "EmuCore/GS", "UserHacks_TargetPartialInvalidation", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(
|
||||
sif, m_ui.targetPartialInvalidation, "EmuCore/GS", "UserHacks_TargetPartialInvalidation", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.estimateTextureRegion, "EmuCore/GS", "UserHacks_EstimateTextureRegion", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.gpuPaletteConversion, "EmuCore/GS", "paltex", false);
|
||||
connect(m_ui.cpuSpriteRenderBW, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&GraphicsSettingsWidget::onCPUSpriteRenderBWChanged);
|
||||
connect(m_ui.textureInsideRt, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&GraphicsSettingsWidget::onTextureInsideRtChanged);
|
||||
connect(
|
||||
m_ui.textureInsideRt, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &GraphicsSettingsWidget::onTextureInsideRtChanged);
|
||||
connect(m_ui.gpuPaletteConversion, QOverload<int>::of(&QCheckBox::stateChanged), this,
|
||||
&GraphicsSettingsWidget::onGpuPaletteConversionChanged);
|
||||
onCPUSpriteRenderBWChanged();
|
||||
@@ -244,11 +245,14 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useDebugDevice, "EmuCore/GS", "UseDebugDevice", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.skipPresentingDuplicateFrames, "EmuCore/GS", "SkipDuplicateFrames", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.threadedPresentation, "EmuCore/GS", "DisableThreadedPresentation", false);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.exclusiveFullscreenControl, "EmuCore/GS", "ExclusiveFullscreenControl", -1, -1);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.overrideTextureBarriers, "EmuCore/GS", "OverrideTextureBarriers", -1, -1);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(
|
||||
sif, m_ui.gsDumpCompression, "EmuCore/GS", "GSDumpCompression", static_cast<int>(GSDumpCompressionMethod::Zstandard));
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableFramebufferFetch, "EmuCore/GS", "DisableFramebufferFetch", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableDualSource, "EmuCore/GS", "DisableDualSourceBlend", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableShaderCache, "EmuCore/GS", "DisableShaderCache", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableVertexShaderExpand, "EmuCore/GS", "DisableVertexShaderExpand", false);
|
||||
SettingWidgetBinder::BindWidgetToIntSetting(
|
||||
sif, m_ui.gsDownloadMode, "EmuCore/GS", "HWDownloadMode", static_cast<int>(GSHardwareDownloadMode::Enabled));
|
||||
|
||||
@@ -295,6 +299,12 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
connect(m_ui.swTextureFiltering, &QComboBox::currentIndexChanged, this, &GraphicsSettingsWidget::onSWTextureFilteringChange);
|
||||
updateRendererDependentOptions();
|
||||
|
||||
#ifndef _WIN32
|
||||
// Exclusive fullscreen control is Windows-only.
|
||||
m_ui.advancedOptionsFormLayout->removeRow(2);
|
||||
m_ui.exclusiveFullscreenControl = nullptr;
|
||||
#endif
|
||||
|
||||
#ifndef PCSX2_DEVBUILD
|
||||
if (!m_dialog->isPerGameSettings())
|
||||
{
|
||||
@@ -384,8 +394,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
dialog->registerWidgetHelp(m_ui.PCRTCOverscan, tr("Show Overscan"), tr("Unchecked"),
|
||||
tr("Enables the option to show the overscan area on games which draw more than the safe area of the screen."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.fmvAspectRatio, tr("FMV Aspect Ratio"), tr("Off (Default)"),
|
||||
tr("Overrides the full-motion video (FMV) aspect ratio."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.fmvAspectRatio, tr("FMV Aspect Ratio"), tr("Off (Default)"), tr("Overrides the full-motion video (FMV) aspect ratio."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.PCRTCAntiBlur, tr("Anti-Blur"), tr("Checked"),
|
||||
tr("Enables internal Anti-Blur hacks. Less accurate to PS2 rendering but will make a lot of games look less blurry."));
|
||||
@@ -422,17 +432,17 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
dialog->registerWidgetHelp(m_ui.fullscreenModes, tr("Fullscreen Mode"), tr("Borderless Fullscreen"),
|
||||
tr("Chooses the fullscreen resolution and frequency."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.cropLeft, tr("Left"), tr("0px"),
|
||||
tr("Changes the number of pixels cropped from the left side of the display."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.cropLeft, tr("Left"), tr("0px"), tr("Changes the number of pixels cropped from the left side of the display."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.cropTop, tr("Top"), tr("0px"),
|
||||
tr("Changes the number of pixels cropped from the top of the display."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.cropTop, tr("Top"), tr("0px"), tr("Changes the number of pixels cropped from the top of the display."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.cropRight, tr("Right"), tr("0px"),
|
||||
tr("Changes the number of pixels cropped from the right side of the display."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.cropRight, tr("Right"), tr("0px"), tr("Changes the number of pixels cropped from the right side of the display."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.cropBottom, tr("Bottom"), tr("0px"),
|
||||
tr("Changes the number of pixels cropped from the bottom of the display."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.cropBottom, tr("Bottom"), tr("0px"), tr("Changes the number of pixels cropped from the bottom of the display."));
|
||||
}
|
||||
|
||||
// Rendering tab
|
||||
@@ -461,9 +471,6 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
"Unscaled: Native Dithering / Lowest dithering effect does not increase size of squares when upscaling.<br> "
|
||||
"Scaled: Upscaling-aware / Highest dithering effect."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.crcFixLevel, tr("CRC Fix Level"), tr("Automatic (Default)"),
|
||||
tr("Control the number of Auto-CRC fixes and hacks applied to games."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.blending, tr("Blending Accuracy"), tr("Basic (Recommended)"),
|
||||
tr("Control the accuracy level of the GS blending unit emulation.<br> "
|
||||
"The higher the setting, the more blending is emulated in the shader accurately, and the higher the speed penalty will "
|
||||
@@ -535,6 +542,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
"Disables accurate GS Memory Clearing to be done on the CPU, and let the GPU handle it, which can help Kingdom Hearts "
|
||||
"games."));
|
||||
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.disableRenderFixes, tr("Disable Render Fixes"), tr("Unchecked"), tr("This option disables game-specific render fixes."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.disablePartialInvalidation, tr("Disable Partial Source Invalidation"), tr("Unchecked"),
|
||||
tr("By default, the texture cache handles partial invalidations. Unfortunately it is very costly to compute CPU wise. "
|
||||
"This hack replaces the partial invalidation with a complete deletion of the texture to reduce the CPU load. "
|
||||
@@ -639,8 +649,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
|
||||
// OSD tab
|
||||
{
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.osdScale, tr("OSD Scale"), tr("100%"), tr("Scales the size of the onscreen OSD from 50% to 500%."));
|
||||
dialog->registerWidgetHelp(m_ui.osdScale, tr("OSD Scale"), tr("100%"), tr("Scales the size of the onscreen OSD from 50% to 500%."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.osdShowMessages, tr("Show OSD Messages"), tr("Checked"),
|
||||
tr("Shows on-screen-display messages when events occur such as save states being "
|
||||
@@ -702,6 +711,10 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
"renderer. This usually results in slower performance, but may be required for some "
|
||||
"streaming applications, or to uncap framerates on some systems."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.exclusiveFullscreenControl, tr("Allow Exclusive Fullscreen"), tr("Automatic (Default)"),
|
||||
tr("Overrides the driver's heuristics for enabling exclusive fullscreen, or direct flip/scanout.<br>"
|
||||
"Disallowing exclusive fullscreen may enable smoother task switching and overlays, but increase input latency."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.useDebugDevice, tr("Use Debug Device"), tr("Unchecked"), tr(""));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.disableDualSource, tr("Disable Dual Source Blending"), tr("Unchecked"), tr(""));
|
||||
@@ -710,10 +723,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.skipPresentingDuplicateFrames, tr("Skip Presenting Duplicate Frames"), tr("Unchecked"),
|
||||
tr("Detects when idle frames are being presented in 25/30fps games, and skips presenting those frames. The frame is still "
|
||||
"rendered, it just means "
|
||||
"the GPU has more time to complete it (this is NOT frame skipping). Can smooth our frame time fluctuations when the CPU/GPU "
|
||||
"are near maximum "
|
||||
"utilization, but makes frame pacing more inconsistent and can increase input lag."));
|
||||
"rendered, it just means the GPU has more time to complete it (this is NOT frame skipping). Can smooth our frame time "
|
||||
"fluctuations when the CPU/GPU are near maximum utilization, but makes frame pacing more inconsistent and can increase "
|
||||
"input lag."));
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.threadedPresentation, tr("Disable Threaded Presentation"), tr("Unchecked"),
|
||||
tr("Presents frames on the main GS thread instead of a worker thread. Used for debugging frametime issues. "
|
||||
@@ -911,6 +923,8 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
|
||||
const bool is_hardware = (type == GSRendererType::DX11 || type == GSRendererType::DX12 || type == GSRendererType::OGL ||
|
||||
type == GSRendererType::VK || type == GSRendererType::Metal);
|
||||
const bool is_software = (type == GSRendererType::SW);
|
||||
const bool is_auto = (type == GSRendererType::Auto);
|
||||
const bool is_vk = (type == GSRendererType::VK);
|
||||
const bool hw_fixes = (is_hardware && m_ui.enableHWFixes && m_ui.enableHWFixes->checkState() == Qt::Checked);
|
||||
const int prev_tab = m_ui.tabs->currentIndex();
|
||||
|
||||
@@ -946,6 +960,9 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
|
||||
|
||||
m_ui.disableFramebufferFetch->setDisabled(is_sw_dx);
|
||||
|
||||
if (m_ui.exclusiveFullscreenControl)
|
||||
m_ui.exclusiveFullscreenControl->setEnabled(is_auto || is_vk);
|
||||
|
||||
// populate adapters
|
||||
std::vector<std::string> adapters;
|
||||
std::vector<std::string> fullscreen_modes;
|
||||
@@ -1034,7 +1051,6 @@ void GraphicsSettingsWidget::resetManualHardwareFixes()
|
||||
|
||||
check_bool("EmuCore/GS", "UserHacks", false);
|
||||
|
||||
check_int("EmuCore/GS", "crc_hack_level", static_cast<int>(CRCHackLevel::Automatic));
|
||||
check_int("EmuCore/GS", "UserHacks_Half_Bottom_Override", -1);
|
||||
check_int("EmuCore/GS", "UserHacks_CPUSpriteRenderBW", 0);
|
||||
check_int("EmuCore/GS", "UserHacks_CPUCLUTRender", 0);
|
||||
@@ -1045,6 +1061,7 @@ void GraphicsSettingsWidget::resetManualHardwareFixes()
|
||||
check_bool("EmuCore/GS", "UserHacks_CPU_FB_Conversion", false);
|
||||
check_bool("EmuCore/GS", "UserHacks_DisableDepthSupport", false);
|
||||
check_bool("EmuCore/GS", "UserHacks_Disable_Safe_Features", false);
|
||||
check_bool("EmuCore/GS", "UserHacks_DisableRenderFixes", false);
|
||||
check_bool("EmuCore/GS", "preload_frame_with_gs_data", false);
|
||||
check_bool("EmuCore/GS", "UserHacks_DisablePartialInvalidation", false);
|
||||
check_int("EmuCore/GS", "UserHacks_TextureInsideRt", static_cast<int>(GSTextureInRtMode::Disabled));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user