Compare commits

..
80 Commits
Author SHA1 Message Date
Stenzekandrefractionpcsx2 a5ed24ca88 Perf: Support instruction-level profiling with jitdump on Linux 2023-04-29 16:22:37 +01:00
Stenzekandrefractionpcsx2 b3697579c0 GS/Vulkan: Simplify GPU selection
Hopefully stops a crash on broken drivers.
2023-04-29 16:20:01 +01:00
JordanTheToasterandrefractionpcsx2 388da2058b GameDB: FFXII texture fixes
Fixes for broken textures on player model.
2023-04-29 08:01:59 +01:00
Stenzekandlightningterror aa9a0dca4b GS/HW: Fix HW move debug message 2023-04-28 13:34:45 +02:00
Stenzekandlightningterror 2a892da0da GS/HW: Improve split shuffle detection
Fixes transition effects in DBZ BT3 PAL.
2023-04-28 13:34:45 +02:00
Stenzekandlightningterror 9237bf9429 GS/HW: Relax PSM match requirement for move targets
Fixes broken car preview in Tokyo Xtreme Racer Zero.
2023-04-28 13:34:45 +02:00
Stenzekandlightningterror 10533dce02 GS/HW: Swap xyxy() for upld()
In theory more efficient.
2023-04-28 13:34:45 +02:00
Stenzekandlightningterror b5ebc19eff GS/HW: Don't mess with ZMSK for no_ds
Fixes broken depth in Superman Returns.
2023-04-28 13:34:45 +02:00
Stenzekandrefractionpcsx2 6535e7e43a GS: Add natvis for vector types 2023-04-28 10:22:45 +01:00
Ty Lamontagneandrefractionpcsx2 5f9473ef02 ci: Update labeler debugger directories 2023-04-28 06:15:27 +01:00
Stenzekandrefractionpcsx2 fbb1c7cb8e GS/OGL: Don't reuse targets when they're the texture
I don't trust drivers to insert a barrier here. At least one of them
gets it wrong.

Also moves the masking of depth/colour to the common renderer for
consistency.
2023-04-27 23:22:09 +01:00
lightningterror 2d97d85ca5 GS-hw: Use correct format specifier for blend log, use constants for mergesprite log. 2023-04-27 21:20:31 +02:00
Stenzekandrefractionpcsx2 ecd7d0fc35 GS/HW: Fix pipeline/shader duplication 2023-04-27 12:01:34 +01:00
Stenzekandrefractionpcsx2 0c389789f3 GS: Fix hang when switching renderer while fullscreen 2023-04-27 09:17:27 +01:00
Stenzekandrefractionpcsx2 d8239664a8 GS/Vulkan: Re-enable async presentation 2023-04-27 09:17:16 +01:00
rayanHappyandrefractionpcsx2 c06d4f477a Misc: remove unneeded file and clean up a 32bit check 2023-04-27 09:09:04 +01:00
refractionpcsx2 cdd88a6e5c GS-HW: Fix std::sort comparator for purging hash cache 2023-04-27 09:07:21 +01:00
refractionpcsx2 5a73fa2d23 GS-TC: Update dirty read overlap on local mem invalidate 2023-04-27 09:07:05 +01:00
Stenzekandrefractionpcsx2 0367851b8e GS: Make TEXFLUSH a flag instead 2023-04-26 16:00:59 +01:00
Stenzekandrefractionpcsx2 33b2f6331c GS/HW: Allow auto flush to be applied only to sprites 2023-04-26 16:00:59 +01:00
Stenzekandrefractionpcsx2 befbf57191 GS: Refactor exclusive fullscreen yet again
Also acquire render window as late as possible.
Limits the duration that the main window isn't displaying anything.
2023-04-26 16:00:39 +01:00
JordanTheToasterandrefractionpcsx2 20d040d5d1 GameDB: Disney Golf Fixes
Fixes for grass rendering to match software with mipmapping and trilinear filtering.
2023-04-26 02:07:15 +01:00
TellowKrinkleandrefractionpcsx2 06fdc75945 CMake: Properly fail on windeployqt failure 2023-04-26 02:06:58 +01:00
refractionpcsx2 ded971a3ec Build: Fix Qtdir for windows build workdflows 2023-04-25 22:57:05 +01:00
Stenzekandrefractionpcsx2 abc0f99ddd Qt: Move themes to its own file
It's getting a bit out of hand.
2023-04-25 21:53:30 +01:00
Stenzekandrefractionpcsx2 b3a88d6ea7 Qt: Migrate to 6.5.0 on Windows 2023-04-25 21:53:30 +01:00
refractionpcsx2 76014b7cb9 GS-TC: Use the expected rect to expand the target when Tex is RT. 2023-04-25 20:53:31 +01:00
Stenzekandrefractionpcsx2 0ab6eb6587 GS: Rework texture pooling behavior
- Split into texture/target pools.
 - Keep textures around when they're used recently regardless of size
   (saves work in the backend/driver).
 - Don't boot textures out of the pool when it's an idle frame.
2023-04-25 20:53:21 +01:00
Stenzekandrefractionpcsx2 a4e99366fb GS/HW: Put a cap on the hash cache count
As well as VRAM usage. Stops Corvette allocating 16,000+ textures.

Also reduce max age for hash cache sources, since they get kept around
in the latter for another 30 frames.
2023-04-25 20:53:21 +01:00
Stenzekandrefractionpcsx2 a32ab4cc97 GS: Refactor ResizeTexture() to ResizeRenderTarget() 2023-04-25 20:53:21 +01:00
JordanTheToasterandrefractionpcsx2 e7f3c42f9d GameDB: Various FIFA 2001-2004 fixes
Fixes for broken player textures and some missing fixes.
2023-04-25 20:53:05 +01:00
Ty Lamontagneandrefractionpcsx2 d76a0d7416 Debugger: Implement BC0XY branches & their condition evaluation 2023-04-25 17:28:31 +01:00
Stuart Kennyandrefractionpcsx2 79f4af8ab5 Qt: Delete SDLRawInput on non win32 builds 2023-04-25 15:05:30 +01:00
Stuart Kennyandrefractionpcsx2 a5f83329cb FullscreenUI: Only show SDL Raw on win32 builds. 2023-04-25 15:05:30 +01:00
rayanHappyandrefractionpcsx2 267479f31d Misc: Cmake and GCC compiler flags cleanups 2023-04-25 15:04:39 +01:00
rayanHappyandrefractionpcsx2 164462e510 Misc: Remove ICC compiler support 2023-04-25 15:04:39 +01:00
TellowKrinkleandrefractionpcsx2 6331df306f qt:resources: Use black and white icon for forum menu entry 2023-04-25 15:04:26 +01:00
TellowKrinkleandrefractionpcsx2 027ceebcbd qt:resources: Use black and white icons for discord menu icon 2023-04-25 15:04:26 +01:00
TellowKrinkleandrefractionpcsx2 edb2c0080c qt:resources: Use separate black/white github icon 2023-04-25 15:04:26 +01:00
TellowKrinkleandrefractionpcsx2 caafc87b29 qt:resources: Sort qrc filenames
Many filesystems (e.g. btrfs, apfs) return results in some internal non-alphabetical ordering, which isn't great for minimizing changes to files
2023-04-25 15:04:26 +01:00
TellowKrinkleandrefractionpcsx2 5d37cac4a0 qt:resources: Skip dotfiles when generating qrc 2023-04-25 15:04:26 +01:00
Stenzekandrefractionpcsx2 8f68e096d4 GS/HW: Avoid render pass restarts to turn off RT
Significantly reduces render passes in Sly 2.
2023-04-24 22:17:25 +01:00
Stenzekandrefractionpcsx2 430cad48e3 GS/Metal: Get rid of redundant pointer 2023-04-24 22:17:04 +01:00
Stenzekandrefractionpcsx2 757a9532e6 GS: Add Render Pass stats 2023-04-24 22:17:04 +01:00
KamFretoZandlightningterror f26f8cafa9 Qt: Even more fixes to the Cobalt Theme 2023-04-24 19:24:19 +02:00
KamFretoZandlightningterror 128a79e355 Qt: Help menu icon fix for macOS user 2023-04-24 19:24:19 +02:00
PCSX2 Botandlightningterror cc2d99db3a PAD: Update to latest controller database. 2023-04-24 19:21:18 +02:00
KamFretoZandrefractionpcsx2 30a31d931a Qt: Add extra icons to the help menu 2023-04-23 03:56:36 +01:00
KamFretoZandrefractionpcsx2 1c30d449bf Tools: Fix filename lookup error on the python glyph updater script 2023-04-23 03:56:36 +01:00
refractionpcsx2 dc4ce58248 VU-JIT: Avoid writeReg stealing loadReg GPR 2023-04-23 03:54:53 +01:00
Mrlinkwiiandrefractionpcsx2 dc93e861c7 GameDB: various fixes 2023-04-22 23:58:58 +01:00
refractionpcsx2 960daf27e2 GameDB: Add FullVU0SyncHack to the linter 2023-04-22 23:38:05 +01:00
Goatman13andrefractionpcsx2 844f21344d GameDB: Add patch for Michigan: Report from Hell 2023-04-22 16:55:27 +01:00
refractionpcsx2 535ad110e3 VU-JIT: Backup non cached VI before writing to it when load != write 2023-04-22 15:15:25 +01:00
JordanTheToasterandrefractionpcsx2 0e0d7a5441 GameDB: Various fixes
Fixes for vertical lines in Ring of Red and invisible text in Dynasty Warriors 2.
2023-04-22 03:03:27 +01:00
SeruranBlueandGitHub ed63221c7e GameDB: Fixes for Air Ranger: Rescue Helicopter (#8711) 2023-04-22 03:02:46 +01:00
seta-sanandrefractionpcsx2 f9c2d7767d CDVD: Fix Error Message in InputIsoFile.cpp 2023-04-21 10:53:48 +01:00
Berylskidandrefractionpcsx2 cb91478590 UI: Correct Recommended Value for Show Indicators 2023-04-20 10:18:25 +01:00
Mrlinkwiiandrefractionpcsx2 5dec7fe27d GameDB : fixes for Hanjuku Hero 4 2023-04-19 23:30:31 +01:00
BerylskidandGitHub 575d487c65 GameDB: Fix Armored Core water and game names (#8702) 2023-04-19 19:14:37 +01:00
Stenzekandrefractionpcsx2 2b9289f402 GS/HW: Fix missing init of non-CLUT replacement textures 2023-04-19 13:38:20 +01:00
Mrlinkwiiandrefractionpcsx2 9273683d3c GameDB: fixes for Dark cloud 2023-04-17 21:06:48 +01:00
PCSX2 Botandlightningterror 88f8465e7e PAD: Update to latest controller database. 2023-04-17 20:02:50 +02:00
refractionpcsx2 f26031cada Vif: Fix up Instant DMA behaviour 2023-04-16 21:15:26 +01:00
JordanTheToasterandlightningterror 3352d71515 GameDB: Various fixes
Fixes for a potential slowdown in Super Robot Taisen and replace disabling Instant VU with InstantDMA in MGS 3 Subsistence.
2023-04-16 21:10:31 +02:00
Connor McLaughlinandrefractionpcsx2 999f9532ee GS: Warning fix 2023-04-16 06:43:22 +01:00
Stenzekandrefractionpcsx2 d1f62ca9bf GS: Remove GSVertexList
It doesn't appear to be used anywhere.
2023-04-16 06:35:05 +01:00
Stenzekandrefractionpcsx2 ef9f0cf635 GS: Replace magic alignment number with constant
And ensure it gets used in None preloading.
2023-04-16 06:35:05 +01:00
Stenzekandrefractionpcsx2 d745564451 GS/HW: Adjust Black/Burnout sky CRC hack
Apparently some stages use a sky height of 128 instead of 256?
2023-04-15 22:01:38 +01:00
Stenzekandrefractionpcsx2 d37ac992fc GS/HW: Use bitfield extract for VS expand (GL/VK) 2023-04-15 20:10:25 +01:00
Stenzekandrefractionpcsx2 e7fc3de90c GS/HW: Ensure region texture mipmaps don't go out of bounds 2023-04-15 20:10:15 +01:00
Stenzekandrefractionpcsx2 bde81380c3 GS/SW: Stop C rasterizer crashing with AA1 2023-04-15 20:10:03 +01:00
Stenzekandrefractionpcsx2 839b482cb5 GS: Use 16-bit indices instead of 32-bit
Save some bandwidth.
2023-04-15 20:10:03 +01:00
Stenzekandrefractionpcsx2 72f70d4789 GS/Vulkan: Clear textures/RTs at end of frame
Prevents us from updating potentially changed-state descriptors.
2023-04-15 20:09:48 +01:00
Stenzekandrefractionpcsx2 d646bbf5c1 GS/Vulkan: Prioritize VK_EXT_rasterization_order_attachment_access
.. over the ARM version. But continue to support both.
2023-04-15 20:09:48 +01:00
Stenzekandrefractionpcsx2 e68aaf6540 GS/Vulkan: Saturate ImGui scissor to zero 2023-04-15 20:09:48 +01:00
Stenzekandrefractionpcsx2 20ab5ed0fa GS/Vulkan: Don't assume opaque composite alpha is supported 2023-04-15 20:09:48 +01:00
Stenzekandrefractionpcsx2 8590e390a2 GS/Vulkan: Don't enable geometryShader 2023-04-15 20:09:48 +01:00
Stenzekandrefractionpcsx2 ab1d558420 GS/Vulkan: Make provoking vertex consistent across pipelines 2023-04-15 20:09:48 +01:00
Stenzekandrefractionpcsx2 53c9021455 GS/HW: Don't attempt to colour copy into depth target 2023-04-15 20:09:26 +01:00
129 changed files with 3448 additions and 3094 deletions
+2 -2
View File
@@ -39,8 +39,8 @@
'Debugger':
- 'pcsx2/DebugTools/*'
- 'pcsx2/DebugTools/**/*'
- 'pcsx2/gui/Debugger/*'
- 'pcsx2/gui/Debugger/**/*'
- 'pcsx2-qt/Debugger/*'
- 'pcsx2-qt/Debugger/**/*'
'IPC':
- 'pcsx2/IPC*'
- 'pcsx2/**/IPC*'
+2 -2
View File
@@ -32,11 +32,11 @@ on:
qt_binary_url:
required: false
type: string
default: https://github.com/PCSX2/pcsx2-windows-dependencies/releases/download/2022-11-20/qt-6.4.0-x64.7z
default: https://github.com/PCSX2/pcsx2-windows-dependencies/releases/download/2023-04-25/qt-6.5.0-x64.7z
qt_dir:
required: false
type: string
default: 3rdparty\qt\6.4.0\msvc2022_64
default: 3rdparty\qt\6.5.0\msvc2022_64
cheats_url:
required: false
type: string
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -537,6 +537,7 @@
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,
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,
@@ -663,6 +664,7 @@
03000000c61100001000000000000000,Tencent Xianyou Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,x:b3,y:b4,platform:Windows,
03000000790000002601000000000000,TGZ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b3,y:b0,platform:Windows,
03000000591c00002400000000000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
03000000591c00002600000000000000,THEGamepad,a:b2,b:b1,back:b6,leftx:a0,lefty:a1,start:b7,x:b3,y:b0,platform:Windows,
030000004f04000015b3000000000000,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:Windows,
030000004f04000023b3000000000000,Thrustmaster Dual Trigger PlayStation 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:a5,start:b9,x:b0,y:b3,platform:Windows,
030000004f0400000ed0000000000000,ThrustMaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
@@ -983,6 +985,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000457500002211000000010000,SZMY Power PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000790000001c18000003100000,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:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000591c00002400000021000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Mac OS X,
03000000591c00002600000021000000,THEGamepad,a:b2,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Mac OS X,
030000004f04000015b3000000000000,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:Mac OS X,
030000004f0400000ed0000000020000,ThrustMaster eSwap Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X,
@@ -1332,7 +1335,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
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,
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,a:b0,b:b1,guide:b11,misc1:b12,start:b6,leftshoulder:b3,rightshoulder:b4,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,-rightx:b9,+rightx:b10,-righty:b7,+righty:b8,lefttrigger:b2,righttrigger:b5,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,
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,
+4 -3
View File
@@ -128,11 +128,12 @@ ProcessedVertex load_vertex(uint index)
#endif
vec2 i_st = rvtx.ST;
vec4 i_c = vec4(uvec4(rvtx.RGBA & 0xFFu, (rvtx.RGBA >> 8) & 0xFFu, (rvtx.RGBA >> 16) & 0xFFu, rvtx.RGBA >> 24));
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(rvtx.XY & 0xFFFFu, rvtx.XY >> 16);
uvec2 i_p = uvec2(bitfieldExtract(rvtx.XY, 0, 16), bitfieldExtract(rvtx.XY, 16, 16));
uint i_z = rvtx.Z;
uvec2 i_uv = uvec2(rvtx.UV & 0xFFFFu, rvtx.UV >> 16);
uvec2 i_uv = uvec2(bitfieldExtract(rvtx.UV, 0, 16), bitfieldExtract(rvtx.UV, 16, 16));
vec4 i_f = unpackUnorm4x8(rvtx.FOG);
ProcessedVertex vtx;
+4 -3
View File
@@ -113,11 +113,12 @@ ProcessedVertex load_vertex(uint index)
RawVertex rvtx = vertex_buffer[gl_BaseVertexARB + index];
vec2 a_st = rvtx.ST;
uvec4 a_c = uvec4(rvtx.RGBA & 0xFFu, (rvtx.RGBA >> 8) & 0xFFu, (rvtx.RGBA >> 16) & 0xFFu, rvtx.RGBA >> 24);
uvec4 a_c = uvec4(bitfieldExtract(rvtx.RGBA, 0, 8), bitfieldExtract(rvtx.RGBA, 8, 8),
bitfieldExtract(rvtx.RGBA, 16, 8), bitfieldExtract(rvtx.RGBA, 24, 8));
float a_q = rvtx.Q;
uvec2 a_p = uvec2(rvtx.XY & 0xFFFFu, rvtx.XY >> 16);
uvec2 a_p = uvec2(bitfieldExtract(rvtx.XY, 0, 16), bitfieldExtract(rvtx.XY, 16, 16));
uint a_z = rvtx.Z;
uvec2 a_uv = uvec2(rvtx.UV & 0xFFFFu, rvtx.UV >> 16);
uvec2 a_uv = uvec2(bitfieldExtract(rvtx.UV, 0, 16), bitfieldExtract(rvtx.UV, 16, 16));
vec4 a_f = unpackUnorm4x8(rvtx.FOG);
ProcessedVertex vtx;
-113
View File
@@ -1,113 +0,0 @@
set(wx_sdl_c_code "
#include <wx/setup.h>
#if (wxUSE_LIBSDL == 0)
#error cmake_WX_SDL
#endif
int main()
{
return 0;
}
")
set(gcc7_mmx_code "
#include <stdio.h>
#include <stdint.h>
#include <xmmintrin.h>
#include <emmintrin.h>
class alignas(16) GSVector4i
{
public:
__m128i m;
explicit GSVector4i(__m128i m)
{
this->m = m;
}
static void storel(void* p, const GSVector4i& v)
{
_mm_storel_epi64((__m128i*)p, v.m);
}
static GSVector4i loadl(const void* p)
{
return GSVector4i(_mm_loadl_epi64((__m128i*)p));
}
bool eq(const GSVector4i& v) const
{
return _mm_movemask_epi8(_mm_cmpeq_epi32(m, v.m)) == 0xffff;
}
};
union GIFRegTRXPOS
{
unsigned long long u64;
void operator = (const GSVector4i& v) {GSVector4i::storel(this, v);}
bool operator != (const union GIFRegTRXPOS& r) const {return !((GSVector4i)r).eq(*this);}
operator GSVector4i() const {return GSVector4i::loadl(this);}
};
extern GIFRegTRXPOS TRXPOS;
GIFRegTRXPOS TRXPOS = {};
void GIFRegHandlerTRXPOS(const GIFRegTRXPOS& p)
{
if(p != TRXPOS)
{
printf(\"foo\");
}
TRXPOS = (GSVector4i)p;
}
int main()
{
GIFRegTRXPOS r = {};
GIFRegHandlerTRXPOS(r);
uint16_t fpu[16] = {0};
__asm__ __volatile__(\"fstenv %0\" : \"=m\"(fpu));
bool ok = fpu[4] == 0xFFFF;
if (!ok) {
printf(\"Wrong MMX state !\");
exit(1);
}
return 0;
}
")
function(GCC7_BUG)
# try_run doesn't work when cross-compiling is enabled. It is completely silly in our case
# as i386 binaries are 100% fine on x64.
set(OLD_CMAKE_CROSSCOMPILING ${CMAKE_CROSSCOMPILING})
set(CMAKE_CROSSCOMPILING 0)
set(IN "${CMAKE_BINARY_DIR}/gcc7_mmx.cpp")
file(WRITE "${IN}" "${gcc7_mmx_code}")
enable_language(CXX)
try_run(
run_result
compile_result_unused
"${CMAKE_BINARY_DIR}"
"${IN}"
CMAKE_FLAGS "-DCOMPILE_DEFINITIONS:STRING=-msse -msse2 -O2 -m32 -march=i686"
)
if (${run_result})
message(FATAL_ERROR "GCC 7.0/7.1 generates invalid code => https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80799\n"
"You can either backport the fix or swith to another version of GCC.")
endif()
set(CMAKE_CROSSCOMPILING ${OLD_CMAKE_CROSSCOMPILING})
endfunction()
+3 -28
View File
@@ -52,9 +52,6 @@ if(MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
set(USE_CLANG TRUE)
message(STATUS "Building with Clang/LLVM.")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
set(USE_ICC TRUE)
message(STATUS "Building with Intel's ICC.")
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(USE_GCC TRUE)
message(STATUS "Building with GNU GCC")
@@ -106,21 +103,13 @@ include(TargetArch)
target_architecture(PCSX2_TARGET_ARCHITECTURES)
if(${PCSX2_TARGET_ARCHITECTURES} MATCHES "x86_64")
message(STATUS "Compiling a ${PCSX2_TARGET_ARCHITECTURES} build on a ${CMAKE_HOST_SYSTEM_PROCESSOR} host.")
else()
message(FATAL_ERROR "Unsupported architecture: ${PCSX2_TARGET_ARCHITECTURES}")
endif()
if(${PCSX2_TARGET_ARCHITECTURES} MATCHES "x86_64")
# x86_64 requires -fPIC
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
if(NOT DEFINED ARCH_FLAG AND NOT MSVC)
if (DISABLE_ADVANCE_SIMD)
if (USE_ICC)
set(ARCH_FLAG "-msse2 -msse4.1")
else()
set(ARCH_FLAG "-msse -msse2 -msse4.1 -mfxsr")
endif()
set(ARCH_FLAG "-msse -msse2 -msse4.1 -mfxsr")
else()
#set(ARCH_FLAG "-march=native -fabi-version=6")
set(ARCH_FLAG "-march=native")
@@ -132,9 +121,6 @@ if(${PCSX2_TARGET_ARCHITECTURES} MATCHES "x86_64")
set(_ARCH_64 1)
set(_M_X86 1)
else()
# All but i386 requires -fPIC
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
message(FATAL_ERROR "Unsupported architecture: ${PCSX2_TARGET_ARCHITECTURES}")
endif()
string(REPLACE " " ";" ARCH_FLAG_LIST "${ARCH_FLAG}")
@@ -220,24 +206,13 @@ endif()
if (MSVC)
set(DEFAULT_WARNINGS)
else()
set(DEFAULT_WARNINGS -Wall -Wextra -Wno-attributes -Wno-unused-function -Wno-unused-parameter -Wno-missing-field-initializers -Wno-format -Wno-format-security)
if (NOT USE_ICC)
list(APPEND DEFAULT_WARNINGS -Wno-unused-value)
endif()
set(DEFAULT_WARNINGS -Wall -Wextra -Wno-attributes -Wno-unused-function -Wno-unused-parameter -Wno-missing-field-initializers -Wno-format -Wno-format-security -Wno-unused-value)
endif()
if (USE_GCC)
list(APPEND DEFAULT_WARNINGS -Wno-stringop-truncation -Wno-stringop-overflow -Wno-maybe-uninitialized )
endif()
# -Wstrict-aliasing=n: to fix one day aliasing issue. n=1/2/3
if (USE_ICC)
set(AGGRESSIVE_WARNING -Wstrict-aliasing)
elseif(NOT MSVC)
set(AGGRESSIVE_WARNING -Wstrict-aliasing -Wstrict-overflow=1)
endif()
if (USE_PGO_GENERATE OR USE_PGO_OPTIMIZE)
add_compile_options("-fprofile-dir=${CMAKE_SOURCE_DIR}/profile")
endif()
@@ -264,7 +239,7 @@ if(USE_CLANG AND TIMETRACE)
add_compile_options(-ftime-trace)
endif()
set(PCSX2_WARNINGS ${DEFAULT_WARNINGS} ${AGGRESSIVE_WARNING})
set(PCSX2_WARNINGS ${DEFAULT_WARNINGS})
if(DISABLE_BUILD_DATE)
message(STATUS "Disabling the inclusion of the binary compile date.")
+6 -16
View File
@@ -12,21 +12,12 @@ find_path(VTUNE_INCLUDE_DIRS NAMES jitprofiling.h PATHS
/opt/intel/vtune_amplifier_xe_2016/include
)
if(${PCSX2_TARGET_ARCHITECTURES} MATCHES "i386")
find_library(VTUNE_LIBRARIES NAMES libjitprofiling.a PATHS
/opt/intel/oneapi/vtune/latest/lib32
/opt/intel/vtune_amplifier_xe_2018/lib32
/opt/intel/vtune_amplifier_xe_2017/lib32
/opt/intel/vtune_amplifier_xe_2016/lib32
)
else()
find_library(VTUNE_LIBRARIES NAMES libjitprofiling.a PATHS
/opt/intel/oneapi/vtune/latest/lib64
/opt/intel/vtune_amplifier_xe_2018/lib64
/opt/intel/vtune_amplifier_xe_2017/lib64
/opt/intel/vtune_amplifier_xe_2016/lib64
)
endif()
find_library(VTUNE_LIBRARIES NAMES libjitprofiling.a PATHS
/opt/intel/oneapi/vtune/latest/lib64
/opt/intel/vtune_amplifier_xe_2018/lib64
/opt/intel/vtune_amplifier_xe_2017/lib64
/opt/intel/vtune_amplifier_xe_2016/lib64
)
# handle the QUIETLY and REQUIRED arguments and set VTUNE_FOUND to TRUE if
# all listed variables are TRUE
@@ -41,4 +32,3 @@ if(VTUNE_LIBRARIES AND NOT TARGET Vtune::Vtune)
endif()
mark_as_advanced(VTUNE_FOUND VTUNE_INCLUDE_DIRS VTUNE_LIBRARIES)
+2 -13
View File
@@ -102,18 +102,7 @@ if(ENABLE_TESTS)
endif()
endif()
#----------------------------------------
# Check correctness of the parameter
# Note: wxWidgets_INCLUDE_DIRS must be defined
#----------------------------------------
include(ApiValidation)
# Blacklist bad GCC
if(GCC_VERSION VERSION_EQUAL "7.0" OR GCC_VERSION VERSION_EQUAL "7.1")
GCC7_BUG()
endif()
if((GCC_VERSION VERSION_EQUAL "9.0" OR GCC_VERSION VERSION_GREATER "9.0") AND GCC_VERSION LESS "9.2")
if(GCC_VERSION VERSION_GREATER_EQUAL "9.0" AND GCC_VERSION VERSION_LESS "9.2")
message(WARNING "
It looks like you are compiling with 9.0.x or 9.1.x. Using these versions is not recommended,
as there is a bug known to cause the compiler to segfault while compiling. See patch
@@ -134,7 +123,7 @@ find_optional_system_library(libzip 3rdparty/libzip 1.8.0)
if(QT_BUILD)
# Default to bundled Qt6 for Windows.
if(WIN32 AND NOT DEFINED Qt6_DIR)
set(Qt6_DIR ${CMAKE_SOURCE_DIR}/3rdparty/qt/6.4.0/msvc2022_64/lib/cmake/Qt6)
set(Qt6_DIR ${CMAKE_SOURCE_DIR}/3rdparty/qt/6.5.0/msvc2022_64/lib/cmake/Qt6)
endif()
# Find the Qt components that we need.
+173 -159
View File
@@ -15,197 +15,211 @@
#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 = 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()
{
auto dynamic = std::remove_if(m_v.begin(), m_v.end(), [](Info i) { return i.m_dynamic; });
m_v.erase(dynamic, m_v.end());
}
////////////////////////////////////////////////////////////////////////////////
// Global function
////////////////////////////////////////////////////////////////////////////////
void dump()
{
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);
}
void dump_and_reset()
{
dump();
any.reset();
ee.reset();
iop.reset();
vu.reset();
}
#else
////////////////////////////////////////////////////////////////////////////////
// Dummy implementation
////////////////////////////////////////////////////////////////////////////////
InfoVector::InfoVector(const char* prefix)
: m_vtune_id(0)
static void RegisterMethod(const void* ptr, size_t size, const char* method)
{
}
void InfoVector::map(uptr x86, u32 size, const char* symbol) {}
void InfoVector::map(uptr x86, u32 size, u32 pc) {}
void InfoVector::reset() {}
#endif
void dump() {}
void dump_and_reset() {}
#if (defined(__linux__) && (defined(ProfileWithPerf) || defined(ProfileWithPerfJitDump))) || defined(ENABLE_VTUNE)
void Group::Register(const void* ptr, size_t size, const char* symbol)
{
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);
}
void Group::RegisterPC(const void* ptr, size_t size, u32 pc)
{
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 Group::RegisterKey(const void* ptr, size_t size, const char* prefix, u64 key)
{
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
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
View File
@@ -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
+52 -155
View File
@@ -26,6 +26,8 @@
#include <array>
#include <cstring>
#include "fmt/format.h"
#ifdef _WIN32
#include "common/RedtapeWindows.h"
#else
@@ -70,8 +72,7 @@ namespace Vulkan
Context::~Context() = default;
VkInstance Context::CreateVulkanInstance(
const WindowInfo* wi, bool enable_debug_utils, bool enable_validation_layer)
VkInstance Context::CreateVulkanInstance(const WindowInfo& wi, bool enable_debug_utils, bool enable_validation_layer)
{
ExtensionList enabled_extensions;
if (!SelectInstanceExtensions(&enabled_extensions, wi, enable_debug_utils))
@@ -118,7 +119,7 @@ namespace Vulkan
return instance;
}
bool Context::SelectInstanceExtensions(ExtensionList* extension_list, const WindowInfo* wi, bool enable_debug_utils)
bool Context::SelectInstanceExtensions(ExtensionList* extension_list, const WindowInfo& wi, bool enable_debug_utils)
{
u32 extension_count = 0;
VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr);
@@ -155,34 +156,32 @@ namespace Vulkan
};
// Common extensions
if (wi && wi->type != WindowInfo::Type::Surfaceless && !SupportsExtension(VK_KHR_SURFACE_EXTENSION_NAME, true))
if (wi.type != WindowInfo::Type::Surfaceless && !SupportsExtension(VK_KHR_SURFACE_EXTENSION_NAME, true))
return false;
#if defined(VK_USE_PLATFORM_WIN32_KHR)
if (wi && wi->type == WindowInfo::Type::Win32 && !SupportsExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, true))
if (wi.type == WindowInfo::Type::Win32 && !SupportsExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, true))
return false;
#endif
#if defined(VK_USE_PLATFORM_XLIB_KHR)
if (wi && wi->type == WindowInfo::Type::X11 && !SupportsExtension(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, true))
if (wi.type == WindowInfo::Type::X11 && !SupportsExtension(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, true))
return false;
#endif
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
if (wi && wi->type == WindowInfo::Type::Wayland &&
!SupportsExtension(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, true))
if (wi.type == WindowInfo::Type::Wayland && !SupportsExtension(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, true))
return false;
#endif
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
if (wi && wi->type == WindowInfo::Type::Android &&
!SupportsExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, true))
if (wi.type == WindowInfo::Type::Android && !SupportsExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, true))
return false;
#endif
#if defined(VK_USE_PLATFORM_METAL_EXT)
if (wi && wi->type == WindowInfo::Type::MacOS && !SupportsExtension(VK_EXT_METAL_SURFACE_EXTENSION_NAME, true))
if (wi.type == WindowInfo::Type::MacOS && !SupportsExtension(VK_EXT_METAL_SURFACE_EXTENSION_NAME, true))
return false;
#endif
#if 0
if (wi && wi->type == WindowInfo::Type::Display && !SupportsExtension(VK_KHR_DISPLAY_EXTENSION_NAME, true))
if (wi.type == WindowInfo::Type::Display && !SupportsExtension(VK_KHR_DISPLAY_EXTENSION_NAME, true))
return false;
#endif
@@ -195,170 +194,67 @@ namespace Vulkan
Context::GPUList Context::EnumerateGPUs(VkInstance instance)
{
GPUList gpus;
u32 gpu_count = 0;
VkResult res = vkEnumeratePhysicalDevices(instance, &gpu_count, nullptr);
if ((res != VK_SUCCESS && res != VK_INCOMPLETE) || gpu_count == 0)
{
LOG_VULKAN_ERROR(res, "vkEnumeratePhysicalDevices (1) failed: ");
return {};
return gpus;
}
GPUList gpus;
gpus.resize(gpu_count);
res = vkEnumeratePhysicalDevices(instance, &gpu_count, gpus.data());
std::vector<VkPhysicalDevice> physical_devices(gpu_count);
res = vkEnumeratePhysicalDevices(instance, &gpu_count, physical_devices.data());
if (res == VK_INCOMPLETE)
{
Console.Warning("First vkEnumeratePhysicalDevices() call returned %zu devices, but second returned %u", gpus.size(), gpu_count);
Console.Warning("First vkEnumeratePhysicalDevices() call returned %zu devices, but second returned %u",
physical_devices.size(), gpu_count);
}
else if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkEnumeratePhysicalDevices (2) failed: ");
return {};
return gpus;
}
// Maybe we lost a GPU?
if (gpu_count < gpus.size())
gpus.resize(gpu_count);
if (gpu_count < physical_devices.size())
physical_devices.resize(gpu_count);
return gpus;
}
Context::GPUNameList Context::EnumerateGPUNames(VkInstance instance)
{
u32 gpu_count = 0;
VkResult res = vkEnumeratePhysicalDevices(instance, &gpu_count, nullptr);
if (res != VK_SUCCESS || gpu_count == 0)
{
LOG_VULKAN_ERROR(res, "vkEnumeratePhysicalDevices failed: ");
return {};
}
GPUList gpus;
gpus.resize(gpu_count);
res = vkEnumeratePhysicalDevices(instance, &gpu_count, gpus.data());
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkEnumeratePhysicalDevices failed: ");
return {};
}
GPUNameList gpu_names;
gpu_names.reserve(gpu_count);
for (u32 i = 0; i < gpu_count; i++)
gpus.reserve(physical_devices.size());
for (VkPhysicalDevice device : physical_devices)
{
VkPhysicalDeviceProperties props = {};
vkGetPhysicalDeviceProperties(gpus[i], &props);
vkGetPhysicalDeviceProperties(device, &props);
std::string gpu_name(props.deviceName);
std::string gpu_name = props.deviceName;
// handle duplicate adapter names
if (std::any_of(gpu_names.begin(), gpu_names.end(),
[&gpu_name](const std::string& other) { return (gpu_name == other); }))
if (std::any_of(gpus.begin(), gpus.end(),
[&gpu_name](const auto& other) { return (gpu_name == other.second); }))
{
std::string original_adapter_name = std::move(gpu_name);
u32 current_extra = 2;
do
{
gpu_name = StringUtil::StdStringFromFormat("%s (%u)", original_adapter_name.c_str(), current_extra);
gpu_name = fmt::format("{} ({})", original_adapter_name, current_extra);
current_extra++;
} while (std::any_of(gpu_names.begin(), gpu_names.end(),
[&gpu_name](const std::string& other) { return (gpu_name == other); }));
} while (std::any_of(gpus.begin(), gpus.end(),
[&gpu_name](const auto& other) { return (gpu_name == other.second); }));
}
gpu_names.push_back(std::move(gpu_name));
gpus.emplace_back(device, std::move(gpu_name));
}
return gpu_names;
return gpus;
}
bool Context::Create(std::string_view gpu_name, const WindowInfo* wi, std::unique_ptr<SwapChain>* out_swap_chain,
VkPresentModeKHR preferred_present_mode, bool threaded_presentation, bool enable_debug_utils,
bool enable_validation_layer)
bool Context::Create(VkInstance instance, VkSurfaceKHR surface, VkPhysicalDevice physical_device,
bool threaded_presentation, bool enable_debug_utils, bool enable_validation_layer)
{
pxAssertMsg(!g_vulkan_context, "Has no current context");
if (!Vulkan::LoadVulkanLibrary())
{
Console.Error("Failed to load Vulkan library");
return false;
}
const bool enable_surface = (wi && wi->type != WindowInfo::Type::Surfaceless);
VkInstance instance = CreateVulkanInstance(wi, enable_debug_utils, enable_validation_layer);
if (instance == VK_NULL_HANDLE)
{
if (enable_debug_utils || enable_validation_layer)
{
// Try again without the validation layer.
enable_debug_utils = false;
enable_validation_layer = false;
instance = CreateVulkanInstance(wi, enable_debug_utils, enable_validation_layer);
if (instance == VK_NULL_HANDLE)
{
Vulkan::UnloadVulkanLibrary();
return false;
}
Console.Error("Vulkan validation/debug layers requested but are unavailable. Creating non-debug device.");
}
}
if (!Vulkan::LoadVulkanInstanceFunctions(instance))
{
Console.Error("Failed to load Vulkan instance functions");
vkDestroyInstance(instance, nullptr);
Vulkan::UnloadVulkanLibrary();
return false;
}
GPUList gpus = EnumerateGPUs(instance);
if (gpus.empty())
{
vkDestroyInstance(instance, nullptr);
Vulkan::UnloadVulkanLibrary();
return false;
}
u32 gpu_index = 0;
GPUNameList gpu_names = EnumerateGPUNames(instance);
if (!gpu_name.empty())
{
for (; gpu_index < static_cast<u32>(gpu_names.size()); gpu_index++)
{
Console.WriteLn("GPU %u: %s", static_cast<u32>(gpu_index), gpu_names[gpu_index].c_str());
if (gpu_names[gpu_index] == gpu_name)
break;
}
if (gpu_index == static_cast<u32>(gpu_names.size()))
{
Console.Warning("Requested GPU '%s' not found, using first (%s)", std::string(gpu_name).c_str(),
gpu_names[0].c_str());
gpu_index = 0;
}
}
else
{
Console.WriteLn("No GPU requested, using first (%s)", gpu_names[0].c_str());
}
VkSurfaceKHR surface = VK_NULL_HANDLE;
WindowInfo wi_copy;
if (wi)
wi_copy = *wi;
if (enable_surface &&
(surface = SwapChain::CreateVulkanSurface(instance, gpus[gpu_index], &wi_copy)) == VK_NULL_HANDLE)
{
vkDestroyInstance(instance, nullptr);
Vulkan::UnloadVulkanLibrary();
return false;
}
g_vulkan_context.reset(new Context(instance, gpus[gpu_index]));
g_vulkan_context.reset(new Context(instance, physical_device));
if (enable_debug_utils)
g_vulkan_context->EnableDebugUtils();
@@ -367,8 +263,7 @@ namespace Vulkan
if (!g_vulkan_context->CreateDevice(surface, enable_validation_layer, nullptr, 0, nullptr, 0, nullptr) ||
!g_vulkan_context->CreateAllocator() || !g_vulkan_context->CreateGlobalDescriptorPool() ||
!g_vulkan_context->CreateCommandBuffers() || !g_vulkan_context->CreateTextureStreamBuffer() ||
!g_vulkan_context->InitSpinResources() ||
(enable_surface && (*out_swap_chain = SwapChain::Create(wi_copy, surface, preferred_present_mode)) == nullptr))
!g_vulkan_context->InitSpinResources())
{
// Since we are destroying the instance, we're also responsible for destroying the surface.
if (surface != VK_NULL_HANDLE)
@@ -468,10 +363,11 @@ namespace Vulkan
SupportsExtension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME, false);
m_optional_extensions.vk_ext_line_rasterization =
SupportsExtension(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false);
m_optional_extensions.vk_ext_rasterization_order_attachment_access =
SupportsExtension(VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME, false) ||
SupportsExtension(VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME, false);
m_optional_extensions.vk_khr_driver_properties =
SupportsExtension(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME, false);
m_optional_extensions.vk_arm_rasterization_order_attachment_access =
SupportsExtension(VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME, false);
m_optional_extensions.vk_khr_fragment_shader_barycentric =
SupportsExtension(VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME, false);
m_optional_extensions.vk_khr_shader_draw_parameters =
@@ -490,7 +386,6 @@ namespace Vulkan
// Enable the features we use.
m_device_features.dualSrcBlend = available_features.dualSrcBlend;
m_device_features.geometryShader = available_features.geometryShader;
m_device_features.largePoints = available_features.largePoints;
m_device_features.wideLines = available_features.wideLines;
m_device_features.fragmentStoresAndAtomics = available_features.fragmentStoresAndAtomics;
@@ -664,8 +559,8 @@ namespace Vulkan
// provoking vertex
VkPhysicalDeviceProvokingVertexFeaturesEXT provoking_vertex_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT};
VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesARM rasterization_order_access_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_ARM};
VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT rasterization_order_access_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT};
VkPhysicalDeviceLineRasterizationFeaturesEXT line_rasterization_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT};
@@ -679,7 +574,7 @@ namespace Vulkan
line_rasterization_feature.bresenhamLines = VK_TRUE;
Util::AddPointerToChain(&device_info, &line_rasterization_feature);
}
if (m_optional_extensions.vk_arm_rasterization_order_attachment_access)
if (m_optional_extensions.vk_ext_rasterization_order_attachment_access)
{
rasterization_order_access_feature.rasterizationOrderColorAttachmentAccess = VK_TRUE;
Util::AddPointerToChain(&device_info, &rasterization_order_access_feature);
@@ -745,15 +640,15 @@ namespace Vulkan
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT};
VkPhysicalDeviceLineRasterizationFeaturesEXT line_rasterization_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT};
VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesARM rasterization_order_access_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_ARM};
VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT rasterization_order_access_feature = {
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT};
// add in optional feature structs
if (m_optional_extensions.vk_ext_provoking_vertex)
Util::AddPointerToChain(&features2, &provoking_vertex_features);
if (m_optional_extensions.vk_ext_line_rasterization)
Util::AddPointerToChain(&features2, &line_rasterization_feature);
if (m_optional_extensions.vk_arm_rasterization_order_attachment_access)
if (m_optional_extensions.vk_ext_rasterization_order_attachment_access)
Util::AddPointerToChain(&features2, &rasterization_order_access_feature);
// query
@@ -761,7 +656,7 @@ namespace Vulkan
// confirm we actually support it
m_optional_extensions.vk_ext_provoking_vertex &= (provoking_vertex_features.provokingVertexLast == VK_TRUE);
m_optional_extensions.vk_arm_rasterization_order_attachment_access &= (rasterization_order_access_feature.rasterizationOrderColorAttachmentAccess == VK_TRUE);
m_optional_extensions.vk_ext_rasterization_order_attachment_access &= (rasterization_order_access_feature.rasterizationOrderColorAttachmentAccess == VK_TRUE);
m_optional_extensions.vk_ext_line_rasterization &= (line_rasterization_feature.bresenhamLines == VK_TRUE);
VkPhysicalDeviceProperties2 properties2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2};
@@ -817,8 +712,8 @@ namespace Vulkan
m_optional_extensions.vk_ext_line_rasterization ? "supported" : "NOT supported");
Console.WriteLn("VK_EXT_calibrated_timestamps is %s",
m_optional_extensions.vk_ext_calibrated_timestamps ? "supported" : "NOT supported");
Console.WriteLn("VK_ARM_rasterization_order_attachment_access is %s",
m_optional_extensions.vk_arm_rasterization_order_attachment_access ? "supported" : "NOT supported");
Console.WriteLn("VK_EXT_rasterization_order_attachment_access is %s",
m_optional_extensions.vk_ext_rasterization_order_attachment_access ? "supported" : "NOT supported");
}
bool Context::CreateAllocator()
@@ -1666,7 +1561,7 @@ namespace Vulkan
input_reference.layout = VK_IMAGE_LAYOUT_GENERAL;
input_reference_ptr = &input_reference;
if (!g_vulkan_context->GetOptionalExtensions().vk_arm_rasterization_order_attachment_access)
if (!g_vulkan_context->GetOptionalExtensions().vk_ext_rasterization_order_attachment_access)
{
// don't need the framebuffer-local dependency when we have rasterization order attachment access
subpass_dependency.srcSubpass = 0;
@@ -1699,7 +1594,9 @@ namespace Vulkan
}
const VkSubpassDescriptionFlags subpass_flags =
(key.color_feedback_loop && g_vulkan_context->GetOptionalExtensions().vk_arm_rasterization_order_attachment_access) ? VK_SUBPASS_DESCRIPTION_RASTERIZATION_ORDER_ATTACHMENT_COLOR_ACCESS_BIT_ARM : 0;
(key.color_feedback_loop && g_vulkan_context->GetOptionalExtensions().vk_ext_rasterization_order_attachment_access) ?
VK_SUBPASS_DESCRIPTION_RASTERIZATION_ORDER_ATTACHMENT_COLOR_ACCESS_BIT_EXT :
0;
const VkSubpassDescription subpass = {subpass_flags, VK_PIPELINE_BIND_POINT_GRAPHICS, input_reference_ptr ? 1u : 0u,
input_reference_ptr ? input_reference_ptr : nullptr, color_reference_ptr ? 1u : 0u,
color_reference_ptr ? color_reference_ptr : nullptr, nullptr, depth_reference_ptr, 0, nullptr};
+6 -9
View File
@@ -53,8 +53,8 @@ namespace Vulkan
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_arm_rasterization_order_attachment_access : 1;
bool vk_khr_fragment_shader_barycentric : 1;
bool vk_khr_shader_draw_parameters : 1;
};
@@ -63,18 +63,15 @@ namespace Vulkan
// Helper method to create a Vulkan instance.
static VkInstance CreateVulkanInstance(
const WindowInfo* wi, bool enable_debug_utils, bool enable_validation_layer);
const WindowInfo& wi, bool enable_debug_utils, bool enable_validation_layer);
// Returns a list of Vulkan-compatible GPUs.
using GPUList = std::vector<VkPhysicalDevice>;
using GPUNameList = std::vector<std::string>;
using GPUList = std::vector<std::pair<VkPhysicalDevice, std::string>>;
static GPUList EnumerateGPUs(VkInstance instance);
static GPUNameList EnumerateGPUNames(VkInstance instance);
// Creates a new context and sets it up as global.
static bool Create(std::string_view gpu_name, const WindowInfo* wi, std::unique_ptr<SwapChain>* out_swap_chain,
VkPresentModeKHR preferred_present_mode, bool threaded_presentation, bool enable_debug_utils,
bool enable_validation_layer);
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();
@@ -267,7 +264,7 @@ namespace Vulkan
using ExtensionList = std::vector<const char*>;
static bool SelectInstanceExtensions(
ExtensionList* extension_list, const WindowInfo* wi, bool enable_debug_utils);
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,
+11 -5
View File
@@ -545,9 +545,7 @@ namespace Vulkan
// Determine the dimensions of the swap chain. Values of -1 indicate the size we specify here
// determines window size?
VkExtent2D size = surface_capabilities.currentExtent;
#ifndef ANDROID
if (size.width == UINT32_MAX)
#endif
{
size.width = m_window_info.surface_width;
size.height = m_window_info.surface_height;
@@ -562,9 +560,17 @@ namespace Vulkan
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;
if (!(surface_capabilities.supportedUsageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT))
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;
@@ -577,7 +583,7 @@ namespace Vulkan
// 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, VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, m_present_mode,
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(),
+3 -3
View File
@@ -2,11 +2,11 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="UserMacros">
<BinaryOutputDir>$(SolutionDir)bin\</BinaryOutputDir>
<PCSX2QTDIRDefault Condition="'$(Platform)'=='x64'">$(SolutionDir)3rdparty\qt\6.4.0\msvc2022_64\</PCSX2QTDIRDefault>
<PCSX2QTDIRDefault Condition="'$(Platform)'=='ARM64'">$(SolutionDir)3rdparty\qt\6.4.0\msvc2022_arm64\</PCSX2QTDIRDefault>
<PCSX2QTDIRDefault Condition="'$(Platform)'=='x64'">$(SolutionDir)3rdparty\qt\6.5.0\msvc2022_64\</PCSX2QTDIRDefault>
<PCSX2QTDIRDefault Condition="'$(Platform)'=='ARM64'">$(SolutionDir)3rdparty\qt\6.5.0\msvc2022_arm64\</PCSX2QTDIRDefault>
<PCSX2QTDIR Condition="Exists('$(PCSX2QTDIRDefault)') And ('$(PCSX2QTDIR)'=='' Or !Exists('$(PCSX2QTDIR)'))">$(PCSX2QTDIRDefault)</PCSX2QTDIR>
<PCSX2QTDIR Condition="Exists('$(PCSX2QTDIR)') And !HasTrailingSlash('$(PCSX2QTDIR)')">$(PCSX2QTDIR)\</PCSX2QTDIR>
<PCSX2QTDIRHost>$(SolutionDir)3rdparty\qt\6.4.0\msvc2022_64\</PCSX2QTDIRHost>
<PCSX2QTDIRHost>$(SolutionDir)3rdparty\qt\6.5.0\msvc2022_64\</PCSX2QTDIRHost>
<QtDirValid>false</QtDirValid>
<QtDirValid Condition="Exists('$(PCSX2QTDIR)')">true</QtDirValid>
<QtIncludeDir>$(PCSX2QTDIR)include\</QtIncludeDir>
+1 -7
View File
@@ -254,17 +254,11 @@ void Host::SetRelativeMouseMode(bool enabled)
{
}
std::optional<WindowInfo> Host::AcquireRenderWindow()
std::optional<WindowInfo> Host::AcquireRenderWindow(bool recreate_window)
{
return GSRunner::GetPlatformWindowInfo();
}
std::optional<WindowInfo> Host::UpdateRenderWindow(bool recreate_window)
{
// We shouldn't ever recreate with the runner, so this is okay..
return GSRunner::GetPlatformWindowInfo();
}
void Host::ReleaseRenderWindow()
{
}
+2 -1
View File
@@ -24,6 +24,8 @@ target_sources(pcsx2-qt PRIVATE
MainWindow.ui
PrecompiledHeader.cpp
PrecompiledHeader.h
SettingWidgetBinder.h
Themes.cpp
QtHost.cpp
QtHost.h
QtKeyCodes.cpp
@@ -31,7 +33,6 @@ target_sources(pcsx2-qt PRIVATE
QtProgressCallback.h
QtUtils.cpp
QtUtils.h
SettingWidgetBinder.h
GameList/EmptyGameListWidget.ui
GameList/GameListModel.cpp
GameList/GameListModel.h
+1 -1
View File
@@ -181,7 +181,7 @@ void GameListWidget::initialize()
m_list_view = new GameListGridListView(m_ui.stack);
m_list_view->setModel(m_sort_model);
m_list_view->setModelColumn(GameListModel::Column_Cover);
m_list_view->setSelectionMode(QAbstractItemView::ExtendedSelection);
m_list_view->setSelectionMode(QAbstractItemView::SingleSelection);
m_list_view->setViewMode(QListView::IconMode);
m_list_view->setResizeMode(QListView::Adjust);
m_list_view->setUniformItemSizes(true);
+50 -519
View File
@@ -66,7 +66,6 @@
#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);;"
"Single-Track Raw Images (*.bin *.iso);;"
@@ -87,16 +86,7 @@ const char* MainWindow::DISC_IMAGE_FILTER = QT_TRANSLATE_NOOP("MainWindow", "All
"GZ Images (*.gz);;"
"Block Dumps (*.dump)");
#ifdef __APPLE__
const char* MainWindow::DEFAULT_THEME_NAME = "";
#else
const char* MainWindow::DEFAULT_THEME_NAME = "darkfusion";
#endif
MainWindow* g_main_window = nullptr;
static QString s_unthemed_style_name;
static QPalette s_unthemed_palette;
static bool s_unthemed_style_name_set;
#if defined(_WIN32) || defined(__APPLE__)
static const bool s_use_central_widget = false;
@@ -415,10 +405,9 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread)
connect(m_ui.actionStartFullscreenUI, &QAction::triggered, thread, &EmuThread::startFullscreenUI);
connect(m_ui.actionStartFullscreenUI2, &QAction::triggered, thread, &EmuThread::startFullscreenUI);
connect(thread, &EmuThread::messageConfirmed, this, &MainWindow::confirmMessage, Qt::BlockingQueuedConnection);
connect(thread, &EmuThread::onCreateDisplayRequested, this, &MainWindow::createDisplayWindow, Qt::BlockingQueuedConnection);
connect(thread, &EmuThread::onUpdateDisplayRequested, this, &MainWindow::updateDisplayWindow, Qt::BlockingQueuedConnection);
connect(thread, &EmuThread::onDestroyDisplayRequested, this, &MainWindow::destroyDisplay, Qt::BlockingQueuedConnection);
connect(thread, &EmuThread::onResizeDisplayRequested, this, &MainWindow::displayResizeRequested);
connect(thread, &EmuThread::onAcquireRenderWindowRequested, this, &MainWindow::acquireRenderWindow, Qt::BlockingQueuedConnection);
connect(thread, &EmuThread::onReleaseRenderWindowRequested, this, &MainWindow::releaseRenderWindow, Qt::BlockingQueuedConnection);
connect(thread, &EmuThread::onResizeRenderWindowRequested, this, &MainWindow::displayResizeRequested);
connect(thread, &EmuThread::onRelativeMouseModeRequested, this, &MainWindow::relativeMouseModeRequested);
connect(thread, &EmuThread::onVMStarting, this, &MainWindow::onVMStarting);
connect(thread, &EmuThread::onVMStarted, this, &MainWindow::onVMStarted);
@@ -503,453 +492,7 @@ void MainWindow::resetSettings(bool ui)
g_main_window->recreateSettings();
}
void MainWindow::updateApplicationTheme()
{
if (!s_unthemed_style_name_set)
{
s_unthemed_style_name_set = true;
s_unthemed_style_name = QApplication::style()->objectName();
s_unthemed_palette = QApplication::style()->standardPalette();
}
setStyleFromSettings();
setIconThemeFromStyle();
}
void MainWindow::setStyleFromSettings()
{
const std::string theme(Host::GetBaseStringSettingValue("UI", "Theme", DEFAULT_THEME_NAME));
if (theme == "fusion")
{
qApp->setPalette(s_unthemed_palette);
qApp->setStyleSheet(QString());
qApp->setStyle(QStyleFactory::create("Fusion"));
}
else if (theme == "darkfusion")
{
// adapted from https://gist.github.com/QuantumCD/6245215
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor lighterGray(75, 75, 75);
const QColor darkGray(53, 53, 53);
const QColor gray(128, 128, 128);
const QColor black(25, 25, 25);
const QColor blue(198, 238, 255);
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, darkGray);
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, black);
darkPalette.setColor(QPalette::AlternateBase, darkGray);
darkPalette.setColor(QPalette::ToolTipBase, darkGray);
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Button, darkGray);
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Link, blue);
darkPalette.setColor(QPalette::Highlight, lighterGray);
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::PlaceholderText, QColor(Qt::white).darker());
darkPalette.setColor(QPalette::Active, QPalette::Button, darkGray);
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Light, darkGray);
qApp->setPalette(darkPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "darkfusionblue")
{
// adapted from https://gist.github.com/QuantumCD/6245215
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor darkGray(53, 53, 53);
const QColor gray(128, 128, 128);
const QColor black(25, 25, 25);
const QColor blue(198, 238, 255);
const QColor blue2(0, 88, 208);
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, darkGray);
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, black);
darkPalette.setColor(QPalette::AlternateBase, darkGray);
darkPalette.setColor(QPalette::ToolTipBase, blue2);
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Button, darkGray);
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Link, blue);
darkPalette.setColor(QPalette::Highlight, blue2);
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::PlaceholderText, QColor(Qt::white).darker());
darkPalette.setColor(QPalette::Active, QPalette::Button, darkGray);
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Light, darkGray);
qApp->setPalette(darkPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "UntouchedLagoon")
{
// Custom palette by RedDevilus, Tame (Light/Washed out) Green as main color and Grayish Blue as complimentary.
// Alternative white theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor black(25, 25, 25);
const QColor darkteal(0, 77, 77);
const QColor teal(0, 128, 128);
const QColor tameTeal(160, 190, 185);
const QColor grayBlue(160, 180, 190);
QPalette standardPalette;
standardPalette.setColor(QPalette::Window, tameTeal);
standardPalette.setColor(QPalette::WindowText, black.lighter());
standardPalette.setColor(QPalette::Base, grayBlue);
standardPalette.setColor(QPalette::AlternateBase, tameTeal);
standardPalette.setColor(QPalette::ToolTipBase, tameTeal);
standardPalette.setColor(QPalette::ToolTipText, grayBlue);
standardPalette.setColor(QPalette::Text, black);
standardPalette.setColor(QPalette::Button, tameTeal);
standardPalette.setColor(QPalette::ButtonText, black);
standardPalette.setColor(QPalette::Link, black.lighter());
standardPalette.setColor(QPalette::Highlight, teal);
standardPalette.setColor(QPalette::HighlightedText, grayBlue.lighter());
standardPalette.setColor(QPalette::Active, QPalette::Button, tameTeal);
standardPalette.setColor(QPalette::Disabled, QPalette::ButtonText, darkteal);
standardPalette.setColor(QPalette::Disabled, QPalette::WindowText, darkteal.lighter());
standardPalette.setColor(QPalette::Disabled, QPalette::Text, darkteal.lighter());
standardPalette.setColor(QPalette::Disabled, QPalette::Light, tameTeal);
qApp->setPalette(standardPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "BabyPastel")
{
// Custom palette by RedDevilus, Blue as main color and blue as complimentary.
// Alternative light theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor gray(150, 150, 150);
const QColor black(25, 25, 25);
const QColor redpinkish(200, 75, 132);
const QColor pink(255, 174, 201);
const QColor brightPink(255, 230, 255);
const QColor congoPink(255, 127, 121);
const QColor blue(221, 225, 239);
QPalette standardPalette;
standardPalette.setColor(QPalette::Window, pink);
standardPalette.setColor(QPalette::WindowText, black);
standardPalette.setColor(QPalette::Base, brightPink);
standardPalette.setColor(QPalette::AlternateBase, blue);
standardPalette.setColor(QPalette::ToolTipBase, pink);
standardPalette.setColor(QPalette::ToolTipText, brightPink);
standardPalette.setColor(QPalette::Text, black);
standardPalette.setColor(QPalette::Button, pink);
standardPalette.setColor(QPalette::ButtonText, black);
standardPalette.setColor(QPalette::Link, black);
standardPalette.setColor(QPalette::Highlight, congoPink);
standardPalette.setColor(QPalette::HighlightedText, black);
standardPalette.setColor(QPalette::Active, QPalette::Button, pink);
standardPalette.setColor(QPalette::Disabled, QPalette::ButtonText, redpinkish);
standardPalette.setColor(QPalette::Disabled, QPalette::WindowText, redpinkish);
standardPalette.setColor(QPalette::Disabled, QPalette::Text, redpinkish);
standardPalette.setColor(QPalette::Disabled, QPalette::Light, gray);
qApp->setPalette(standardPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "PizzaBrown")
{
// Custom palette by KamFretoZ, a Pizza Tower Reference!
// With a mixtures of Light Brown, Peachy/Creamy White, Latte-like Color.
// Thanks to Jordan for the idea :P
// Alternative light theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor gray(128, 128, 128);
const QColor extr(248, 192, 88);
const QColor main(233, 187, 147);
const QColor comp(248, 230, 213);
const QColor highlight(188, 100, 60);
QPalette standardPalette;
standardPalette.setColor(QPalette::Window, main);
standardPalette.setColor(QPalette::WindowText, Qt::black);
standardPalette.setColor(QPalette::Base, comp);
standardPalette.setColor(QPalette::AlternateBase, extr);
standardPalette.setColor(QPalette::ToolTipBase, comp);
standardPalette.setColor(QPalette::ToolTipText, Qt::black);
standardPalette.setColor(QPalette::Text, Qt::black);
standardPalette.setColor(QPalette::Button, extr);
standardPalette.setColor(QPalette::ButtonText, Qt::black);
standardPalette.setColor(QPalette::Link, Qt::black);
standardPalette.setColor(QPalette::Highlight, highlight);
standardPalette.setColor(QPalette::HighlightedText, Qt::white);
standardPalette.setColor(QPalette::Active, QPalette::Button, extr);
standardPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray.darker());
standardPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray.darker());
standardPalette.setColor(QPalette::Disabled, QPalette::Text, Qt::gray);
standardPalette.setColor(QPalette::Disabled, QPalette::Light, gray.lighter());
qApp->setPalette(standardPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #cc3f18; border: 1px solid white; }");
}
else if (theme == "PCSX2Blue")
{
// Custom palette by RedDevilus, White as main color and Blue as complimentary.
// Alternative light theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor blackish(35, 35, 35);
const QColor darkBlue(73, 97, 177);
const QColor blue2(80, 120, 200);
const QColor blue(106, 156, 255);
const QColor lightBlue(130, 155, 241);
QPalette standardPalette;
standardPalette.setColor(QPalette::Window, blue2.lighter());
standardPalette.setColor(QPalette::WindowText, blackish);
standardPalette.setColor(QPalette::Base, lightBlue);
standardPalette.setColor(QPalette::AlternateBase, blue2.lighter());
standardPalette.setColor(QPalette::ToolTipBase, blue2);
standardPalette.setColor(QPalette::ToolTipText, Qt::white);
standardPalette.setColor(QPalette::Text, blackish);
standardPalette.setColor(QPalette::Button, blue);
standardPalette.setColor(QPalette::ButtonText, blackish);
standardPalette.setColor(QPalette::Link, darkBlue);
standardPalette.setColor(QPalette::Highlight, Qt::white);
standardPalette.setColor(QPalette::HighlightedText, blackish);
standardPalette.setColor(QPalette::Active, QPalette::Button, blue);
standardPalette.setColor(QPalette::Disabled, QPalette::ButtonText, darkBlue);
standardPalette.setColor(QPalette::Disabled, QPalette::WindowText, darkBlue);
standardPalette.setColor(QPalette::Disabled, QPalette::Text, darkBlue);
standardPalette.setColor(QPalette::Disabled, QPalette::Light, darkBlue);
qApp->setPalette(standardPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "ScarletDevilRed")
{
// Custom palette by RedDevilus, Red as main color and Purple as complimentary.
// Alternative dark theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor darkRed(80, 45, 69);
const QColor purplishRed(120, 45, 69);
const QColor brightRed(200, 45, 69);
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, darkRed);
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, purplishRed);
darkPalette.setColor(QPalette::AlternateBase, darkRed);
darkPalette.setColor(QPalette::ToolTipBase, darkRed);
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Button, purplishRed.darker());
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Link, brightRed);
darkPalette.setColor(QPalette::Highlight, brightRed);
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::Active, QPalette::Button, purplishRed.darker());
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, brightRed);
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, brightRed);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, brightRed);
darkPalette.setColor(QPalette::Disabled, QPalette::Light, darkRed);
qApp->setPalette(darkPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "CobaltSky")
{
// Custom palette by KamFretoZ, A soothing deep royal blue
// that are meant to be easy on the eyes as the main color.
// Alternative dark theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor gray(192, 192, 192);
const QColor royalBlue(29, 41, 81);
const QColor darkishBlue(17, 30, 108);
const QColor highlight(36, 93, 218);
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, royalBlue);
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, royalBlue.lighter());
darkPalette.setColor(QPalette::AlternateBase, darkishBlue);
darkPalette.setColor(QPalette::ToolTipBase, darkishBlue);
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Button, darkishBlue);
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Link, Qt::white);
darkPalette.setColor(QPalette::Highlight, highlight);
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::Active, QPalette::Button, darkishBlue);
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Light, gray);
qApp->setPalette(darkPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "VioletAngelPurple")
{
// Custom palette by RedDevilus, Blue as main color and Purple as complimentary.
// Alternative dark theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor blackishblue(50, 25, 70);
const QColor darkerPurple(90, 30, 105);
const QColor nauticalPurple(110, 30, 125);
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, blackishblue);
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, nauticalPurple);
darkPalette.setColor(QPalette::AlternateBase, blackishblue);
darkPalette.setColor(QPalette::ToolTipBase, nauticalPurple);
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Button, nauticalPurple.darker());
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Link, darkerPurple.lighter());
darkPalette.setColor(QPalette::Highlight, darkerPurple.lighter());
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::Active, QPalette::Button, nauticalPurple.darker());
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, darkerPurple.lighter());
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, darkerPurple.lighter());
darkPalette.setColor(QPalette::Disabled, QPalette::Text, darkerPurple.darker());
darkPalette.setColor(QPalette::Disabled, QPalette::Light, nauticalPurple);
qApp->setPalette(darkPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "Ruby")
{
// Custom palette by Daisouji, Black as main color and Red as complimentary.
// Alternative dark (black) theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor gray(128, 128, 128);
const QColor slate(18, 18, 18);
const QColor rubyish(172, 21, 31);
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, slate);
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, slate.lighter());
darkPalette.setColor(QPalette::AlternateBase, slate.lighter());
darkPalette.setColor(QPalette::ToolTipBase, slate);
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Button, slate);
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Link, Qt::white);
darkPalette.setColor(QPalette::Highlight, rubyish);
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::Active, QPalette::Button, slate);
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Light, slate.lighter());
qApp->setPalette(darkPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "Sapphire")
{
// Custom palette by RedDevilus, Black as main color and Blue as complimentary.
// Alternative dark (black) theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor gray(128, 128, 128);
const QColor slate(18, 18, 18);
const QColor persianBlue(32, 35, 204);
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, slate);
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, slate.lighter());
darkPalette.setColor(QPalette::AlternateBase, slate.lighter());
darkPalette.setColor(QPalette::ToolTipBase, slate);
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Button, slate);
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Link, Qt::white);
darkPalette.setColor(QPalette::Highlight, persianBlue);
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::Active, QPalette::Button, slate);
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Light, slate.lighter());
qApp->setPalette(darkPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "Custom")
{
//Additional Theme option than loads .qss from main PCSX2 Directory
qApp->setStyle(QStyleFactory::create("Fusion"));
QString sheet_content;
QFile sheets(QString::fromStdString(Path::Combine(EmuFolders::DataRoot, "custom.qss")));
if (sheets.open(QFile::ReadOnly))
{
QString sheet_content = QString::fromUtf8(sheets.readAll().data());
qApp->setStyleSheet(sheet_content);
}
else
{
qApp->setStyle(QStyleFactory::create("Fusion"));
}
}
else
{
qApp->setPalette(s_unthemed_palette);
qApp->setStyleSheet(QString());
qApp->setStyle(s_unthemed_style_name);
}
}
void MainWindow::setIconThemeFromStyle()
{
QPalette palette = qApp->palette();
bool dark = palette.windowText().color().value() > palette.window().color().value();
QIcon::setThemeName(dark ? QStringLiteral("white") : QStringLiteral("black"));
}
void MainWindow::onScreenshotActionTriggered()
{
@@ -1574,7 +1117,11 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
if (m_ui.menuDebug->menuAction()->isVisible())
{
action = menu.addAction(tr("Boot and Debug"));
connect(action, &QAction::triggered, [this, entry]() { DebugInterface::setPauseOnEntry(true); startGameListEntry(entry); getDebuggerWindow()->show(); });
connect(action, &QAction::triggered, [this, entry]() {
DebugInterface::setPauseOnEntry(true);
startGameListEntry(entry);
getDebuggerWindow()->show();
});
}
menu.addSeparator();
@@ -1838,7 +1385,7 @@ void MainWindow::onToolsCoverDownloaderTriggered()
void MainWindow::updateTheme()
{
updateApplicationTheme();
QtHost::UpdateApplicationTheme();
m_game_list_widget->refreshImages();
}
@@ -2200,57 +1747,27 @@ bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr
#endif
std::optional<WindowInfo> MainWindow::createDisplayWindow(bool fullscreen, bool render_to_main)
std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool recreate_window, bool fullscreen, bool render_to_main, bool surfaceless)
{
DevCon.WriteLn(
"createDisplayWindow() fullscreen=%s render_to_main=%s", fullscreen ? "true" : "false", render_to_main ? "true" : "false");
createDisplayWidget(fullscreen, render_to_main);
// we need the surface visible.. this might be able to be replaced with something else
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
std::optional<WindowInfo> wi = m_display_widget->getWindowInfo();
if (!wi.has_value())
{
QMessageBox::critical(this, tr("Error"), tr("Failed to get window info from widget"));
destroyDisplayWidget(true);
return std::nullopt;
}
g_emu_thread->connectDisplaySignals(m_display_widget);
m_display_created = true;
updateWindowTitle();
updateWindowState();
m_ui.actionStartFullscreenUI->setEnabled(false);
m_ui.actionStartFullscreenUI2->setEnabled(false);
updateDisplayWidgetCursor();
m_display_widget->setFocus();
return wi;
}
std::optional<WindowInfo> MainWindow::updateDisplayWindow(bool recreate_window, bool fullscreen, bool render_to_main, bool surfaceless)
{
DevCon.WriteLn("updateDisplayWindow() recreate=%s fullscreen=%s render_to_main=%s surfaceless=%s", recreate_window ? "true" : "false",
DevCon.WriteLn("acquireRenderWindow() recreate=%s fullscreen=%s render_to_main=%s surfaceless=%s", recreate_window ? "true" : "false",
fullscreen ? "true" : "false", render_to_main ? "true" : "false", surfaceless ? "true" : "false");
QWidget* container = m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget);
const bool is_fullscreen = isRenderingFullscreen();
const bool is_rendering_to_main = isRenderingToMain();
const bool changing_surfaceless = (!m_display_widget != surfaceless);
if (!recreate_window && fullscreen == is_fullscreen && is_rendering_to_main == render_to_main && !changing_surfaceless)
return m_display_widget->getWindowInfo();
if (m_display_created && !recreate_window && fullscreen == is_fullscreen && is_rendering_to_main == render_to_main &&
!changing_surfaceless)
{
return m_display_widget ? m_display_widget->getWindowInfo() : WindowInfo();
}
// Skip recreating the surface if we're just transitioning between fullscreen and windowed with render-to-main off.
// .. except on Wayland, where everything tends to break if you don't recreate.
const bool has_container = (m_display_container != nullptr);
const bool needs_container = DisplayContainer::isNeeded(fullscreen, render_to_main);
if (!recreate_window && !is_rendering_to_main && !render_to_main && has_container == needs_container && !needs_container &&
!changing_surfaceless)
if (m_display_created && !recreate_window && !is_rendering_to_main && !render_to_main && has_container == needs_container &&
!needs_container && !changing_surfaceless)
{
DevCon.WriteLn("Toggling to %s without recreating surface", (fullscreen ? "fullscreen" : "windowed"));
@@ -2277,6 +1794,7 @@ std::optional<WindowInfo> MainWindow::updateDisplayWindow(bool recreate_window,
}
destroyDisplayWidget(surfaceless);
m_display_created = true;
// if we're going to surfaceless, we're done here
if (surfaceless)
@@ -2284,10 +1802,13 @@ std::optional<WindowInfo> MainWindow::updateDisplayWindow(bool recreate_window,
createDisplayWidget(fullscreen, render_to_main);
// we need the surface visible.. this might be able to be replaced with something else
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
std::optional<WindowInfo> wi = m_display_widget->getWindowInfo();
if (!wi.has_value())
{
QMessageBox::critical(this, tr("Error"), tr("Failed to get new window info from widget"));
QMessageBox::critical(this, tr("Error"), tr("Failed to get window info from widget"));
destroyDisplayWidget(true);
return std::nullopt;
}
@@ -2297,6 +1818,9 @@ std::optional<WindowInfo> MainWindow::updateDisplayWindow(bool recreate_window,
updateWindowTitle();
updateWindowState();
m_ui.actionStartFullscreenUI->setEnabled(false);
m_ui.actionStartFullscreenUI2->setEnabled(false);
updateDisplayWidgetCursor();
m_display_widget->setFocus();
@@ -2406,7 +1930,7 @@ void MainWindow::relativeMouseModeRequested(bool enabled)
updateDisplayWidgetCursor();
}
void MainWindow::destroyDisplay()
void MainWindow::releaseRenderWindow()
{
// Now we can safely destroy the display window.
destroyDisplayWidget(true);
@@ -2958,19 +2482,33 @@ void MainWindow::doDiscChange(CDVD_SourceType source, const QString& path)
MainWindow::VMLock MainWindow::pauseAndLockVM()
{
const bool was_fullscreen = isRenderingFullscreen();
// To switch out of fullscreen when displaying a popup, or not to?
// For Windows, with driver's direct scanout, what renders behind tends to be hit and miss.
// We can't draw anything over exclusive fullscreen, so get out of it in that case.
// Wayland's a pain as usual, we need to recreate the window, which means there'll be a brief
// period when there's no window, and Qt might shut us down. So avoid it there.
// On MacOS, it forces a workspace switch, which is kinda jarring.
#ifndef __APPLE__
const bool was_fullscreen = g_emu_thread->isFullscreen() && !s_use_central_widget;
#else
const bool was_fullscreen = false;
#endif
const bool was_paused = s_vm_paused;
// We use surfaceless rather than switching out of fullscreen, because
// we're paused, so we're not going to be rendering anyway.
if (was_fullscreen)
g_emu_thread->setSurfaceless(true);
if (!was_paused)
g_emu_thread->setVMPaused(true);
// We want to parent dialogs to the display widget, except if we were fullscreen,
// since it's going to get destroyed by the surfaceless call above.
QWidget* dialog_parent = was_fullscreen ? static_cast<QWidget*>(this) : getDisplayContainer();
// We need to switch out of exclusive fullscreen before we can display our popup.
// However, we do not want to switch back to render-to-main, the window might have generated this event.
if (was_fullscreen)
{
g_emu_thread->setFullscreen(false, false);
while (QtHost::IsVMValid() && g_emu_thread->isFullscreen())
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
}
// Now we'll either have a borderless window, or a regular window (if we were exclusive fullscreen).
QWidget* dialog_parent = getDisplayContainer();
return VMLock(dialog_parent, was_paused, was_fullscreen);
}
@@ -3000,17 +2538,10 @@ MainWindow::VMLock::VMLock(VMLock&& lock)
MainWindow::VMLock::~VMLock()
{
if (m_was_fullscreen)
g_emu_thread->setSurfaceless(false);
g_emu_thread->setFullscreen(true, true);
if (!m_was_paused)
g_emu_thread->setVMPaused(false);
if (m_was_fullscreen)
{
// Wait until we get the display widget back, so we're not the last window left and closing.
while (QtHost::IsVMValid() && !g_main_window->m_display_widget)
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 1);
}
}
void MainWindow::VMLock::cancelResume()
+3 -10
View File
@@ -69,7 +69,7 @@ public:
void cancelResume();
private:
VMLock(QWidget* dialog_parent, bool was_paused, bool was_fullscreen);
VMLock(QWidget* dialog_parent, bool was_paused, bool was_exclusive_fullscreen);
friend MainWindow;
QWidget* m_dialog_parent;
@@ -77,9 +77,6 @@ public:
bool m_was_fullscreen;
};
/// Default theme name for the platform.
static const char* DEFAULT_THEME_NAME;
/// Default filter for opening a file.
static const char* OPEN_FILE_FILTER;
@@ -90,9 +87,6 @@ public:
MainWindow();
~MainWindow();
/// Sets application theme according to settings.
static void updateApplicationTheme();
void initialize();
void connectVMThreadSignals(EmuThread* thread);
void startupUpdateCheck();
@@ -128,11 +122,10 @@ public Q_SLOTS:
private Q_SLOTS:
void onUpdateCheckComplete();
std::optional<WindowInfo> createDisplayWindow(bool fullscreen, bool render_to_main);
std::optional<WindowInfo> updateDisplayWindow(bool recreate_window, bool fullscreen, bool render_to_main, bool surfaceless);
std::optional<WindowInfo> acquireRenderWindow(bool recreate_window, bool fullscreen, bool render_to_main, bool surfaceless);
void displayResizeRequested(qint32 width, qint32 height);
void relativeMouseModeRequested(bool enabled);
void destroyDisplay();
void releaseRenderWindow();
void focusDisplayWidget();
void onGameListRefreshComplete();
+16
View File
@@ -438,16 +438,28 @@
<property name="text">
<string>&amp;GitHub Repository...</string>
</property>
<property name="icon">
<iconset theme="github">
<normaloff>.</normaloff>.</iconset>
</property>
</action>
<action name="actionSupportForums">
<property name="text">
<string>Support &amp;Forums...</string>
</property>
<property name="icon">
<iconset theme="at">
<normaloff>.</normaloff>.</iconset>
</property>
</action>
<action name="actionDiscordServer">
<property name="text">
<string>&amp;Discord Server...</string>
</property>
<property name="icon">
<iconset theme="discord">
<normaloff>.</normaloff>.</iconset>
</property>
</action>
<action name="actionCheckForUpdates">
<property name="icon">
@@ -471,6 +483,10 @@
<property name="text">
<string>&amp;About PCSX2...</string>
</property>
<property name="icon">
<iconset resource="resources/resources.qrc">
<normaloff>:/icons/AppIcon.png</normaloff>:/icons/AppIcon.png</iconset>
</property>
</action>
<action name="actionChangeDisc">
<property name="icon">
+20 -26
View File
@@ -196,8 +196,8 @@ void EmuThread::startFullscreenUI(bool fullscreen)
// this should just set the flag so it gets automatically started
ImGuiManager::InitializeFullscreenUI();
m_run_fullscreen_ui = true;
if (fullscreen)
m_is_fullscreen = true;
m_is_rendering_to_main = shouldRenderToMain();
m_is_fullscreen = fullscreen;
if (!GetMTGS().WaitForOpen())
{
@@ -242,6 +242,7 @@ void EmuThread::startVM(std::shared_ptr<VMBootParameters> boot_params)
pxAssertRel(!VMManager::HasValidVM(), "VM is shut down");
// Determine whether to start fullscreen or not.
m_is_rendering_to_main = shouldRenderToMain();
if (boot_params->fullscreen.has_value())
m_is_fullscreen = boot_params->fullscreen.value();
else
@@ -492,14 +493,14 @@ void EmuThread::toggleFullscreen()
return;
}
setFullscreen(!m_is_fullscreen);
setFullscreen(!m_is_fullscreen, true);
}
void EmuThread::setFullscreen(bool fullscreen)
void EmuThread::setFullscreen(bool fullscreen, bool allow_render_to_main)
{
if (!isOnEmuThread())
{
QMetaObject::invokeMethod(this, "setFullscreen", Qt::QueuedConnection, Q_ARG(bool, fullscreen));
QMetaObject::invokeMethod(this, "setFullscreen", Qt::QueuedConnection, Q_ARG(bool, fullscreen), Q_ARG(bool, allow_render_to_main));
return;
}
@@ -508,6 +509,7 @@ void EmuThread::setFullscreen(bool fullscreen)
// This will call back to us on the MTGS thread.
m_is_fullscreen = fullscreen;
m_is_rendering_to_main = allow_render_to_main && shouldRenderToMain();
GetMTGS().UpdateDisplayWindow();
GetMTGS().WaitGS();
@@ -893,32 +895,24 @@ void EmuThread::endCapture()
GetMTGS().RunOnGSThread(&GSEndCapture);
}
std::optional<WindowInfo> EmuThread::acquireRenderWindow()
std::optional<WindowInfo> EmuThread::acquireRenderWindow(bool recreate_window)
{
m_is_rendering_to_main = shouldRenderToMain();
m_is_surfaceless = false;
// Check if we're wanting to get exclusive fullscreen. This should be safe to read, since we're going to be calling from the GS thread.
m_is_exclusive_fullscreen = m_is_fullscreen && GSWantsExclusiveFullscreen();
const bool window_fullscreen = m_is_fullscreen && !m_is_exclusive_fullscreen;
const bool render_to_main = !m_is_exclusive_fullscreen && !window_fullscreen && m_is_rendering_to_main;
return emit onCreateDisplayRequested(m_is_fullscreen, m_is_rendering_to_main);
}
std::optional<WindowInfo> EmuThread::updateRenderWindow(bool recreate_window)
{
return emit onUpdateDisplayRequested(recreate_window, m_is_fullscreen, !m_is_fullscreen && m_is_rendering_to_main, m_is_surfaceless);
return emit onAcquireRenderWindowRequested(recreate_window, window_fullscreen, render_to_main, m_is_surfaceless);
}
void EmuThread::releaseRenderWindow()
{
emit onDestroyDisplayRequested();
emit onReleaseRenderWindowRequested();
}
std::optional<WindowInfo> Host::AcquireRenderWindow()
std::optional<WindowInfo> Host::AcquireRenderWindow(bool recreate_window)
{
return g_emu_thread->acquireRenderWindow();
}
std::optional<WindowInfo> Host::UpdateRenderWindow(bool recreate_window)
{
return g_emu_thread->updateRenderWindow(recreate_window);
return g_emu_thread->acquireRenderWindow(recreate_window);
}
void Host::ReleaseRenderWindow()
@@ -932,7 +926,7 @@ void Host::BeginPresentFrame()
void Host::RequestResizeHostDisplay(s32 width, s32 height)
{
g_emu_thread->onResizeDisplayRequested(width, height);
g_emu_thread->onResizeRenderWindowRequested(width, height);
}
void Host::OnVMStarting()
@@ -1194,7 +1188,7 @@ bool Host::IsFullscreen()
void Host::SetFullscreen(bool enabled)
{
g_emu_thread->setFullscreen(enabled);
g_emu_thread->setFullscreen(enabled, true);
}
alignas(16) static SysMtgsThread s_mtgs_thread;
@@ -1250,7 +1244,7 @@ void Host::SetDefaultUISettings(SettingsInterface& si)
si.SetBoolValue("UI", "RenderToSeparateWindow", false);
si.SetBoolValue("UI", "HideMainWindowWhenRunning", false);
si.SetBoolValue("UI", "DisableWindowResize", false);
si.SetStringValue("UI", "Theme", MainWindow::DEFAULT_THEME_NAME);
si.SetStringValue("UI", "Theme", QtHost::GetDefaultThemeName());
}
void QtHost::SaveSettings()
@@ -1797,7 +1791,7 @@ int main(int argc, char* argv[])
return EXIT_SUCCESS;
// Set theme before creating any windows.
MainWindow::updateApplicationTheme();
QtHost::UpdateApplicationTheme();
MainWindow* main_window = new MainWindow();
// Start up the CPU thread.
+13 -7
View File
@@ -61,6 +61,7 @@ public:
__fi QEventLoop* getEventLoop() const { return m_event_loop; }
__fi bool isFullscreen() const { return m_is_fullscreen; }
__fi bool isExclusiveFullscreen() const { return m_is_exclusive_fullscreen; }
__fi bool isRenderingToMain() const { return m_is_rendering_to_main; }
__fi bool isSurfaceless() const { return m_is_surfaceless; }
__fi bool isRunningFullscreenUI() const { return m_run_fullscreen_ui; }
@@ -69,8 +70,7 @@ public:
bool shouldRenderToMain() const;
/// Called back from the GS thread when the display state changes (e.g. fullscreen, render to main).
std::optional<WindowInfo> acquireRenderWindow();
std::optional<WindowInfo> updateRenderWindow(bool recreate_window);
std::optional<WindowInfo> acquireRenderWindow(bool recreate_window);
void connectDisplaySignals(DisplayWidget* widget);
void releaseRenderWindow();
@@ -93,7 +93,7 @@ public Q_SLOTS:
void saveState(const QString& filename);
void saveStateToSlot(qint32 slot);
void toggleFullscreen();
void setFullscreen(bool fullscreen);
void setFullscreen(bool fullscreen, bool allow_render_to_main);
void setSurfaceless(bool surfaceless);
void applySettings();
void reloadGameSettings();
@@ -117,10 +117,9 @@ public Q_SLOTS:
Q_SIGNALS:
bool messageConfirmed(const QString& title, const QString& message);
std::optional<WindowInfo> onCreateDisplayRequested(bool fullscreen, bool render_to_main);
std::optional<WindowInfo> onUpdateDisplayRequested(bool recreate_window, bool fullscreen, bool render_to_main, bool surfaceless);
void onResizeDisplayRequested(qint32 width, qint32 height);
void onDestroyDisplayRequested();
std::optional<WindowInfo> onAcquireRenderWindowRequested(bool recreate_window, bool fullscreen, bool render_to_main, bool surfaceless);
void onResizeRenderWindowRequested(qint32 width, qint32 height);
void onReleaseRenderWindowRequested();
void onRelativeMouseModeRequested(bool enabled);
/// Called when the VM is starting initialization, but has not been completed yet.
@@ -195,6 +194,7 @@ private:
bool m_run_fullscreen_ui = false;
bool m_is_rendering_to_main = false;
bool m_is_fullscreen = false;
bool m_is_exclusive_fullscreen = false;
bool m_is_surfaceless = false;
bool m_save_state_on_shutdown = false;
bool m_pause_on_focus_loss = false;
@@ -213,6 +213,12 @@ extern EmuThread* g_emu_thread;
namespace QtHost
{
/// Default theme name for the platform.
const char* GetDefaultThemeName();
/// Sets application theme according to settings.
void UpdateApplicationTheme();
/// Sets batch mode (exit after game shutdown).
bool InBatchMode();
@@ -37,13 +37,19 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent,
#ifdef SDL_BUILD
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLSource, "InputSources", "SDL", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLEnhancedMode, "InputSources", "SDLControllerEnhancedMode", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLRawInput, "InputSources", "SDLRawInput", false);
connect(m_ui.enableSDLSource, &QCheckBox::stateChanged, this, &ControllerGlobalSettingsWidget::updateSDLOptionsEnabled);
connect(m_ui.ledSettings, &QToolButton::clicked, this, &ControllerGlobalSettingsWidget::ledSettingsClicked);
#else
m_ui.enableSDLSource->setEnabled(false);
m_ui.ledSettings->setEnabled(false);
m_ui.enableSDLRawInput->setEnabled(false);
#endif
#if defined(SDL_BUILD) && defined(_WIN32)
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLRawInput, "InputSources", "SDLRawInput", false);
#else
m_ui.gridLayout_2->removeWidget(m_ui.enableSDLRawInput);
m_ui.enableSDLRawInput->deleteLater();
m_ui.enableSDLRawInput = nullptr;
#endif
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableMouseMapping, "UI", "EnableMouseMapping", false);
@@ -62,7 +68,6 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent,
m_ui.mainLayout->removeWidget(m_ui.dinputGroup);
m_ui.dinputGroup->deleteLater();
m_ui.dinputGroup = nullptr;
m_ui.enableSDLRawInput->hide();
#endif
if (dialog->isEditingProfile())
@@ -116,7 +121,9 @@ void ControllerGlobalSettingsWidget::updateSDLOptionsEnabled()
const bool enabled = m_ui.enableSDLSource->isChecked();
m_ui.enableSDLEnhancedMode->setEnabled(enabled);
m_ui.ledSettings->setEnabled(enabled);
#ifdef _WIN32
m_ui.enableSDLRawInput->setEnabled(enabled);
#endif
}
void ControllerGlobalSettingsWidget::ledSettingsClicked()
+2 -2
View File
@@ -189,7 +189,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.gpuTargetCLUTMode, "EmuCore/GS", "UserHacks_GPUTargetCLUTMode", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.skipDrawStart, "EmuCore/GS", "UserHacks_SkipDraw_Start", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.skipDrawEnd, "EmuCore/GS", "UserHacks_SkipDraw_End", 0);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hwAutoFlush, "EmuCore/GS", "UserHacks_AutoFlush", false);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.hwAutoFlush, "EmuCore/GS", "UserHacks_AutoFlushLevel", 0);
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);
@@ -662,7 +662,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
dialog->registerWidgetHelp(m_ui.osdShowGSStats, tr("Show Statistics"), tr("Unchecked"),
tr("Shows counters for internal graphical utilization, useful for debugging."));
dialog->registerWidgetHelp(m_ui.osdShowIndicators, tr("Show Indicators"), tr("Unchecked"),
dialog->registerWidgetHelp(m_ui.osdShowIndicators, tr("Show Indicators"), tr("Checked"),
tr("Shows OSD icon indicators for emulation states such as Pausing, Turbo, Fast-Forward, and Slow-Motion."));
dialog->registerWidgetHelp(m_ui.osdShowSettings, tr("Show Settings"), tr("Unchecked"),
+236 -217
View File
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>720</width>
<height>476</height>
<height>498</height>
</rect>
</property>
<property name="windowTitle">
@@ -780,222 +780,6 @@
<string>Hardware Fixes</string>
</attribute>
<layout class="QFormLayout" name="hardwareFixesLayout">
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Half Screen Fix:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="halfScreenFix">
<item>
<property name="text">
<string>Automatic (Default)</string>
</property>
</item>
<item>
<property name="text">
<string>Force Disabled</string>
</property>
</item>
<item>
<property name="text">
<string>Force Enabled</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_36">
<property name="text">
<string>CPU Sprite Render Size:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Software CLUT Render:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="cpuCLUTRender">
<property name="currentText">
<string extracomment="0 (Disabled)">0 (Disabled)</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>0 (Disabled)</string>
</property>
</item>
<item>
<property name="text">
<string>1 (Normal)</string>
</property>
</item>
<item>
<property name="text">
<string>2 (Aggressive)</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_47">
<property name="text">
<string extracomment="CLUT: Color Look Up Table, often referred to as a palette in non-PS2 things. GPU Target CLUT: GPU handling of when a game uses data from a render target as a CLUT.">GPU Target CLUT:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="gpuTargetCLUTMode">
<item>
<property name="text">
<string>Disabled (Default)</string>
</property>
</item>
<item>
<property name="text">
<string>Enabled (Exact Match)</string>
</property>
</item>
<item>
<property name="text">
<string>Enabled (Check Inside Target)</string>
</property>
</item>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_45">
<property name="text">
<string>Texture Inside RT:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="textureInsideRt">
<item>
<property name="text">
<string>Disabled (Default)</string>
</property>
</item>
<item>
<property name="text">
<string>Inside Target</string>
</property>
</item>
<item>
<property name="text">
<string>Merge Targets</string>
</property>
</item>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Skipdraw Range:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSpinBox" name="skipDrawStart">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="skipDrawEnd">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="7" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="hwAutoFlush">
<property name="text">
<string>Auto Flush</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="disableSafeFeatures">
<property name="text">
<string>Disable Safe Features</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="disableDepthEmulation">
<property name="text">
<string>Disable Depth Emulation</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="frameBufferConversion">
<property name="text">
<string>Frame Buffer Conversion</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="readTCOnClose">
<property name="text">
<string>Read Targets When Closing</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="disablePartialInvalidation">
<property name="text">
<string>Disable Partial Source Invalidation</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="targetPartialInvalidation">
<property name="text">
<string>Target Partial Invalidation</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="preloadFrameData">
<property name="text">
<string>Preload Frame Data</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="estimateTextureRegion">
<property name="text">
<string>Estimate Texture Region</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="gpuPaletteConversion">
<property name="text">
<string>GPU Palette Conversion</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
@@ -1037,6 +821,39 @@
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Half Screen Fix:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="halfScreenFix">
<item>
<property name="text">
<string>Automatic (Default)</string>
</property>
</item>
<item>
<property name="text">
<string>Force Disabled</string>
</property>
</item>
<item>
<property name="text">
<string>Force Enabled</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_36">
<property name="text">
<string>CPU Sprite Render Size:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_5" stretch="1,0">
<item>
@@ -1119,6 +936,208 @@
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Software CLUT Render:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="cpuCLUTRender">
<property name="currentText">
<string extracomment="0 (Disabled)">0 (Disabled)</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>0 (Disabled)</string>
</property>
</item>
<item>
<property name="text">
<string>1 (Normal)</string>
</property>
</item>
<item>
<property name="text">
<string>2 (Aggressive)</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_47">
<property name="text">
<string extracomment="CLUT: Color Look Up Table, often referred to as a palette in non-PS2 things. GPU Target CLUT: GPU handling of when a game uses data from a render target as a CLUT.">GPU Target CLUT:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="gpuTargetCLUTMode">
<item>
<property name="text">
<string>Disabled (Default)</string>
</property>
</item>
<item>
<property name="text">
<string>Enabled (Exact Match)</string>
</property>
</item>
<item>
<property name="text">
<string>Enabled (Check Inside Target)</string>
</property>
</item>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_45">
<property name="text">
<string>Texture Inside RT:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QComboBox" name="textureInsideRt">
<item>
<property name="text">
<string>Disabled (Default)</string>
</property>
</item>
<item>
<property name="text">
<string>Inside Target</string>
</property>
</item>
<item>
<property name="text">
<string>Merge Targets</string>
</property>
</item>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_30">
<property name="text">
<string>Auto Flush:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="hwAutoFlush">
<item>
<property name="text">
<string>Disabled (Default)</string>
</property>
</item>
<item>
<property name="text">
<string>Enabled (Sprites Only)</string>
</property>
</item>
<item>
<property name="text">
<string>Enabled (All Primitives)</string>
</property>
</item>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Skipdraw Range:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSpinBox" name="skipDrawStart">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="skipDrawEnd">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="8" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QCheckBox" name="frameBufferConversion">
<property name="text">
<string>Frame Buffer Conversion</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="readTCOnClose">
<property name="text">
<string>Read Targets When Closing</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="targetPartialInvalidation">
<property name="text">
<string>Target Partial Invalidation</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="preloadFrameData">
<property name="text">
<string>Preload Frame Data</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="disableDepthEmulation">
<property name="text">
<string>Disable Depth Emulation</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="disableSafeFeatures">
<property name="text">
<string>Disable Safe Features</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="disablePartialInvalidation">
<property name="text">
<string>Disable Partial Source Invalidation</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="estimateTextureRegion">
<property name="text">
<string>Estimate Texture Region</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="gpuPaletteConversion">
<property name="text">
<string>GPU Palette Conversion</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QGroupBox" name="upscalingFixesTab">
@@ -20,11 +20,12 @@
#include "MainWindow.h"
#include "SettingWidgetBinder.h"
#include "SettingsDialog.h"
#include "QtHost.h"
static const char* THEME_NAMES[] = {
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Native"),
//: Ignore what Crowdin says in this string about "[Light]/[Dark]" being untouchable here, these are not variables in this case and must be translated.
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Fusion [Light]"),
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Fusion [Light/Dark]"),
//: Ignore what Crowdin says in this string about "[Light]/[Dark]" being untouchable here, these are not variables in this case and must be translated.
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Dark Fusion (Gray) [Dark]"),
//: Ignore what Crowdin says in this string about "[Light]/[Dark]" being untouchable here, these are not variables in this case and must be translated.
@@ -91,7 +92,7 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsDialog* dialog, QWidget
connect(m_ui.renderToSeparateWindow, &QCheckBox::stateChanged, this, &InterfaceSettingsWidget::onRenderToSeparateWindowChanged);
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.theme, "UI", "Theme", THEME_NAMES, THEME_VALUES,
MainWindow::DEFAULT_THEME_NAME);
QtHost::GetDefaultThemeName());
connect(m_ui.theme, QOverload<int>::of(&QComboBox::currentIndexChanged), [this]() { emit themeChanged(); });
// Per-game settings is special, we don't want to bind it if we're editing per-game settings.
+495
View File
@@ -0,0 +1,495 @@
/* PCSX2 - PS2 Emulator for PCs
* 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-
* 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 "PrecompiledHeader.h"
#include "QtHost.h"
#include "pcsx2/Config.h"
#include "pcsx2/HostSettings.h"
#include "common/Path.h"
#include <QtGui/QPalette>
#include <QtWidgets/QApplication>
#include <QtWidgets/QStyle>
#include <QtWidgets/QStyleFactory>
namespace QtHost
{
static void SetStyleFromSettings();
static void SetIconThemeFromStyle();
} // namespace QtHost
static QString s_unthemed_style_name;
static bool s_unthemed_style_name_set;
const char* QtHost::GetDefaultThemeName()
{
#ifdef __APPLE__
return "";
#else
return "darkfusion";
#endif
}
void QtHost::UpdateApplicationTheme()
{
if (!s_unthemed_style_name_set)
{
s_unthemed_style_name_set = true;
s_unthemed_style_name = QApplication::style()->objectName();
}
SetStyleFromSettings();
SetIconThemeFromStyle();
}
void QtHost::SetStyleFromSettings()
{
const std::string theme(Host::GetBaseStringSettingValue("UI", "Theme", GetDefaultThemeName()));
if (theme == "fusion")
{
// setPalette() shouldn't be necessary, as the documentation claims that setStyle() resets the palette, but it
// is here, to work around a bug in 6.4.x and 6.5.x where the palette doesn't restore after changing themes.
qApp->setPalette(QPalette());
qApp->setStyle(QStyleFactory::create("Fusion"));
qApp->setStyleSheet(QString());
}
else if (theme == "darkfusion")
{
// adapted from https://gist.github.com/QuantumCD/6245215
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor lighterGray(75, 75, 75);
const QColor darkGray(53, 53, 53);
const QColor gray(128, 128, 128);
const QColor black(25, 25, 25);
const QColor blue(198, 238, 255);
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, darkGray);
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, black);
darkPalette.setColor(QPalette::AlternateBase, darkGray);
darkPalette.setColor(QPalette::ToolTipBase, darkGray);
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Button, darkGray);
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Link, blue);
darkPalette.setColor(QPalette::Highlight, lighterGray);
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::PlaceholderText, QColor(Qt::white).darker());
darkPalette.setColor(QPalette::Active, QPalette::Button, darkGray);
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Light, darkGray);
qApp->setPalette(darkPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "darkfusionblue")
{
// adapted from https://gist.github.com/QuantumCD/6245215
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor darkGray(53, 53, 53);
const QColor gray(128, 128, 128);
const QColor black(25, 25, 25);
const QColor blue(198, 238, 255);
const QColor blue2(0, 88, 208);
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, darkGray);
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, black);
darkPalette.setColor(QPalette::AlternateBase, darkGray);
darkPalette.setColor(QPalette::ToolTipBase, blue2);
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Button, darkGray);
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Link, blue);
darkPalette.setColor(QPalette::Highlight, blue2);
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::PlaceholderText, QColor(Qt::white).darker());
darkPalette.setColor(QPalette::Active, QPalette::Button, darkGray);
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Light, darkGray);
qApp->setPalette(darkPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "UntouchedLagoon")
{
// Custom palette by RedDevilus, Tame (Light/Washed out) Green as main color and Grayish Blue as complimentary.
// Alternative white theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor black(25, 25, 25);
const QColor darkteal(0, 77, 77);
const QColor teal(0, 128, 128);
const QColor tameTeal(160, 190, 185);
const QColor grayBlue(160, 180, 190);
QPalette standardPalette;
standardPalette.setColor(QPalette::Window, tameTeal);
standardPalette.setColor(QPalette::WindowText, black.lighter());
standardPalette.setColor(QPalette::Base, grayBlue);
standardPalette.setColor(QPalette::AlternateBase, tameTeal);
standardPalette.setColor(QPalette::ToolTipBase, tameTeal);
standardPalette.setColor(QPalette::ToolTipText, grayBlue);
standardPalette.setColor(QPalette::Text, black);
standardPalette.setColor(QPalette::Button, tameTeal);
standardPalette.setColor(QPalette::ButtonText, black);
standardPalette.setColor(QPalette::Link, black.lighter());
standardPalette.setColor(QPalette::Highlight, teal);
standardPalette.setColor(QPalette::HighlightedText, grayBlue.lighter());
standardPalette.setColor(QPalette::Active, QPalette::Button, tameTeal);
standardPalette.setColor(QPalette::Disabled, QPalette::ButtonText, darkteal);
standardPalette.setColor(QPalette::Disabled, QPalette::WindowText, darkteal.lighter());
standardPalette.setColor(QPalette::Disabled, QPalette::Text, darkteal.lighter());
standardPalette.setColor(QPalette::Disabled, QPalette::Light, tameTeal);
qApp->setPalette(standardPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "BabyPastel")
{
// Custom palette by RedDevilus, Blue as main color and blue as complimentary.
// Alternative light theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor gray(150, 150, 150);
const QColor black(25, 25, 25);
const QColor redpinkish(200, 75, 132);
const QColor pink(255, 174, 201);
const QColor brightPink(255, 230, 255);
const QColor congoPink(255, 127, 121);
const QColor blue(221, 225, 239);
QPalette standardPalette;
standardPalette.setColor(QPalette::Window, pink);
standardPalette.setColor(QPalette::WindowText, black);
standardPalette.setColor(QPalette::Base, brightPink);
standardPalette.setColor(QPalette::AlternateBase, blue);
standardPalette.setColor(QPalette::ToolTipBase, pink);
standardPalette.setColor(QPalette::ToolTipText, brightPink);
standardPalette.setColor(QPalette::Text, black);
standardPalette.setColor(QPalette::Button, pink);
standardPalette.setColor(QPalette::ButtonText, black);
standardPalette.setColor(QPalette::Link, black);
standardPalette.setColor(QPalette::Highlight, congoPink);
standardPalette.setColor(QPalette::HighlightedText, black);
standardPalette.setColor(QPalette::Active, QPalette::Button, pink);
standardPalette.setColor(QPalette::Disabled, QPalette::ButtonText, redpinkish);
standardPalette.setColor(QPalette::Disabled, QPalette::WindowText, redpinkish);
standardPalette.setColor(QPalette::Disabled, QPalette::Text, redpinkish);
standardPalette.setColor(QPalette::Disabled, QPalette::Light, gray);
qApp->setPalette(standardPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "PizzaBrown")
{
// Custom palette by KamFretoZ, a Pizza Tower Reference!
// With a mixtures of Light Brown, Peachy/Creamy White, Latte-like Color.
// Thanks to Jordan for the idea :P
// Alternative light theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor gray(128, 128, 128);
const QColor extr(248, 192, 88);
const QColor main(233, 187, 147);
const QColor comp(248, 230, 213);
const QColor highlight(188, 100, 60);
QPalette standardPalette;
standardPalette.setColor(QPalette::Window, main);
standardPalette.setColor(QPalette::WindowText, Qt::black);
standardPalette.setColor(QPalette::Base, comp);
standardPalette.setColor(QPalette::AlternateBase, extr);
standardPalette.setColor(QPalette::ToolTipBase, comp);
standardPalette.setColor(QPalette::ToolTipText, Qt::black);
standardPalette.setColor(QPalette::Text, Qt::black);
standardPalette.setColor(QPalette::Button, extr);
standardPalette.setColor(QPalette::ButtonText, Qt::black);
standardPalette.setColor(QPalette::Link, Qt::black);
standardPalette.setColor(QPalette::Highlight, highlight);
standardPalette.setColor(QPalette::HighlightedText, Qt::white);
standardPalette.setColor(QPalette::Active, QPalette::Button, extr);
standardPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray.darker());
standardPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray.darker());
standardPalette.setColor(QPalette::Disabled, QPalette::Text, Qt::gray);
standardPalette.setColor(QPalette::Disabled, QPalette::Light, gray.lighter());
qApp->setPalette(standardPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #cc3f18; border: 1px solid white; }");
}
else if (theme == "PCSX2Blue")
{
// Custom palette by RedDevilus, White as main color and Blue as complimentary.
// Alternative light theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor blackish(35, 35, 35);
const QColor darkBlue(73, 97, 177);
const QColor blue2(80, 120, 200);
const QColor blue(106, 156, 255);
const QColor lightBlue(130, 155, 241);
QPalette standardPalette;
standardPalette.setColor(QPalette::Window, blue2.lighter());
standardPalette.setColor(QPalette::WindowText, blackish);
standardPalette.setColor(QPalette::Base, lightBlue);
standardPalette.setColor(QPalette::AlternateBase, blue2.lighter());
standardPalette.setColor(QPalette::ToolTipBase, blue2);
standardPalette.setColor(QPalette::ToolTipText, Qt::white);
standardPalette.setColor(QPalette::Text, blackish);
standardPalette.setColor(QPalette::Button, blue);
standardPalette.setColor(QPalette::ButtonText, blackish);
standardPalette.setColor(QPalette::Link, darkBlue);
standardPalette.setColor(QPalette::Highlight, Qt::white);
standardPalette.setColor(QPalette::HighlightedText, blackish);
standardPalette.setColor(QPalette::Active, QPalette::Button, blue);
standardPalette.setColor(QPalette::Disabled, QPalette::ButtonText, darkBlue);
standardPalette.setColor(QPalette::Disabled, QPalette::WindowText, darkBlue);
standardPalette.setColor(QPalette::Disabled, QPalette::Text, darkBlue);
standardPalette.setColor(QPalette::Disabled, QPalette::Light, darkBlue);
qApp->setPalette(standardPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "ScarletDevilRed")
{
// Custom palette by RedDevilus, Red as main color and Purple as complimentary.
// Alternative dark theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor darkRed(80, 45, 69);
const QColor purplishRed(120, 45, 69);
const QColor brightRed(200, 45, 69);
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, darkRed);
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, purplishRed);
darkPalette.setColor(QPalette::AlternateBase, darkRed);
darkPalette.setColor(QPalette::ToolTipBase, darkRed);
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Button, purplishRed.darker());
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Link, brightRed);
darkPalette.setColor(QPalette::Highlight, brightRed);
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::Active, QPalette::Button, purplishRed.darker());
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, brightRed);
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, brightRed);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, brightRed);
darkPalette.setColor(QPalette::Disabled, QPalette::Light, darkRed);
qApp->setPalette(darkPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "CobaltSky")
{
// Custom palette by KamFretoZ, A soothing deep royal blue
// that are meant to be easy on the eyes as the main color.
// Alternative dark theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor gray(150, 150, 150);
const QColor royalBlue(29, 41, 81);
const QColor darkishBlue(17, 30, 108);
const QColor highlight(36, 93, 218);
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, royalBlue);
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, royalBlue.lighter());
darkPalette.setColor(QPalette::AlternateBase, darkishBlue);
darkPalette.setColor(QPalette::ToolTipBase, darkishBlue);
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Button, darkishBlue);
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Link, Qt::white);
darkPalette.setColor(QPalette::Highlight, highlight);
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::Active, QPalette::Button, darkishBlue);
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Light, gray);
qApp->setPalette(darkPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "VioletAngelPurple")
{
// Custom palette by RedDevilus, Blue as main color and Purple as complimentary.
// Alternative dark theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor blackishblue(50, 25, 70);
const QColor darkerPurple(90, 30, 105);
const QColor nauticalPurple(110, 30, 125);
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, blackishblue);
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, nauticalPurple);
darkPalette.setColor(QPalette::AlternateBase, blackishblue);
darkPalette.setColor(QPalette::ToolTipBase, nauticalPurple);
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Button, nauticalPurple.darker());
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Link, darkerPurple.lighter());
darkPalette.setColor(QPalette::Highlight, darkerPurple.lighter());
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::Active, QPalette::Button, nauticalPurple.darker());
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, darkerPurple.lighter());
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, darkerPurple.lighter());
darkPalette.setColor(QPalette::Disabled, QPalette::Text, darkerPurple.darker());
darkPalette.setColor(QPalette::Disabled, QPalette::Light, nauticalPurple);
qApp->setPalette(darkPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "Ruby")
{
// Custom palette by Daisouji, Black as main color and Red as complimentary.
// Alternative dark (black) theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor gray(128, 128, 128);
const QColor slate(18, 18, 18);
const QColor rubyish(172, 21, 31);
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, slate);
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, slate.lighter());
darkPalette.setColor(QPalette::AlternateBase, slate.lighter());
darkPalette.setColor(QPalette::ToolTipBase, slate);
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Button, slate);
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Link, Qt::white);
darkPalette.setColor(QPalette::Highlight, rubyish);
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::Active, QPalette::Button, slate);
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Light, slate.lighter());
qApp->setPalette(darkPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "Sapphire")
{
// Custom palette by RedDevilus, Black as main color and Blue as complimentary.
// Alternative dark (black) theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor gray(128, 128, 128);
const QColor slate(18, 18, 18);
const QColor persianBlue(32, 35, 204);
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, slate);
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, slate.lighter());
darkPalette.setColor(QPalette::AlternateBase, slate.lighter());
darkPalette.setColor(QPalette::ToolTipBase, slate);
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Button, slate);
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Link, Qt::white);
darkPalette.setColor(QPalette::Highlight, persianBlue);
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::Active, QPalette::Button, slate);
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Light, slate.lighter());
qApp->setPalette(darkPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "Custom")
{
//Additional Theme option than loads .qss from main PCSX2 Directory
qApp->setStyle(QStyleFactory::create("Fusion"));
QString sheet_content;
QFile sheets(QString::fromStdString(Path::Combine(EmuFolders::DataRoot, "custom.qss")));
if (sheets.open(QFile::ReadOnly))
{
QString sheet_content = QString::fromUtf8(sheets.readAll().data());
qApp->setStyleSheet(sheet_content);
}
else
{
qApp->setStyle(QStyleFactory::create("Fusion"));
}
}
else
{
// setPalette() shouldn't be necessary, as the documentation claims that setStyle() resets the palette, but it
// is here, to work around a bug in 6.4.x and 6.5.x where the palette doesn't restore after changing themes.
qApp->setPalette(QPalette());
qApp->setStyle(s_unthemed_style_name);
qApp->setStyleSheet(QString());
}
}
void QtHost::SetIconThemeFromStyle()
{
QPalette palette = qApp->palette();
bool dark = palette.windowText().color().value() > palette.window().color().value();
QIcon::setThemeName(dark ? QStringLiteral("white") : QStringLiteral("black"));
}
+1
View File
@@ -144,6 +144,7 @@
<ClCompile Include="Settings\DebugSettingsWidget.cpp" />
<ClCompile Include="Settings\FolderSettingsWidget.cpp" />
<ClCompile Include="Settings\MemoryCardConvertWorker.cpp" />
<ClCompile Include="Themes.cpp" />
<ClCompile Include="Tools\InputRecording\InputRecordingViewer.cpp" />
<ClCompile Include="Tools\InputRecording\NewInputRecordingDlg.cpp" />
<ClCompile Include="Debugger\CpuWidget.cpp" />
+1
View File
@@ -315,6 +315,7 @@
<ClCompile Include="$(IntDir)moc_ColorPickerButton.cpp">
<Filter>moc</Filter>
</ClCompile>
<ClCompile Include="Themes.cpp" />
</ItemGroup>
<ItemGroup>
<Manifest Include="..\pcsx2\windows\PCSX2.manifest">
+1 -1
View File
@@ -5,7 +5,7 @@ IFS="
printf "<RCC>\n"
printf "\t<qresource>\n"
for i in $(find . -not -iname '*.sh' -not -iname '*.qrc' -type f | cut -d'/' -f2-99); do
for i in $(find . -not -iname '.*' -not -iname '*.sh' -not -iname '*.qrc' -type f | cut -d'/' -f2-99 | sort -f); do
printf "\t\t<file>%s</file>\n" "$i"
done
printf "\t</qresource>\n"
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -2,6 +2,10 @@
Name=Black Icon Theme
Comment=Black Icon Theme
[32]
Size=32
Type=Fixed
[svg]
Size=64
Type=Scalable
@@ -0,0 +1,3 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-at" viewBox="0 0 16 16"> <path d="M13.106 7.222c0-2.967-2.249-5.032-5.482-5.032-3.35 0-5.646 2.318-5.646 5.702 0 3.493 2.235 5.708 5.762 5.708.862 0 1.689-.123 2.304-.335v-.862c-.43.199-1.354.328-2.29.328-2.926 0-4.813-1.88-4.813-4.798 0-2.844 1.921-4.881 4.594-4.881 2.735 0 4.608 1.688 4.608 4.156 0 1.682-.554 2.769-1.416 2.769-.492 0-.772-.28-.772-.76V5.206H8.923v.834h-.11c-.266-.595-.881-.964-1.6-.964-1.4 0-2.378 1.162-2.378 2.823 0 1.737.957 2.906 2.379 2.906.8 0 1.415-.39 1.709-1.087h.11c.081.67.703 1.148 1.503 1.148 1.572 0 2.57-1.415 2.57-3.643zm-7.177.704c0-1.197.54-1.907 1.456-1.907.93 0 1.524.738 1.524 1.907S8.308 9.84 7.371 9.84c-.895 0-1.442-.725-1.442-1.914z"/> </svg>

After

Width:  |  Height:  |  Size: 939 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -2,6 +2,10 @@
Name=White Icon Theme
Comment=White Icon Theme
[32]
Size=32
Type=Fixed
[svg]
Size=64
Type=Scalable
@@ -0,0 +1,3 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg style="color: white" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-at" viewBox="0 0 16 16"> <path d="M13.106 7.222c0-2.967-2.249-5.032-5.482-5.032-3.35 0-5.646 2.318-5.646 5.702 0 3.493 2.235 5.708 5.762 5.708.862 0 1.689-.123 2.304-.335v-.862c-.43.199-1.354.328-2.29.328-2.926 0-4.813-1.88-4.813-4.798 0-2.844 1.921-4.881 4.594-4.881 2.735 0 4.608 1.688 4.608 4.156 0 1.682-.554 2.769-1.416 2.769-.492 0-.772-.28-.772-.76V5.206H8.923v.834h-.11c-.266-.595-.881-.964-1.6-.964-1.4 0-2.378 1.162-2.378 2.823 0 1.737.957 2.906 2.379 2.906.8 0 1.415-.39 1.709-1.087h.11c.081.67.703 1.148 1.503 1.148 1.572 0 2.57-1.415 2.57-3.643zm-7.177.704c0-1.197.54-1.907 1.456-1.907.93 0 1.524.738 1.524 1.907S8.308 9.84 7.371 9.84c-.895 0-1.442-.725-1.442-1.914z" fill="white"></path> </svg>

After

Width:  |  Height:  |  Size: 978 B

+7 -2
View File
@@ -1,10 +1,13 @@
<RCC>
<qresource>
<file>icons/address-book-new-22.png</file>
<file>icons/AppIcon.png</file>
<file>icons/applications-system-24.png</file>
<file>icons/black/32/discord.png</file>
<file>icons/black/32/github.png</file>
<file>icons/black/index.theme</file>
<file>icons/black/svg/arrow-left-right-line.svg</file>
<file>icons/black/svg/artboard-2-line.svg</file>
<file>icons/black/svg/at.svg</file>
<file>icons/black/svg/book-open-line.svg</file>
<file>icons/black/svg/brush-line.svg</file>
<file>icons/black/svg/checkbox-multiple-blank-line.svg</file>
@@ -56,16 +59,18 @@
<file>icons/black/svg/usb-fill.svg</file>
<file>icons/black/svg/volume-up-line.svg</file>
<file>icons/black/svg/window-2-line.svg</file>
<file>icons/discord.png</file>
<file>icons/logo.png</file>
<file>icons/media-optical-24.png</file>
<file>icons/media-optical-gear-24.png</file>
<file>icons/media-optical.png</file>
<file>icons/QT.png</file>
<file>icons/update.png</file>
<file>icons/white/32/discord.png</file>
<file>icons/white/32/github.png</file>
<file>icons/white/index.theme</file>
<file>icons/white/svg/arrow-left-right-line.svg</file>
<file>icons/white/svg/artboard-2-line.svg</file>
<file>icons/white/svg/at.svg</file>
<file>icons/white/svg/book-open-line.svg</file>
<file>icons/white/svg/brush-line.svg</file>
<file>icons/white/svg/checkbox-multiple-blank-line.svg</file>
+1 -1
View File
@@ -253,7 +253,7 @@ bool InputIsoFile::Open(std::string srcfile, bool testOnly)
if (!detected)
{
Console.Error(fmt::format("Unable to identify the ISO image type for '{}'", srcfile));
Console.Error(fmt::format("Unable to identify the ISO image type for '{}'", m_filename));
Close();
return false;
}
+1 -2
View File
@@ -566,7 +566,6 @@ set(pcsx2GSHeaders
GS/Renderers/Common/GSRenderer.h
GS/Renderers/Common/GSTexture.h
GS/Renderers/Common/GSVertex.h
GS/Renderers/Common/GSVertexList.h
GS/Renderers/Common/GSVertexTrace.h
GS/Renderers/Null/GSRendererNull.h
GS/Renderers/HW/GSHwHack.h
@@ -1250,7 +1249,7 @@ function(setup_main_executable target)
install(FILES $<TARGET_PDB_FILE:${target}> DESTINATION ${CMAKE_SOURCE_DIR}/bin)
endif()
find_program(WINDEPLOYQT_EXE windeployqt HINTS "${QT_BINARY_DIRECTORY}")
install(CODE "execute_process(COMMAND \"${WINDEPLOYQT_EXE}\" \"${CMAKE_SOURCE_DIR}/bin/$<TARGET_FILE_NAME:${target}>\" --plugindir ${CMAKE_SOURCE_DIR}/bin/QtPlugins --no-compiler-runtime --no-system-d3d-compiler)")
install(CODE "execute_process(COMMAND \"${WINDEPLOYQT_EXE}\" \"${CMAKE_SOURCE_DIR}/bin/$<TARGET_FILE_NAME:${target}>\" --plugindir \"${CMAKE_SOURCE_DIR}/bin/QtPlugins\" --no-compiler-runtime --no-system-d3d-compiler COMMAND_ERROR_IS_FATAL ANY)")
install(CODE "file(WRITE \"${CMAKE_SOURCE_DIR}/bin/qt.conf\" \"[Paths]\\nPlugins = ./QtPlugins\")")
endif()
+69 -61
View File
@@ -350,6 +350,13 @@ enum class GSCASMode : u8
SharpenAndResize,
};
enum class GSHWAutoFlushLevel : u8
{
Disabled,
SpritesOnly,
Enabled,
};
enum class GSGPUTargetCLUTMode : u8
{
Disabled,
@@ -669,7 +676,6 @@ struct Pcsx2Config
Mipmap : 1,
ManualUserHacks : 1,
UserHacks_AlignSpriteX : 1,
UserHacks_AutoFlush : 1,
UserHacks_CPUFBConversion : 1,
UserHacks_ReadTCOnClose : 1,
UserHacks_DisableDepthSupport : 1,
@@ -704,85 +710,87 @@ struct Pcsx2Config
};
};
int VsyncQueueSize{2};
int VsyncQueueSize = 2;
// forces the MTGS to execute tags/tasks in fully blocking/synchronous
// style. Useful for debugging potential bugs in the MTGS pipeline.
bool SynchronousMTGS{false};
bool FrameLimitEnable{true};
bool SynchronousMTGS = false;
bool FrameLimitEnable = true;
VsyncMode VsyncEnable{VsyncMode::Off};
VsyncMode VsyncEnable = VsyncMode::Off;
float LimitScalar{1.0f};
float FramerateNTSC{DEFAULT_FRAME_RATE_NTSC};
float FrameratePAL{DEFAULT_FRAME_RATE_PAL};
float LimitScalar = 1.0f;
float FramerateNTSC = DEFAULT_FRAME_RATE_NTSC;
float FrameratePAL = DEFAULT_FRAME_RATE_PAL;
AspectRatioType AspectRatio{AspectRatioType::RAuto4_3_3_2};
FMVAspectRatioSwitchType FMVAspectRatioSwitch{FMVAspectRatioSwitchType::Off};
GSInterlaceMode InterlaceMode{GSInterlaceMode::Automatic};
GSPostBilinearMode LinearPresent{ GSPostBilinearMode::BilinearSmooth };
AspectRatioType AspectRatio = AspectRatioType::RAuto4_3_3_2;
FMVAspectRatioSwitchType FMVAspectRatioSwitch = FMVAspectRatioSwitchType::Off;
GSInterlaceMode InterlaceMode = GSInterlaceMode::Automatic;
GSPostBilinearMode LinearPresent = GSPostBilinearMode::BilinearSmooth;
float StretchY{100.0f};
int Crop[4]{};
float StretchY = 100.0f;
int Crop[4] = {};
float OsdScale{100.0};
float OsdScale = 100.0;
GSRendererType Renderer{GSRendererType::Auto};
float UpscaleMultiplier{1.0f};
GSRendererType Renderer = GSRendererType::Auto;
float UpscaleMultiplier = 1.0f;
HWMipmapLevel HWMipmap{HWMipmapLevel::Automatic};
AccBlendLevel AccurateBlendingUnit{AccBlendLevel::Basic};
CRCHackLevel CRCHack{CRCHackLevel::Automatic};
BiFiltering TextureFiltering{BiFiltering::PS2};
TexturePreloadingLevel TexturePreloading{TexturePreloadingLevel::Full};
GSDumpCompressionMethod GSDumpCompression{GSDumpCompressionMethod::Zstandard};
GSHardwareDownloadMode HWDownloadMode{GSHardwareDownloadMode::Enabled};
GSCASMode CASMode{GSCASMode::Disabled};
int Dithering{2};
int MaxAnisotropy{0};
int SWExtraThreads{2};
int SWExtraThreadsHeight{4};
int TVShader{0};
s16 GetSkipCountFunctionId{-1};
s16 BeforeDrawFunctionId{-1};
int SkipDrawStart{0};
int SkipDrawEnd{0};
HWMipmapLevel HWMipmap = HWMipmapLevel::Automatic;
AccBlendLevel AccurateBlendingUnit = AccBlendLevel::Basic;
CRCHackLevel CRCHack = CRCHackLevel::Automatic;
BiFiltering TextureFiltering = BiFiltering::PS2;
TexturePreloadingLevel TexturePreloading = TexturePreloadingLevel::Full;
GSDumpCompressionMethod GSDumpCompression = GSDumpCompressionMethod::Zstandard;
GSHardwareDownloadMode HWDownloadMode = GSHardwareDownloadMode::Enabled;
GSCASMode CASMode = GSCASMode::Disabled;
u8 Dithering = 2;
u8 MaxAnisotropy = 0;
u8 TVShader = 0;
s16 GetSkipCountFunctionId = -1;
s16 BeforeDrawFunctionId = -1;
int SkipDrawStart = 0;
int SkipDrawEnd = 0;
int UserHacks_HalfBottomOverride{-1};
int UserHacks_HalfPixelOffset{0};
int UserHacks_RoundSprite{0};
int UserHacks_TCOffsetX{0};
int UserHacks_TCOffsetY{0};
int UserHacks_CPUSpriteRenderBW{0};
int UserHacks_CPUSpriteRenderLevel{0};
int UserHacks_CPUCLUTRender{0};
GSGPUTargetCLUTMode UserHacks_GPUTargetCLUTMode{GSGPUTargetCLUTMode::Disabled};
GSTextureInRtMode UserHacks_TextureInsideRt{GSTextureInRtMode::Disabled};
TriFiltering TriFilter{TriFiltering::Automatic};
int OverrideTextureBarriers{-1};
GSHWAutoFlushLevel UserHacks_AutoFlush = GSHWAutoFlushLevel::Disabled;
s8 UserHacks_HalfBottomOverride = -1;
s8 UserHacks_HalfPixelOffset = 0;
s8 UserHacks_RoundSprite = 0;
s32 UserHacks_TCOffsetX = 0;
s32 UserHacks_TCOffsetY = 0;
u8 UserHacks_CPUSpriteRenderBW = 0;
u8 UserHacks_CPUSpriteRenderLevel = 0;
u8 UserHacks_CPUCLUTRender = 0;
GSGPUTargetCLUTMode UserHacks_GPUTargetCLUTMode = GSGPUTargetCLUTMode::Disabled;
GSTextureInRtMode UserHacks_TextureInsideRt = GSTextureInRtMode::Disabled;
TriFiltering TriFilter = TriFiltering::Automatic;
s8 OverrideTextureBarriers = -1;
int CAS_Sharpness{50};
int ShadeBoost_Brightness{50};
int ShadeBoost_Contrast{50};
int ShadeBoost_Saturation{50};
int PNGCompressionLevel{1};
u8 CAS_Sharpness = 50;
u8 ShadeBoost_Brightness = 50;
u8 ShadeBoost_Contrast = 50;
u8 ShadeBoost_Saturation = 50;
u8 PNGCompressionLevel = 1;
int SaveN{0};
int SaveL{5000};
u16 SWExtraThreads = 2;
u16 SWExtraThreadsHeight = 4;
GSScreenshotSize ScreenshotSize{GSScreenshotSize::WindowResolution};
GSScreenshotFormat ScreenshotFormat{GSScreenshotFormat::PNG};
int ScreenshotQuality{50};
int SaveN = 0;
int SaveL = 5000;
std::string CaptureContainer{DEFAULT_CAPTURE_CONTAINER};
GSScreenshotSize ScreenshotSize = GSScreenshotSize::WindowResolution;
GSScreenshotFormat ScreenshotFormat = GSScreenshotFormat::PNG;
int ScreenshotQuality = 50;
std::string CaptureContainer = DEFAULT_CAPTURE_CONTAINER;
std::string VideoCaptureCodec;
std::string VideoCaptureParameters;
std::string AudioCaptureCodec;
std::string AudioCaptureParameters;
int VideoCaptureBitrate{DEFAULT_VIDEO_CAPTURE_BITRATE};
int VideoCaptureWidth{DEFAULT_VIDEO_CAPTURE_WIDTH};
int VideoCaptureHeight{DEFAULT_VIDEO_CAPTURE_HEIGHT};
int AudioCaptureBitrate{DEFAULT_AUDIO_CAPTURE_BITRATE};
int VideoCaptureBitrate = DEFAULT_VIDEO_CAPTURE_BITRATE;
int VideoCaptureWidth = DEFAULT_VIDEO_CAPTURE_WIDTH;
int VideoCaptureHeight = DEFAULT_VIDEO_CAPTURE_HEIGHT;
int AudioCaptureBitrate = DEFAULT_AUDIO_CAPTURE_BITRATE;
std::string Adapter;
std::string HWDumpDirectory;
+11
View File
@@ -598,6 +598,12 @@ u32 R5900DebugInterface::getPC()
return cpuRegs.pc;
}
// Taken from COP0.cpp
bool R5900DebugInterface::getCPCOND0()
{
return (((dmacRegs.stat.CIS | ~dmacRegs.pcr.CPC) & 0x3FF) == 0x3ff);
}
void R5900DebugInterface::setPc(u32 newPc)
{
cpuRegs.pc = newPc;
@@ -945,6 +951,11 @@ u32 R3000DebugInterface::getPC()
return psxRegs.pc;
}
bool R3000DebugInterface::getCPCOND0()
{
return false;
}
void R3000DebugInterface::setPc(u32 newPc)
{
psxRegs.pc = newPc;
+3
View File
@@ -77,6 +77,7 @@ public:
virtual u128 getHI() = 0;
virtual u128 getLO() = 0;
virtual u32 getPC() = 0;
virtual bool getCPCOND0() = 0;
virtual void setPc(u32 newPc) = 0;
virtual void setRegister(int cat, int num, u128 newValue) = 0;
@@ -129,6 +130,7 @@ public:
u128 getHI() override;
u128 getLO() override;
u32 getPC() override;
bool getCPCOND0() override;
void setPc(u32 newPc) override;
void setRegister(int cat, int num, u128 newValue) override;
[[nodiscard]] SymbolMap& GetSymbolMap() const override;
@@ -168,6 +170,7 @@ public:
u128 getHI() override;
u128 getLO() override;
u32 getPC() override;
bool getCPCOND0() override;
void setPc(u32 newPc) override;
void setRegister(int cat, int num, u128 newValue) override;
[[nodiscard]] SymbolMap& GetSymbolMap() const override;
+16 -2
View File
@@ -59,7 +59,7 @@ namespace MIPSAnalyst
const R5900::OPCODE& opcode = R5900::GetInstruction(op);
int branchType = (opcode.flags & BRANCHTYPE_MASK);
if ((opcode.flags & IS_BRANCH) && (branchType == BRANCHTYPE_BRANCH || branchType == BRANCHTYPE_BC1))
if ((opcode.flags & IS_BRANCH) && (branchType == BRANCHTYPE_BRANCH || branchType == BRANCHTYPE_BC1 || branchType == BRANCHTYPE_BC0))
return addr + 4 + ((signed short)(op&0xFFFF)<<2);
else
return INVALIDTARGET;
@@ -71,7 +71,7 @@ namespace MIPSAnalyst
const R5900::OPCODE& opcode = R5900::GetInstruction(op);
int branchType = (opcode.flags & BRANCHTYPE_MASK);
if ((opcode.flags & IS_BRANCH) && (branchType == BRANCHTYPE_BRANCH || branchType == BRANCHTYPE_BC1))
if ((opcode.flags & IS_BRANCH) && (branchType == BRANCHTYPE_BRANCH || branchType == BRANCHTYPE_BC1 || branchType == BRANCHTYPE_BC0))
{
if (!(opcode.flags & IS_LINKED))
return addr + 4 + ((signed short)(op&0xFFFF)<<2);
@@ -409,6 +409,20 @@ namespace MIPSAnalyst
value = info.cpu->getRegister(EECAT_FCR,31)._u32[0] & 0x00800000;
info.branchTarget = info.opcodeAddress + 4 + ((s16)(op&0xFFFF)<<2);
switch (opcode.flags & CONDTYPE_MASK)
{
case CONDTYPE_EQ:
info.conditionMet = value == 0;
break;
case CONDTYPE_NE:
info.conditionMet = value != 0;
break;
}
break;
case BRANCHTYPE_BC0:
info.isConditional = true;
value = info.cpu->getCPCOND0();
info.branchTarget = info.opcodeAddress + 4 + ((s16)(op&0xFFFF)<<2);
switch (opcode.flags & CONDTYPE_MASK)
{
case CONDTYPE_EQ:
+4
View File
@@ -38,6 +38,7 @@ SERIAL-12345: # !required! Serial number for the game, this is how games are loo
- GIFFIFOHack
- GoemonTlbHack
- IbitHack
- FullVU0SyncHack
- VUSyncHack
- VUOverflowHack
- SoftwareRendererFMVHack
@@ -220,6 +221,9 @@ These values are case-sensitive so take care. If you incorrectly specify a Game
* `IbitHack`
* VU I bit Hack avoid constant recompilation in some games (Scarface The World Is Yours, Crash Tag Team Racing).
* `FullVU0SyncHack`
* Increase the synchronisation points between COP2 and VU0.
* `VUSyncHack`
* Make the VU's run behind/in sync with the EE to fix some timing issues.
+2 -1
View File
@@ -101,6 +101,7 @@
"VIFFIFOHack",
"VuAddSubHack",
"VUOverflowHack",
"FullVU0SyncHack",
"VUSyncHack",
"XGKickHack"
]
@@ -114,7 +115,7 @@
"autoFlush": {
"type": "integer",
"minimum": 0,
"maximum": 1
"maximum": 2
},
"conservativeFramebuffer": {
"type": "integer",
+6 -2
View File
@@ -3182,6 +3182,8 @@ void FullscreenUI::DrawGraphicsSettingsPage()
static constexpr const char* s_half_pixel_offset_options[] = {
"Off (Default)", "Normal (Vertex)", "Special (Texture)", "Special (Texture - Aggressive)"};
static constexpr const char* s_round_sprite_options[] = {"Off (Default)", "Half", "Full"};
static constexpr const char* s_auto_flush_options[] = {
"Disabled (Default)", "Enabled (Sprites Only)", "Enabled (All Primitives)"};
DrawIntListSetting(bsi, "CRC Fix Level", "Applies manual fixes to difficult-to-emulate effects in the hardware renderers.",
"EmuCore/GS", "crc_hack_level", static_cast<int>(CRCHackLevel::Automatic), s_crc_fix_options, std::size(s_crc_fix_options),
@@ -3198,8 +3200,8 @@ void FullscreenUI::DrawGraphicsSettingsPage()
bsi, "Skip Draw Start", "Object range to skip drawing.", "EmuCore/GS", "UserHacks_SkipDraw_Start", 0, 0, 5000, 1);
DrawIntSpinBoxSetting(
bsi, "Skip Draw End", "Object range to skip drawing.", "EmuCore/GS", "UserHacks_SkipDraw_End", 0, 0, 5000, 1);
DrawToggleSetting(bsi, "Auto Flush (Hardware)", "Force a primitive flush when a framebuffer is also an input texture.",
"EmuCore/GS", "UserHacks_AutoFlush", false, manual_hw_fixes);
DrawIntListSetting(bsi, "Auto Flush (Hardware)", "Force a primitive flush when a framebuffer is also an input texture.",
"EmuCore/GS", "UserHacks_AutoFlushLevel", 0, s_auto_flush_options, std::size(s_auto_flush_options), 0, manual_hw_fixes);
DrawToggleSetting(bsi, "CPU Framebuffer Conversion", "Convert 4-bit and 8-bit frame buffer on the CPU instead of the GPU.",
"EmuCore/GS", "UserHacks_CPU_FB_Conversion", false, manual_hw_fixes);
DrawToggleSetting(bsi, "Disable Depth Support", "Disable the support of depth buffer in the texture cache.", "EmuCore/GS",
@@ -3748,6 +3750,8 @@ void FullscreenUI::DrawControllerSettingsPage()
DrawToggleSetting(bsi, ICON_FA_WIFI " SDL DualShock 4 / DualSense Enhanced Mode",
"Provides vibration and LED control support over Bluetooth.", "InputSources", "SDLControllerEnhancedMode", false,
bsi->GetBoolValue("InputSources", "SDL", true), false);
#endif
#if defined(SDL_BUILD) && defined(_WIN32)
DrawToggleSetting(bsi, ICON_FA_COG " SDL Raw Input", "Allow SDL to use raw access to input devices.", "InputSources", "SDLRawInput",
false, bsi->GetBoolValue("InputSources", "SDL", true), false);
#endif
+4 -4
View File
@@ -407,11 +407,11 @@ void ImGuiManager::DrawSettingsOverlay()
if (GSConfig.UserHacks_CPUCLUTRender != 0)
APPEND("CCLUT={} ", GSConfig.UserHacks_CPUCLUTRender);
if (GSConfig.UserHacks_GPUTargetCLUTMode != GSGPUTargetCLUTMode::Disabled)
APPEND("GCLUT={} ", static_cast<int>(GSConfig.UserHacks_GPUTargetCLUTMode));
APPEND("GCLUT={} ", static_cast<unsigned>(GSConfig.UserHacks_GPUTargetCLUTMode));
if (GSConfig.SkipDrawStart != 0 || GSConfig.SkipDrawEnd != 0)
APPEND("SD={}/{} ", GSConfig.SkipDrawStart, GSConfig.SkipDrawEnd);
if (GSConfig.UserHacks_TextureInsideRt != GSTextureInRtMode::Disabled)
APPEND("TexRT={} ", static_cast<int>(GSConfig.UserHacks_TextureInsideRt));
APPEND("TexRT={} ", static_cast<unsigned>(GSConfig.UserHacks_TextureInsideRt));
if (GSConfig.UserHacks_WildHack)
APPEND("WA ");
if (GSConfig.UserHacks_BilinearHack)
@@ -422,8 +422,8 @@ void ImGuiManager::DrawSettingsOverlay()
APPEND("MS ");
if (GSConfig.UserHacks_AlignSpriteX)
APPEND("AS ");
if (GSConfig.UserHacks_AutoFlush)
APPEND("ATFL ");
if (GSConfig.UserHacks_AutoFlush != GSHWAutoFlushLevel::Disabled)
APPEND("ATFL={} ", static_cast<unsigned>(GSConfig.UserHacks_AutoFlush));
if (GSConfig.UserHacks_CPUFBConversion)
APPEND("FBC ");
if (GSConfig.UserHacks_ReadTCOnClose)
+26 -78
View File
@@ -133,52 +133,8 @@ static RenderAPI GetAPIForRenderer(GSRendererType renderer)
}
}
static void UpdateExclusiveFullscreen(bool force_off)
static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail, bool recreate_window)
{
if (!g_gs_device->SupportsExclusiveFullscreen())
return;
std::string fullscreen_mode = Host::GetBaseStringSettingValue("EmuCore/GS", "FullscreenMode", "");
u32 width, height;
float refresh_rate;
const bool wants_fullscreen = !force_off && Host::IsFullscreen() && !fullscreen_mode.empty() &&
GSDevice::ParseFullscreenMode(fullscreen_mode, &width, &height, &refresh_rate);
const bool is_fullscreen = g_gs_device->IsExclusiveFullscreen();
if (wants_fullscreen == is_fullscreen)
return;
if (wants_fullscreen)
{
Console.WriteLn("Trying to acquire exclusive fullscreen...");
if (g_gs_device->SetExclusiveFullscreen(true, width, height, refresh_rate))
{
Host::AddKeyedOSDMessage(
"UpdateExclusiveFullscreen", "Acquired exclusive fullscreen.", Host::OSD_INFO_DURATION);
}
else
{
Host::AddKeyedOSDMessage(
"UpdateExclusiveFullscreen", "Failed to acquire exclusive fullscreen.", Host::OSD_WARNING_DURATION);
}
}
else
{
Console.WriteLn("Leaving exclusive fullscreen...");
g_gs_device->SetExclusiveFullscreen(false, 0, 0, 0.0f);
Host::AddKeyedOSDMessage("UpdateExclusiveFullscreen", "Lost exclusive fullscreen.", Host::OSD_INFO_DURATION);
}
}
static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail, bool reopening, bool recreate_window)
{
std::optional<WindowInfo> wi = reopening ? Host::UpdateRenderWindow(recreate_window) : Host::AcquireRenderWindow();
if (!wi.has_value())
{
Console.Error("Failed to acquire render window.");
return false;
}
const RenderAPI new_api = GetAPIForRenderer(renderer);
switch (new_api)
{
@@ -212,8 +168,7 @@ static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail, bool
return false;
}
const VsyncMode vsync_mode = Host::GetEffectiveVSyncMode();
bool okay = g_gs_device->Create(wi.value(), vsync_mode);
bool okay = g_gs_device->Create();
if (okay)
{
okay = ImGuiManager::Initialize();
@@ -239,24 +194,17 @@ static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail, bool
Console.WriteLn(Color_StrongGreen, "%s Graphics Driver Info:", GSDevice::RenderAPIToString(new_api));
Console.Indent().WriteLn(g_gs_device->GetDriverInfo());
// Switch to exclusive fullscreen if enabled.
UpdateExclusiveFullscreen(false);
return true;
}
static void CloseGSDevice(bool clear_state, bool reopening)
static void CloseGSDevice(bool clear_state)
{
if (!g_gs_device)
return;
UpdateExclusiveFullscreen(true);
ImGuiManager::Shutdown(clear_state);
g_gs_device->Destroy();
g_gs_device.reset();
if (!reopening)
Host::ReleaseRenderWindow();
}
static bool OpenGSRenderer(GSRendererType renderer, u8* basemem)
@@ -336,18 +284,18 @@ bool GSreopen(bool recreate_device, bool recreate_renderer, const Pcsx2Config::G
{
// We need a new render window when changing APIs.
const bool recreate_window = (g_gs_device->GetRenderAPI() != GetAPIForRenderer(GSConfig.Renderer));
CloseGSDevice(false, true);
CloseGSDevice(false);
if (!OpenGSDevice(GSConfig.Renderer, false, true, recreate_window) ||
if (!OpenGSDevice(GSConfig.Renderer, false, recreate_window) ||
(recreate_renderer && !OpenGSRenderer(GSConfig.Renderer, basemem)))
{
Host::AddKeyedOSDMessage(
"GSReopenFailed", "Failed to reopen, restoring old configuration.", Host::OSD_CRITICAL_ERROR_DURATION);
CloseGSDevice(false, true);
CloseGSDevice(false);
GSConfig = old_config;
if (!OpenGSDevice(GSConfig.Renderer, false, true, recreate_window) ||
if (!OpenGSDevice(GSConfig.Renderer, false, recreate_window) ||
(recreate_renderer && !OpenGSRenderer(GSConfig.Renderer, basemem)))
{
pxFailRel("Failed to reopen GS on old config");
@@ -387,12 +335,12 @@ bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* b
GSConfig = config;
GSConfig.Renderer = renderer;
bool res = OpenGSDevice(renderer, true, false, false);
bool res = OpenGSDevice(renderer, true, false);
if (res)
{
res = OpenGSRenderer(renderer, basemem);
if (!res)
CloseGSDevice(true, false);
CloseGSDevice(true);
}
if (!res)
@@ -410,7 +358,8 @@ bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* b
void GSclose()
{
CloseGSRenderer();
CloseGSDevice(true, false);
CloseGSDevice(true);
Host::ReleaseRenderWindow();
}
void GSreset(bool hardware_reset)
@@ -516,7 +465,7 @@ void GSvsync(u32 field, bool registers_written)
{
try
{
g_gs_renderer->VSync(field, registers_written);
g_gs_renderer->VSync(field, registers_written, g_gs_renderer->IsIdleFrame());
}
catch (GSRecoverableError)
{
@@ -626,24 +575,12 @@ void GSResizeDisplayWindow(int width, int height, float scale)
void GSUpdateDisplayWindow()
{
UpdateExclusiveFullscreen(true);
g_gs_device->DestroySurface();
const std::optional<WindowInfo> wi = Host::UpdateRenderWindow(false);
if (!wi.has_value())
if (!g_gs_device->UpdateWindow())
{
pxFailRel("Failed to get window info after update.");
Host::ReportErrorAsync("Error", "Failed to change window after update. The log may contain more information.");
return;
}
if (!g_gs_device->ChangeWindow(wi.value()))
{
pxFailRel("Failed to change window after update.");
return;
}
UpdateExclusiveFullscreen(false);
ImGuiManager::WindowResized();
}
@@ -652,6 +589,16 @@ void GSSetVSyncMode(VsyncMode mode)
g_gs_device->SetVSync(mode);
}
bool GSWantsExclusiveFullscreen()
{
if (!g_gs_device || !g_gs_device->SupportsExclusiveFullscreen())
return false;
u32 width, height;
float refresh_rate;
return GSDevice::GetRequestedExclusiveFullscreenMode(&width, &height, &refresh_rate);
}
bool GSGetHostRefreshRate(float* refresh_rate)
{
if (!g_gs_device)
@@ -748,12 +695,13 @@ void GSgetStats(std::string& info)
}
else
{
fmt::format_to(std::back_inserter(info), "{} HW | {} P | {} D | {} DC | {} B | {} RB | {} TC | {} TU",
fmt::format_to(std::back_inserter(info), "{} HW | {} P | {} D | {} DC | {} B | {} RP | {} RB | {} TC | {} TU",
api_name,
(int)pm.Get(GSPerfMon::Prim),
(int)pm.Get(GSPerfMon::Draw),
(int)std::ceil(pm.Get(GSPerfMon::DrawCalls)),
(int)std::ceil(pm.Get(GSPerfMon::Barriers)),
(int)std::ceil(pm.Get(GSPerfMon::RenderPasses)),
(int)std::ceil(pm.Get(GSPerfMon::Readbacks)),
(int)std::ceil(pm.Get(GSPerfMon::TextureCopies)),
(int)std::ceil(pm.Get(GSPerfMon::TextureUploads)));
+3 -5
View File
@@ -99,6 +99,7 @@ void GSResizeDisplayWindow(int width, int height, float scale);
void GSUpdateDisplayWindow();
void GSSetVSyncMode(VsyncMode mode);
bool GSWantsExclusiveFullscreen();
bool GSGetHostRefreshRate(float* refresh_rate);
void GSGetAdaptersAndFullscreenModes(
GSRendererType renderer, std::vector<std::string>* adapters, std::vector<std::string>* fullscreen_modes);
@@ -128,11 +129,8 @@ struct GSRecoverableError : GSError
namespace Host
{
/// Called when the GS is creating a render device.
std::optional<WindowInfo> AcquireRenderWindow();
/// Called on the MTGS thread when a request to update the display is received.
/// This could be a fullscreen transition, for example.
std::optional<WindowInfo> UpdateRenderWindow(bool recreate_window);
/// This could also be fullscreen transition.
std::optional<WindowInfo> AcquireRenderWindow(bool recreate_window);
/// Called before drawing the OSD and other display elements.
void BeginPresentFrame();
+36
View File
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="FastList&lt;*&gt;">
<DisplayString>{{ size={m_free_indexes_stack_top} }}</DisplayString>
<Expand>
<Item Name="[size]" ExcludeView="simple">m_free_indexes_stack_top</Item>
<Item Name="[capacity]" ExcludeView="simple">m_capacity - 1</Item>
<CustomListItems MaxItemsPerView="5000" ExcludeView="simple">
<Variable Name="index" InitialValue="m_buffer[0].next_index" />
<Size>m_free_indexes_stack_top</Size>
<Loop>
<Item>m_buffer[index].data,na</Item>
<Exec>index = m_buffer[index].next_index</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
<Type Name="GSVector2T&lt;*&gt;">
<DisplayString>{{ {x}, {y} }}</DisplayString>
</Type>
<Type Name="GSVector4">
<DisplayString>{{ {F32[0]}, {F32[1]}, {F32[2]}, {F32[3]} }}</DisplayString>
</Type>
<Type Name="GSVector4i">
<DisplayString>{{ {I32[0]}, {I32[1]}, {I32[2]}, {I32[3]} }}</DisplayString>
</Type>
<Type Name="GSTextureCache::Target">
<DisplayString Condition="m_type == 0">{{ RT @ BP={m_TEX0.TBP0,X}-{m_end_block,X} BW={m_TEX0.TBW} PSM={m_TEX0.PSM,X} {m_unscaled_size.x}x{m_unscaled_size.y} {m_valid.z},{m_valid.w} }}</DisplayString>
<DisplayString Condition="m_type == 1">{{ Depth @ BP={m_TEX0.TBP0,X}-{m_end_block,X} BW={m_TEX0.TBW} PSM={m_TEX0.PSM,X} {m_unscaled_size.x}x{m_unscaled_size.y} {m_valid.z},{m_valid.w} }}</DisplayString>
</Type>
</AutoVisualizer>
+2 -1
View File
@@ -15,6 +15,7 @@
#include "PrecompiledHeader.h"
#include "GS/GSClut.h"
#include "GS/GSExtra.h"
#include "GS/GSLocalMemory.h"
#include "GS/GSGL.h"
#include "GS/Renderers/Common/GSDevice.h"
@@ -27,7 +28,7 @@ GSClut::GSClut(GSLocalMemory* mem)
static constexpr u32 CLUT_ALLOC_SIZE = 4096 * 2;
// 1k + 1k for mirrored area simulating wrapping memory
m_clut = static_cast<u16*>(_aligned_malloc(CLUT_ALLOC_SIZE, 32));
m_clut = static_cast<u16*>(_aligned_malloc(CLUT_ALLOC_SIZE, VECTOR_ALIGNMENT));
if (!m_clut)
throw std::bad_alloc();
+13 -1
View File
@@ -15,8 +15,9 @@
#pragma once
#include "GSVector.h"
#include "GS/GSVector.h"
#include "pcsx2/Config.h"
#include "common/Align.h"
/// Like `memcmp(&a, &b, sizeof(T)) == 0` but faster
template <typename T>
@@ -117,6 +118,17 @@ static constexpr u32 MAX_SKIPPED_DUPLICATE_FRAMES = 3;
extern void* GSAllocateWrappedMemory(size_t size, size_t repeat);
extern void GSFreeWrappedMemory(void* ptr, size_t size, size_t repeat);
/// We want all allocations and pitches to be aligned to 32-bit, regardless of whether we're
/// SSE4 or AVX2, because of multi-ISA.
static constexpr u32 VECTOR_ALIGNMENT = 32;
/// Aligns allocation/pitch size to preferred host size.
template<typename T>
__fi static T VectorAlign(T value)
{
return Common::AlignUpPow2(value, VECTOR_ALIGNMENT);
}
// clang-format off
#ifdef __POSIX__
+3 -3
View File
@@ -286,7 +286,7 @@ GSPixelOffset* GSLocalMemory::GetPixelOffset(const GIFRegFRAME& FRAME, const GIF
return it->second;
}
GSPixelOffset* off = (GSPixelOffset*)_aligned_malloc(sizeof(GSPixelOffset), 32);
GSPixelOffset* off = (GSPixelOffset*)_aligned_malloc(sizeof(GSPixelOffset), VECTOR_ALIGNMENT);
off->hash = hash;
off->fbp = fbp;
@@ -339,7 +339,7 @@ GSPixelOffset4* GSLocalMemory::GetPixelOffset4(const GIFRegFRAME& FRAME, const G
return it->second;
}
GSPixelOffset4* off = (GSPixelOffset4*)_aligned_malloc(sizeof(GSPixelOffset4), 32);
GSPixelOffset4* off = (GSPixelOffset4*)_aligned_malloc(sizeof(GSPixelOffset4), VECTOR_ALIGNMENT);
off->hash = hash;
off->fbp = fbp;
@@ -544,7 +544,7 @@ void GSLocalMemory::SaveBMP(const std::string& fn, u32 bp, u32 bw, u32 psm, int
{
int pitch = w * 4;
int size = pitch * h;
void* bits = _aligned_malloc(size, 32);
void* bits = _aligned_malloc(size, VECTOR_ALIGNMENT);
GIFRegTEX0 TEX0;
+5 -4
View File
@@ -19,8 +19,9 @@
#include "common/FileSystem.h"
#include "common/StringUtil.h"
#include "GSDump.h"
#include "GSLzma.h"
#include "GS/GSDump.h"
#include "GS/GSLzma.h"
#include "GS/GSExtra.h"
using namespace GSDumpTypes;
@@ -273,8 +274,8 @@ void GSDumpLzma::Initialize()
}
m_buff_size = 1024*1024;
m_area = (uint8_t*)_aligned_malloc(m_buff_size, 32);
m_inbuf = (uint8_t*)_aligned_malloc(BUFSIZ, 32);
m_area = (uint8_t*)_aligned_malloc(m_buff_size, VECTOR_ALIGNMENT);
m_inbuf = (uint8_t*)_aligned_malloc(BUFSIZ, VECTOR_ALIGNMENT);
m_avail = 0;
m_start = 0;
+1 -1
View File
@@ -27,9 +27,9 @@ public:
Swizzle,
Unswizzle,
Fillrate,
Quad,
SyncPoint,
Barriers,
RenderPasses,
CounterLast,
// Reused counters for HW.
+44 -45
View File
@@ -30,7 +30,7 @@ int GSState::s_transfer_n = 0;
static __fi bool IsAutoFlushEnabled()
{
return (GSConfig.Renderer == GSRendererType::SW) ? GSConfig.AutoFlushSW : GSConfig.UserHacks_AutoFlush;
return (GSConfig.Renderer == GSRendererType::SW) ? GSConfig.AutoFlushSW : (GSConfig.UserHacks_AutoFlush != GSHWAutoFlushLevel::Disabled);
}
static __fi bool IsFirstProvokingVertex()
@@ -188,6 +188,7 @@ void GSState::Reset(bool hardware_reset)
m_vertex.next = 0;
m_index.tail = 0;
m_scanmask_used = 0;
m_texflush_flag = false;
m_dirty_gs_regs = 0;
m_backed_up_ctx = -1;
@@ -407,9 +408,6 @@ void GSState::DumpVertices(const std::string& filename)
case GSFlushReason::CLUTCHANGE:
file << "CLUT CHANGE (RELOAD REQ)";
break;
case GSFlushReason::TEXFLUSH:
file << "TEXFLUSH CALL";
break;
case GSFlushReason::GSTRANSFER:
file << "GS TRANSFER";
break;
@@ -1168,12 +1166,7 @@ void GSState::GIFRegHandlerFOGCOL(const GIFReg* RESTRICT r)
void GSState::GIFRegHandlerTEXFLUSH(const GIFReg* RESTRICT r)
{
GL_REG("TEXFLUSH = 0x%x_%x PRIM TME %x", r->U32[1], r->U32[0], PRIM->TME);
// Some games do a single sprite draw to itself, then flush the texture cache, then use that texture again.
// This won't get picked up by the new autoflush logic (which checks for page crossings for the PS2 Texture Cache flush)
// so we need to do it here.
if (IsAutoFlushEnabled() && IsAutoFlushDraw())
Flush(GSFlushReason::TEXFLUSH);
m_texflush_flag = true;
}
template <int i>
@@ -1580,6 +1573,9 @@ void GSState::FlushPrim()
{
GL_REG("FlushPrim ctxt %d", PRIM->CTXT);
// clear texture cache flushed flag, since we're reading from it
m_texflush_flag = PRIM->TME ? false : m_texflush_flag;
// internal frame rate detection based on sprite blits to the display framebuffer
{
const u32 FRAME_FBP = m_context->FRAME.FBP;
@@ -2638,31 +2634,31 @@ void GSState::GrowVertexBuffer()
{
const u32 maxcount = std::max<u32>(m_vertex.maxcount * 3 / 2, 10000);
GSVertex* vertex = (GSVertex*)_aligned_malloc(sizeof(GSVertex) * maxcount, 32);
GSVertex* vertex = static_cast<GSVertex*>(_aligned_malloc(sizeof(GSVertex) * maxcount, 32));
// Worst case index list is a list of points with vs expansion, 6 indices per point
u32* index = (u32*)_aligned_malloc(sizeof(u32) * maxcount * 6, 32);
u16* index = static_cast<u16*>(_aligned_malloc(sizeof(u16) * maxcount * 6, 32));
if (vertex == NULL || index == NULL)
if (!vertex || !index)
{
const u32 vert_byte_count = sizeof(GSVertex) * maxcount;
const u32 idx_byte_count = sizeof(u32) * maxcount * 3;
const u32 idx_byte_count = sizeof(u16) * maxcount * 3;
Console.Error("GS: failed to allocate %zu bytes for verticles and %zu for indices.",
Console.Error("GS: failed to allocate %zu bytes for vertices and %zu for indices.",
vert_byte_count, idx_byte_count);
throw GSError();
}
if (m_vertex.buff != NULL)
if (m_vertex.buff)
{
memcpy(vertex, m_vertex.buff, sizeof(GSVertex) * m_vertex.tail);
std::memcpy(vertex, m_vertex.buff, sizeof(GSVertex) * m_vertex.tail);
_aligned_free(m_vertex.buff);
}
if (m_index.buff != NULL)
if (m_index.buff)
{
memcpy(index, m_index.buff, sizeof(u32) * m_index.tail);
std::memcpy(index, m_index.buff, sizeof(u16) * m_index.tail);
_aligned_free(m_index.buff);
}
@@ -2776,9 +2772,9 @@ GSState::PRIM_OVERLAP GSState::PrimitiveOverlap()
return overlap;
}
__forceinline bool GSState::IsAutoFlushDraw()
__forceinline bool GSState::IsAutoFlushDraw(u32 prim)
{
if (!PRIM->TME)
if (!PRIM->TME || (GSConfig.UserHacks_AutoFlush == GSHWAutoFlushLevel::SpritesOnly && prim != GS_SPRITE))
return false;
const u32 frame_mask = GSLocalMemory::m_psm[m_context->TEX0.PSM].fmsk;
@@ -2846,13 +2842,13 @@ template<u32 prim, bool index_swap>
__forceinline void GSState::HandleAutoFlush()
{
// Kind of a cheat, making the assumption that 2 consecutive fan/strip triangles won't overlap each other (*should* be safe)
if ((m_index.tail & 1) && (PRIM->PRIM == GS_TRIANGLESTRIP || PRIM->PRIM == GS_TRIANGLEFAN))
if ((m_index.tail & 1) && (prim == GS_TRIANGLESTRIP || prim == GS_TRIANGLEFAN))
return;
// To briefly explain what's going on here, what we are checking for is draws over a texture when the source and destination are themselves.
// Because one page of the texture gets buffered in the Texture Cache (the PS2's one) if any of those pixels are overwritten, you still read the old data.
// So we need to calculate if a page boundary is being crossed for the format it is in and if the same part of the texture being written and read inside the draw.
if (IsAutoFlushDraw())
if (IsAutoFlushDraw(prim))
{
int n = 1;
u32 buff[3];
@@ -2955,7 +2951,7 @@ __forceinline void GSState::HandleAutoFlush()
const GSVector4i tex_page = tex_rect.xyxy() & page_mask;
// Crossed page since last draw end
if(!tex_page.eq(last_tex_page))
if (!tex_page.eq(last_tex_page) || m_texflush_flag)
{
const u32 frame_mask = GSLocalMemory::m_psm[m_context->TEX0.PSM].fmsk;
const bool frame_hit = (m_context->FRAME.Block() == m_context->TEX0.TBP0) && !(m_context->TEST.ATE && m_context->TEST.ATST == 0 && m_context->TEST.AFAIL == 2) && ((m_context->FRAME.FBMSK & frame_mask) != frame_mask);
@@ -3063,21 +3059,24 @@ static constexpr u32 MaxVerticesForPrim(u32 prim)
{
switch (prim)
{
// Four indices per 1 vertex.
case GS_POINTLIST:
case GS_INVALID:
// Needed due to expansion in hardware renderers.
// Indices are shifted left by 2 to form quads.
case GS_LINELIST:
case GS_LINESTRIP:
return (std::numeric_limits<u16>::max() / 4) - 4;
// Four indices per two vertices.
case GS_SPRITE:
return (std::numeric_limits<u16>::max() / 2) - 2;
case GS_LINELIST:
case GS_LINESTRIP:
case GS_TRIANGLELIST:
case GS_TRIANGLESTRIP:
case GS_TRIANGLEFAN:
default:
return 0;
return (std::numeric_limits<u16>::max() - 3);
}
}
@@ -3229,19 +3228,19 @@ __forceinline void GSState::VertexKick(u32 skip)
m_backed_up_ctx = m_env.PRIM.CTXT;
}
u32* RESTRICT buff = &m_index.buff[m_index.tail];
u16* RESTRICT buff = &m_index.buff[m_index.tail];
switch (prim)
{
case GS_POINTLIST:
buff[0] = head + 0;
buff[0] = static_cast<u16>(head + 0);
m_vertex.head = head + 1;
m_vertex.next = head + 1;
m_index.tail += 1;
break;
case GS_LINELIST:
buff[0] = head + (index_swap ? 1 : 0);
buff[1] = head + (index_swap ? 0 : 1);
buff[0] = static_cast<u16>(head + (index_swap ? 1 : 0));
buff[1] = static_cast<u16>(head + (index_swap ? 0 : 1));
m_vertex.head = head + 2;
m_vertex.next = head + 2;
m_index.tail += 2;
@@ -3254,16 +3253,16 @@ __forceinline void GSState::VertexKick(u32 skip)
head = next;
m_vertex.tail = next + 2;
}
buff[0] = head + (index_swap ? 1 : 0);
buff[1] = head + (index_swap ? 0 : 1);
buff[0] = static_cast<u16>(head + (index_swap ? 1 : 0));
buff[1] = static_cast<u16>(head + (index_swap ? 0 : 1));
m_vertex.head = head + 1;
m_vertex.next = head + 2;
m_index.tail += 2;
break;
case GS_TRIANGLELIST:
buff[0] = head + (index_swap ? 2 : 0);
buff[1] = head + 1;
buff[2] = head + (index_swap ? 0 : 2);
buff[0] = static_cast<u16>(head + (index_swap ? 2 : 0));
buff[1] = static_cast<u16>(head + 1);
buff[2] = static_cast<u16>(head + (index_swap ? 0 : 2));
m_vertex.head = head + 3;
m_vertex.next = head + 3;
m_index.tail += 3;
@@ -3277,24 +3276,24 @@ __forceinline void GSState::VertexKick(u32 skip)
head = next;
m_vertex.tail = next + 3;
}
buff[0] = head + (index_swap ? 2 : 0);
buff[1] = head + 1;
buff[2] = head + (index_swap ? 0 : 2);
buff[0] = static_cast<u16>(head + (index_swap ? 2 : 0));
buff[1] = static_cast<u16>(head + 1);
buff[2] = static_cast<u16>(head + (index_swap ? 0 : 2));
m_vertex.head = head + 1;
m_vertex.next = head + 3;
m_index.tail += 3;
break;
case GS_TRIANGLEFAN:
// TODO: remove gaps, next == head && head < tail - 3 || next > head && next < tail - 2 (very rare)
buff[0] = index_swap ? (tail - 1) : (head + 0);
buff[1] = tail - 2;
buff[2] = index_swap ? (head + 0) : (tail - 1);
buff[0] = static_cast<u16>(index_swap ? (tail - 1) : (head + 0));
buff[1] = static_cast<u16>(tail - 2);
buff[2] = static_cast<u16>(index_swap ? (head + 0) : (tail - 1));
m_vertex.next = tail;
m_index.tail += 3;
break;
case GS_SPRITE:
buff[0] = head + 0;
buff[1] = head + 1;
buff[0] = static_cast<u16>(head + 0);
buff[1] = static_cast<u16>(head + 1);
m_vertex.head = head + 2;
m_vertex.next = head + 2;
m_index.tail += 2;
+15 -17
View File
@@ -144,10 +144,6 @@ protected:
GSVector4i m_scissor = {};
GSVector4i m_ofxy = {};
u8 m_scanmask_used = 0;
bool tex_flushed = true;
bool m_isPackedUV_HackFlag = false;
struct
{
GSVertex* buff;
@@ -158,7 +154,7 @@ protected:
struct
{
u32* buff;
u16* buff;
u32 tail;
} m_index = {};
@@ -168,7 +164,7 @@ protected:
void UpdateVertexKick();
void GrowVertexBuffer();
bool IsAutoFlushDraw();
bool IsAutoFlushDraw(u32 prim);
template<u32 prim, bool index_swap>
void HandleAutoFlush();
void CLUTAutoFlush(u32 prim);
@@ -228,6 +224,9 @@ public:
std::unique_ptr<GSDumpBase> m_dump;
bool m_nativeres = false;
bool m_mipmap = false;
bool m_texflush_flag = false;
bool m_isPackedUV_HackFlag = false;
u8 m_scanmask_used = 0;
u8 m_force_preload = 0;
u32 m_dirty_gs_regs = 0;
int m_backed_up_ctx = 0;
@@ -268,17 +267,16 @@ public:
RESET = 1 << 1,
CONTEXTCHANGE = 1 << 2,
CLUTCHANGE = 1 << 3,
TEXFLUSH = 1 << 4,
GSTRANSFER = 1 << 5,
UPLOADDIRTYTEX = 1 << 6,
LOCALTOLOCALMOVE = 1 << 7,
DOWNLOADFIFO = 1 << 8,
SAVESTATE = 1 << 9,
LOADSTATE = 1 << 10,
AUTOFLUSH = 1 << 11,
VSYNC = 1 << 12,
GSREOPEN = 1 << 13,
VERTEXCOUNT = 1 << 14,
GSTRANSFER = 1 << 4,
UPLOADDIRTYTEX = 1 << 5,
LOCALTOLOCALMOVE = 1 << 6,
DOWNLOADFIFO = 1 << 7,
SAVESTATE = 1 << 8,
LOADSTATE = 1 << 9,
AUTOFLUSH = 1 << 10,
VSYNC = 1 << 11,
GSREOPEN = 1 << 12,
VERTEXCOUNT = 1 << 13,
};
GSFlushReason m_state_flush_reason = UNKNOWN;
+24
View File
@@ -28,6 +28,16 @@ class alignas(16) GSVector4i
{
}
constexpr GSVector4i(cxpr_init_tag, short s0, short s1, short s2, short s3, short s4, short s5, short s6, short s7)
: I16{s0, s1, s2, s3, s4, s5, s6, s7}
{
}
constexpr GSVector4i(cxpr_init_tag, char b0, char b1, char b2, char b3, char b4, char b5, char b6, char b7, char b8, char b9, char b10, char b11, char b12, char b13, char b14, char b15)
: I8{b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15}
{
}
public:
union
{
@@ -62,6 +72,16 @@ public:
return GSVector4i(cxpr_init, x, x, x, x);
}
constexpr static GSVector4i cxpr16(short s0, short s1, short s2, short s3, short s4, short s5, short s6, short s7)
{
return GSVector4i(cxpr_init, s0, s1, s2, s3, s4, s5, s6, s7);
}
constexpr static GSVector4i cxpr8(char b0, char b1, char b2, char b3, char b4, char b5, char b6, char b7, char b8, char b9, char b10, char b11, char b12, char b13, char b14, char b15)
{
return GSVector4i(cxpr_init, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15);
}
__forceinline GSVector4i(int x, int y, int z, int w)
{
m = _mm_set_epi32(w, z, y, x);
@@ -2001,6 +2021,10 @@ public:
return v;
}
__forceinline static GSVector4i broadcast16(u16 value)
{
return GSVector4i(_mm_set1_epi16(value));
}
__forceinline static GSVector4i zero() { return GSVector4i(_mm_setzero_si128()); }
+112 -91
View File
@@ -18,6 +18,8 @@
#include "GS/GSGL.h"
#include "GS/GS.h"
#include "Host.h"
#include "HostSettings.h"
#include "common/Align.h"
#include "common/StringUtil.h"
#include "imgui.h"
@@ -90,7 +92,7 @@ GSDevice::GSDevice() = default;
GSDevice::~GSDevice()
{
// should've been cleaned up in Destroy()
pxAssert(m_pool.empty() && !m_merge && !m_weavebob && !m_blend && !m_mad && !m_target_tmp && !m_cas);
pxAssert(m_pool[0].empty() && m_pool[1].empty() && !m_merge && !m_weavebob && !m_blend && !m_mad && !m_target_tmp && !m_cas);
}
const char* GSDevice::RenderAPIToString(RenderAPI api)
@@ -112,14 +114,16 @@ const char* GSDevice::RenderAPIToString(RenderAPI api)
}
}
bool GSDevice::ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate)
bool GSDevice::GetRequestedExclusiveFullscreenMode(u32* width, u32* height, float* refresh_rate)
{
const std::string mode = Host::GetBaseStringSettingValue("EmuCore/GS", "FullscreenMode", "");
if (!mode.empty())
{
const std::string_view mode_view = mode;
std::string_view::size_type sep1 = mode.find('x');
if (sep1 != std::string_view::npos)
{
std::optional<u32> owidth = StringUtil::FromChars<u32>(mode.substr(0, sep1));
std::optional<u32> owidth = StringUtil::FromChars<u32>(mode_view.substr(0, sep1));
sep1++;
while (sep1 < mode.length() && std::isspace(mode[sep1]))
@@ -130,7 +134,7 @@ bool GSDevice::ParseFullscreenMode(const std::string_view& mode, u32* width, u32
std::string_view::size_type sep2 = mode.find('@', sep1);
if (sep2 != std::string_view::npos)
{
std::optional<u32> oheight = StringUtil::FromChars<u32>(mode.substr(sep1, sep2 - sep1));
std::optional<u32> oheight = StringUtil::FromChars<u32>(mode_view.substr(sep1, sep2 - sep1));
sep2++;
while (sep2 < mode.length() && std::isspace(mode[sep2]))
@@ -138,7 +142,7 @@ bool GSDevice::ParseFullscreenMode(const std::string_view& mode, u32* width, u32
if (oheight.has_value() && sep2 < mode.length())
{
std::optional<float> orefresh_rate = StringUtil::FromChars<float>(mode.substr(sep2));
std::optional<float> orefresh_rate = StringUtil::FromChars<float>(mode_view.substr(sep2));
if (orefresh_rate.has_value())
{
*width = owidth.value();
@@ -165,9 +169,9 @@ std::string GSDevice::GetFullscreenModeString(u32 width, u32 height, float refre
void GSDevice::GenerateExpansionIndexBuffer(void* buffer)
{
static constexpr u32 MAX_INDEX = std::numeric_limits<u16>::max();
static constexpr u32 MAX_INDEX = EXPAND_BUFFER_SIZE / 6 / sizeof(u16);
u32* idx_buffer = static_cast<u32*>(buffer);
u16* idx_buffer = static_cast<u16*>(buffer);
for (u32 i = 0; i < MAX_INDEX; i++)
{
const u32 base = i * 4;
@@ -180,10 +184,9 @@ void GSDevice::GenerateExpansionIndexBuffer(void* buffer)
}
}
bool GSDevice::Create(const WindowInfo& wi, VsyncMode vsync)
bool GSDevice::Create()
{
m_window_info = wi;
m_vsync_mode = vsync;
m_vsync_mode = Host::GetEffectiveVSyncMode();
return true;
}
@@ -199,6 +202,20 @@ void GSDevice::Destroy()
PurgePool();
}
bool GSDevice::AcquireWindow(bool recreate_window)
{
std::optional<WindowInfo> wi = Host::AcquireRenderWindow(recreate_window);
if (!wi.has_value())
{
Console.Error("Failed to acquire render window.");
Host::ReportErrorAsync("Error", "Failed to acquire render window. The log may have more information.");
return false;
}
m_window_info = std::move(wi.value());
return true;
}
bool GSDevice::GetHostRefreshRate(float* refresh_rate)
{
if (m_window_info.surface_refresh_rate > 0.0f)
@@ -247,11 +264,12 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, i
{
const GSVector2i size(width, height);
const bool prefer_new_texture = (m_features.prefer_new_textures && type == GSTexture::Type::Texture && !prefer_reuse);
FastList<GSTexture*>& pool = m_pool[type != GSTexture::Type::Texture];
GSTexture* t = nullptr;
auto fallback = m_pool.end();
auto fallback = pool.end();
for (auto i = m_pool.begin(); i != m_pool.end(); ++i)
for (auto i = pool.begin(); i != pool.end(); ++i)
{
t = *i;
@@ -262,10 +280,10 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, i
if (!prefer_new_texture || t->GetLastFrameUsed() != m_frame)
{
m_pool_memory_usage -= t->GetMemUsage();
m_pool.erase(i);
pool.erase(i);
break;
}
else if (fallback == m_pool.end())
else if (fallback == pool.end())
{
fallback = i;
}
@@ -276,11 +294,12 @@ GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, i
if (!t)
{
if (m_pool.size() >= MAX_POOLED_TEXTURES && fallback != m_pool.end())
if (pool.size() >= ((type == GSTexture::Type::Texture) ? MAX_POOLED_TEXTURES : MAX_POOLED_TARGETS) &&
fallback != pool.end())
{
t = *fallback;
m_pool_memory_usage -= t->GetMemUsage();
m_pool.erase(fallback);
pool.erase(fallback);
}
else
{
@@ -322,17 +341,24 @@ void GSDevice::Recycle(GSTexture* t)
t->SetLastFrameUsed(m_frame);
m_pool.push_front(t);
FastList<GSTexture*>& pool = m_pool[!t->IsTexture()];
pool.push_front(t);
m_pool_memory_usage += t->GetMemUsage();
//printf("%d\n",m_pool.size());
while (m_pool.size() > MAX_POOLED_TEXTURES)
const u32 max_size = t->IsTexture() ? MAX_POOLED_TEXTURES : MAX_POOLED_TARGETS;
const u32 max_age = t->IsTexture() ? MAX_TEXTURE_AGE : MAX_TARGET_AGE;
while (pool.size() > max_size)
{
m_pool_memory_usage -= m_pool.back()->GetMemUsage();
delete m_pool.back();
// Don't toss when the texture was last used in this frame.
// Because we're going to need to keep it alive anyway.
GSTexture* back = pool.back();
if ((m_frame - back->GetLastFrameUsed()) < max_age)
break;
m_pool.pop_back();
m_pool_memory_usage -= back->GetMemUsage();
delete back;
pool.pop_back();
}
}
@@ -346,20 +372,33 @@ void GSDevice::AgePool()
{
m_frame++;
while (m_pool.size() > 40 && m_frame - m_pool.back()->GetLastFrameUsed() > 10)
// Toss out textures when they're not too-recently used.
for (u32 pool_idx = 0; pool_idx < m_pool.size(); pool_idx++)
{
m_pool_memory_usage -= m_pool.back()->GetMemUsage();
delete m_pool.back();
const u32 max_age = (pool_idx == 0) ? MAX_TEXTURE_AGE : MAX_TARGET_AGE;
FastList<GSTexture*>& pool = m_pool[pool_idx];
while (!pool.empty())
{
GSTexture* back = pool.back();
if ((m_frame - back->GetLastFrameUsed()) < max_age)
break;
m_pool.pop_back();
m_pool_memory_usage -= back->GetMemUsage();
delete back;
pool.pop_back();
}
}
}
void GSDevice::PurgePool()
{
for (auto t : m_pool)
delete t;
m_pool.clear();
for (FastList<GSTexture*>& pool : m_pool)
{
for (GSTexture* t : pool)
delete t;
pool.clear();
}
m_pool_memory_usage = 0;
}
@@ -379,14 +418,6 @@ GSTexture* GSDevice::CreateTexture(int w, int h, int mipmap_levels, GSTexture::F
return FetchSurface(GSTexture::Type::Texture, w, h, levels, format, false, prefer_reuse);
}
GSTexture::Format GSDevice::GetDefaultTextureFormat(GSTexture::Type type)
{
if (type == GSTexture::Type::DepthStencil)
return GSTexture::Format::DepthStencil;
else
return GSTexture::Format::Color;
}
void GSDevice::StretchRect(GSTexture* sTex, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader, bool linear)
{
StretchRect(sTex, GSVector4(0, 0, 1, 1), dTex, dRect, shader, linear);
@@ -440,7 +471,7 @@ void GSDevice::ClearCurrent()
void GSDevice::Merge(GSTexture* sTex[3], GSVector4* sRect, GSVector4* dRect, const GSVector2i& fs, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c)
{
if (ResizeTarget(&m_merge, fs.x, fs.y))
if (ResizeRenderTarget(&m_merge, fs.x, fs.y, false, false))
DoMerge(sTex, sRect, m_merge, dRect, PMODE, EXTBUF, c, GSConfig.PCRTCOffsets);
m_current = m_merge;
@@ -480,20 +511,20 @@ void GSDevice::Interlace(const GSVector2i& ds, int field, int mode, float yoffse
switch (mode)
{
case 0: // Weave
ResizeTarget(&m_weavebob, ds.x, ds.y);
ResizeRenderTarget(&m_weavebob, ds.x, ds.y, true, false);
do_interlace(m_merge, m_weavebob, ShaderInterlace::WEAVE, false, offset, field);
m_current = m_weavebob;
break;
case 1: // Bob
// Field is reversed here as we are countering the bounce.
ResizeTarget(&m_weavebob, ds.x, ds.y);
ResizeRenderTarget(&m_weavebob, ds.x, ds.y, true, false);
do_interlace(m_merge, m_weavebob, ShaderInterlace::BOB, true, yoffset * (1 - field), 0);
m_current = m_weavebob;
break;
case 2: // Blend
ResizeTarget(&m_weavebob, ds.x, ds.y);
ResizeRenderTarget(&m_weavebob, ds.x, ds.y, true, false);
do_interlace(m_merge, m_weavebob, ShaderInterlace::WEAVE, false, offset, field);
ResizeTarget(&m_blend, ds.x, ds.y);
ResizeRenderTarget(&m_blend, ds.x, ds.y, true, false);
do_interlace(m_weavebob, m_blend, ShaderInterlace::BLEND, false, 0, 0);
m_current = m_blend;
break;
@@ -502,9 +533,9 @@ void GSDevice::Interlace(const GSVector2i& ds, int field, int mode, float yoffse
bufIdx &= ~1;
bufIdx |= field;
bufIdx &= 3;
ResizeTarget(&m_mad, ds.x, ds.y * 2.0f);
ResizeRenderTarget(&m_mad, ds.x, ds.y * 2.0f, true, false);
do_interlace(m_merge, m_mad, ShaderInterlace::MAD_BUFFER, false, offset, bufIdx);
ResizeTarget(&m_weavebob, ds.x, ds.y);
ResizeRenderTarget(&m_weavebob, ds.x, ds.y, true, false);
do_interlace(m_mad, m_weavebob, ShaderInterlace::MAD_RECONSTRUCT, false, 0, bufIdx);
m_current = m_weavebob;
break;
@@ -518,9 +549,8 @@ void GSDevice::FXAA()
{
// Combining FXAA+ShadeBoost can't share the same target.
GSTexture*& dTex = (m_current == m_target_tmp) ? m_merge : m_target_tmp;
if (ResizeTexture(&dTex, GSTexture::Type::RenderTarget, m_current->GetWidth(), m_current->GetHeight(), false, true))
if (ResizeRenderTarget(&dTex, m_current->GetWidth(), m_current->GetHeight(), false, false))
{
InvalidateRenderTarget(dTex);
DoFXAA(m_current, dTex);
m_current = dTex;
}
@@ -528,10 +558,8 @@ void GSDevice::FXAA()
void GSDevice::ShadeBoost()
{
if (ResizeTexture(&m_target_tmp, GSTexture::Type::RenderTarget, m_current->GetWidth(), m_current->GetHeight(), false, true))
if (ResizeRenderTarget(&m_target_tmp, m_current->GetWidth(), m_current->GetHeight(), false, false))
{
InvalidateRenderTarget(m_target_tmp);
// predivide to avoid the divide (multiply) in the shader
const float params[4] = {
static_cast<float>(GSConfig.ShadeBoost_Brightness) * (1.0f / 50.0f),
@@ -556,7 +584,7 @@ void GSDevice::Resize(int width, int height)
s = m_current->GetSize() * GSVector2i(++multiplier);
}
if (ResizeTexture(&dTex, GSTexture::Type::RenderTarget, s.x, s.y, false))
if (ResizeRenderTarget(&dTex, s.x, s.y, false, false))
{
const GSVector4 sRect(0, 0, 1, 1);
const GSVector4 dRect(0, 0, s.x, s.y);
@@ -565,55 +593,48 @@ void GSDevice::Resize(int width, int height)
}
}
bool GSDevice::ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h, bool clear, bool prefer_reuse)
bool GSDevice::ResizeRenderTarget(GSTexture** t, int w, int h, bool preserve_contents, bool recycle)
{
if (t == NULL)
pxAssert(t);
GSTexture* orig_tex = *t;
if (orig_tex && orig_tex->GetWidth() == w && orig_tex->GetHeight() == h)
{
ASSERT(0);
if (!preserve_contents)
InvalidateRenderTarget(orig_tex);
return true;
}
GSTexture* new_tex;
try
{
const GSTexture::Format fmt = orig_tex ? orig_tex->GetFormat() : GSTexture::Format::Color;
new_tex = FetchSurface(GSTexture::Type::RenderTarget, w, h, 1, fmt, !preserve_contents, true);
}
catch (std::bad_alloc&)
{
Console.WriteLn("%dx%d texture allocation failed in ResizeTexture()", w, h);
return false;
}
GSTexture* t2 = *t;
if (t2 == NULL || t2->GetWidth() != w || t2->GetHeight() != h)
if (preserve_contents && orig_tex)
{
const GSTexture::Format fmt = t2 ? t2->GetFormat() : GetDefaultTextureFormat(type);
const int levels = t2 ? (t2->IsMipmap() ? MipmapLevelsForSize(w, h) : 1) : 1;
GSTexture* new_t = FetchSurface(type, w, h, levels, fmt, clear, prefer_reuse);
if (new_t)
{
if (t2)
{
// TODO: We probably want to make this optional if we're overwriting it...
const GSVector4 sRect(0, 0, 1, 1);
const GSVector4 dRect(0, 0, t2->GetWidth(), t2->GetHeight());
StretchRect(t2, sRect, new_t, dRect, ShaderConvert::COPY, true);
Recycle(t2);
}
t2 = new_t;
*t = t2;
}
constexpr GSVector4 sRect = GSVector4::cxpr(0, 0, 1, 1);
const GSVector4 dRect = GSVector4(orig_tex->GetRect());
StretchRect(orig_tex, sRect, new_tex, dRect, ShaderConvert::COPY, true);
}
return t2 != NULL;
}
if (orig_tex)
{
if (recycle)
Recycle(orig_tex);
else
delete orig_tex;
}
bool GSDevice::ResizeTexture(GSTexture** t, int w, int h, bool prefer_reuse)
{
return ResizeTexture(t, GSTexture::Type::Texture, w, h, false, prefer_reuse);
}
bool GSDevice::ResizeTarget(GSTexture** t, int w, int h)
{
return ResizeTexture(t, GSTexture::Type::RenderTarget, w, h);
}
bool GSDevice::ResizeTarget(GSTexture** t)
{
GSVector2i s = m_current->GetSize();
return ResizeTexture(t, GSTexture::Type::RenderTarget, s.x, s.y);
*t = new_tex;
return true;
}
void GSDevice::SetHWDrawConfigForAlphaPass(GSHWDrawConfig::PSSelector* ps,
+18 -22
View File
@@ -264,6 +264,7 @@ struct alignas(16) GSHWDrawConfig
/// Returns true if the fixed index buffer should be used.
__fi bool UseExpandIndexBuffer() const { return (expand == VSExpand::Point || expand == VSExpand::Sprite); }
};
static_assert(sizeof(VSSelector) == 1, "VSSelector is a single byte");
#pragma pack(pop)
#pragma pack(push, 4)
struct PSSelector
@@ -382,6 +383,7 @@ struct alignas(16) GSHWDrawConfig
no_color = no_color1 = 1;
}
};
static_assert(sizeof(PSSelector) == 12, "PSSelector is 12 bytes");
#pragma pack(pop)
struct PSSelectorHash
{
@@ -627,7 +629,7 @@ struct alignas(16) GSHWDrawConfig
GSTexture* tex; ///< Source texture
GSTexture* pal; ///< Palette texture
const GSVertex* verts;///< Vertices to draw
const u32* indices; ///< Indices to draw
const u16* indices; ///< Indices to draw
u32 nverts; ///< Number of vertices
u32 nindices; ///< Number of indices
u32 indices_per_prim; ///< Number of indices that make up one primitive
@@ -738,18 +740,21 @@ public:
// clang-format on
private:
FastList<GSTexture*> m_pool;
std::array<FastList<GSTexture*>, 2> m_pool; // [texture, target]
u64 m_pool_memory_usage = 0;
static const std::array<HWBlend, 3*3*3*3> m_blendMap;
static const std::array<u8, 16> m_replaceDualSrcBlendMap;
protected:
static constexpr int NUM_INTERLACE_SHADERS = 5;
static constexpr int NUM_INTERLACE_SHADERS = 5;
static constexpr float MAD_SENSITIVITY = 0.08f;
static constexpr u32 MAX_POOLED_TEXTURES = 300;
static constexpr u32 NUM_CAS_CONSTANTS = 12; // 8 plus src offset x/y, 16 byte alignment
static constexpr u32 EXPAND_BUFFER_SIZE = sizeof(u32) * std::numeric_limits<u16>::max() * 6;
static constexpr u32 MAX_POOLED_TARGETS = 300;
static constexpr u32 MAX_TARGET_AGE = 20;
static constexpr u32 MAX_POOLED_TEXTURES = 300;
static constexpr u32 MAX_TEXTURE_AGE = 10;
static constexpr u32 NUM_CAS_CONSTANTS = 12; // 8 plus src offset x/y, 16 byte alignment
static constexpr u32 EXPAND_BUFFER_SIZE = sizeof(u16) * 65532 * 6;
WindowInfo m_window_info;
VsyncMode m_vsync_mode = VsyncMode::Off;
@@ -776,6 +781,8 @@ protected:
bool m_rbswapped = false;
FeatureSupport m_features;
bool AcquireWindow(bool recreate_window);
virtual GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) = 0;
GSTexture* FetchSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format, bool clear, bool prefer_reuse);
@@ -797,8 +804,8 @@ public:
/// Returns a string representing the specified API.
static const char* RenderAPIToString(RenderAPI api);
/// Parses a fullscreen mode into its components (width * height @ refresh hz)
static bool ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate);
/// Parses the configured fullscreen mode into its components (width * height @ refresh hz)
static bool GetRequestedExclusiveFullscreenMode(u32* width, u32* height, float* refresh_rate);
/// Converts a fullscreen mode to a string.
static std::string GetFullscreenModeString(u32 width, u32 height, float refresh_rate);
@@ -806,7 +813,6 @@ public:
/// Generates a fixed index buffer for expanding points and sprites. Buffer is assumed to be at least EXPAND_BUFFER_SIZE in size.
static void GenerateExpansionIndexBuffer(void* buffer);
__fi unsigned int GetFrameNumber() const { return m_frame; }
__fi u64 GetPoolMemoryUsage() const { return m_pool_memory_usage; }
__fi FeatureSupport Features() const { return m_features; }
@@ -828,7 +834,7 @@ public:
/// Recreates the font, call when the window scaling changes.
bool UpdateImGuiFontTexture();
virtual bool Create(const WindowInfo& wi, VsyncMode vsync);
virtual bool Create();
virtual void Destroy();
/// Returns the graphics API used by this device.
@@ -841,7 +847,7 @@ public:
virtual void DestroySurface() = 0;
/// Switches to a new window/surface.
virtual bool ChangeWindow(const WindowInfo& wi) = 0;
virtual bool UpdateWindow() = 0;
/// Call when the window size changes externally to recreate any resources.
virtual void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) = 0;
@@ -849,12 +855,6 @@ public:
/// Returns true if exclusive fullscreen is supported.
virtual bool SupportsExclusiveFullscreen() const = 0;
/// Returns true if exclusive fullscreen is active.
virtual bool IsExclusiveFullscreen() = 0;
/// Attempts to switch to the specified mode in exclusive fullscreen.
virtual bool SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) = 0;
/// Returns false if the window was completely occluded. If frame_skip is set, the frame won't be
/// displayed, but the GPU command queue will still be flushed.
virtual PresentResult BeginPresent(bool frame_skip) = 0;
@@ -890,7 +890,6 @@ public:
GSTexture* CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear = true);
GSTexture* CreateDepthStencil(int w, int h, GSTexture::Format format, bool clear = true);
GSTexture* CreateTexture(int w, int h, int mipmap_levels, GSTexture::Format format, bool prefer_reuse = false);
GSTexture::Format GetDefaultTextureFormat(GSTexture::Type type);
virtual std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) = 0;
@@ -929,10 +928,7 @@ public:
void CAS(GSTexture*& tex, GSVector4i& src_rect, GSVector4& src_uv, const GSVector4& draw_rect, bool sharpen_only);
bool ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h, bool clear = true, bool prefer_reuse = false);
bool ResizeTexture(GSTexture** t, int w, int h, bool prefer_reuse = false);
bool ResizeTarget(GSTexture** t, int w, int h);
bool ResizeTarget(GSTexture** t);
bool ResizeRenderTarget(GSTexture** t, int w, int h, bool preserve_contents, bool recycle);
bool IsRBSwapped() { return m_rbswapped; }
@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="FastList&lt;*&gt;">
<DisplayString>{{ size={m_free_indexes_stack_top} }}</DisplayString>
<Expand>
<Item Name="[size]" ExcludeView="simple">m_free_indexes_stack_top</Item>
<Item Name="[capacity]" ExcludeView="simple">m_capacity - 1</Item>
<CustomListItems MaxItemsPerView="5000" ExcludeView="simple">
<Variable Name="index" InitialValue="m_buffer[0].next_index" />
<Size>m_free_indexes_stack_top</Size>
<Loop>
<Item>m_buffer[index].data,na</Item>
<Exec>index = m_buffer[index].next_index</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
</AutoVisualizer>
+11 -2
View File
@@ -527,7 +527,7 @@ void GSRenderer::EndPresentFrame()
ImGuiManager::NewFrame();
}
void GSRenderer::VSync(u32 field, bool registers_written)
void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
{
Flush(GSFlushReason::VSYNC);
@@ -569,6 +569,9 @@ void GSRenderer::VSync(u32 field, bool registers_written)
const bool blank_frame = !Merge(field);
m_last_draw_n = s_n;
m_last_transfer_n = s_transfer_n;
if (skip_frame)
{
if (BeginPresentFrame(true))
@@ -578,7 +581,8 @@ void GSRenderer::VSync(u32 field, bool registers_written)
return;
}
g_gs_device->AgePool();
if (!idle_frame)
g_gs_device->AgePool();
g_perfmon.EndFrame();
if ((g_perfmon.GetFrame() & 0x1f) == 0)
@@ -898,6 +902,11 @@ GSTexture* GSRenderer::LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2
return nullptr;
}
bool GSRenderer::IsIdleFrame() const
{
return (m_last_draw_n == s_n && m_last_transfer_n == s_transfer_n);
}
bool GSRenderer::SaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,
u32* width, u32* height, std::vector<u32>* pixels)
{
+7 -1
View File
@@ -32,6 +32,10 @@ private:
u32 m_dump_frames = 0;
u32 m_skipped_duplicate_frames = 0;
// Tracking draw counters for idle frame detection.
int m_last_draw_n = 0;
int m_last_transfer_n = 0;
protected:
GSVector2i m_real_size{0, 0};
bool m_texture_shuffle = false;
@@ -48,7 +52,7 @@ public:
virtual void Destroy();
virtual void VSync(u32 field, bool registers_written);
virtual void VSync(u32 field, bool registers_written, bool idle_frame);
virtual bool CanUpscale() { return false; }
virtual float GetUpscaleMultiplier() { return 1.0f; }
virtual float GetTextureScaleFactor() { return 1.0f; }
@@ -57,6 +61,8 @@ public:
virtual GSTexture* LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, float* scale, const GSVector2i& size);
bool IsIdleFrame() const;
bool SaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,
u32* width, u32* height, std::vector<u32>* pixels);
+4
View File
@@ -126,6 +126,10 @@ public:
{
return (m_type == Type::DepthStencil);
}
__fi bool IsTexture() const
{
return (m_type == Type::Texture);
}
__fi State GetState() const { return m_state; }
__fi void SetState(State state) { m_state = state; }
@@ -1,17 +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 "PrecompiledHeader.h"
#include "GSVertexList.h"
-82
View File
@@ -1,82 +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
template <class Vertex>
class GSVertexList
{
void* m_base;
Vertex* m_v[3];
int m_count;
public:
GSVertexList()
: m_count(0)
{
m_base = _aligned_malloc(sizeof(Vertex) * std::size(m_v), 32);
for (size_t i = 0; i < std::size(m_v); i++)
{
m_v[i] = &((Vertex*)m_base)[i];
}
}
virtual ~GSVertexList()
{
_aligned_free(m_base);
}
void RemoveAll()
{
m_count = 0;
}
__forceinline Vertex& AddTail()
{
ASSERT(m_count < 3);
return *m_v[m_count++];
}
__forceinline void RemoveAt(int pos, int keep)
{
if (keep == 1)
{
Vertex* tmp = m_v[pos + 0];
m_v[pos + 0] = m_v[pos + 1];
m_v[pos + 1] = tmp;
}
else if (keep == 2)
{
Vertex* tmp = m_v[pos + 0];
m_v[pos + 0] = m_v[pos + 1];
m_v[pos + 1] = m_v[pos + 2];
m_v[pos + 2] = tmp;
}
m_count = pos + keep;
}
__forceinline void GetAt(int i, Vertex& v)
{
v = *m_v[i];
}
int GetCount()
{
return m_count;
}
};
+2 -2
View File
@@ -24,7 +24,7 @@ GSVertexTrace::GSVertexTrace(const GSState* state, bool provoking_vertex_first)
MULTI_ISA_SELECT(GSVertexTracePopulateFunctions)(*this, provoking_vertex_first);
}
void GSVertexTrace::Update(const void* vertex, const u32* index, int v_count, int i_count, GS_PRIM_CLASS primclass)
void GSVertexTrace::Update(const void* vertex, const u16* index, int v_count, int i_count, GS_PRIM_CLASS primclass)
{
if (i_count == 0)
return;
@@ -43,7 +43,7 @@ void GSVertexTrace::Update(const void* vertex, const u32* index, int v_count, in
// that feel big enough.
if (!fst && !m_accurate_stq && m_min.t.z > 1e30)
{
fprintf(stderr, "Vertex Trace: float overflow detected ! min %e max %e\n", m_min.t.z, m_max.t.z);
Console.Warning("Vertex Trace: float overflow detected ! min %e max %e", m_min.t.z, m_max.t.z);
m_accurate_stq = true;
}
+2 -2
View File
@@ -49,7 +49,7 @@ public:
protected:
const GSState* m_state;
typedef void (*FindMinMaxPtr)(GSVertexTrace& vt, const void* vertex, const u32* index, int count);
typedef void (*FindMinMaxPtr)(GSVertexTrace& vt, const void* vertex, const u16* index, int count);
FindMinMaxPtr m_fmm[2][2][2][2][4];
@@ -77,7 +77,7 @@ public:
public:
GSVertexTrace(const GSState* state, bool provoking_vertex_first);
void Update(const void* vertex, const u32* index, int v_count, int i_count, GS_PRIM_CLASS primclass);
void Update(const void* vertex, const u16* index, int v_count, int i_count, GS_PRIM_CLASS primclass);
bool IsLinear() const { return m_filter.opt_linear; }
bool IsRealLinear() const { return m_filter.linear; }
@@ -22,7 +22,7 @@ class CURRENT_ISA::GSVertexTraceFMM
static constexpr GSVector4 s_minmax = GSVector4::cxpr(FLT_MAX, -FLT_MAX, 0.f, 0.f);
template <GS_PRIM_CLASS primclass, u32 iip, u32 tme, u32 fst, u32 color, bool flat_swapped>
static void FindMinMax(GSVertexTrace& vt, const void* vertex, const u32* index, int count);
static void FindMinMax(GSVertexTrace& vt, const void* vertex, const u16* index, int count);
template <GS_PRIM_CLASS primclass, u32 iip, u32 tme, u32 fst, u32 color>
static constexpr GSVertexTrace::FindMinMaxPtr GetFMM(bool provoking_vertex_first);
@@ -76,7 +76,7 @@ void GSVertexTraceFMM::Populate(GSVertexTrace& vt, bool provoking_vertex_first)
}
template <GS_PRIM_CLASS primclass, u32 iip, u32 tme, u32 fst, u32 color, bool flat_swapped>
void GSVertexTraceFMM::FindMinMax(GSVertexTrace& vt, const void* vertex, const u32* index, int count)
void GSVertexTraceFMM::FindMinMax(GSVertexTrace& vt, const void* vertex, const u16* index, int count)
{
const GSDrawingContext* context = vt.m_state->m_context;
+73
View File
@@ -120,6 +120,79 @@ std::vector<std::string> D3D::GetFullscreenModes(IDXGIFactory5* factory, const s
return modes;
}
bool D3D::GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect, u32 width,
u32 height, float refresh_rate, DXGI_FORMAT format, DXGI_MODE_DESC* fullscreen_mode, IDXGIOutput** output)
{
// We need to find which monitor the window is located on.
const GSVector4i client_rc_vec = GSVector4i::load<false>(&window_rect);
// The window might be on a different adapter to which we are rendering.. so we have to enumerate them all.
HRESULT hr;
wil::com_ptr_nothrow<IDXGIOutput> first_output, intersecting_output;
for (u32 adapter_index = 0; !intersecting_output; adapter_index++)
{
wil::com_ptr_nothrow<IDXGIAdapter1> adapter;
hr = factory->EnumAdapters1(adapter_index, adapter.put());
if (hr == DXGI_ERROR_NOT_FOUND)
break;
else if (FAILED(hr))
continue;
for (u32 output_index = 0;; output_index++)
{
wil::com_ptr_nothrow<IDXGIOutput> this_output;
DXGI_OUTPUT_DESC output_desc;
hr = adapter->EnumOutputs(output_index, this_output.put());
if (hr == DXGI_ERROR_NOT_FOUND)
break;
else if (FAILED(hr) || FAILED(this_output->GetDesc(&output_desc)))
continue;
const GSVector4i output_rc = GSVector4i::load<false>(&output_desc.DesktopCoordinates);
if (!client_rc_vec.rintersect(output_rc).rempty())
{
intersecting_output = std::move(this_output);
break;
}
// Fallback to the first monitor.
if (!first_output)
first_output = std::move(this_output);
}
}
if (!intersecting_output)
{
if (!first_output)
{
Console.Error("No DXGI output found. Can't use exclusive fullscreen.");
return false;
}
Console.Warning("No DXGI output found for window, using first.");
intersecting_output = std::move(first_output);
}
DXGI_MODE_DESC request_mode = {};
request_mode.Width = width;
request_mode.Height = height;
request_mode.Format = format;
request_mode.RefreshRate.Numerator = static_cast<UINT>(std::floor(refresh_rate * 1000.0f));
request_mode.RefreshRate.Denominator = 1000u;
if (FAILED(hr = intersecting_output->FindClosestMatchingMode(&request_mode, fullscreen_mode, nullptr)) ||
request_mode.Format != format)
{
Console.Error("Failed to find closest matching mode, hr=%08X", hr);
return false;
}
*output = intersecting_output.get();
intersecting_output->AddRef();
return true;
}
wil::com_ptr_nothrow<IDXGIAdapter1> D3D::GetAdapterByName(IDXGIFactory5* factory, const std::string_view& name)
{
if (name.empty())
+4
View File
@@ -36,6 +36,10 @@ namespace D3D
// returns a list of fullscreen modes for the specified adapter
std::vector<std::string> GetFullscreenModes(IDXGIFactory5* factory, const std::string_view& adapter_name);
// returns the fullscreen mode to use for the specified dimensions
bool GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect, u32 width, u32 height,
float refresh_rate, DXGI_FORMAT format, DXGI_MODE_DESC* fullscreen_mode, IDXGIOutput** output);
// get an adapter based on name
wil::com_ptr_nothrow<IDXGIAdapter1> GetAdapterByName(IDXGIFactory5* factory, const std::string_view& name);
+130 -133
View File
@@ -37,6 +37,8 @@
// #define REPORT_LEAKED_OBJECTS 1
static constexpr std::array<float, 4> s_present_clear_color = {};
static bool SupportsTextureFormat(ID3D11Device* dev, DXGI_FORMAT format)
{
UINT support;
@@ -75,9 +77,9 @@ RenderAPI GSDevice11::GetRenderAPI() const
return RenderAPI::D3D11;
}
bool GSDevice11::Create(const WindowInfo& wi, VsyncMode vsync)
bool GSDevice11::Create()
{
if (!GSDevice::Create(wi, vsync))
if (!GSDevice::Create())
return false;
UINT create_flags = 0;
@@ -150,7 +152,7 @@ bool GSDevice11::Create(const WindowInfo& wi, VsyncMode vsync)
DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, sizeof(allow_tearing_supported));
m_allow_tearing_supported = (SUCCEEDED(hr) && allow_tearing_supported == TRUE);
if (wi.type != WindowInfo::Type::Surfaceless && !CreateSwapChain(nullptr))
if (!AcquireWindow(true) || (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain()))
return false;
D3D11_BUFFER_DESC bd;
@@ -490,7 +492,7 @@ bool GSDevice11::Create(const WindowInfo& wi, VsyncMode vsync)
void GSDevice11::Destroy()
{
GSDevice::Destroy();
GSDevice11::DestroySurface();
DestroySwapChain();
DestroyTimestampQueries();
m_convert = {};
@@ -561,7 +563,7 @@ bool GSDevice11::HasSurface() const
bool GSDevice11::GetHostRefreshRate(float* refresh_rate)
{
if (m_swap_chain && IsExclusiveFullscreen())
if (m_swap_chain && m_is_exclusive_fullscreen)
{
DXGI_SWAP_CHAIN_DESC desc;
if (SUCCEEDED(m_swap_chain->GetDesc(&desc)) && desc.BufferDesc.RefreshRate.Numerator > 0 &&
@@ -583,50 +585,84 @@ void GSDevice11::SetVSync(VsyncMode mode)
m_vsync_mode = mode;
}
bool GSDevice11::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode)
bool GSDevice11::CreateSwapChain()
{
constexpr DXGI_FORMAT swap_chain_format = DXGI_FORMAT_R8G8B8A8_UNORM;
if (m_window_info.type != WindowInfo::Type::Win32)
return false;
m_using_flip_model_swap_chain = !EmuConfig.GS.UseBlitSwapChain || fullscreen_mode;
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
RECT client_rc{};
GetClientRect(window_hwnd, &client_rc);
const u32 width = static_cast<u32>(client_rc.right - client_rc.left);
const u32 height = static_cast<u32>(client_rc.bottom - client_rc.top);
DXGI_MODE_DESC fullscreen_mode;
wil::com_ptr_nothrow<IDXGIOutput> fullscreen_output;
if (Host::IsFullscreen())
{
u32 fullscreen_width, fullscreen_height;
float fullscreen_refresh_rate;
m_is_exclusive_fullscreen =
GetRequestedExclusiveFullscreenMode(&fullscreen_width, &fullscreen_height, &fullscreen_refresh_rate) &&
D3D::GetRequestedExclusiveFullscreenModeDesc(m_dxgi_factory.get(), client_rc, fullscreen_width,
fullscreen_height, fullscreen_refresh_rate, swap_chain_format, &fullscreen_mode,
fullscreen_output.put());
}
else
{
m_is_exclusive_fullscreen = false;
}
m_using_flip_model_swap_chain = !EmuConfig.GS.UseBlitSwapChain || m_is_exclusive_fullscreen;
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
swap_chain_desc.Width = width;
swap_chain_desc.Height = height;
swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swap_chain_desc.Width = static_cast<u32>(client_rc.right - client_rc.left);
swap_chain_desc.Height = static_cast<u32>(client_rc.bottom - client_rc.top);
swap_chain_desc.Format = swap_chain_format;
swap_chain_desc.SampleDesc.Count = 1;
swap_chain_desc.BufferCount = 3;
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swap_chain_desc.SwapEffect =
m_using_flip_model_swap_chain ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
m_using_allow_tearing = (m_allow_tearing_supported && m_using_flip_model_swap_chain && !fullscreen_mode);
m_using_allow_tearing = (m_allow_tearing_supported && m_using_flip_model_swap_chain && !m_is_exclusive_fullscreen);
if (m_using_allow_tearing)
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {};
if (fullscreen_mode)
HRESULT hr = S_OK;
if (m_is_exclusive_fullscreen)
{
swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
swap_chain_desc.Width = fullscreen_mode->Width;
swap_chain_desc.Height = fullscreen_mode->Height;
fs_desc.RefreshRate = fullscreen_mode->RefreshRate;
fs_desc.ScanlineOrdering = fullscreen_mode->ScanlineOrdering;
fs_desc.Scaling = fullscreen_mode->Scaling;
DXGI_SWAP_CHAIN_DESC1 fs_sd_desc = swap_chain_desc;
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {};
fs_sd_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
fs_sd_desc.Width = fullscreen_mode.Width;
fs_sd_desc.Height = fullscreen_mode.Height;
fs_desc.RefreshRate = fullscreen_mode.RefreshRate;
fs_desc.ScanlineOrdering = fullscreen_mode.ScanlineOrdering;
fs_desc.Scaling = fullscreen_mode.Scaling;
fs_desc.Windowed = FALSE;
Console.WriteLn("Creating a %dx%d exclusive fullscreen swap chain", fs_sd_desc.Width, fs_sd_desc.Height);
hr = m_dxgi_factory->CreateSwapChainForHwnd(
m_dev.get(), window_hwnd, &fs_sd_desc, &fs_desc, fullscreen_output.get(), m_swap_chain.put());
if (FAILED(hr))
{
Console.Warning("Failed to create fullscreen swap chain, trying windowed.");
m_is_exclusive_fullscreen = false;
m_using_allow_tearing = m_allow_tearing_supported && m_using_flip_model_swap_chain;
}
}
Console.WriteLn("Creating a %dx%d %s %s swap chain", swap_chain_desc.Width, swap_chain_desc.Height,
m_using_flip_model_swap_chain ? "flip-discard" : "discard", fullscreen_mode ? "full-screen" : "windowed");
if (!m_is_exclusive_fullscreen)
{
Console.WriteLn("Creating a %dx%d %s windowed swap chain", swap_chain_desc.Width, swap_chain_desc.Height,
m_using_flip_model_swap_chain ? "flip-discard" : "discard");
hr = m_dxgi_factory->CreateSwapChainForHwnd(
m_dev.get(), window_hwnd, &swap_chain_desc, nullptr, nullptr, m_swap_chain.put());
}
HRESULT hr = m_dxgi_factory->CreateSwapChainForHwnd(m_dev.get(), window_hwnd, &swap_chain_desc,
fullscreen_mode ? &fs_desc : nullptr, nullptr, m_swap_chain.put());
if (FAILED(hr) && m_using_flip_model_swap_chain)
{
Console.Warning("Failed to create a flip-discard swap chain, trying discard.");
@@ -635,8 +671,8 @@ bool GSDevice11::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode)
m_using_flip_model_swap_chain = false;
m_using_allow_tearing = false;
hr = m_dxgi_factory->CreateSwapChainForHwnd(m_dev.get(), window_hwnd, &swap_chain_desc,
fullscreen_mode ? &fs_desc : nullptr, nullptr, m_swap_chain.put());
hr = m_dxgi_factory->CreateSwapChainForHwnd(
m_dev.get(), window_hwnd, &swap_chain_desc, nullptr, nullptr, m_swap_chain.put());
if (FAILED(hr))
{
Console.Error("CreateSwapChainForHwnd failed: 0x%08X", hr);
@@ -648,7 +684,16 @@ bool GSDevice11::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode)
if (FAILED(hr))
Console.Warning("MakeWindowAssociation() to disable ALT+ENTER failed");
return CreateSwapChainRTV();
if (!CreateSwapChainRTV())
{
DestroySwapChain();
return false;
}
// Render a frame as soon as possible to clear out whatever was previously being displayed.
m_ctx->ClearRenderTargetView(m_swap_chain_rtv.get(), s_present_clear_color.data());
m_swap_chain->Present(0, m_using_allow_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0);
return true;
}
bool GSDevice11::CreateSwapChainRTV()
@@ -670,6 +715,7 @@ bool GSDevice11::CreateSwapChainRTV()
if (FAILED(hr))
{
Console.Error("CreateRenderTargetView for swap chain failed: 0x%08X", hr);
m_swap_chain_rtv.reset();
return false;
}
@@ -696,25 +742,41 @@ bool GSDevice11::CreateSwapChainRTV()
return true;
}
bool GSDevice11::ChangeWindow(const WindowInfo& new_wi)
void GSDevice11::DestroySwapChain()
{
DestroySurface();
if (!m_swap_chain)
return;
m_window_info = new_wi;
if (new_wi.type == WindowInfo::Type::Surfaceless)
return true;
m_swap_chain_rtv.reset();
return CreateSwapChain(nullptr);
// switch out of fullscreen before destroying
BOOL is_fullscreen;
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen)
m_swap_chain->SetFullscreenState(FALSE, nullptr);
m_swap_chain.reset();
m_is_exclusive_fullscreen = false;
}
bool GSDevice11::UpdateWindow()
{
DestroySwapChain();
if (!AcquireWindow(false))
return false;
if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain())
{
Console.WriteLn("Failed to create swap chain on updated window");
return false;
}
return true;
}
void GSDevice11::DestroySurface()
{
m_swap_chain_rtv.reset();
if (IsExclusiveFullscreen())
SetExclusiveFullscreen(false, 0, 0, 0.0f);
m_swap_chain.reset();
DestroySwapChain();
}
std::string GSDevice11::GetDriverInfo() const
@@ -768,7 +830,7 @@ std::string GSDevice11::GetDriverInfo() const
void GSDevice11::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
{
if (!m_swap_chain)
if (!m_swap_chain || m_is_exclusive_fullscreen)
return;
m_window_info.surface_scale = new_window_scale;
@@ -792,79 +854,21 @@ bool GSDevice11::SupportsExclusiveFullscreen() const
return true;
}
bool GSDevice11::IsExclusiveFullscreen()
{
BOOL is_fullscreen = FALSE;
return (m_swap_chain && SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen);
}
bool GSDevice11::SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
{
if (!m_swap_chain)
return false;
BOOL is_fullscreen = FALSE;
HRESULT hr = m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr);
if (!fullscreen)
{
// leaving fullscreen
if (is_fullscreen)
return SUCCEEDED(m_swap_chain->SetFullscreenState(FALSE, nullptr));
else
return true;
}
IDXGIOutput* output;
if (FAILED(hr = m_swap_chain->GetContainingOutput(&output)))
return false;
DXGI_SWAP_CHAIN_DESC current_desc;
hr = m_swap_chain->GetDesc(&current_desc);
if (FAILED(hr))
return false;
DXGI_MODE_DESC new_mode = current_desc.BufferDesc;
new_mode.Width = width;
new_mode.Height = height;
new_mode.RefreshRate.Numerator = static_cast<UINT>(std::floor(refresh_rate * 1000.0f));
new_mode.RefreshRate.Denominator = 1000u;
DXGI_MODE_DESC closest_mode;
if (FAILED(hr = output->FindClosestMatchingMode(&new_mode, &closest_mode, nullptr)) ||
new_mode.Format != current_desc.BufferDesc.Format)
{
Console.Error("Failed to find closest matching mode, hr=%08X", hr);
return false;
}
if (new_mode.Width == current_desc.BufferDesc.Width && new_mode.Height == current_desc.BufferDesc.Height &&
new_mode.RefreshRate.Numerator == current_desc.BufferDesc.RefreshRate.Numerator &&
new_mode.RefreshRate.Denominator == current_desc.BufferDesc.RefreshRate.Denominator)
{
DevCon.WriteLn("Fullscreen mode already set");
return true;
}
m_swap_chain_rtv.reset();
m_swap_chain.reset();
if (!CreateSwapChain(&closest_mode))
{
Console.Error("Failed to create a fullscreen swap chain");
if (!CreateSwapChain(nullptr))
pxFailRel("Failed to recreate windowed swap chain");
return false;
}
return true;
}
GSDevice::PresentResult GSDevice11::BeginPresent(bool frame_skip)
{
if (frame_skip || !m_swap_chain)
return PresentResult::FrameSkipped;
// Check if we lost exclusive fullscreen. If so, notify the host, so it can switch to windowed mode.
// This might get called repeatedly if it takes a while to switch back, that's the host's problem.
BOOL is_fullscreen;
if (m_is_exclusive_fullscreen &&
(FAILED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) || !is_fullscreen))
{
Host::RunOnCPUThread([]() { Host::SetFullscreen(false); });
return PresentResult::FrameSkipped;
}
// When using vsync, the time here seems to include the time for the buffer to become available.
// This blows our our GPU usage number considerably, so read the timestamp before the final blit
// in this configuration. It does reduce accuracy a little, but better than seeing 100% all of
@@ -872,8 +876,7 @@ GSDevice::PresentResult GSDevice11::BeginPresent(bool frame_skip)
if (m_vsync_mode != VsyncMode::Off && m_gpu_timing_enabled)
PopTimestampQuery();
static constexpr std::array<float, 4> clear_color = {};
m_ctx->ClearRenderTargetView(m_swap_chain_rtv.get(), clear_color.data());
m_ctx->ClearRenderTargetView(m_swap_chain_rtv.get(), s_present_clear_color.data());
m_ctx->OMSetRenderTargets(1, m_swap_chain_rtv.addressof(), nullptr);
if (m_state.rt_view)
m_state.rt_view->Release();
@@ -883,7 +886,9 @@ GSDevice::PresentResult GSDevice11::BeginPresent(bool frame_skip)
{
m_state.dsv->Release();
m_state.dsv = nullptr;
}
}
g_perfmon.Put(GSPerfMon::RenderPasses, 1);
const GSVector2i size = GetWindowSize();
SetViewport(size);
@@ -1440,7 +1445,7 @@ void GSDevice11::DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rect
const u32 vertex_reserve_size = num_rects * 4;
const u32 index_reserve_size = num_rects * 6;
GSVertexPT1* verts = static_cast<GSVertexPT1*>(IAMapVertexBuffer(sizeof(GSVertexPT1), vertex_reserve_size));
u32* idx = IAMapIndexBuffer(index_reserve_size);
u16* idx = IAMapIndexBuffer(index_reserve_size);
u32 icount = 0;
u32 vcount = 0;
for (u32 i = 0; i < num_rects; i++)
@@ -1712,7 +1717,6 @@ void GSDevice11::RenderImGui()
const UINT vb_stride = sizeof(ImDrawVert);
const UINT vb_offset = 0;
m_ctx->IASetVertexBuffers(0, 1, m_vb.addressof(), &vb_stride, &vb_offset);
m_ctx->IASetIndexBuffer(m_ib.get(), DXGI_FORMAT_R16_UINT, 0);
IASetInputLayout(m_imgui.il.get());
IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
VSSetShader(m_imgui.vs.get(), m_imgui.vs_cb.get());
@@ -1756,16 +1760,8 @@ void GSDevice11::RenderImGui()
m_ctx->Unmap(m_vb.get(), 0);
}
// Bit awkward, because this is using 16-bit indices, not 32-bit.
static_assert(sizeof(ImDrawIdx) == sizeof(u16));
const u32 index_count = static_cast<u32>(cmd_list->IdxBuffer.Size + 1) / 2;
u32* index_map = IAMapIndexBuffer(index_count);
if (!index_map)
continue;
const u32 index_start = m_index.start * 2;
std::memcpy(index_map, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
IAUnmapIndexBuffer(index_count);
IASetIndexBuffer(cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
@@ -1787,14 +1783,13 @@ void GSDevice11::RenderImGui()
m_state.ps_sr_views[0] = static_cast<ID3D11ShaderResourceView*>(pcmd->GetTexID());
PSUpdateShaderState();
m_ctx->DrawIndexed(pcmd->ElemCount, index_start + pcmd->IdxOffset, vertex_offset + pcmd->VtxOffset);
m_ctx->DrawIndexed(pcmd->ElemCount, m_index.start + pcmd->IdxOffset, vertex_offset + pcmd->VtxOffset);
}
g_perfmon.Put(GSPerfMon::DrawCalls, cmd_list->CmdBuffer.Size);
}
m_ctx->IASetVertexBuffers(0, 1, m_vb.addressof(), &m_state.vb_stride, &vb_offset);
m_ctx->IASetIndexBuffer(m_state.index_buffer, DXGI_FORMAT_R32_UINT, 0);
}
void GSDevice11::SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* vertices, bool datm)
@@ -1912,9 +1907,9 @@ bool GSDevice11::IASetExpandVertexBuffer(const void* vertex, u32 stride, u32 cou
return true;
}
u32* GSDevice11::IAMapIndexBuffer(u32 count)
u16* GSDevice11::IAMapIndexBuffer(u32 count)
{
if (count > (INDEX_BUFFER_SIZE / sizeof(u32)))
if (count > (INDEX_BUFFER_SIZE / sizeof(u16)))
return nullptr;
D3D11_MAP type = D3D11_MAP_WRITE_NO_OVERWRITE;
@@ -1922,7 +1917,7 @@ u32* GSDevice11::IAMapIndexBuffer(u32 count)
m_index.start = m_ib_pos;
m_ib_pos += count;
if (m_ib_pos > (INDEX_BUFFER_SIZE / sizeof(u32)))
if (m_ib_pos > (INDEX_BUFFER_SIZE / sizeof(u16)))
{
m_index.start = 0;
m_ib_pos = count;
@@ -1933,7 +1928,7 @@ u32* GSDevice11::IAMapIndexBuffer(u32 count)
if (FAILED(m_ctx->Map(m_ib.get(), 0, type, 0, &m)))
return nullptr;
return static_cast<u32*>(m.pData) + m_index.start;
return static_cast<u16*>(m.pData) + m_index.start;
}
void GSDevice11::IAUnmapIndexBuffer(u32 count)
@@ -1944,11 +1939,11 @@ void GSDevice11::IAUnmapIndexBuffer(u32 count)
bool GSDevice11::IASetIndexBuffer(const void* index, u32 count)
{
u32* map = IAMapIndexBuffer(count);
u16* map = IAMapIndexBuffer(count);
if (!map)
return false;
std::memcpy(map, index, count * sizeof(u32));
std::memcpy(map, index, count * sizeof(u16));
IAUnmapIndexBuffer(count);
IASetIndexBuffer(m_ib.get());
return true;
@@ -1958,7 +1953,7 @@ void GSDevice11::IASetIndexBuffer(ID3D11Buffer* buffer)
{
if (m_state.index_buffer != buffer)
{
m_ctx->IASetIndexBuffer(buffer, DXGI_FORMAT_R32_UINT, 0);
m_ctx->IASetIndexBuffer(buffer, DXGI_FORMAT_R16_UINT, 0);
m_state.index_buffer = buffer;
}
}
@@ -2073,6 +2068,8 @@ void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector
if (ds) dsv = *(GSTexture11*)ds;
const bool changed = (m_state.rt_view != rtv || m_state.dsv != dsv);
g_perfmon.Put(GSPerfMon::RenderPasses, static_cast<double>(changed));
if (m_state.rt_view != rtv)
{
if (m_state.rt_view)
+6 -6
View File
@@ -120,8 +120,9 @@ private:
void SetFeatures();
bool CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode);
bool CreateSwapChain();
bool CreateSwapChainRTV();
void DestroySwapChain();
bool CreateTimestampQueries();
void DestroyTimestampQueries();
@@ -160,6 +161,7 @@ private:
bool m_allow_tearing_supported = false;
bool m_using_flip_model_swap_chain = true;
bool m_using_allow_tearing = false;
bool m_is_exclusive_fullscreen = false;
struct
{
@@ -281,16 +283,14 @@ public:
__fi ID3D11Device1* GetD3DDevice() const { return m_dev.get(); }
__fi ID3D11DeviceContext1* GetD3DContext() const { return m_ctx.get(); }
bool Create(const WindowInfo& wi, VsyncMode vsync) override;
bool Create() override;
void Destroy() override;
RenderAPI GetRenderAPI() const override;
bool ChangeWindow(const WindowInfo& new_wi) override;
bool UpdateWindow() override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
bool SupportsExclusiveFullscreen() const override;
bool IsExclusiveFullscreen() override;
bool SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
bool HasSurface() const override;
void DestroySurface() override;
std::string GetDriverInfo() const override;
@@ -343,7 +343,7 @@ public:
bool IASetVertexBuffer(const void* vertex, u32 stride, u32 count);
bool IASetExpandVertexBuffer(const void* vertex, u32 stride, u32 count);
u32* IAMapIndexBuffer(u32 count);
u16* IAMapIndexBuffer(u32 count);
void IAUnmapIndexBuffer(u32 count);
bool IASetIndexBuffer(const void* index, u32 count);
void IASetIndexBuffer(ID3D11Buffer* buffer);
+153 -150
View File
@@ -44,6 +44,8 @@ static bool IsDATEModePrimIDInit(u32 flag) { return flag == 1 || flag == 2; }
static constexpr std::array<D3D12_PRIMITIVE_TOPOLOGY, 3> s_primitive_topology_mapping =
{{D3D_PRIMITIVE_TOPOLOGY_POINTLIST, D3D_PRIMITIVE_TOPOLOGY_LINELIST, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST}};
static constexpr std::array<float, 4> s_present_clear_color = {};
static D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE GetLoadOpForTexture(GSTexture12* tex)
{
if (!tex)
@@ -116,9 +118,9 @@ bool GSDevice12::HasSurface() const
return static_cast<bool>(m_swap_chain);
}
bool GSDevice12::Create(const WindowInfo& wi, VsyncMode vsync)
bool GSDevice12::Create()
{
if (!GSDevice::Create(wi, vsync))
if (!GSDevice::Create())
return false;
m_dxgi_factory = D3D::CreateFactory(EmuConfig.GS.UseDebugDevice);
@@ -144,7 +146,7 @@ bool GSDevice12::Create(const WindowInfo& wi, VsyncMode vsync)
return false;
}
if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain(nullptr))
if (!AcquireWindow(true) || (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain()))
return false;
{
@@ -215,16 +217,14 @@ void GSDevice12::Destroy()
EndRenderPass();
ExecuteCommandList(true);
DestroyResources();
g_d3d12_context->WaitForGPUIdle();
GSDevice12::DestroySurface();
DestroySwapChain();
g_d3d12_context->Destroy();
}
}
bool GSDevice12::GetHostRefreshRate(float* refresh_rate)
{
if (m_swap_chain && IsExclusiveFullscreen())
if (m_swap_chain && m_is_exclusive_fullscreen)
{
DXGI_SWAP_CHAIN_DESC desc;
if (SUCCEEDED(m_swap_chain->GetDesc(&desc)) && desc.BufferDesc.RefreshRate.Numerator > 0 &&
@@ -246,59 +246,101 @@ void GSDevice12::SetVSync(VsyncMode mode)
m_vsync_mode = mode;
}
bool GSDevice12::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode)
bool GSDevice12::CreateSwapChain()
{
constexpr DXGI_FORMAT swap_chain_format = DXGI_FORMAT_R8G8B8A8_UNORM;
if (m_window_info.type != WindowInfo::Type::Win32)
return false;
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
RECT client_rc{};
GetClientRect(window_hwnd, &client_rc);
const u32 width = static_cast<u32>(client_rc.right - client_rc.left);
const u32 height = static_cast<u32>(client_rc.bottom - client_rc.top);
DXGI_MODE_DESC fullscreen_mode;
wil::com_ptr_nothrow<IDXGIOutput> fullscreen_output;
if (Host::IsFullscreen())
{
u32 fullscreen_width, fullscreen_height;
float fullscreen_refresh_rate;
m_is_exclusive_fullscreen =
GetRequestedExclusiveFullscreenMode(&fullscreen_width, &fullscreen_height, &fullscreen_refresh_rate) &&
D3D::GetRequestedExclusiveFullscreenModeDesc(m_dxgi_factory.get(), client_rc, fullscreen_width,
fullscreen_height, fullscreen_refresh_rate, swap_chain_format, &fullscreen_mode,
fullscreen_output.put());
}
else
{
m_is_exclusive_fullscreen = false;
}
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
swap_chain_desc.Width = width;
swap_chain_desc.Height = height;
swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swap_chain_desc.Width = static_cast<u32>(client_rc.right - client_rc.left);
swap_chain_desc.Height = static_cast<u32>(client_rc.bottom - client_rc.top);
swap_chain_desc.Format = swap_chain_format;
swap_chain_desc.SampleDesc.Count = 1;
swap_chain_desc.BufferCount = 3;
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
m_using_allow_tearing = (m_allow_tearing_supported && !fullscreen_mode);
m_using_allow_tearing = (m_allow_tearing_supported && !m_is_exclusive_fullscreen);
if (m_using_allow_tearing)
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {};
if (fullscreen_mode)
HRESULT hr = S_OK;
if (m_is_exclusive_fullscreen)
{
swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
swap_chain_desc.Width = fullscreen_mode->Width;
swap_chain_desc.Height = fullscreen_mode->Height;
fs_desc.RefreshRate = fullscreen_mode->RefreshRate;
fs_desc.ScanlineOrdering = fullscreen_mode->ScanlineOrdering;
fs_desc.Scaling = fullscreen_mode->Scaling;
DXGI_SWAP_CHAIN_DESC1 fs_sd_desc = swap_chain_desc;
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {};
fs_sd_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
fs_sd_desc.Width = fullscreen_mode.Width;
fs_sd_desc.Height = fullscreen_mode.Height;
fs_desc.RefreshRate = fullscreen_mode.RefreshRate;
fs_desc.ScanlineOrdering = fullscreen_mode.ScanlineOrdering;
fs_desc.Scaling = fullscreen_mode.Scaling;
fs_desc.Windowed = FALSE;
Console.WriteLn("Creating a %dx%d exclusive fullscreen swap chain", fs_sd_desc.Width, fs_sd_desc.Height);
hr = m_dxgi_factory->CreateSwapChainForHwnd(g_d3d12_context->GetCommandQueue(), window_hwnd, &fs_sd_desc,
&fs_desc, fullscreen_output.get(), m_swap_chain.put());
if (FAILED(hr))
{
Console.Warning("Failed to create fullscreen swap chain, trying windowed.");
m_is_exclusive_fullscreen = false;
m_using_allow_tearing = m_allow_tearing_supported;
}
}
DevCon.WriteLn("Creating a %dx%d %s swap chain", swap_chain_desc.Width, swap_chain_desc.Height,
fullscreen_mode ? "full-screen" : "windowed");
HRESULT hr = m_dxgi_factory->CreateSwapChainForHwnd(g_d3d12_context->GetCommandQueue(), window_hwnd,
&swap_chain_desc, fullscreen_mode ? &fs_desc : nullptr, nullptr, m_swap_chain.put());
if (FAILED(hr))
if (!m_is_exclusive_fullscreen)
{
Console.Error("CreateSwapChainForHwnd failed: 0x%08X", hr);
return false;
Console.WriteLn("Creating a %dx%d windowed swap chain", swap_chain_desc.Width, swap_chain_desc.Height);
hr = m_dxgi_factory->CreateSwapChainForHwnd(
g_d3d12_context->GetCommandQueue(), window_hwnd, &swap_chain_desc, nullptr, nullptr, m_swap_chain.put());
}
hr = m_dxgi_factory->MakeWindowAssociation(window_hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
if (FAILED(hr))
Console.Warning("MakeWindowAssociation() to disable ALT+ENTER failed");
return CreateSwapChainRTV();
if (!CreateSwapChainRTV())
{
DestroySwapChain();
return false;
}
// Render a frame as soon as possible to clear out whatever was previously being displayed.
EndRenderPass();
D3D12::Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
ID3D12GraphicsCommandList4* cmdlist = g_d3d12_context->GetCommandList();
m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast<u32>(m_swap_chain_buffers.size()));
swap_chain_buf.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
cmdlist->ClearRenderTargetView(swap_chain_buf.GetWriteDescriptor(), s_present_clear_color.data(), 0, nullptr);
swap_chain_buf.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_PRESENT);
ExecuteCommandList(false);
m_swap_chain->Present(0, m_using_allow_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0);
return true;
}
bool GSDevice12::CreateSwapChainRTV()
@@ -315,6 +357,7 @@ bool GSDevice12::CreateSwapChainRTV()
if (FAILED(hr))
{
Console.Error("GetBuffer for RTV failed: 0x%08X", hr);
m_swap_chain_buffers.clear();
return false;
}
@@ -322,6 +365,7 @@ bool GSDevice12::CreateSwapChainRTV()
if (!tex.Adopt(std::move(backbuffer), DXGI_FORMAT_UNKNOWN, swap_chain_desc.BufferDesc.Format,
DXGI_FORMAT_UNKNOWN, D3D12_RESOURCE_STATE_PRESENT))
{
m_swap_chain_buffers.clear();
return false;
}
@@ -360,26 +404,43 @@ void GSDevice12::DestroySwapChainRTVs()
m_current_swap_chain_buffer = 0;
}
bool GSDevice12::ChangeWindow(const WindowInfo& new_wi)
void GSDevice12::DestroySwapChain()
{
DestroySurface();
if (!m_swap_chain)
return;
m_window_info = new_wi;
if (new_wi.type == WindowInfo::Type::Surfaceless)
return true;
DestroySwapChainRTVs();
return CreateSwapChain(nullptr);
// switch out of fullscreen before destroying
BOOL is_fullscreen;
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen)
m_swap_chain->SetFullscreenState(FALSE, nullptr);
m_swap_chain.reset();
m_is_exclusive_fullscreen = false;
}
bool GSDevice12::UpdateWindow()
{
ExecuteCommandList(true);
DestroySwapChain();
if (!AcquireWindow(false))
return false;
if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain())
{
Console.WriteLn("Failed to create swap chain on updated window");
return false;
}
return true;
}
void GSDevice12::DestroySurface()
{
ExecuteCommandList(true);
if (IsExclusiveFullscreen())
SetExclusiveFullscreen(false, 0, 0, 0.0f);
DestroySwapChainRTVs();
m_swap_chain.reset();
DestroySwapChain();
}
std::string GSDevice12::GetDriverInfo() const
@@ -452,75 +513,6 @@ bool GSDevice12::SupportsExclusiveFullscreen() const
return true;
}
bool GSDevice12::IsExclusiveFullscreen()
{
BOOL is_fullscreen = FALSE;
return (m_swap_chain && SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen);
}
bool GSDevice12::SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
{
if (!m_swap_chain)
return false;
BOOL is_fullscreen = FALSE;
HRESULT hr = m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr);
if (!fullscreen)
{
// leaving fullscreen
if (is_fullscreen)
return SUCCEEDED(m_swap_chain->SetFullscreenState(FALSE, nullptr));
else
return true;
}
IDXGIOutput* output;
if (FAILED(hr = m_swap_chain->GetContainingOutput(&output)))
return false;
DXGI_SWAP_CHAIN_DESC current_desc;
hr = m_swap_chain->GetDesc(&current_desc);
if (FAILED(hr))
return false;
DXGI_MODE_DESC new_mode = current_desc.BufferDesc;
new_mode.Width = width;
new_mode.Height = height;
new_mode.RefreshRate.Numerator = static_cast<UINT>(std::floor(refresh_rate * 1000.0f));
new_mode.RefreshRate.Denominator = 1000u;
DXGI_MODE_DESC closest_mode;
if (FAILED(hr = output->FindClosestMatchingMode(&new_mode, &closest_mode, nullptr)) ||
new_mode.Format != current_desc.BufferDesc.Format)
{
Console.Error("Failed to find closest matching mode, hr=%08X", hr);
return false;
}
if (new_mode.Width == current_desc.BufferDesc.Width && new_mode.Height == current_desc.BufferDesc.Width &&
new_mode.RefreshRate.Numerator == current_desc.BufferDesc.RefreshRate.Numerator &&
new_mode.RefreshRate.Denominator == current_desc.BufferDesc.RefreshRate.Denominator)
{
DevCon.WriteLn("Fullscreen mode already set");
return true;
}
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::Sleep);
DestroySwapChainRTVs();
m_swap_chain.reset();
if (!CreateSwapChain(&closest_mode))
{
Console.Error("Failed to create a fullscreen swap chain");
if (!CreateSwapChain(nullptr))
pxFailRel("Failed to recreate windowed swap chain");
return false;
}
return true;
}
GSDevice::PresentResult GSDevice12::BeginPresent(bool frame_skip)
{
EndRenderPass();
@@ -531,13 +523,23 @@ GSDevice::PresentResult GSDevice12::BeginPresent(bool frame_skip)
if (frame_skip || !m_swap_chain)
return PresentResult::FrameSkipped;
static constexpr std::array<float, 4> clear_color = {};
// Check if we lost exclusive fullscreen. If so, notify the host, so it can switch to windowed mode.
// This might get called repeatedly if it takes a while to switch back, that's the host's problem.
BOOL is_fullscreen;
if (m_is_exclusive_fullscreen &&
(FAILED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) || !is_fullscreen))
{
Host::RunOnCPUThread([]() { Host::SetFullscreen(false); });
return PresentResult::FrameSkipped;
}
D3D12::Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList();
swap_chain_buf.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
cmdlist->ClearRenderTargetView(swap_chain_buf.GetWriteDescriptor(), clear_color.data(), 0, nullptr);
cmdlist->ClearRenderTargetView(swap_chain_buf.GetWriteDescriptor(), s_present_clear_color.data(), 0, nullptr);
cmdlist->OMSetRenderTargets(1, &swap_chain_buf.GetWriteDescriptor().cpu_handle, FALSE, nullptr);
g_perfmon.Put(GSPerfMon::RenderPasses, 1);
const D3D12_VIEWPORT vp{0.0f, 0.0f, static_cast<float>(m_window_info.surface_width),
static_cast<float>(m_window_info.surface_height), 0.0f, 1.0f};
@@ -949,13 +951,13 @@ void GSDevice12::DoMultiStretchRects(
{
// Set up vertices first.
const u32 vertex_reserve_size = num_rects * 4 * sizeof(GSVertexPT1);
const u32 index_reserve_size = num_rects * 6 * sizeof(u32);
const u32 index_reserve_size = num_rects * 6 * sizeof(u16);
if (!m_vertex_stream_buffer.ReserveMemory(vertex_reserve_size, sizeof(GSVertexPT1)) ||
!m_index_stream_buffer.ReserveMemory(index_reserve_size, sizeof(u32)))
!m_index_stream_buffer.ReserveMemory(index_reserve_size, sizeof(u16)))
{
ExecuteCommandListAndRestartRenderPass(false, "Uploading bytes to vertex buffer");
if (!m_vertex_stream_buffer.ReserveMemory(vertex_reserve_size, sizeof(GSVertexPT1)) ||
!m_index_stream_buffer.ReserveMemory(index_reserve_size, sizeof(u32)))
!m_index_stream_buffer.ReserveMemory(index_reserve_size, sizeof(u16)))
{
pxFailRel("Failed to reserve space for vertices");
}
@@ -965,7 +967,7 @@ void GSDevice12::DoMultiStretchRects(
// Don't use primitive restart here, it ends up slower on some drivers.
const GSVector2 ds(static_cast<float>(dTex->GetWidth()), static_cast<float>(dTex->GetHeight()));
GSVertexPT1* verts = reinterpret_cast<GSVertexPT1*>(m_vertex_stream_buffer.GetCurrentHostPointer());
u32* idx = reinterpret_cast<u32*>(m_index_stream_buffer.GetCurrentHostPointer());
u16* idx = reinterpret_cast<u16*>(m_index_stream_buffer.GetCurrentHostPointer());
u32 icount = 0;
u32 vcount = 0;
for (u32 i = 0; i < num_rects; i++)
@@ -996,12 +998,12 @@ void GSDevice12::DoMultiStretchRects(
m_vertex.start = m_vertex_stream_buffer.GetCurrentOffset() / sizeof(GSVertexPT1);
m_vertex.count = vcount;
m_index.start = m_index_stream_buffer.GetCurrentOffset() / sizeof(u32);
m_index.start = m_index_stream_buffer.GetCurrentOffset() / sizeof(u16);
m_index.count = icount;
m_vertex_stream_buffer.CommitMemory(vcount * sizeof(GSVertexPT1));
m_index_stream_buffer.CommitMemory(icount * sizeof(u32));
m_index_stream_buffer.CommitMemory(icount * sizeof(u16));
SetVertexBuffer(m_vertex_stream_buffer.GetGPUPointer(), m_vertex_stream_buffer.GetSize(), sizeof(GSVertexPT1));
SetIndexBuffer(m_index_stream_buffer.GetGPUPointer(), m_index_stream_buffer.GetSize(), DXGI_FORMAT_R32_UINT);
SetIndexBuffer(m_index_stream_buffer.GetGPUPointer(), m_index_stream_buffer.GetSize(), DXGI_FORMAT_R16_UINT);
// Even though we're batching, a cmdbuffer submit could've messed this up.
const GSVector4i rc(dTex->GetRect());
@@ -1422,22 +1424,10 @@ void GSDevice12::RenderImGui()
m_vertex_stream_buffer.CommitMemory(size);
}
u32 index_offset;
{
const u32 size = sizeof(ImDrawIdx) * static_cast<u32>(cmd_list->IdxBuffer.Size);
if (!m_index_stream_buffer.ReserveMemory(size, sizeof(ImDrawIdx)))
{
Console.Warning("Skipping ImGui draw because of no vertex buffer space");
return;
}
index_offset = m_index_stream_buffer.GetCurrentOffset() / sizeof(ImDrawIdx);
std::memcpy(m_index_stream_buffer.GetCurrentHostPointer(), cmd_list->IdxBuffer.Data, size);
m_index_stream_buffer.CommitMemory(size);
}
SetVertexBuffer(m_vertex_stream_buffer.GetGPUPointer(), m_vertex_stream_buffer.GetSize(), sizeof(ImDrawVert));
SetIndexBuffer(m_index_stream_buffer.GetGPUPointer(), m_index_stream_buffer.GetSize(), DXGI_FORMAT_R16_UINT);
static_assert(sizeof(ImDrawIdx) == sizeof(u16));
IASetIndexBuffer(cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
@@ -1468,7 +1458,7 @@ void GSDevice12::RenderImGui()
if (ApplyUtilityState())
{
g_d3d12_context->GetCommandList()->DrawIndexedInstanced(
pcmd->ElemCount, 1, index_offset + pcmd->IdxOffset, vertex_offset + pcmd->VtxOffset, 0);
pcmd->ElemCount, 1, m_index.start + pcmd->IdxOffset, vertex_offset + pcmd->VtxOffset, 0);
}
}
@@ -1536,17 +1526,17 @@ void GSDevice12::IASetVertexBuffer(const void* vertex, size_t stride, size_t cou
void GSDevice12::IASetIndexBuffer(const void* index, size_t count)
{
const u32 size = sizeof(u32) * static_cast<u32>(count);
if (!m_index_stream_buffer.ReserveMemory(size, sizeof(u32)))
const u32 size = sizeof(u16) * static_cast<u32>(count);
if (!m_index_stream_buffer.ReserveMemory(size, sizeof(u16)))
{
ExecuteCommandListAndRestartRenderPass(false, "Uploading bytes to index buffer");
if (!m_index_stream_buffer.ReserveMemory(size, sizeof(u32)))
if (!m_index_stream_buffer.ReserveMemory(size, sizeof(u16)))
pxFailRel("Failed to reserve space for vertices");
}
m_index.start = m_index_stream_buffer.GetCurrentOffset() / sizeof(u32);
m_index.start = m_index_stream_buffer.GetCurrentOffset() / sizeof(u16);
m_index.count = count;
SetIndexBuffer(m_index_stream_buffer.GetGPUPointer(), m_index_stream_buffer.GetSize(), DXGI_FORMAT_R32_UINT);
SetIndexBuffer(m_index_stream_buffer.GetGPUPointer(), m_index_stream_buffer.GetSize(), DXGI_FORMAT_R16_UINT);
std::memcpy(m_index_stream_buffer.GetCurrentHostPointer(), index, size);
m_index_stream_buffer.CommitMemory(size);
@@ -1622,7 +1612,7 @@ bool GSDevice12::GetSampler(D3D12::DescriptorHandle* cpu_handle, GSHWDrawConfig:
sd.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
sd.MinLOD = 0.0f;
sd.MaxLOD = (ss.lodclamp || !ss.UseMipmapFiltering()) ? 0.25f : FLT_MAX;
sd.MaxAnisotropy = std::clamp(GSConfig.MaxAnisotropy, 1, 16);
sd.MaxAnisotropy = std::clamp<u8>(GSConfig.MaxAnisotropy, 1, 16);
sd.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
if (!g_d3d12_context->GetSamplerHeapManager().Allocate(cpu_handle))
@@ -2758,11 +2748,15 @@ void GSDevice12::EndRenderPass()
if (!m_in_render_pass)
return;
g_d3d12_context->GetCommandList()->EndRenderPass();
m_in_render_pass = false;
// to render again, we need to reset OM
m_dirty_flags |= DIRTY_FLAG_RENDER_TARGET;
g_perfmon.Put(GSPerfMon::RenderPasses, 1);
g_d3d12_context->GetCommandList()->EndRenderPass();
}
void GSDevice12::SetViewport(const D3D12_VIEWPORT& viewport)
@@ -3212,13 +3206,22 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
}
// avoid restarting the render pass just to switch from rt+depth to rt and vice versa
if (m_in_render_pass && !hdr_rt && !draw_ds && m_current_depth_target && m_current_render_target == draw_rt &&
config.tex != m_current_depth_target && m_current_depth_target->GetSize() == draw_rt->GetSize())
if (m_in_render_pass && (m_current_render_target == draw_rt || m_current_depth_target == draw_ds))
{
// avoid restarting the render pass just to switch from rt+depth to rt and vice versa
// keep the depth even if doing HDR draws, because the next draw will probably re-enable depth
if (!draw_rt && m_current_render_target && config.tex != m_current_render_target &&
m_current_render_target->GetSize() == draw_ds->GetSize())
{
draw_rt = m_current_render_target;
m_pipeline_selector.rt = true;
}
}
else if (!draw_ds && m_current_depth_target && config.tex != m_current_depth_target &&
m_current_depth_target->GetSize() == draw_rt->GetSize())
{
draw_ds = m_current_depth_target;
m_pipeline_selector.ds = true;
m_pipeline_selector.dss.ztst = ZTST_ALWAYS;
m_pipeline_selector.dss.zwe = false;
}
OMSetRenderTargets(draw_rt, draw_ds, config.scissor);
@@ -3353,7 +3356,7 @@ void GSDevice12::UploadHWDrawVerticesAndIndices(const GSHWDrawConfig& config)
{
m_index.start = 0;
m_index.count = config.nindices;
SetIndexBuffer(m_expand_index_buffer->GetGPUVirtualAddress(), EXPAND_BUFFER_SIZE, DXGI_FORMAT_R32_UINT);
SetIndexBuffer(m_expand_index_buffer->GetGPUVirtualAddress(), EXPAND_BUFFER_SIZE, DXGI_FORMAT_R16_UINT);
}
else
{
+10 -9
View File
@@ -51,15 +51,16 @@ public:
u32 key;
};
GSHWDrawConfig::BlendState bs;
GSHWDrawConfig::VSSelector vs;
GSHWDrawConfig::DepthStencilSelector dss;
GSHWDrawConfig::ColorMaskSelector cms;
GSHWDrawConfig::BlendState bs;
u8 pad;
__fi bool operator==(const PipelineSelector& p) const { return (memcmp(this, &p, sizeof(p)) == 0); }
__fi bool operator!=(const PipelineSelector& p) const { return (memcmp(this, &p, sizeof(p)) != 0); }
__fi bool operator==(const PipelineSelector& p) const { return BitEqual(*this, p); }
__fi bool operator!=(const PipelineSelector& p) const { return !BitEqual(*this, p); }
__fi PipelineSelector() { memset(this, 0, sizeof(*this)); }
__fi PipelineSelector() { std::memset(this, 0, sizeof(*this)); }
};
static_assert(sizeof(PipelineSelector) == 24, "Pipeline selector is 24 bytes");
@@ -145,6 +146,7 @@ private:
bool m_allow_tearing_supported = false;
bool m_using_allow_tearing = false;
bool m_is_exclusive_fullscreen = false;
bool m_device_lost = false;
ComPtr<ID3D12RootSignature> m_tfx_root_signature;
@@ -191,9 +193,10 @@ private:
void LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format, DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format) const;
bool CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode);
bool CreateSwapChain();
bool CreateSwapChainRTV();
void DestroySwapChainRTVs();
void DestroySwapChain();
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
@@ -243,14 +246,12 @@ public:
RenderAPI GetRenderAPI() const override;
bool HasSurface() const override;
bool Create(const WindowInfo& wi, VsyncMode vsync) override;
bool Create() override;
void Destroy() override;
bool ChangeWindow(const WindowInfo& new_wi) override;
bool UpdateWindow() override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
bool SupportsExclusiveFullscreen() const override;
bool IsExclusiveFullscreen() override;
bool SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
void DestroySurface() override;
std::string GetDriverInfo() const override;
+1 -1
View File
@@ -297,7 +297,7 @@ bool GSHwHack::GSC_BlackAndBurnoutSky(GSRendererHW& r, int& skip)
if (RPRIM->PRIM == GS_SPRITE && !RPRIM->IIP && RPRIM->TME && !RPRIM->FGE && RPRIM->ABE && !RPRIM->AA1 && !RPRIM->FST && !RPRIM->FIX &&
ALPHA.A == ALPHA.B && ALPHA.D == 0 && FRAME.PSM == PSMCT32 && TEX0.CPSM == PSMCT32 && TEX0.TCC && !TEX0.TFX && !TEX0.CSM)
{
if (TEX0.TBW == 16 && TEX0.TW == 10 && TEX0.PSM == PSMT8 && TEX0.TH == 8 && FRAME.FBW == 16)
if (TEX0.TBW == 16 && TEX0.TW == 10 && TEX0.PSM == PSMT8 && TEX0.TH >= 7 && FRAME.FBW == 16)
{
// Readback clouds being rendered during level loading.
// Later the alpha channel from the 32 bit frame buffer is used as an 8 bit indexed texture to draw
+51 -51
View File
@@ -112,7 +112,7 @@ void GSRendererHW::UpdateSettings(const Pcsx2Config::GSOptions& old_config)
SetTCOffset();
}
void GSRendererHW::VSync(u32 field, bool registers_written)
void GSRendererHW::VSync(u32 field, bool registers_written, bool idle_frame)
{
if (m_force_preload > 0)
{
@@ -138,7 +138,7 @@ void GSRendererHW::VSync(u32 field, bool registers_written)
// Don't age the texture cache when no draws or EE writes have occurred.
// Xenosaga needs its targets kept around while it's loading, because it uses them for a fade transition.
if (m_last_draw_n == s_n && m_last_transfer_n == s_transfer_n)
if (idle_frame)
{
GL_INS("No draws or transfers, not aging TC");
}
@@ -147,10 +147,7 @@ void GSRendererHW::VSync(u32 field, bool registers_written)
g_texture_cache->IncAge();
}
m_last_draw_n = s_n + 1; // +1 for vsync
m_last_transfer_n = s_transfer_n;
GSRenderer::VSync(field, registers_written);
GSRenderer::VSync(field, registers_written, idle_frame);
if (g_texture_cache->GetHashCacheMemoryUsage() > 1024 * 1024 * 1024)
{
@@ -258,14 +255,10 @@ void GSRendererHW::Lines2Sprites()
int i = static_cast<int>(count) * 2 - 4;
GSVertex* s = &m_vertex.buff[count - 2];
GSVertex* q = &m_vertex.buff[count * 2 - 4];
u32* RESTRICT index = &m_index.buff[count * 3 - 6];
u16* RESTRICT index = &m_index.buff[count * 3 - 6];
alignas(16) static constexpr std::array<int, 8> tri_normal_indices = {{0, 1, 2, 1, 2, 3}};
alignas(16) static constexpr std::array<int, 8> tri_swapped_indices = {{0, 1, 2, 1, 2, 3}};
const bool index_swap = !g_gs_device->Features().provoking_vertex_last;
const int* tri_indices = index_swap ? tri_swapped_indices.data() : tri_normal_indices.data();
const GSVector4i indices_low(GSVector4i::load<true>(tri_indices));
const GSVector4i indices_high(GSVector4i::loadl(tri_indices + 4));
// Sprites are flat shaded, so the provoking vertex doesn't matter here.
constexpr GSVector4i indices = GSVector4i::cxpr16(0, 1, 2, 1, 2, 3, 0, 0);
for (; i >= 0; i -= 4, s -= 2, q -= 4, index -= 6)
{
@@ -310,9 +303,10 @@ void GSRendererHW::Lines2Sprites()
q[1] = v0;
q[2] = v1;
const GSVector4i i_splat(i);
GSVector4i::store<false>(index, i_splat + indices_low);
GSVector4i::storel(index + 4, i_splat + indices_high);
const GSVector4i this_indices = GSVector4i::broadcast16(i).add16(indices);
const int high = this_indices.extract32<2>();
GSVector4i::storel(index, this_indices);
std::memcpy(&index[4], &high, sizeof(high));
}
m_vertex.head = m_vertex.tail = m_vertex.next = count * 2;
@@ -322,26 +316,30 @@ void GSRendererHW::Lines2Sprites()
void GSRendererHW::ExpandLineIndices()
{
const u32 process_count = (m_index.tail + 3) / 4 * 4;
const u32 process_count = (m_index.tail + 7) / 8 * 8;
const u32 expansion_factor = 3;
m_index.tail *= expansion_factor;
GSVector4i* end = reinterpret_cast<GSVector4i*>(m_index.buff);
GSVector4i* read = reinterpret_cast<GSVector4i*>(m_index.buff + process_count);
GSVector4i* write = reinterpret_cast<GSVector4i*>(m_index.buff + process_count * expansion_factor);
constexpr GSVector4i low0 = GSVector4i::cxpr(0, 1, 2, 1);
constexpr GSVector4i low1 = GSVector4i::cxpr(2, 3, 0, 1);
constexpr GSVector4i low2 = GSVector4i::cxpr(2, 1, 2, 3);
constexpr GSVector4i mask0 = GSVector4i::cxpr8(0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 2, 3, 4, 5, 4, 5);
constexpr GSVector4i mask1 = GSVector4i::cxpr8(6, 7, 4, 5, 6, 7, 6, 7, 8, 9, 8, 9, 10, 11, 8, 9);
constexpr GSVector4i mask2 = GSVector4i::cxpr8(10, 11, 10, 11, 12, 13, 12, 13, 14, 15, 12, 13, 14, 15, 14, 15);
constexpr GSVector4i low0 = GSVector4i::cxpr16(0, 1, 2, 1, 2, 3, 0, 1);
constexpr GSVector4i low1 = GSVector4i::cxpr16(2, 1, 2, 3, 0, 1, 2, 1);
constexpr GSVector4i low2 = GSVector4i::cxpr16(2, 3, 0, 1, 2, 1, 2, 3);
while (read > end)
{
read -= 1;
write -= expansion_factor;
const GSVector4i in = read->sll32(2);
write[0] = in.xxyx() | low0;
write[1] = in.yyzz() | low1;
write[2] = in.wzww() | low2;
const GSVector4i in = read->sll16(2);
write[0] = in.shuffle8(mask0) | low0;
write[1] = in.shuffle8(mask1) | low1;
write[2] = in.shuffle8(mask2) | low2;
}
}
@@ -623,7 +621,7 @@ GSVector4 GSRendererHW::RealignTargetTextureCoordinate(const GSTextureCache::Sou
GSVector4i GSRendererHW::ComputeBoundingBox(const GSVector2i& rtsize, float rtscale)
{
const GSVector4 offset = GSVector4(-1.0f, 1.0f); // Round value
const GSVector4 box = m_vt.m_min.p.xyxy(m_vt.m_max.p) + offset.xxyy();
const GSVector4 box = m_vt.m_min.p.upld(m_vt.m_max.p) + offset.xxyy();
return GSVector4i(box * GSVector4(rtscale)).rintersect(GSVector4i(0, 0, rtsize.x, rtsize.y));
}
@@ -655,9 +653,9 @@ void GSRendererHW::MergeSprite(GSTextureCache::Source* tex)
}
#if 0
GSVector4 delta_p = m_vt.m_max.p - m_vt.m_min.p;
GSVector4 delta_t = m_vt.m_max.t - m_vt.m_min.t;
bool is_blit = PrimitiveOverlap() == PRIM_OVERLAP_NO;
const GSVector4 delta_p = m_vt.m_max.p - m_vt.m_min.p;
const GSVector4 delta_t = m_vt.m_max.t - m_vt.m_min.t;
const bool is_blit = PrimitiveOverlap() == PRIM_OVERLAP_NO;
GL_INS("PP SAMPLER: Dp %f %f Dt %f %f. Is blit %d, is paving %d, count %d", delta_p.x, delta_p.y, delta_t.x, delta_t.y, is_blit, is_paving, m_vertex.tail);
#endif
@@ -769,7 +767,7 @@ bool GSRendererHW::IsPossibleChannelShuffle() const
// WRC 4 does channel shuffles in vertical strips. So check for page alignment.
// Texture TBW should also be twice the framebuffer FBW, because the page is twice as wide.
if (m_cached_ctx.TEX0.TBW == (m_cached_ctx.FRAME.FBW * 2) &&
GSLocalMemory::IsPageAligned(m_cached_ctx.FRAME.PSM, GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p))))
GSLocalMemory::IsPageAligned(m_cached_ctx.FRAME.PSM, GSVector4i(m_vt.m_min.p.upld(m_vt.m_max.p))))
{
return true;
}
@@ -804,11 +802,11 @@ bool GSRendererHW::IsSplitTextureShuffle()
// For texture shuffles, the U will be offset by 8.
const GSLocalMemory::psm_t& frame_psm = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM];
const GSVector4i pos_rc = GSVector4i(m_vt.m_min.p.upld(m_vt.m_max.p));
const GSVector4i pos_rc = GSVector4i(m_vt.m_min.p.upld(m_vt.m_max.p + GSVector4::cxpr(0.5f)));
const GSVector4i tex_rc = GSVector4i(m_vt.m_min.t.upld(m_vt.m_max.t));
// Width/height should match.
if (pos_rc.width() != tex_rc.width() || pos_rc.height() != tex_rc.height())
if (std::abs(pos_rc.width() - tex_rc.width()) > 8 || pos_rc.height() != tex_rc.height())
return false;
// X might be offset by up to -8/+8, but either the position or UV should be aligned.
@@ -867,7 +865,7 @@ bool GSRendererHW::IsSplitTextureShuffle()
GSVector4i GSRendererHW::GetSplitTextureShuffleDrawRect() const
{
const GSLocalMemory::psm_t& frame_psm = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM];
GSVector4i r = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(GSVector4i(m_context->scissor.in));
GSVector4i r = GSVector4i(m_vt.m_min.p.upld(m_vt.m_max.p + GSVector4::cxpr(0.5f))).rintersect(GSVector4i(m_context->scissor.in));
// Some games (e.g. Crash Twinsanity) adjust both FBP and TBP0, so the rectangle will be half the size
// of the actual shuffle. Others leave the FBP alone, but only adjust TBP0, and offset the draw rectangle
@@ -1434,7 +1432,7 @@ void GSRendererHW::Draw()
#endif
}
GL_PUSH("HW Draw %d", s_n);
GL_PUSH("HW Draw %d (Context %u)", s_n, PRIM->CTXT);
// When the format is 24bit (Z or C), DATE ceases to function.
// It was believed that in 24bit mode all pixels pass because alpha doesn't exist
@@ -1572,7 +1570,7 @@ void GSRendererHW::Draw()
}
// The rectangle of the draw rounded up.
const GSVector4 rect = m_vt.m_min.p.xyxy(m_vt.m_max.p) + GSVector4(0.0f, 0.0f, 0.5f, 0.5f);
const GSVector4 rect = m_vt.m_min.p.upld(m_vt.m_max.p + GSVector4::cxpr(0.5f));
m_r = GSVector4i(rect).rintersect(GSVector4i(context->scissor.in));
if (!m_channel_shuffle && m_cached_ctx.FRAME.Block() == m_cached_ctx.TEX0.TBP0 &&
@@ -2513,9 +2511,9 @@ void GSRendererHW::SetupIA(float target_scale, float sx, float sy)
m_conf.nindices = m_index.tail;
}
void GSRendererHW::EmulateZbuffer()
void GSRendererHW::EmulateZbuffer(const GSTextureCache::Target* ds)
{
if (m_cached_ctx.TEST.ZTE)
if (ds && m_cached_ctx.TEST.ZTE)
{
m_conf.depth.ztst = m_cached_ctx.TEST.ZTST;
// AA1: Z is not written on lines since coverage is always less than 0x80.
@@ -3316,7 +3314,7 @@ void GSRendererHW::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool&
// For stat to optimize accurate option
#if 0
GL_INS("BLEND_INFO: %u/%u/%u/%u. Clamp:%u. Prim:%d number %u (drawlist %u) (sw %d)",
GL_INS("BLEND_INFO: %u/%u/%u/%u. Clamp:%u. Prim:%d number %u (drawlist %zu) (sw %d)",
m_conf.ps.blend_a, m_conf.ps.blend_b, m_conf.ps.blend_c, m_conf.ps.blend_d,
m_env.COLCLAMP.CLAMP, m_vt.m_primclass, m_vertex.next, m_drawlist.size(), sw_blending);
#endif
@@ -3606,11 +3604,11 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
const u8 wms = EffectiveClamp(m_cached_ctx.CLAMP.WMS, !tex->m_target && (source_region.HasX() || redundant_wms));
const u8 wmt = EffectiveClamp(m_cached_ctx.CLAMP.WMT, !tex->m_target && (source_region.HasY() || redundant_wmt));
const bool complex_wms_wmt = !!((wms | wmt) & 2) || target_region;
GL_CACHE("WMS: %s [%s%s] WMT: %s [%s%s] Complex: %d TargetRegion: %d MINU: %d MAXU: %d MINV: %d MAXV: %d",
clamp_modes[m_cached_ctx.CLAMP.WMS], redundant_wms ? "redundant," : "", clamp_modes[wms],
clamp_modes[m_cached_ctx.CLAMP.WMT], redundant_wmt ? "redundant," : "", clamp_modes[wmt],
complex_wms_wmt, target_region,
m_cached_ctx.CLAMP.MINU, m_cached_ctx.CLAMP.MAXU, m_cached_ctx.CLAMP.MINV, m_cached_ctx.CLAMP.MAXV);
GL_CACHE("FST: %s WMS: %s [%s%s] WMT: %s [%s%s] Complex: %d TargetRegion: %d MINU: %d MAXU: %d MINV: %d MAXV: %d",
PRIM->FST ? "UV" : "STQ", clamp_modes[m_cached_ctx.CLAMP.WMS], redundant_wms ? "redundant," : "",
clamp_modes[wms], clamp_modes[m_cached_ctx.CLAMP.WMT], redundant_wmt ? "redundant," : "", clamp_modes[wmt],
complex_wms_wmt, target_region, m_cached_ctx.CLAMP.MINU, m_cached_ctx.CLAMP.MAXU, m_cached_ctx.CLAMP.MINV,
m_cached_ctx.CLAMP.MAXV);
const bool need_mipmap = IsMipMapDraw();
const bool shader_emulated_sampler = tex->m_palette || (tex->m_target && !m_conf.ps.shuffle && cpsm.fmt != 0) ||
@@ -4092,7 +4090,7 @@ bool GSRendererHW::CanUseTexIsFB(const GSTextureCache::Target* rt, const GSTextu
// Make sure that we're not sampling away from the area we're rendering.
// We need to take the absolute here, because Beyond Good and Evil undithers itself using a -1,-1 offset.
const GSVector4 diff(m_vt.m_min.p.xyxy(m_vt.m_max.p) - m_vt.m_min.t.xyxy(m_vt.m_max.t));
const GSVector4 diff(m_vt.m_min.p.upld(m_vt.m_max.p) - m_vt.m_min.t.upld(m_vt.m_max.t));
GL_CACHE("Coord diff: %f,%f", diff.x, diff.y);
if ((diff.abs() < GSVector4(1.0f)).alltrue())
{
@@ -4162,8 +4160,8 @@ void GSRendererHW::ResetStates()
__ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Target* ds, GSTextureCache::Source* tex, const TextureMinMaxResult& tmm)
{
#ifdef ENABLE_OGL_DEBUG
const GSVector4i area_out = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(GSVector4i(m_context->scissor.in));
const GSVector4i area_in = GSVector4i(m_vt.m_min.t.xyxy(m_vt.m_max.t));
const GSVector4i area_out = GSVector4i(m_vt.m_min.p.upld(m_vt.m_max.p)).rintersect(GSVector4i(m_context->scissor.in));
const GSVector4i area_in = GSVector4i(m_vt.m_min.t.upld(m_vt.m_max.t));
GL_PUSH("GL Draw from (area %d,%d => %d,%d) in (area %d,%d => %d,%d)",
area_in.x, area_in.y, area_in.z, area_in.w,
@@ -4189,7 +4187,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
m_conf.ds = ds ? ds->m_texture : nullptr;
// Z setup has to come before channel shuffle
EmulateZbuffer();
EmulateZbuffer(ds);
// HLE implementation of the channel selection effect
//
@@ -4321,7 +4319,10 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
// No point outputting colours if we're just writing depth.
// We might still need the framebuffer for DATE, though.
if (!rt || m_conf.colormask.wrgba == 0)
{
m_conf.ps.DisableColorOutput();
m_conf.colormask.wrgba = 0;
}
if (m_conf.ps.scanmsk & 2)
DATE_PRIMID = false; // to have discard in the shader work correctly
@@ -4934,16 +4935,15 @@ void GSRendererHW::OI_DoubleHalfClear(GSTextureCache::Target*& rt, GSTextureCach
clear_depth ? "depth" : "target", base << 5, half << 5, w_pages, h_pages, m_cached_ctx.FRAME.FBW, color);
// If some of the channels are masked, we need to keep them.
if (m_cached_ctx.FRAME.FBMSK != 0)
if (!clear_depth && m_cached_ctx.FRAME.FBMSK != 0)
{
GSTexture* tex = nullptr;
GSTextureCache::Target* target = clear_depth ? ds : rt;
const GSVector2 size = GSVector2(static_cast<float>(target->GetUnscaledWidth()) * target->m_scale, static_cast<float>(target->GetUnscaledHeight()) * target->m_scale);
pxAssert(!target->m_texture->IsDepthStencil());
try
{
tex = target->m_texture->IsDepthStencil() ?
g_gs_device->CreateDepthStencil(size.x, size.y, target->m_texture->GetFormat(), true) :
g_gs_device->CreateRenderTarget(size.x, size.y, target->m_texture->GetFormat(), true);
tex = g_gs_device->CreateRenderTarget(size.x, size.y, target->m_texture->GetFormat(), false);
}
catch (const std::bad_alloc&)
{
@@ -5059,7 +5059,7 @@ bool GSRendererHW::OI_GsMemClear()
if (((m_vertex.next == 2) || ZisFrame) && m_vt.m_eq.rgba == 0xFFFF)
{
const GSOffset& off = m_context->offset.fb;
GSVector4i r = GSVector4i(m_vt.m_min.p.xyxy(m_vt.m_max.p)).rintersect(GSVector4i(m_context->scissor.in));
GSVector4i r = GSVector4i(m_vt.m_min.p.upld(m_vt.m_max.p)).rintersect(GSVector4i(m_context->scissor.in));
if (r.width() == 32 && ZisFrame)
r.z += 32;
+2 -6
View File
@@ -86,7 +86,7 @@ private:
bool& target_region, GSVector2i& unscaled_size, float& scale, GSTexture*& src_copy);
bool CanUseTexIsFB(const GSTextureCache::Target* rt, const GSTextureCache::Source* tex) const;
void EmulateZbuffer();
void EmulateZbuffer(const GSTextureCache::Target* ds);
void EmulateATST(float& AREF, GSHWDrawConfig::PSSelector& ps, bool pass_2);
void SetTCOffset();
@@ -149,10 +149,6 @@ private:
std::unique_ptr<GSTextureCacheSW::Texture> m_sw_texture[7 + 1];
std::unique_ptr<GSVirtualAlignedClass<32>> m_sw_rasterizer;
// Tracking draw counters for idle frame detection.
int m_last_draw_n = 0;
int m_last_transfer_n = 0;
public:
GSRendererHW();
virtual ~GSRendererHW() override;
@@ -178,7 +174,7 @@ public:
void Reset(bool hardware_reset) override;
void UpdateSettings(const Pcsx2Config::GSOptions& old_config) override;
void VSync(u32 field, bool registers_written) override;
void VSync(u32 field, bool registers_written, bool idle_frame) override;
GSTexture* GetOutput(int i, float& scale, int& y_offset) override;
GSTexture* GetFeedbackOutput(float& scale) override;
+119 -43
View File
@@ -35,12 +35,16 @@ std::unique_ptr<GSTextureCache> g_texture_cache;
static u8* s_unswizzle_buffer;
/// List of candidates for purging when the hash cache gets too large.
static std::vector<std::pair<GSTextureCache::HashCacheMap::iterator, s32>> s_hash_cache_purge_list;
GSTextureCache::GSTextureCache()
{
// In theory 4MB is enough but 9MB is safer for overflow (8MB
// isn't enough in custom resolution)
// Test: onimusha 3 PAL 60Hz
s_unswizzle_buffer = (u8*)_aligned_malloc(9 * 1024 * 1024, 32);
s_unswizzle_buffer = (u8*)_aligned_malloc(9 * 1024 * 1024, VECTOR_ALIGNMENT);
pxAssertRel(s_unswizzle_buffer, "Failed to allocate unswizzle buffer");
m_surface_offset_cache.reserve(S_SURFACE_OFFSET_CACHE_MAX_SIZE);
}
@@ -49,6 +53,7 @@ GSTextureCache::~GSTextureCache()
{
RemoveAll();
s_hash_cache_purge_list = {};
_aligned_free(s_unswizzle_buffer);
}
@@ -851,8 +856,7 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
// If the source is reading the rt, make sure it's big enough.
if (t && GSUtil::HasCompatibleBits(psm, t->m_TEX0.PSM))
{
GSVector4i dirty_rect = t->m_dirty.GetTotalRect(t->m_TEX0, GSVector2i(new_rect.z, new_rect.w));
const GSVector2i size_delta = { (dirty_rect.z - t->m_valid.z), (dirty_rect.w - t->m_valid.w) };
const GSVector2i size_delta = { (new_rect.z - t->m_valid.z), (new_rect.w - t->m_valid.w) };
if (size_delta.x > 0 || size_delta.y > 0)
{
RGBAMask rgba;
@@ -1267,7 +1271,13 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
g_gs_device->StretchRect(dst->m_texture, sRect, tex, dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
m_target_memory_usage = (m_target_memory_usage - dst->m_texture->GetMemUsage()) + tex->GetMemUsage();
g_gs_device->Recycle(dst->m_texture);
// If we're changing resolution scale, just toss the texture, it's not going to get reused.
if (!GSConfig.UserHacks_NativePaletteDraw || (dst->m_scale != 1.0f && scale != 1.0f))
delete dst->m_texture;
else
g_gs_device->Recycle(dst->m_texture);
dst->m_texture = tex;
dst->m_scale = scale;
dst->m_unscaled_size = new_size;
@@ -2209,9 +2219,9 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
}
const GSVector4i draw_rect = (t->readbacks_since_draw > 1) ? t->m_drawn_since_read : targetr.rintersect(t->m_drawn_since_read);
const GSVector4i dirty_rect = t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size);
// Recently made this section dirty, no need to read it.
if (draw_rect.rintersect(t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size)).eq(draw_rect))
if (draw_rect.rintersect(dirty_rect).eq(draw_rect))
return;
if (t->m_drawn_since_read.eq(GSVector4i::zero()))
@@ -2224,6 +2234,10 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
if (!draw_rect.rempty())
{
// The draw rect and read rect overlap somewhat, we should update the target before downloading it.
if (!dirty_rect.rintersect(targetr).rempty())
t->Update(false);
Read(t, draw_rect);
z_found = read_start >= t->m_TEX0.TBP0 && read_end <= t->m_end_block;
@@ -2346,8 +2360,9 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
continue;
}
const GSVector4i dirty_rect = t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size);
// Recently made this section dirty, no need to read it.
if (targetr.rintersect(t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size)).eq(targetr))
if (targetr.rintersect(dirty_rect).eq(targetr))
{
if (exact_bp)
return;
@@ -2368,6 +2383,10 @@ void GSTextureCache::InvalidateLocalMem(const GSOffset& off, const GSVector4i& r
continue;
}
// The draw rect and read rect overlap somewhat, we should update the target before downloading it.
if (!dirty_rect.rintersect(targetr).rempty())
t->Update(false);
Read(t, targetr);
// Try to cut down how much we read next, if we can.
@@ -2417,15 +2436,17 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
// DX11/12 is a bit lame and can't partial copy depth targets. We could do this with a blit instead,
// but so far haven't seen anything which needs it.
const GSLocalMemory::psm_t& spsm_s = GSLocalMemory::m_psm[SPSM];
const GSLocalMemory::psm_t& dpsm_s = GSLocalMemory::m_psm[DPSM];
if (GSConfig.Renderer == GSRendererType::DX11 || GSConfig.Renderer == GSRendererType::DX12)
{
if (GSLocalMemory::m_psm[SPSM].depth || GSLocalMemory::m_psm[DPSM].depth)
if (spsm_s.depth || dpsm_s.depth)
return false;
}
// Look for an exact match on the targets.
GSTextureCache::Target* src = GetExactTarget(SBP, SBW, SPSM);
GSTextureCache::Target* dst = GetExactTarget(DBP, DBW, DPSM);
GSTextureCache::Target* src = GetExactTarget(SBP, SBW, spsm_s.depth ? DepthStencil : RenderTarget);
GSTextureCache::Target* dst = GetExactTarget(DBP, DBW, dpsm_s.depth ? DepthStencil : RenderTarget);
// Beware of the case where a game might create a larger texture by moving a bunch of chunks around.
// We use dx/dy == 0 and the TBW check as a safeguard to make sure these go through to local memory.
@@ -2498,7 +2519,8 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
// Make sure the copy doesn't go out of bounds (it shouldn't).
if ((scaled_dx + scaled_w) > dst->m_texture->GetWidth() || (scaled_dy + scaled_h) > dst->m_texture->GetHeight())
return false;
GL_CACHE("HW Move 0x%x to 0x%x <%d,%d->%d,%d> -> <%d,%d->%d,%d>", SBP, DBP, sx, sy, sx + w, sy, h, dx, dy, dx + w, dy + h);
GL_CACHE("HW Move 0x%x[BW:%u PSM:%s] to 0x%x[BW:%u PSM:%s] <%d,%d->%d,%d> -> <%d,%d->%d,%d>", SBP, SBW,
psm_str(SPSM), DBP, DBW, psm_str(DPSM), sx, sy, sx + w, sy + h, dx, dy, dx + w, dy + h);
g_gs_device->CopyRect(src->m_texture, dst->m_texture,
GSVector4i(scaled_sx, scaled_sy, scaled_sx + scaled_w, scaled_sy + scaled_h),
scaled_dx, scaled_dy);
@@ -2576,7 +2598,7 @@ bool GSTextureCache::ShuffleMove(u32 BP, u32 BW, u32 PSM, int sx, int sy, int dx
#undef V
static constexpr u32 indices[6] = { 0, 1, 2, 2, 1, 3 };
static constexpr u16 indices[6] = { 0, 1, 2, 2, 1, 3 };
// If we ever do this sort of thing somewhere else, extract this to a helper function.
GSHWDrawConfig config;
@@ -2633,14 +2655,17 @@ bool GSTextureCache::ShuffleMove(u32 BP, u32 BW, u32 PSM, int sx, int sy, int dx
return true;
}
GSTextureCache::Target* GSTextureCache::GetExactTarget(u32 BP, u32 BW, u32 PSM) const
GSTextureCache::Target* GSTextureCache::GetExactTarget(u32 BP, u32 BW, int type)
{
auto& rts = m_dst[GSLocalMemory::m_psm[PSM].depth ? DepthStencil : RenderTarget];
auto& rts = m_dst[type];
for (auto it = rts.begin(); it != rts.end(); ++it) // Iterate targets from MRU to LRU.
{
Target* t = *it;
if (t->m_TEX0.TBP0 == BP && t->m_TEX0.TBW == BW && t->m_TEX0.PSM == PSM)
if (t->m_TEX0.TBP0 == BP && t->m_TEX0.TBW == BW)
{
rts.MoveFront(it.Index());
return t;
}
}
return nullptr;
@@ -2832,29 +2857,11 @@ void GSTextureCache::IncAge()
Source* s = *i;
++i;
if (++s->m_age > (s->CanPreload() ? max_preload_age : max_age))
if (++s->m_age > ((!s->m_from_hash_cache && s->CanPreload()) ? max_preload_age : max_age))
m_src.RemoveAt(s);
}
const u32 max_hash_cache_age = 30;
for (auto it = m_hash_cache.begin(); it != m_hash_cache.end();)
{
HashCacheEntry& e = it->second;
if (e.refcount == 0 && ++e.age > max_hash_cache_age)
{
const u32 mem_usage = e.texture->GetMemUsage();
if (e.is_replacement)
m_hash_cache_replacement_memory_usage -= mem_usage;
else
m_hash_cache_memory_usage -= mem_usage;
g_gs_device->Recycle(e.texture);
m_hash_cache.erase(it++);
}
else
{
++it;
}
}
AgeHashCache();
// Clearing of Rendertargets causes flickering in many scene transitions.
// Sigh, this seems to be used to invalidate surfaces. So set a huge maxage to avoid flicker,
@@ -3605,6 +3612,7 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
if (it != m_hash_cache.end())
{
// super easy, cache hit. remove paltex if it's a replacement texture.
GL_CACHE("HC Hit: %" PRIx64 " %" PRIx64 " R-%" PRIx64, key.TEX0Hash, key.CLUTHash, key.region.bits);
HashCacheEntry* entry = &it->second;
paltex &= (entry->texture->GetFormat() == GSTexture::Format::UNorm8);
entry->refcount++;
@@ -3612,6 +3620,8 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
}
// cache miss.
GL_CACHE("HC Miss: %" PRIx64 " %" PRIx64 " R-%" PRIx64, key.TEX0Hash, key.CLUTHash, key.region.bits);
// check for a replacement texture with the full clut key
if (replace)
{
@@ -3697,6 +3707,70 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
return &m_hash_cache.emplace(key, entry).first->second;
}
void GSTextureCache::RemoveFromHashCache(HashCacheMap::iterator it)
{
HashCacheEntry& e = it->second;
const u32 mem_usage = e.texture->GetMemUsage();
if (e.is_replacement)
m_hash_cache_replacement_memory_usage -= mem_usage;
else
m_hash_cache_memory_usage -= mem_usage;
g_gs_device->Recycle(e.texture);
m_hash_cache.erase(it);
}
void GSTextureCache::AgeHashCache()
{
// Where did this number come from?
// A game called Corvette draws its background FMVs with a ton of 17x17 tiles, which ends up
// being about 600 texture uploads per frame. We'll use 800 as an upper bound for a bit of
// a buffer, hopefully nothing's going to end up with more textures than that.
constexpr u32 MAX_HASH_CACHE_SIZE = 800;
constexpr u32 MAX_HASH_CACHE_AGE = 30;
bool might_need_cache_purge = (m_hash_cache.size() > MAX_HASH_CACHE_SIZE);
if (might_need_cache_purge)
s_hash_cache_purge_list.clear();
for (auto it = m_hash_cache.begin(); it != m_hash_cache.end();)
{
HashCacheEntry& e = it->second;
if (e.refcount > 0)
{
++it;
continue;
}
if (++e.age > MAX_HASH_CACHE_AGE)
{
RemoveFromHashCache(it++);
continue;
}
// We might free up enough just with "normal" removals above.
if (might_need_cache_purge)
{
might_need_cache_purge = (m_hash_cache.size() > MAX_HASH_CACHE_SIZE);
if (might_need_cache_purge)
s_hash_cache_purge_list.emplace_back(it, static_cast<s32>(e.age));
}
++it;
}
// Pushing to a list, sorting, and removing ends up faster than re-iterating the map.
if (might_need_cache_purge)
{
std::sort(s_hash_cache_purge_list.begin(), s_hash_cache_purge_list.end(),
[](const auto& lhs, const auto& rhs) { return lhs.second > rhs.second; });
const u32 entries_to_purge = std::min(static_cast<u32>(m_hash_cache.size() - MAX_HASH_CACHE_SIZE),
static_cast<u32>(s_hash_cache_purge_list.size()));
for (u32 i = 0; i < entries_to_purge; i++)
RemoveFromHashCache(s_hash_cache_purge_list[i].first);
}
}
GSTextureCache::Target* GSTextureCache::CreateTarget(const GIFRegTEX0& TEX0, int w, int h, float scale, int type, const bool clear)
{
ASSERT(type == RenderTarget || type == DepthStencil);
@@ -3796,7 +3870,8 @@ GSTexture* GSTextureCache::LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVec
void GSTextureCache::Read(Target* t, const GSVector4i& r)
{
if (!t->m_dirty.empty() || r.width() == 0 || r.height() == 0)
if ((!t->m_dirty.empty() && !t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size).rintersect(r).rempty())
|| r.width() == 0 || r.height() == 0)
return;
const GIFRegTEX0& TEX0 = t->m_TEX0;
@@ -4154,7 +4229,7 @@ void GSTextureCache::Source::UpdateLayer(const GIFRegTEX0& TEX0, const GSVector4
void GSTextureCache::Source::Write(const GSVector4i& r, int layer, const GSOffset& off)
{
if (!m_write.rect)
m_write.rect = static_cast<GSVector4i*>(_aligned_malloc(3 * sizeof(GSVector4i), 32));
m_write.rect = static_cast<GSVector4i*>(_aligned_malloc(3 * sizeof(GSVector4i), 16));
m_write.rect[m_write.count++] = r;
@@ -4214,6 +4289,8 @@ void GSTextureCache::Source::Flush(u32 count, int layer, const GSOffset& off)
rtx = psm.rtxP;
}
pitch = VectorAlign(pitch);
for (u32 i = 0; i < count; i++)
{
const GSVector4i r(m_write.rect[i]);
@@ -4385,7 +4462,7 @@ void GSTextureCache::Target::Update(bool reset_age)
}
else
{
const int pitch = Common::AlignUpPow2(r.width() * sizeof(u32), 32);
const int pitch = VectorAlign(r.width() * sizeof(u32));
g_gs_renderer->m_mem.ReadTexture(off, r, s_unswizzle_buffer, pitch, TEXA);
t->Update(t_r, s_unswizzle_buffer, pitch);
@@ -5127,13 +5204,13 @@ GSTextureCache::SourceRegion GSTextureCache::SourceRegion::AdjustForMipmap(u32 l
if (HasX())
{
const u32 new_minx = GetMinX() >> level;
const u32 new_maxx = std::max<u32>(GetMaxX() >> level, new_minx + 1);
const u32 new_maxx = new_minx + std::max(GetWidth() >> level, 1u);
ret.SetX(new_minx, new_maxx);
}
if (HasY())
{
const u32 new_miny = GetMinY() >> level;
const u32 new_maxy = std::max<u32>(GetMaxY() >> level, new_miny + 1);
const u32 new_maxy = new_miny + std::max(GetHeight() >> level, 1u);
ret.SetY(new_miny, new_maxy);
}
return ret;
@@ -5241,7 +5318,7 @@ static void HashTextureLevel(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, GST
{
// Expand texture indices. Align to 32 bytes for AVX2.
const bool palette = (psm.pal > 0);
const u32 pitch = Common::AlignUpPow2(static_cast<u32>(block_rect.z) << (palette ? 0 : 2), 32);
const u32 pitch = VectorAlign(static_cast<u32>(block_rect.z) << (palette ? 0 : 2));
const u32 row_size = static_cast<u32>(tw) << (palette ? 0 : 2);
const GSLocalMemory::readTexture rtx = palette ? psm.rtxP : psm.rtx;
@@ -5317,8 +5394,7 @@ void GSTextureCache::PreloadTexture(const GIFRegTEX0& TEX0, const GIFRegTEXA& TE
}
else
{
// Align pitch to 32 bytes for AVX2 if we're going through the temp buffer path.
pitch = Common::AlignUpPow2(pitch, 32);
pitch = VectorAlign(pitch);
u8* buff = s_unswizzle_buffer;
rtx(mem, off, block_rect, buff, pitch, TEXA);
+7 -3
View File
@@ -123,6 +123,8 @@ public:
bool is_replacement;
};
using HashCacheMap = std::unordered_map<HashCacheKey, HashCacheEntry, HashCacheKeyHash>;
class Surface : public GSAlignedClass<32>
{
protected:
@@ -374,7 +376,7 @@ protected:
PaletteMap m_palette_map;
SourceMap m_src;
u64 m_source_memory_usage = 0;
std::unordered_map<HashCacheKey, HashCacheEntry, HashCacheKeyHash> m_hash_cache;
HashCacheMap m_hash_cache;
u64 m_hash_cache_memory_usage = 0;
u64 m_hash_cache_replacement_memory_usage = 0;
@@ -402,6 +404,8 @@ protected:
bool PrepareDownloadTexture(u32 width, u32 height, GSTexture::Format format, std::unique_ptr<GSDownloadTexture>* tex);
HashCacheEntry* LookupHashCache(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, bool& paltex, const u32* clut, const GSVector2i* lod, SourceRegion region);
void RemoveFromHashCache(HashCacheMap::iterator it);
void AgeHashCache();
static void PreloadTexture(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, SourceRegion region, GSLocalMemory& mem, bool paltex, GSTexture* tex, u32 level);
static HashType HashTexture(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, SourceRegion region);
@@ -440,8 +444,8 @@ public:
bool is_frame = false, bool is_clear = false, bool preload = GSConfig.PreloadFrameWithGSData);
Target* LookupDisplayTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale);
/// Looks up a target in the cache, and only returns it if the BP/BW/PSM match exactly.
Target* GetExactTarget(u32 BP, u32 BW, u32 PSM) const;
/// Looks up a target in the cache, and only returns it if the BP/BW match exactly.
Target* GetExactTarget(u32 BP, u32 BW, int type);
Target* GetTargetWithSharedBits(u32 BP, u32 PSM) const;
GSVector2i GetTargetSize(u32 bp, u32 fbw, u32 psm, s32 min_width, s32 min_height);
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="GSTextureCache::Target">
<DisplayString Condition="m_type == 0">{{ RT @ BP={m_TEX0.TBP0,X}-{m_end_block,X} BW={m_TEX0.TBW} PSM={m_TEX0.PSM,X} {m_unscaled_size.x}x{m_unscaled_size.y} {m_valid.z},{m_valid.w} }}</DisplayString>
<DisplayString Condition="m_type == 1">{{ Depth @ BP={m_TEX0.TBP0,X}-{m_end_block,X} BW={m_TEX0.TBW} PSM={m_TEX0.PSM,X} {m_unscaled_size.x}x{m_unscaled_size.y} {m_valid.z},{m_valid.w} }}</DisplayString>
</Type>
</AutoVisualizer>
@@ -209,6 +209,7 @@ std::optional<TextureName> GSTextureReplacements::ParseReplacementName(const std
&ret.bits, &extension_dot) == 4 &&
extension_dot == '.')
{
ret.CLUTHash = 0;
return ret;
}
@@ -225,6 +226,7 @@ std::optional<TextureName> GSTextureReplacements::ParseReplacementName(const std
3 &&
extension_dot == '.')
{
ret.CLUTHash = 0;
return ret;
}
+2 -4
View File
@@ -378,7 +378,7 @@ public:
MRCOwned<id<MTLFunction>> LoadShader(NSString* name);
MRCOwned<id<MTLRenderPipelineState>> MakePipeline(MTLRenderPipelineDescriptor* desc, id<MTLFunction> vertex, id<MTLFunction> fragment, NSString* name);
MRCOwned<id<MTLComputePipelineState>> MakeComputePipeline(id<MTLFunction> compute, NSString* name);
bool Create(const WindowInfo& wi, VsyncMode vsync) override;
bool Create() override;
void Destroy() override;
void AttachSurfaceOnMainThread();
@@ -387,10 +387,8 @@ public:
RenderAPI GetRenderAPI() const override;
bool HasSurface() const override;
void DestroySurface() override;
bool ChangeWindow(const WindowInfo& wi) override;
bool UpdateWindow() override;
bool SupportsExclusiveFullscreen() const override;
bool IsExclusiveFullscreen() override;
bool SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
std::string GetDriverInfo() const override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
+37 -29
View File
@@ -371,6 +371,7 @@ void GSDeviceMTL::EndRenderPass()
if (m_current_render.encoder)
{
EndDebugGroup(m_current_render.encoder);
g_perfmon.Put(GSPerfMon::RenderPasses, 1);
if (m_spin_timer)
[m_current_render.encoder updateFence:m_spin_fence afterStages:MTLRenderStageFragment];
[m_current_render.encoder endEncoding];
@@ -763,9 +764,9 @@ static MRCOwned<id<MTLBuffer>> CreatePrivateBufferWithContent(
return actual;
}
bool GSDeviceMTL::Create(const WindowInfo& wi, VsyncMode vsync)
bool GSDeviceMTL::Create()
{ @autoreleasepool {
if (!GSDevice::Create(wi, vsync))
if (!GSDevice::Create())
return false;
NSString* ns_adapter_name = [NSString stringWithUTF8String:EmuConfig.GS.Adapter.c_str()];
@@ -809,11 +810,16 @@ bool GSDeviceMTL::Create(const WindowInfo& wi, VsyncMode vsync)
if (m_dev.IsOk() && m_queue)
{
// This is a little less than ideal, pinging back and forward between threads, but we don't really
// have any other option, because Qt uses a blocking queued connection for window acquire.
if (!AcquireWindow(true))
return false;
OnMainThread([this]
{
AttachSurfaceOnMainThread();
});
SetVSync(vsync);
[m_layer setDisplaySyncEnabled:m_vsync_mode != VsyncMode::Off];
}
else
{
@@ -1176,20 +1182,21 @@ void GSDeviceMTL::DestroySurface()
m_layer = nullptr;
}
bool GSDeviceMTL::ChangeWindow(const WindowInfo& wi)
bool GSDeviceMTL::UpdateWindow()
{
OnMainThread([this, &wi]
{
DetachSurfaceOnMainThread();
m_window_info = wi;
AttachSurfaceOnMainThread();
});
DestroySurface();
if (!AcquireWindow(false))
return false;
if (m_window_info.type == WindowInfo::Type::Surfaceless)
return true;
OnMainThread([this] { AttachSurfaceOnMainThread(); });
return true;
}
bool GSDeviceMTL::SupportsExclusiveFullscreen() const { return false; }
bool GSDeviceMTL::IsExclusiveFullscreen() { return false; }
bool GSDeviceMTL::SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) { return false; }
std::string GSDeviceMTL::GetDriverInfo() const
{ @autoreleasepool {
@@ -1240,53 +1247,51 @@ static bool s_capture_next = false;
GSDevice::PresentResult GSDeviceMTL::BeginPresent(bool frame_skip)
{ @autoreleasepool {
GSDeviceMTL* dev = static_cast<GSDeviceMTL*>(g_gs_device.get());
if (dev && m_capture_start_frame && dev->FrameNo() == m_capture_start_frame)
if (m_capture_start_frame && FrameNo() == m_capture_start_frame)
s_capture_next = true;
if (frame_skip || m_window_info.type == WindowInfo::Type::Surfaceless || !g_gs_device)
{
ImGui::EndFrame();
return PresentResult::FrameSkipped;
}
id<MTLCommandBuffer> buf = dev->GetRenderCmdBuf();
id<MTLCommandBuffer> buf = GetRenderCmdBuf();
m_current_drawable = MRCRetain([m_layer nextDrawable]);
dev->EndRenderPass();
EndRenderPass();
if (!m_current_drawable)
{
[buf pushDebugGroup:@"Present Skipped"];
[buf popDebugGroup];
dev->FlushEncoders();
FlushEncoders();
ImGui::EndFrame();
return PresentResult::FrameSkipped;
}
[m_pass_desc colorAttachments][0].texture = [m_current_drawable texture];
id<MTLRenderCommandEncoder> enc = [buf renderCommandEncoderWithDescriptor:m_pass_desc];
[enc setLabel:@"Present"];
dev->m_current_render.encoder = MRCRetain(enc);
m_current_render.encoder = MRCRetain(enc);
return PresentResult::OK;
}}
void GSDeviceMTL::EndPresent()
{ @autoreleasepool {
GSDeviceMTL* dev = static_cast<GSDeviceMTL*>(g_gs_device.get());
pxAssertDev(dev && dev->m_current_render.encoder && dev->m_current_render_cmdbuf, "BeginPresent cmdbuf was destroyed");
pxAssertDev(m_current_render.encoder && m_current_render_cmdbuf, "BeginPresent cmdbuf was destroyed");
ImGui::Render();
dev->RenderImGui(ImGui::GetDrawData());
dev->EndRenderPass();
RenderImGui(ImGui::GetDrawData());
EndRenderPass();
if (m_current_drawable)
{
const bool use_present_drawable = m_use_present_drawable == UsePresentDrawable::Always ||
(m_use_present_drawable == UsePresentDrawable::IfVsync && m_vsync_mode != VsyncMode::Off);
if (use_present_drawable)
[dev->m_current_render_cmdbuf presentDrawable:m_current_drawable];
[m_current_render_cmdbuf presentDrawable:m_current_drawable];
else
[dev->m_current_render_cmdbuf addScheduledHandler:[drawable = std::move(m_current_drawable)](id<MTLCommandBuffer>){
[m_current_render_cmdbuf addScheduledHandler:[drawable = std::move(m_current_drawable)](id<MTLCommandBuffer>){
[drawable present];
}];
}
dev->FlushEncoders();
dev->FrameCompleted();
FlushEncoders();
FrameCompleted();
m_current_drawable = nullptr;
if (m_capture_start_frame)
{
@@ -1340,6 +1345,9 @@ void GSDeviceMTL::EndPresent()
void GSDeviceMTL::SetVSync(VsyncMode mode)
{
if (m_vsync_mode == mode)
return;
[m_layer setDisplaySyncEnabled:mode != VsyncMode::Off];
m_vsync_mode = mode;
}
@@ -2178,7 +2186,7 @@ void GSDeviceMTL::SendHWDraw(GSHWDrawConfig& config, id<MTLRenderCommandEncoder>
textureBarrier(enc);
[enc drawIndexedPrimitives:topology
indexCount:count
indexType:MTLIndexTypeUInt32
indexType:MTLIndexTypeUInt16
indexBuffer:buffer
indexBufferOffset:off + p * sizeof(*config.indices)];
p += count;
@@ -2200,7 +2208,7 @@ void GSDeviceMTL::SendHWDraw(GSHWDrawConfig& config, id<MTLRenderCommandEncoder>
textureBarrier(enc);
[enc drawIndexedPrimitives:topology
indexCount:config.indices_per_prim
indexType:MTLIndexTypeUInt32
indexType:MTLIndexTypeUInt16
indexBuffer:buffer
indexBufferOffset:off + p * sizeof(*config.indices)];
}
@@ -2217,7 +2225,7 @@ void GSDeviceMTL::SendHWDraw(GSHWDrawConfig& config, id<MTLRenderCommandEncoder>
[enc drawIndexedPrimitives:topology
indexCount:config.nindices
indexType:MTLIndexTypeUInt32
indexType:MTLIndexTypeUInt16
indexBuffer:buffer
indexBufferOffset:off];
+59 -48
View File
@@ -85,9 +85,13 @@ void GSDeviceOGL::SetVSync(VsyncMode mode)
m_vsync_mode = mode;
}
bool GSDeviceOGL::Create(const WindowInfo& wi, VsyncMode vsync)
bool GSDeviceOGL::Create()
{
if (!GSDevice::Create(wi, vsync))
if (!GSDevice::Create())
return false;
// GL is a pain and needs the window super early to create the context.
if (!AcquireWindow(true))
return false;
// We need at least GL3.3.
@@ -95,7 +99,7 @@ bool GSDeviceOGL::Create(const WindowInfo& wi, VsyncMode vsync)
{GL::Context::Profile::Core, 4, 5}, {GL::Context::Profile::Core, 4, 4}, {GL::Context::Profile::Core, 4, 3},
{GL::Context::Profile::Core, 4, 2}, {GL::Context::Profile::Core, 4, 1}, {GL::Context::Profile::Core, 4, 0},
{GL::Context::Profile::Core, 3, 3}};
m_gl_context = GL::Context::Create(wi, version_list);
m_gl_context = GL::Context::Create(m_window_info, version_list);
if (!m_gl_context)
{
Console.Error("Failed to create any GL context");
@@ -109,6 +113,10 @@ bool GSDeviceOGL::Create(const WindowInfo& wi, VsyncMode vsync)
return false;
}
// Render a frame as soon as possible to clear out whatever was previously being displayed.
if (m_window_info.type != WindowInfo::Type::Surfaceless)
RenderBlankFrame();
if (!GLLoader::check_gl_requirements())
return false;
@@ -163,7 +171,7 @@ bool GSDeviceOGL::Create(const WindowInfo& wi, VsyncMode vsync)
GLint max_vertex_ssbos = 0;
glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &max_vertex_ssbos);
DevCon.WriteLn("GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS: %d", max_vertex_ssbos);
m_features.vs_expand = (max_vertex_ssbos > 0);
m_features.vs_expand = (max_vertex_ssbos > 0 && GLAD_GL_ARB_gpu_shader5);
}
if (!m_features.vs_expand)
Console.Warning("Vertex expansion is not supported. This will reduce performance.");
@@ -651,11 +659,16 @@ void GSDeviceOGL::DestroyResources()
glDeleteFramebuffers(1, &m_fbo_write);
}
bool GSDeviceOGL::ChangeWindow(const WindowInfo& new_wi)
bool GSDeviceOGL::UpdateWindow()
{
pxAssert(m_gl_context);
if (!m_gl_context->ChangeSurface(new_wi))
DestroySurface();
if (!AcquireWindow(false))
return false;
if (!m_gl_context->ChangeSurface(m_window_info))
{
Console.Error("Failed to change surface");
return false;
@@ -663,11 +676,13 @@ bool GSDeviceOGL::ChangeWindow(const WindowInfo& new_wi)
m_window_info = m_gl_context->GetWindowInfo();
if (new_wi.type != WindowInfo::Type::Surfaceless)
if (m_window_info.type != WindowInfo::Type::Surfaceless)
{
// reset vsync rate, since it (usually) gets lost
if (m_vsync_mode != VsyncMode::Adaptive || !m_gl_context->SetSwapInterval(-1))
m_gl_context->SetSwapInterval(static_cast<s32>(m_vsync_mode != VsyncMode::Off));
RenderBlankFrame();
}
return true;
@@ -691,16 +706,6 @@ bool GSDeviceOGL::SupportsExclusiveFullscreen() const
return false;
}
bool GSDeviceOGL::IsExclusiveFullscreen()
{
return false;
}
bool GSDeviceOGL::SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
{
return false;
}
void GSDeviceOGL::DestroySurface()
{
m_window_info = {};
@@ -876,8 +881,8 @@ void GSDeviceOGL::DrawPrimitive()
void GSDeviceOGL::DrawIndexedPrimitive()
{
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
glDrawElementsBaseVertex(m_draw_topology, static_cast<u32>(m_index.count), GL_UNSIGNED_INT,
reinterpret_cast<void*>(static_cast<u32>(m_index.start) * sizeof(u32)), static_cast<GLint>(m_vertex.start));
glDrawElementsBaseVertex(m_draw_topology, static_cast<u32>(m_index.count), GL_UNSIGNED_SHORT,
reinterpret_cast<void*>(static_cast<u32>(m_index.start) * sizeof(u16)), static_cast<GLint>(m_vertex.start));
}
void GSDeviceOGL::DrawIndexedPrimitive(int offset, int count)
@@ -885,8 +890,8 @@ void GSDeviceOGL::DrawIndexedPrimitive(int offset, int count)
//ASSERT(offset + count <= (int)m_index.count);
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
glDrawElementsBaseVertex(m_draw_topology, count, GL_UNSIGNED_INT,
reinterpret_cast<void*>((static_cast<u32>(m_index.start) + static_cast<u32>(offset)) * sizeof(u32)),
glDrawElementsBaseVertex(m_draw_topology, count, GL_UNSIGNED_SHORT,
reinterpret_cast<void*>((static_cast<u32>(m_index.start) + static_cast<u32>(offset)) * sizeof(u16)),
static_cast<GLint>(m_vertex.start));
}
@@ -1134,7 +1139,9 @@ std::string GSDeviceOGL::GenGlslHeader(const std::string_view& entry, GLenum typ
else
{
header = "#version 330 core\n";
header += "#extension GL_ARB_shading_language_420pack: require\n";
header += "#extension GL_ARB_shading_language_420pack : require\n";
if (GLAD_GL_ARB_gpu_shader5)
header += "#extension GL_ARB_gpu_shader5 : require\n";
if (m_features.vs_expand)
header += "#extension GL_ARB_shader_storage_buffer_object: require\n";
}
@@ -1548,15 +1555,15 @@ void GSDeviceOGL::DrawMultiStretchRects(
void GSDeviceOGL::DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, const GSVector2& ds)
{
const u32 vertex_reserve_size = num_rects * 4 * sizeof(GSVertexPT1);
const u32 index_reserve_size = num_rects * 6 * sizeof(u32);
const u32 index_reserve_size = num_rects * 6 * sizeof(u16);
auto vertex_map = m_vertex_stream_buffer->Map(sizeof(GSVertexPT1), vertex_reserve_size);
auto index_map = m_index_stream_buffer->Map(sizeof(u32), index_reserve_size);
auto index_map = m_index_stream_buffer->Map(sizeof(u16), index_reserve_size);
m_vertex.start = vertex_map.index_aligned;
m_index.start = index_map.index_aligned;
// Don't use primitive restart here, it ends up slower on some drivers.
GSVertexPT1* verts = reinterpret_cast<GSVertexPT1*>(vertex_map.pointer);
u32* idx = reinterpret_cast<u32*>(index_map.pointer);
u16* idx = reinterpret_cast<u16*>(index_map.pointer);
u32 icount = 0;
u32 vcount = 0;
for (u32 i = 0; i < num_rects; i++)
@@ -1587,7 +1594,7 @@ void GSDeviceOGL::DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rec
m_vertex.count = vcount;
m_index.count = icount;
m_vertex_stream_buffer->Unmap(vcount * sizeof(GSVertexPT1));
m_index_stream_buffer->Unmap(icount * sizeof(u32));
m_index_stream_buffer->Unmap(icount * sizeof(u16));
PSSetShaderResource(0, rects[0].src);
PSSetSamplerState(rects[0].linear ? m_convert.ln : m_convert.pt);
@@ -1807,8 +1814,8 @@ void GSDeviceOGL::IASetVertexBuffer(const void* vertices, size_t count)
void GSDeviceOGL::IASetIndexBuffer(const void* index, size_t count)
{
const u32 size = static_cast<u32>(count) * sizeof(u32);
auto res = m_index_stream_buffer->Map(sizeof(u32), size);
const u32 size = static_cast<u32>(count) * sizeof(u16);
auto res = m_index_stream_buffer->Map(sizeof(u16), size);
m_index.start = res.index_aligned;
m_index.count = count;
std::memcpy(res.pointer, index, size);
@@ -1999,18 +2006,7 @@ void GSDeviceOGL::RenderImGui()
m_vertex_stream_buffer->Unmap(size);
}
// Bit awkward, because this is using 16-bit indices, not 32-bit.
u32 index_start;
{
static_assert(sizeof(ImDrawIdx) == sizeof(u16));
const u32 size = static_cast<u32>(cmd_list->IdxBuffer.Size) * sizeof(ImDrawIdx);
auto res = m_index_stream_buffer->Map(sizeof(u16), size);
index_start = res.index_aligned;
std::memcpy(res.pointer, cmd_list->IdxBuffer.Data, size);
m_index_stream_buffer->Unmap(size);
m_index_stream_buffer->Bind();
}
IASetIndexBuffer(cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
@@ -2038,7 +2034,7 @@ void GSDeviceOGL::RenderImGui()
}
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, GL_UNSIGNED_SHORT,
(void*)(intptr_t)((pcmd->IdxOffset + index_start) * sizeof(ImDrawIdx)), pcmd->VtxOffset + vertex_start);
(void*)(intptr_t)((pcmd->IdxOffset + m_index.start) * sizeof(ImDrawIdx)), pcmd->VtxOffset + vertex_start);
}
g_perfmon.Put(GSPerfMon::DrawCalls, cmd_list->CmdBuffer.Size);
@@ -2048,6 +2044,17 @@ void GSDeviceOGL::RenderImGui()
glScissor(GLState::scissor.x, GLState::scissor.y, GLState::scissor.width(), GLState::scissor.height());
}
void GSDeviceOGL::RenderBlankFrame()
{
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glDisable(GL_SCISSOR_TEST);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
m_gl_context->SwapBuffers();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLState::fbo);
glEnable(GL_SCISSOR_TEST);
}
void GSDeviceOGL::OMAttachRt(GSTextureOGL* rt)
{
if (rt)
@@ -2165,6 +2172,8 @@ void GSDeviceOGL::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVecto
GSTextureOGL* RT = static_cast<GSTextureOGL*>(rt);
GSTextureOGL* DS = static_cast<GSTextureOGL*>(ds);
g_perfmon.Put(GSPerfMon::RenderPasses, static_cast<double>(GLState::rt != RT || GLState::ds != DS));
OMSetFBO(m_fbo);
if (rt)
{
@@ -2384,7 +2393,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
psel.vs = config.vs;
psel.ps.key_hi = config.ps.key_hi;
psel.ps.key_lo = config.ps.key_lo;
psel.pad = 0;
std::memset(psel.pad, 0, sizeof(psel.pad));
SetupPipeline(psel);
@@ -2431,21 +2440,23 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
OMSetBlendState(config.blend.enable, s_gl_blend_factors[config.blend.src_factor],
s_gl_blend_factors[config.blend.dst_factor], s_gl_blend_ops[config.blend.op],
config.blend.constant_enable, config.blend.constant);
OMSetColorMaskState(config.colormask);
// avoid changing framebuffer just to switch from rt+depth to rt and vice versa
GSTexture* draw_rt = hdr_rt ? hdr_rt : config.rt;
GSTexture* draw_ds = config.ds;
if (!draw_ds && GLState::ds && GLState::rt == draw_rt && config.tex != GLState::ds &&
GLState::ds->GetSize() == draw_rt->GetSize())
if (!draw_rt && GLState::rt && GLState::ds == draw_ds && config.tex != GLState::rt &&
GLState::rt->GetSize() == draw_ds->GetSize())
{
draw_rt = GLState::rt;
}
else if (!draw_ds && GLState::ds && GLState::rt == draw_rt && config.tex != GLState::ds &&
GLState::ds->GetSize() == draw_rt->GetSize())
{
// should already be always-pass.
draw_ds = GLState::ds;
config.depth.ztst = ZTST_ALWAYS;
config.depth.zwe = false;
}
OMSetRenderTargets(draw_rt, draw_ds, &config.scissor);
OMSetColorMaskState(config.colormask);
SetupOM(config.depth);
SendHWDraw(config, psel.ps.IsFeedbackLoop());
+6 -7
View File
@@ -130,10 +130,10 @@ public:
{
PSSelector ps;
VSSelector vs;
u16 pad;
u8 pad[3];
__fi bool operator==(const ProgramSelector& p) const { return (std::memcmp(this, &p, sizeof(*this)) == 0); }
__fi bool operator!=(const ProgramSelector& p) const { return (std::memcmp(this, &p, sizeof(*this)) != 0); }
__fi bool operator==(const ProgramSelector& p) const { return BitEqual(*this, p); }
__fi bool operator!=(const ProgramSelector& p) const { return !BitEqual(*this, p); }
};
static_assert(sizeof(ProgramSelector) == 16, "Program selector is 16 bytes");
@@ -262,6 +262,7 @@ private:
bool CreateImGuiProgram();
void RenderImGui();
void RenderBlankFrame();
void OMAttachRt(GSTextureOGL* rt = nullptr);
void OMAttachDs(GSTextureOGL* ds = nullptr);
@@ -286,14 +287,12 @@ public:
RenderAPI GetRenderAPI() const override;
bool HasSurface() const override;
bool Create(const WindowInfo& wi, VsyncMode vsync) override;
bool Create() override;
void Destroy() override;
bool ChangeWindow(const WindowInfo& new_wi) override;
bool UpdateWindow() override;
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
bool SupportsExclusiveFullscreen() const override;
bool IsExclusiveFullscreen() override;
bool SetExclusiveFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
void DestroySurface() override;
std::string GetDriverInfo() const override;
+2 -1
View File
@@ -18,6 +18,7 @@
#include "GS/Renderers/OpenGL/GSDeviceOGL.h"
#include "GS/Renderers/OpenGL/GSTextureOGL.h"
#include "GS/Renderers/OpenGL/GLState.h"
#include "GS/GSExtra.h"
#include "GS/GSPerfMon.h"
#include "GS/GSPng.h"
#include "GS/GSGL.h"
@@ -476,7 +477,7 @@ std::unique_ptr<GSDownloadTextureOGL> GSDownloadTextureOGL::Create(u32 width, u3
}
// Fallback to glReadPixels() + CPU buffer.
u8* cpu_buffer = static_cast<u8*>(_aligned_malloc(buffer_size, 32));
u8* cpu_buffer = static_cast<u8*>(_aligned_malloc(buffer_size, VECTOR_ALIGNMENT));
if (!cpu_buffer)
return {};
+10 -13
View File
@@ -161,7 +161,7 @@ typedef GSVector4 VectorF;
#define LOCAL_STEP local.d4
#endif
void GSDrawScanline::CSetupPrim(const GSVertexSW* vertex, const u32* index, const GSVertexSW& dscan, GSScanlineLocalData& local)
void GSDrawScanline::CSetupPrim(const GSVertexSW* vertex, const u16* index, const GSVertexSW& dscan, GSScanlineLocalData& local)
{
const GSScanlineGlobalData& global = GlobalFromLocal(local);
GSScanlineSelector sel = global.sel;
@@ -451,10 +451,14 @@ __ri static void WritePixel(const T& src, int addr, int i, u32 psm, const GSScan
}
void GSDrawScanline::CDrawScanline(int pixels, int left, int top, const GSVertexSW& scan, GSScanlineLocalData& local)
{
CDrawScanline(pixels, left, top, scan, local, GlobalFromLocal(local).sel);
}
__ri void GSDrawScanline::CDrawScanline(int pixels, int left, int top, const GSVertexSW& scan, GSScanlineLocalData& local, GSScanlineSelector sel)
{
const GSScanlineGlobalData& global = GlobalFromLocal(local);
GSScanlineSelector sel = global.sel;
constexpr int vlen = sizeof(VectorF) / sizeof(float);
#if _M_SSE < 0x501
@@ -1729,17 +1733,10 @@ void GSDrawScanline::CDrawScanline(int pixels, int left, int top, const GSVertex
void GSDrawScanline::CDrawEdge(int pixels, int left, int top, const GSVertexSW& scan, GSScanlineLocalData& local)
{
// This sucks. But so does not jitting!
const GSScanlineGlobalData* old_gd = local.gd;
GSScanlineGlobalData gd;
std::memcpy(&gd, &local.gd, sizeof(gd));
gd.sel.zwrite = 0;
gd.sel.edge = 1;
local.gd = &gd;
CDrawScanline(pixels, left, top, scan, local);
local.gd = old_gd;
GSScanlineSelector sel = local.gd->sel;
sel.zwrite = 0;
sel.edge = 1;
CDrawScanline(pixels, left, top, scan, local, sel);
}
template <class T, bool masked>
+3 -2
View File
@@ -38,7 +38,7 @@ public:
~GSDrawScanline() override;
/// Function pointer types which we call back into.
using SetupPrimPtr = void(*)(const GSVertexSW* vertex, const u32* index, const GSVertexSW& dscan, GSScanlineLocalData& local);
using SetupPrimPtr = void(*)(const GSVertexSW* vertex, const u16* index, const GSVertexSW& dscan, GSScanlineLocalData& local);
using DrawScanlinePtr = void(*)(int pixels, int left, int top, const GSVertexSW& scan, GSScanlineLocalData& local);
/// Flushes the code cache, forcing everything to be recompiled.
@@ -60,9 +60,10 @@ private:
GSCodeGeneratorFunctionMap<GSSetupPrimCodeGenerator, u64, SetupPrimPtr> m_sp_map;
GSCodeGeneratorFunctionMap<GSDrawScanlineCodeGenerator, u64, DrawScanlinePtr> m_ds_map;
static void CSetupPrim(const GSVertexSW* vertex, const u32* index, const GSVertexSW& dscan, GSScanlineLocalData& local);
static void CSetupPrim(const GSVertexSW* vertex, const u16* index, const GSVertexSW& dscan, GSScanlineLocalData& local);
static void CDrawScanline(int pixels, int left, int top, const GSVertexSW& scan, GSScanlineLocalData& local);
static void CDrawEdge(int pixels, int left, int top, const GSVertexSW& scan, GSScanlineLocalData& local);
__ri static void CDrawScanline(int pixels, int left, int top, const GSVertexSW& scan, GSScanlineLocalData& local, GSScanlineSelector sel);
};
MULTI_ISA_UNSHARED_END
@@ -17,6 +17,7 @@
#include "GSDrawScanlineCodeGenerator.all.h"
#include "GS/Renderers/Common/GSFunctionMap.h"
#include "GSVertexSW.h"
#include "common/Perf.h"
MULTI_ISA_UNSHARED_IMPL;
using namespace Xbyak;
@@ -590,6 +591,8 @@ L("exit");
if (isYmm)
vzeroupper();
ret();
Perf::any.RegisterKey(actual.getCode(), actual.getSize(), "GSDrawScanline_", m_sel.key);
}
/// Inputs: a0=pixels, a1=left, a2[x64]=top, a3[x64]=v
@@ -91,7 +91,8 @@ GSDrawScanlineCodeGenerator::GSDrawScanlineCodeGenerator(u64 key, void* code, si
if (shouldUseCDrawScanline(key))
{
jmp(reinterpret_cast<const void*>(&GSDrawScanline::CDrawScanline));
jmp(reinterpret_cast<const void*>(static_cast<void (*)(int, int, int, const GSVertexSW&, GSScanlineLocalData&)>(
&GSDrawScanline::CDrawScanline)));
return;
}
+11 -11
View File
@@ -55,7 +55,7 @@ GSRasterizer::GSRasterizer(GSDrawScanline* ds, int id, int threads)
m_thread_height = compute_best_thread_height(threads);
m_edge.buff = static_cast<GSVertexSW*>(_aligned_malloc(sizeof(GSVertexSW) * 2048, 32));
m_edge.buff = static_cast<GSVertexSW*>(_aligned_malloc(sizeof(GSVertexSW) * 2048, VECTOR_ALIGNMENT));
m_edge.count = 0;
if (!m_edge.buff)
throw std::bad_alloc();
@@ -154,10 +154,10 @@ void GSRasterizer::Draw(GSRasterizerData& data)
const GSVertexSW* vertex = data.vertex;
const GSVertexSW* vertex_end = data.vertex + data.vertex_count;
const u32* index = data.index;
const u32* index_end = data.index + data.index_count;
const u16* index = data.index;
const u16* index_end = data.index + data.index_count;
u32 tmp_index[] = {0, 1, 2};
static constexpr u16 tmp_index[] = {0, 1, 2};
bool scissor_test = !data.bbox.eq(data.bbox.rintersect(data.scissor));
@@ -261,7 +261,7 @@ void GSRasterizer::Draw(GSRasterizerData& data)
}
template <bool scissor_test>
void GSRasterizer::DrawPoint(const GSVertexSW* vertex, int vertex_count, const u32* index, int index_count)
void GSRasterizer::DrawPoint(const GSVertexSW* vertex, int vertex_count, const u16* index, int index_count)
{
m_primcount++;
@@ -286,7 +286,7 @@ void GSRasterizer::DrawPoint(const GSVertexSW* vertex, int vertex_count, const u
}
else
{
u32 tmp_index[1] = {0};
static constexpr u16 tmp_index[1] = {0};
for (int i = 0; i < vertex_count; i++, vertex++)
{
@@ -307,7 +307,7 @@ void GSRasterizer::DrawPoint(const GSVertexSW* vertex, int vertex_count, const u
}
}
void GSRasterizer::DrawLine(const GSVertexSW* vertex, const u32* index)
void GSRasterizer::DrawLine(const GSVertexSW* vertex, const u16* index)
{
m_primcount++;
@@ -425,7 +425,7 @@ static const u8 s_ysort[8][4] =
#if _M_SSE >= 0x501
void GSRasterizer::DrawTriangle(const GSVertexSW* vertex, const u32* index)
void GSRasterizer::DrawTriangle(const GSVertexSW* vertex, const u16* index)
{
m_primcount++;
@@ -606,7 +606,7 @@ void GSRasterizer::DrawTriangleSection(int top, int bottom, GSVertexSW2& RESTRIC
#else
void GSRasterizer::DrawTriangle(const GSVertexSW* vertex, const u32* index)
void GSRasterizer::DrawTriangle(const GSVertexSW* vertex, const u16* index)
{
m_primcount++;
@@ -784,7 +784,7 @@ void GSRasterizer::DrawTriangleSection(int top, int bottom, GSVertexSW& RESTRICT
#endif
void GSRasterizer::DrawSprite(const GSVertexSW* vertex, const u32* index)
void GSRasterizer::DrawSprite(const GSVertexSW* vertex, const u16* index)
{
m_primcount++;
@@ -1082,7 +1082,7 @@ void GSRasterizer::AddScanline(GSVertexSW* e, int pixels, int left, int top, con
AddScanlineInfo(e, pixels, left, top);
}
void GSRasterizer::Flush(const GSVertexSW* vertex, const u32* index, const GSVertexSW& dscan, bool edge /* = false */)
void GSRasterizer::Flush(const GSVertexSW* vertex, const u16* index, const GSVertexSW& dscan, bool edge /* = false */)
{
// TODO: on win64 this could be the place where xmm6-15 are preserved (not by each DrawScanline)
+6 -6
View File
@@ -38,7 +38,7 @@ public:
u8* buff;
GSVertexSW* vertex;
int vertex_count;
u32* index;
u16* index;
int index_count;
u64 frame;
u64 start;
@@ -101,10 +101,10 @@ protected:
__forceinline bool HasEdge() const { return (m_draw_edge != nullptr); }
template <bool scissor_test>
void DrawPoint(const GSVertexSW* vertex, int vertex_count, const u32* index, int index_count);
void DrawLine(const GSVertexSW* vertex, const u32* index);
void DrawTriangle(const GSVertexSW* vertex, const u32* index);
void DrawSprite(const GSVertexSW* vertex, const u32* index);
void DrawPoint(const GSVertexSW* vertex, int vertex_count, const u16* index, int index_count);
void DrawLine(const GSVertexSW* vertex, const u16* index);
void DrawTriangle(const GSVertexSW* vertex, const u16* index);
void DrawSprite(const GSVertexSW* vertex, const u16* index);
#if _M_SSE >= 0x501
__forceinline void DrawTriangleSection(int top, int bottom, GSVertexSW2& RESTRICT edge, const GSVertexSW2& RESTRICT dedge, const GSVertexSW2& RESTRICT dscan, const GSVector4& RESTRICT p0);
@@ -115,7 +115,7 @@ protected:
void DrawEdge(const GSVertexSW& v0, const GSVertexSW& v1, const GSVertexSW& dv, int orientation, int side);
__forceinline void AddScanline(GSVertexSW* e, int pixels, int left, int top, const GSVertexSW& scan);
__forceinline void Flush(const GSVertexSW* vertex, const u32* index, const GSVertexSW& dscan, bool edge = false);
__forceinline void Flush(const GSVertexSW* vertex, const u16* index, const GSVertexSW& dscan, bool edge = false);
__forceinline void DrawScanline(int pixels, int left, int top, const GSVertexSW& scan);
__forceinline void DrawEdge(int pixels, int left, int top, const GSVertexSW& scan);

Some files were not shown because too many files have changed in this diff Show More