Compare commits

...

207 Commits

Author SHA1 Message Date
PCSX2 Bot
3462f02ce2 PAD: Update to latest controller database. 2023-03-06 17:07:54 +01:00
Stenzek
1a67b2146a GS/HW: Fix incorrect condition in DX11/OGL 2023-03-06 09:11:51 +00:00
Stenzek
eb0d18f484 GSDumpReplayer: Fix resetting
And get rid of memory reset, it's never used.
2023-03-06 09:11:08 +00:00
Stenzek
c783fc0f59 GSDumpReplayer: Update serial on dump load
Fixes title and HW fixes not applying when dragging a new GS dump onto a
running PCSX2.
2023-03-05 15:41:42 +00:00
refractionpcsx2
31d02c1278 GS-TC: Allow TBW expansion 2023-03-05 11:17:33 +00:00
JordanTheToaster
85f96bb248 GameDB: Various recommendations and fixes
Adds a variety of fixes and recommendation's for users to ignore.
2023-03-05 10:06:51 +00:00
Stenzek
229cf908b7 GS/HW: Use multi stretch for preloading 2023-03-05 10:03:15 +00:00
Stenzek
89b18275c0 GS/DX11: Get rid of runtime blend state creation 2023-03-05 10:03:15 +00:00
Stenzek
b8a86baec7 GS/HW: Implement multi stretch for DX11/DX12/OpenGL 2023-03-05 10:03:15 +00:00
Stenzek
8505e9203a Qt: Support changing running GS dump by drag/dropping 2023-03-03 16:43:16 +00:00
Stenzek
5d95a503bf Qt: Fix crash when spamming shutdown button 2023-03-03 16:43:16 +00:00
Stenzek
36c7f96a1e GS/HW: Get rid of GetOutputSize()
And use PCRTCDisplays instead.
2023-03-03 16:42:54 +00:00
Mrlinkwii
b40b606608 GameDB: update Oneechanbara2Special 2023-03-03 16:42:24 +00:00
Mrlinkwii
03764a624f GS/CRC: purge Oneechanbara2Special CRC 2023-03-03 16:42:24 +00:00
RedDevilus
962cfa9441 GameDB: Steambot Chronicles + typo fix
- Steambot Chronicles has quite an extensive hack for a game doing weird stuff with their Z-buffer, it does mess with the sea being green which is incorrect but the game also looks off for rest of the cutscene.

- misalgined typo to misaligned
2023-03-03 16:42:09 +00:00
lightningterror
b2f30ab080 GS: Bump shader cache version. 2023-03-03 16:35:10 +01:00
lightningterror
24c42ae2d9 GS-hw: Replace alpha factor/alpha source1 with source 1 color.
Output 1 will be used since we want to modify each color channel based on overflow value.
2023-03-03 16:35:10 +01:00
lightningterror
af9353298c GS-metal: Add clr3 case for blend mix 2.
When Cs*(Alpha + 1) overflows compensate with adjusting Alpha output for Cd*Alpha.
2023-03-03 16:35:10 +01:00
lightningterror
a3ecf0b0bd GS-vk: Add clr3 case for blend mix 2.
When Cs*(Alpha + 1) overflows compensate with adjusting Alpha output for Cd*Alpha.
2023-03-03 16:35:10 +01:00
lightningterror
58cb6ab728 GS-ogl: Add clr3 case for blend mix 2.
When Cs*(Alpha + 1) overflows compensate with adjusting Alpha output for Cd*Alpha.
2023-03-03 16:35:10 +01:00
lightningterror
f478b3959c GS-d3d: Add clr3 case for blend mix 2.
When Cs*(Alpha + 1) overflows compensate with adjusting Alpha output for Cd*Alpha.
2023-03-03 16:35:10 +01:00
Stenzek
35ce680859 GameDB: Add PCRTCOffsets to Xiaolin Showdown
Stops screen shaking.
2023-03-03 15:33:47 +00:00
Stenzek
7df189ced4 GameDB: Add PCRTC and blending HW fixes 2023-03-03 15:33:47 +00:00
Stenzek
c35092504c GS/HW: Avoid copying an empty rectangle
Fixes a possible validation error in Ultraman Fighting Evolution -
Rebirth.
2023-03-03 14:59:07 +00:00
Stenzek
0bff6f7ad9 GS/HW: Require FBW=1 for Jak OI fix
The legit palette draws all seem to use FBW 1.

There's a couple of draws which use the alpha channel of the FB which
are currently falsely triggering.
2023-03-03 14:04:43 +00:00
JordanTheToaster
805f985144 GameDB: Gun fixes
Fixes for rainbowing at the edges of the screen missing bloom intensity and alignment and reflections on water and ground textures.
2023-03-02 21:16:57 +00:00
Stenzek
12e578b93c GS/Vulkan: Don't skip barrier with colclip+readwrite date
Value written from HDR setup must be visible for any DATE reads. Fixes
flickering in DBZ BT2.
2023-03-02 17:01:50 +00:00
JordanTheToaster
1312952305 GameDB: Various fixes
Adds Tex In RT 2 to Armoured Core 2 to fix texture corruption in future and adds merge sprite to Destroy All Humans 1&2 to further fix misaligned bloom.
2023-03-02 15:50:02 +00:00
Stenzek
6118b94f9e GS/Vulkan: Fix a bunch of validation warnings
None of these were errors, but it's still good to have clean output.
2023-03-02 15:49:13 +00:00
Stenzek
520320535e Qt: Clear all keyboard bind states when focus is lost 2023-03-02 15:49:04 +00:00
Mrlinkwii
262b5f1dc0 GameDB: add fixes to Knight Rider - The Game 2023-03-02 11:07:28 +00:00
Mrlinkwii
ac36162ddc GameDB: upscaling fixes for Springdale 2023-03-02 09:50:38 +00:00
Stenzek
9b813f4ae3 Qt: Fix skipdraw not being hidden globally again
And slience a warning in TC.
2023-03-02 09:34:41 +00:00
JordanTheToaster
35d05b8653 GameDB: DAH 1&2 Fixes
Fixes for misaligned bloom and sun occlusion and intensity in Destroy All Humans 1 and 2.
2023-03-02 09:18:18 +00:00
Mrlinkwii
e4d6b87e5d GameDB: add textureInsideRT to NBA '07 featuring The Life Vol.2 2023-03-02 01:34:39 +00:00
Stenzek
64b38e5a4a GS/HW: Add "Merge Targets" texture-in-RT mode
Can take several targets from the cache, and create a combined/merged
source from them.

Fixes shadow maps in Destroy All Humans.
2023-03-01 21:13:37 +00:00
Stenzek
75957c84e3 GS: Add "multi stretch rect" drawing to device 2023-03-01 21:13:37 +00:00
Silent
c33fb2adbd Qt: Add a context menu to the toolbar's Settings button when the game is running
A new small context menu that allows to select between global settings
and game settings.
2023-03-01 20:38:42 +00:00
Silent
97d3baba35 Qt: Move "Game Properties" from View to Settings 2023-03-01 20:38:42 +00:00
Stenzek
e91f9925f8 Qt: Display a slightly more helpful error on display create failure 2023-03-01 20:37:18 +00:00
Stenzek
b484f7aef0 Context/Vulkan: Handle VK_INCOMPLETE return from vkEnumeratePhysicalDevices() 2023-03-01 20:37:18 +00:00
Stenzek
9a3904103a GS/HW: Fix off by one in estimate texture region
And enable it for Justice League.
2023-03-01 20:36:36 +00:00
refractionpcsx2
2a2d39b392 GS-TC: On invalidate of the alpha of 32bit, drop back to 24bit 2023-03-01 20:35:38 +00:00
Stenzek
3005ba629f GS/Vulkan: Fix RT being left bound as texture 2023-03-01 21:02:57 +01:00
Mrlinkwii
795741a341 GameDB: various fixes 2023-02-28 21:39:50 +00:00
JordanTheToaster
da98465d4b GameDB: Remove unneeded Midnight Club 3 fixes
Removes the texture preloading restriction as the game no longer violently blows up during gameplay with the hash cache enabled.
2023-02-28 20:52:03 +00:00
Stenzek
d28e46796f GameDB: Enable estimate texture region on Snowblind games 2023-02-28 17:39:15 +00:00
Stenzek
43c6e321f5 GS/HW: Add a new option to attempt to reduce large texture sizes
For Snowblind games which use 1024x1024 textures and UVs.
2023-02-28 17:39:15 +00:00
Stenzek
4595c2feec GS/HW: Allow moves to create larger targets, align to 64 width 2023-02-28 17:39:15 +00:00
Stenzek
8b4402c517 Qt: Skipdraw shouldn't be visible in global settings 2023-02-28 11:49:26 +00:00
Stenzek
e08ae7e8fa GS/HW: Handle end-of-memory wrapping for surface Overlaps() 2023-02-28 09:12:14 +00:00
Stenzek
753efd8c4a GS/HW: Track target sources in all pages
... instead of just the first page it falls within.
2023-02-28 09:12:14 +00:00
forrvalhalla
cb786f0320 GameDB: Various Armored Core Improvements 2023-02-28 08:51:39 +00:00
PCSX2 Bot
6ff64cc984 PAD: Update to latest controller database. 2023-02-27 18:29:35 +01:00
Mrlinkwii
475b816280 Gamedb: remove HPO normal from Dragon Ball Z Budokai - Tenkaichi 2 2023-02-27 13:51:37 +01:00
Stenzek
229005942f GS/DX12: Add missing clip_control support flag
Fixes depth inaccuracy in WWE SmackDown! Here Comes the Pain.
2023-02-27 13:19:51 +01:00
Stenzek
4af25d20fe GS: Fix output equal check in Merge() 2023-02-27 10:21:39 +00:00
Stenzek
6bf5b9a8e3 GS/HW: Fix incorrect size of region mipmap levels 2023-02-27 07:55:58 +00:00
Stenzek
be769c28fa Qt: Cancel game list refresh before GetSaveStateFileName()
Fixes lockup/crash when starting a file early.
2023-02-26 22:27:49 +00:00
JordanTheToaster
3128c48d5b GameDB: Dance Summit 2001 Fixes
Adds paltex to Dance Summit 2001 to massive increase FPS and reduce hash cache usage dramatically.
2023-02-26 22:27:39 +00:00
RedDevilus
5c7161fd2f GameDB: NASCAR '06 Total Team Control
Forces Adaptive TFF to stop bouncing/shaking vertical screen
2023-02-26 15:38:48 +00:00
Stenzek
e85790b84b GS/HW: Don't try to draw with invalid TEX0 configuration
Fixes the texture cache falling apart in 007: Everything or Nothing.
2023-02-26 15:37:58 +00:00
Stenzek
980e2f67fd Qt: Don't display updater if running a game or fullscreen 2023-02-26 15:33:02 +00:00
TellowKrinkle
7781907f0e GS: Blend truncation and dither goes the other way when subtracting
Truncation happens after subtraction, so it's the equivalent of rounding the value to subtract *up* instead of down
2023-02-26 07:22:32 +01:00
TellowKrinkle
1d145dd48a GS:MTL: Clip control is supported
(Well technically, it's not but the default value is the one we want)
2023-02-26 07:22:32 +01:00
TellowKrinkle
1a1eb30e60 GS:MTL: Properly initialize has variable 2023-02-26 07:22:32 +01:00
TellowKrinkle
a7e2b98dc7 Vulkan: Format tfx.glsl 2023-02-26 07:22:32 +01:00
Stenzek
2bf74622a5 GS/DX12: Fix potential crash in PrimID DATE setup 2023-02-26 03:49:40 +00:00
refractionpcsx2
6bcaef9325 GS-HW: Allow swapping of start/end block on overlap check within page 2023-02-26 01:29:49 +00:00
refractionpcsx2
0b3aac3d91 GS-HW: Add channel masks to dirty rects, allows partial updates 2023-02-26 01:29:49 +00:00
refractionpcsx2
9a53f0f853 GS-HW: Improve Local->Host and preload accuracy. 2023-02-26 01:29:49 +00:00
refractionpcsx2
a97df14064 GS-HW: Set scale on temporary depth stencil 2023-02-26 01:29:34 +00:00
RedDevilus
9a0cd1157f GameDB: Timesplitters Series + minor maintenance
- Timesplitters 2 (normal vertex screwing up bottom)
- Timesplitters Future Perfect (remove depth lines)
- Dragon Ball Z - Infinite World (dash
2023-02-26 01:13:24 +00:00
Mrlinkwii
16c41255d0 Gamedb: remove disable partial invalidation from fatal frame 2023-02-26 00:24:24 +00:00
Stenzek
c2d1e5bd18 GameDB: Switch CPU CLUT for GPU CLUT in The Godfather
It can do 10+ readbacks in one frame, which is way too much.
2023-02-25 12:31:28 +00:00
Stenzek
3c85ba3eb8 GS/HW: Use 1023 for skip-target-creation threshold
Fixes target blowout in The Godfather (it does a 1023x1023 draw on
boot).
2023-02-25 12:31:28 +00:00
JordanTheToaster
f3b67b158c GameDB: Gorwlanser 5&6 and Poinies Poin Fixes
Adds Texture in RT to Growlanser 5 and 6 to fix a layering issue and to Poinies Poin to fix text corruption after an FMV.
2023-02-25 12:28:05 +00:00
kamfretoz
8dac10ae36 Qt: Add a new theme, Cobalt Sky. 2023-02-25 11:00:56 +00:00
JordanTheToaster
6b81050283 GameDB: 007 EON Fixes
Adds mipmapping full and trilinear PS2 to 007 Everything Or Nothing to fix up texture detail and removes mipmapping from Fifa games to prevent players from going bald or having rainbow vomit shirts.
2023-02-25 10:55:53 +00:00
Stenzek
b4beacfc43 GS/OGL: Avoid framebuffer swaps when depth is being alternated 2023-02-25 08:18:34 +00:00
Stenzek
1b607a8c4d GameDB: Add partial target invalidation to affected games 2023-02-25 08:18:34 +00:00
Stenzek
4583c64ff7 GS/HW: Add partial target invalidation option
Eventually hopefully we can make this the default, but it breaks too
much at the moment.

Fixes missing/corrupted textures in True Crime: New York City.
2023-02-25 08:18:34 +00:00
Stenzek
a06a07d961 GS/HW: Allow sw prim render when texture rect is dirty
Gets rid of an unnecessary readback in True Crime: New York City.
2023-02-25 08:18:34 +00:00
Stenzek
520a369872 GS/HW: Prevent out of bounds copy in OI_BlitFMV() 2023-02-25 08:18:34 +00:00
Stenzek
1eaec773e2 GS/HW: Don't allocate Z buffer if tests always pass
.. and Z is masked.

Stops some offscreen targets from expanding in size.

Partial fix for regressions in True Crime: New York City.
2023-02-25 08:18:34 +00:00
Stenzek
3433a42e42 GS/DX12: Fix incorrect pipeline RT format 2023-02-25 03:04:50 +00:00
lightningterror
e01eac615d GS-tc: Fix Wreorder-ctor warning. 2023-02-24 11:29:53 +01:00
lightningterror
8116646dee GS: Bump shader cache version. 2023-02-24 02:06:00 +01:00
lightningterror
7f7950cd6b GS-d3d: Check each channel individually if it overflows and do corrections. 2023-02-24 02:06:00 +01:00
lightningterror
2eb11ded52 GS-metal: Check each channel individually if it overflows and do corrections. 2023-02-24 02:06:00 +01:00
lightningterror
bd64ad510b GS-vk: Check each channel individually if it overflows and do corrections. 2023-02-24 02:06:00 +01:00
lightningterror
4e9b7e61a7 GS-ogl: Check each channel individually if it overflows and do corrections. 2023-02-24 02:06:00 +01:00
lightningterror
37b19495a8 GS-hw: Use rgb color for second output instead of alpha.
We want different alpha value for each channel.
2023-02-24 02:06:00 +01:00
refractionpcsx2
c12b412e87 GS-HW: Remove debug message, partial Clang format GSRendererHW.cpp 2023-02-23 23:21:53 +00:00
refractionpcsx2
17f137f8be GS-HW: Slightly better check for target expanding 2023-02-23 20:13:01 +00:00
JordanTheToaster
fcc627c65c GameDB: Various fixes
Fixes for missing bloom intensity in Ferrari Challenge mipmapping in Dark Cloud and adjusts HPO on The Dog Island and adding missing Scandinavia entrys.
2023-02-23 19:37:29 +00:00
refractionpcsx2
5bb3d8e60d GS-HW: Improve GS read target detection, avoid reading dirty targets. 2023-02-23 18:27:01 +00:00
refractionpcsx2
264086e0aa GS-HW: Improve clear detection and avoid making bad render targets 2023-02-23 18:27:01 +00:00
refractionpcsx2
6013d7172a GS-HW: Drop to CPU CLUT w/e invalidate only if dirty covers whole tex 2023-02-23 18:27:01 +00:00
refractionpcsx2
a7714b2725 MCD: Stop Memcard Folders spamming the console/OSD when saving a game 2023-02-23 18:02:07 +00:00
Stenzek
f9dcac8cd0 GS/HW: Make readback-on-close a HW fix
Unfortunately it's too risky to enable by default all the time. So,
we'll make it a hw fix, and hopefully one day can make it default on.

Also makes save states readback the TC as well.
2023-02-23 17:25:03 +00:00
Stenzek
2487322e47 GS/HW: Track which bits are actually written to targets
Fixes Burnout 3 sky getting corrupted when flushing.
2023-02-23 17:25:03 +00:00
Stenzek
c7e9c9542e GS/HW: Don't try to dirty targets which don't overlap
The current invalidation code sucks, but at least this stops things
which are blatently wrong from happening.
2023-02-23 10:12:05 +00:00
Stenzek
c1bc1af302 GS/HW: Don't mess with the buffer width of the target on invalidate 2023-02-23 10:12:05 +00:00
Stenzek
739f9ec758 GS/HW: Set valid rect on targets created by Move 2023-02-23 10:12:05 +00:00
refractionpcsx2
f70a140f42 GS-HW: Fix Download readbacks and limit FB resizing 2023-02-22 11:50:11 +00:00
refractionpcsx2
a716e69dc0 GameDB: Remove no longer needed SW FMV switch fixes 2023-02-21 16:15:29 +00:00
refractionpcsx2
0c6e1a4d56 GS-HW: Invalidate overlapped target when expanding. 2023-02-21 16:15:29 +00:00
Stenzek
faf36ecba6 GS/D3D: Default to D3D11 for Intel 2023-02-21 15:49:59 +00:00
Stenzek
49f2900e1f GS/D3D: Combine driver version lookup for D3D11+D3D12 2023-02-21 15:49:59 +00:00
AKuHAK
8bd522d283 BiosTools: Implement SysGetBiosDiscID function, for Bios Serial used EXTINFO build unique timestamp 2023-02-21 15:24:36 +00:00
AKuHAK
e9034a1ba1 Biostools: remove unused external variable BiosZone 2023-02-21 15:24:36 +00:00
Stenzek
03feacd69a GS/HW: Use FB size instead of output size for target lookup
Fixes render target height doubling unnecessarily in Ridge Racer V.
2023-02-21 15:22:09 +00:00
Stenzek
0c9f44d8a4 GS/HW: Don't allow region textures to make sources larger
Currently not handled. Fixes Ridge Racer V intro again.
2023-02-21 15:22:09 +00:00
RedDevilus
39fb64cdcd GameDB: Dark Cloud 2 + Dark Chronicle
I had a dark joke on the fix, but let's not jester about it. The clown now has more colors than just 1.
2023-02-21 14:10:14 +00:00
JordanTheToaster
d531a7f1af GameDB: Remove CPU CLUT from Transformers The Game
Something changed and now this just causes rainbow vomit in game and in menus instead of fixing the issue it was originally put in for.
2023-02-21 09:36:49 +00:00
PCSX2 Bot
bb7ff414ae PAD: Update to latest controller database. 2023-02-21 02:14:48 +01:00
JordanTheToaster
215f112521 GameDB: Port patch to PAL Transformers ROTF
Ports a patch over to the PAL version to fix the pause menu hud and other parts.
2023-02-19 15:50:05 +00:00
Florin9doi
644766d965 USB: Fix Sega Seamic buttons 2023-02-19 13:15:53 +01:00
JordanTheToaster
1e1a555d3b GameDB: Various fixes
Fixes for Transformers Revenge of the Fallen misaligned bloom and soft shadows and Legend of Kay broken shadow rectangles and misaligned bloom.
2023-02-19 03:53:20 +00:00
refractionpcsx2
c64ae2684d GS-PCRTC: Handle extended height when there's a negative offset 2023-02-19 00:57:52 +00:00
refractionpcsx2
50ed04436d GS-HW: Preserve width of frame textures 2023-02-19 00:57:38 +00:00
Stenzek
2fb9beca52 GS/HW: Use block instead of page pointer for height lookup
Some callers were using blocks, others were using pages before.

Enables target expansion in Stuntman, which slightly improves rendering
without CPU sprite. The full "fix" needs page handling in P8 conversion.
2023-02-19 00:57:24 +00:00
refractionpcsx2
cd4c1e920e GS: Flush targets back to GS memory when swapping renderers 2023-02-19 00:57:01 +00:00
refractionpcsx2
e846ac367a GS-HW: Fix downloads in offset BP and different widths 2023-02-19 00:03:49 +00:00
Mrlinkwii
ef31c733ee Gamedb: update Armored Core - Nexus 2023-02-18 15:53:30 +00:00
Stenzek
724aa657f3 Qt: Fix window geometry not saving on exit 2023-02-18 14:50:09 +00:00
kenshen112
0284c35f4c GameDB: Add Fatal Frame 1 / Project Zero final boss game bug fix patch 2023-02-18 05:04:20 +00:00
TheTechnician27
6ce33de287 UI: Update and Add mouseover dialog (#8119) 2023-02-18 01:58:32 +00:00
refractionpcsx2
6ccfa011d4 Pad/Counters: Rearrange Pad/Core updates on VSync
Apparently this gets around some weird input lag issue.
2023-02-18 01:13:39 +00:00
refractionpcsx2
c9078af45e GS: Bump shader cache version 2023-02-17 19:40:16 +00:00
refractionpcsx2
6745428d0c GS-SW: Fix SW texture dumping 2023-02-17 19:06:35 +00:00
refractionpcsx2
925e874ada GS-HW: Fix real 16bit value shuffles 2023-02-17 19:06:35 +00:00
lightningterror
03f0f2f803 GameDB: Add missing db entry for Silent Scope 3.
Korean version.
2023-02-17 16:35:46 +01:00
lightningterror
857360d6b2 GameDB: Add full mipmap with ps2 trilinear to Matrix, The - Path of Neo.
Improves ground textures to match sw renderer.
2023-02-17 16:35:46 +01:00
Stenzek
2dd76c3f12 GS/HW: Force region match on fixed TEX0
Fixes lightmapping in Spinter Cell - Double Agent.
2023-02-17 13:20:04 +00:00
Stenzek
666de3a874 GS/HW: Allow hardware sampling when clamp_max is larger
Fixes some mipmapping issues in GT4.
2023-02-17 13:20:04 +00:00
Stenzek
e0e9b64db6 GS/HW: Fix region textures being used when redundant 2023-02-17 13:20:04 +00:00
JordanTheToaster
2f521348c6 GameDB: Add VU Sync hack to Salt Lake 2002
Fixes the awful flickering and missing textures that happens all over the game.
2023-02-17 13:12:33 +00:00
Stenzek
8ac2949a1f GS/HW: Fix incorrect bitfield width in height cache
This lead to duplication and possibly larger-than-intended heights.
2023-02-16 16:44:40 +00:00
Stenzek
d0d5d991ce GS/HW: Fix repeat sampler being used for region clamp/repeat
The coordinates are clamped in the shader, but normalized UV of 1 and
bilinear may repeat.
2023-02-16 16:44:40 +00:00
Mrlinkwii
1fc2d7de3c GameDB : remove skipdraw from Need for Speed - Undercover 2023-02-16 15:00:27 +00:00
refractionpcsx2
2598e8d9b9 GS-PCRTC: Fix Anti-blur in screen offset.
No this doesn't mean it won't look blurry, that's a downside of screen offsets in some games, but it might look *less* blurry.
2023-02-16 14:47:09 +00:00
refractionpcsx2
01f65e98e6 GS-PCRTC: Fix up some PCRTC anti-blur behaviour + code cleanup 2023-02-16 14:47:09 +00:00
Ganael Laplanche
c5330cf166 Common: FreeBSD fixes. (#8163)
* Fix SIGSEGV handler on FreeBSD

* Fix config dir location on FreeBSD
2023-02-16 09:30:18 +01:00
JordanTheToaster
b78796d0c1 GameDB: Add HPO Special to Arthur and the minimoys
Fixes misaligned bloom that looks gross.
2023-02-16 00:32:17 +00:00
RedDevilus
da1e9db2c0 GameDB: Ruff Trigger
Rename Europe serial and fix offset and blooming.
2023-02-15 19:18:11 +00:00
RedDevilus
6beb6aa05b GameDB: Bakugan Battle Brawlers
Se Deinterlace to Adaptive TFF. I thought this was fixed but needs to be checked later.
2023-02-15 19:15:49 +00:00
Mrlinkwii
6ea7777a3a GS-HW: remove Fighting Beauty Wulong & Spartan Total Warrior CRC (#8158) 2023-02-15 14:56:53 +00:00
Stenzek
f97191e241 GS/HW: Split up consecutive channel shuffles 2023-02-15 11:04:05 +00:00
Stenzek
51420dade4 VMManager: Clear host root on booting ISO 2023-02-15 10:04:39 +00:00
Tokman5
cc55c01197 GameDB: Add EETimingHack to Crazy Taxi 2023-02-15 10:04:04 +00:00
JordanTheToaster
86ce464ee3 GameDB: Fix V-Rally 3 graphical corruption
Fixes random graphical corruption that can happen by adding EE Timing hack.
2023-02-15 00:04:44 +00:00
refractionpcsx2
0fa52a75ad GS-HW: Optimise readback performance for some cases 2023-02-14 22:27:35 +00:00
Mrlinkwii
c91e7dc3b0 GameDB: remove not needed hw fixes for Metal Slug 6 2023-02-14 22:27:17 +00:00
Mrlinkwii
88034b176c GS-HW:purge MetalSlug6 CRC 2023-02-14 22:27:17 +00:00
JordanTheToaster
efeaff488c GameDB: Add HPO Special to 007 EON
Lines bad must fix lines.
2023-02-14 17:49:53 +00:00
Berylskid
5df30f5bdd OSD: Change the lower limit of OSD Scale from 100 to 50 (#8135) 2023-02-14 14:03:24 +00:00
C.W. Betts
cf179c42b8 cmake: Quiet macOS building warning, we can build on macOS just fine. (#8136) 2023-02-14 11:32:39 +01:00
Berylskid
a615f8bf17 Qt: Change "Save State On Shutdown" to Unchecked (#8147) 2023-02-14 10:30:34 +00:00
refractionpcsx2
b38964e814 GS: Partial revert of #8101 pending investigation.
Sometimes great ideas don't always go smoothly to plan, this is one of those, but it will be back when we work out what's up with Soul Calibur 2 and DBZ BT3 :)
2023-02-14 10:23:09 +00:00
JordanTheToaster
013c9eec58 GameDB: Various fixes
Some fixes I missed in the last PR because I am a doughnut and can't remember what I needed to do.
2023-02-14 00:20:52 +00:00
refractionpcsx2
ddbd6eddf7 GameDB: Add Autoflush for Astro Boy and CPU Sprite for Alias 2023-02-14 00:18:08 +00:00
refractionpcsx2
982fd42683 GS: Preload whenever it matches an upload. GUI option forces preloads. 2023-02-14 00:18:08 +00:00
RedDevilus
90e28e7957 GameDB: Fix Japanese KH2 - Final Mix
Change Normal Vertex halfpixel with roundsprite
2023-02-13 23:59:41 +00:00
JordanTheToaster
f4201f3947 GameDB: Remove fix from Soulcalibur 3
No longer needed as the hash cache no longer does an explosion.
2023-02-13 23:27:27 +00:00
PCSX2 Bot
7844b40243 PAD: Update to latest controller database. 2023-02-13 17:37:59 +01:00
JordanTheToaster
d0839a3d55 GameDB: Fixes for graphics in Mana Khemia 2
Fixes missing graphical effects in battles by disabling mvu speedhack.
2023-02-13 16:07:31 +00:00
JordanTheToaster
876fd9ba9e GameDB: Vexx underwater rendering fixes
Adds Tex in RT to fix underwater rendering being totally broken.
2023-02-13 09:37:31 +00:00
lightningterror
e12717c108 CDVD: Fix FreeBSD compile. 2023-02-13 03:02:59 +01:00
refractionpcsx2
af0b17bb7a Counters: Present at VSync End 2023-02-12 21:13:42 +00:00
Stenzek
6f595b7d87 GS/HW: Allow previous frame in depth source lookup
Fixes pulling random junk from local memory in Black after adjusting
vsync timing.
2023-02-12 21:13:42 +00:00
JordanTheToaster
59cbdc79f5 GameDB: Fixes for V-Rally 3 and Berserk
Fixes sun occlusion in V-Rally 3 and fixes missing subtitles in FMVs in Berserk.
2023-02-12 19:28:13 +00:00
refractionpcsx2
50ff3649b1 GS: Fix divide by zero error 2023-02-12 19:14:54 +00:00
refractionpcsx2
9ca9db8770 GS: Fix undefined behaviour bug in PCRTC 2023-02-12 15:03:24 +00:00
Stenzek
fa70f0e764 GS/HW: Don't age texture cache on idle frames 2023-02-12 08:34:39 +00:00
Stenzek
3eb629f133 InputManager: Restore passing wheel events to ImGui 2023-02-12 07:25:56 +00:00
Stenzek
c9aba6bbe1 GS/TextureCache: Allow tex-in-rt for 16/24/32-bit targets
Fixes swirl battle transition in Valkyrie Profile 2.
Fixes top-left screen rendering in Lego Racers 2.
2023-02-12 07:07:52 +00:00
Stenzek
0a26adae76 GS/TextureCache: Dirty with target PSM/TBW, not the EE write 2023-02-12 07:07:52 +00:00
Stenzek
f0798f6510 GS/HW: Combine dirty rectangles for target updates
Fewer texture uploads makes Intel GPUs happy.
2023-02-12 07:07:52 +00:00
refractionpcsx2
245b03e208 GS: Limit the height of framebuffer reads 2023-02-12 07:07:26 +00:00
refractionpcsx2
4e31e5fdc2 GS: Use linear interpolation for Screen Offsets 2023-02-12 04:34:07 +00:00
Stenzek
750a74206c InputManager: Warning fix/default interia value 2023-02-12 04:32:47 +00:00
Stenzek
7e64dc2576 GS/HW: Don't defer TC reset until next vsync 2023-02-12 04:32:47 +00:00
Stenzek
8a08e2fd97 GS: Make sure everything in GSState is initialized 2023-02-12 04:32:47 +00:00
Stenzek
d0a933cda8 GS/TextureCache: Clear surface offset cache on reset
Also get rid of RemovePartial(), it's never called.
2023-02-12 04:32:47 +00:00
lightningterror
d00845f56b GameDB: Add missing Need for Speed - Carbon entry. 2023-02-12 00:46:01 +00:00
lightningterror
3350e5ebb1 GameDB: Replace DMABusyHack with InstantDMAHack for MGS2 Sons of Liberty.
DMA timing problem.
Fixes broken half-bottom artifacts.
2023-02-12 00:46:01 +00:00
SideProjectsLab
aeb4445cad Qt/Input: Improved how mouse movements are mapped to analog (#7910) 2023-02-11 23:58:58 +00:00
refractionpcsx2
73abae8cb9 GameDB: Remove forced deinterlacing modes which are no longer required 2023-02-11 20:08:36 +00:00
refractionpcsx2
7ecc7b76ba GS-PCRTC: Improve automatic de-interlacing to avoid it more often. 2023-02-11 20:08:36 +00:00
refractionpcsx2
71d0bbbc25 GS: Rework of PCRTC code. 2023-02-11 20:08:36 +00:00
JordanTheToaster
26e691ba93 GameDB: Update HPO on Spider Man 2
Changes HPO to HPO Special to fix rainbow garbage on the top and left side of the screen when moving.
2023-02-11 17:43:05 +00:00
Stenzek
c7352d9e10 GS: Attempt to recreate device if GPU crashes 2023-02-11 15:33:55 +00:00
Stenzek
7b8f9a54ec GS/HW: Purge FFX-2 depth clear CRC hack 2023-02-11 15:26:04 +00:00
JordanTheToaster
28980af858 GameDB: Port COP2 patch for Disneys Cars
Ports patch to fix broken collisions to PAL Disney's Cars
2023-02-11 15:24:23 +00:00
Stenzek
80dce398e0 GS/HW: Carefully allow move to create new targets
Xenosaga I does a move from BP 1C00 to E00, then from E00 to 2A00 a few
frames later for its cutscene transitions. 2A00 then gets used as a
texture and blended on top of the later frames. Because there's no
target at 2A00, the move falls back to the CPU, and E00 contains junk
which gets moved and eventually preloaded instead.

Gradius V uses moves for a screen move/wobble-like effect, by moving
chunks of the framebuffer out, then using those as a texture. It's not
broken at the moment, but it does readback (slow), and break upscaling.
2023-02-11 07:16:19 +00:00
TheTechnician27
06db8eec48 Context.cpp: fix minor typo 2023-02-11 06:46:44 +00:00
Stenzek
9c720efe46 GS/OGL: Fix possible crash downloading odd texture sizes 2023-02-11 06:43:20 +00:00
refractionpcsx2
cbf91a8d19 GS-HW: Tighten CLUT detection slightly. 2023-02-11 02:07:01 +00:00
TheTechnician27
f99414708d Readme: Two minor changes to the README (#8105) 2023-02-11 00:00:25 +00:00
refractionpcsx2
9549a6b16a GS: Fix TME processing when Alpha->IsBlack & !TEX0->TCC 2023-02-10 23:48:43 +00:00
lightningterror
3206094545 GameDB: Add full mipmap and trilinear ps2 to Hard Hitter games.
Improves ground texture rendering.
2023-02-10 22:24:33 +01:00
Stenzek
5cfae80701 GL/OpenGL: Add a hidden [EmuCore/GS] DisableGLDownloadPBO option
.. to disable the use of PBOs when reading back.
2023-02-10 14:38:21 +00:00
JordanTheToaster
b4d140c6bb GameDB: Fixes for 187 Ride or Die
Add autoflush to soften bloom and HPO Special Texture to fix misaligned bloom.
2023-02-10 14:15:51 +00:00
Stenzek
c65eb3c3ee GS/HW: Fix crash with AVX2 due to unaligned pitch 2023-02-10 14:15:33 +00:00
Mrlinkwii
eec0984dbe Gamedb: remove skipdraw from Need for Speed - Undercover 2023-02-10 14:47:52 +01:00
125 changed files with 5958 additions and 2902 deletions

View File

@@ -36,10 +36,10 @@ _Note: Recommended GPU is based on 3x Internal, ~1080p resolution requirements.
### Technical Notes
- You need the [Visual C++ 2019 x64 Redistributables](https://support.microsoft.com/en-us/help/2977003/) to run PCSX2.
- You need the [Visual C++ 2019 x64 Redistributables](https://support.microsoft.com/en-us/help/2977003/) to run PCSX2 on Windows.
- Windows XP and Direct3D9 support was dropped after stable release 1.4.0.
- Windows 7, Windows 8.0, and Windows 8.1 support was dropped after stable release 1.6.0.
- 32-bit and wxwidgets support was dropped after stable release 1.6.0, with the wxwidgets code being removed completely on 25th December 2022.
- 32-bit and wxWidgets support was dropped after stable release 1.6.0, with the wxWidgets code being removed completely on 25th December 2022.
- Make sure to update your operating system and drivers to ensure you have the best experience possible. Having a newer GPU is also recommended so you have the latest supported drivers.
- Because of copyright issues, and the complexity of trying to work around it, you need a BIOS dump extracted from a legitimately-owned PS2 console to use the emulator. For more information about the BIOS and how to get it from your console, visit [this page](pcsx2/Docs/PCSX2_FAQ.md#question-13-where-do-i-get-a-ps2-bios).
- PCSX2 uses two CPU cores for emulation by default. A third core can be used via the MTVU speed hack, which is compatible with most games. This can be a significant speedup on CPUs with 3+ cores, but it may be a slowdown on GS-limited games (or on CPUs with fewer than 2 cores). Software renderers will then additionally use however many rendering threads it is set to and will need higher core counts to run efficiently.

File diff suppressed because it is too large Load Diff

View File

@@ -66,12 +66,12 @@
03000000c82d00000021000000000000,8BitDo SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000121000000000000,8BitDo SN30 Pro for Android,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00000260000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000261000000000000,8BitDo SN30 Pro Plus,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00001130000000000000,8BitDo Ultimate Wired,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b26,paddle1:b24,paddle2:b25,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00001230000000000000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00001330000000000000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00000121000000000000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000a00500003232000000000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00003032000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,
@@ -446,7 +446,7 @@
03000000120c0000f60e000000000000,P4 Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,
03000000790000002201000000000000,PC Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
030000006f0e00008501000000000000,PDP Fightpad Pro,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b0,platform:Windows,
030000006f0e00000901000000000000,PDP Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
030000006f0e00000901000000000000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
030000008f0e00004100000000000000,PlaySega,a:b1,b:b0,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b4,y:b3,platform:Windows,
03000000666600006706000000000000,PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Windows,
03000000e30500009605000000000000,PlayStation Adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
@@ -543,7 +543,7 @@
030000009b2800001e00000000000000,Raphnet Vectrex Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a1,lefty:a2,x:b2,y:b3,platform:Windows,
030000009b2800002b00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800002c00000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800008000000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,x:b0,y:b5,back:b2,guide:b10,start:b3,leftshoulder:b6,rightshoulder:b7,dpup:b12,dpleft:b14,dpdown:b13,dpright:b15,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b8,righttrigger:b9,platform:Windows,
030000009b2800008000000000000000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Windows,
03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000321500000204000000000000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000321500000104000000000000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
@@ -850,6 +850,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000000d0f00003801000008010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Mac OS X,
030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f0000aa00000072050000,Hori Real Arcade Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
030000000d0f00000002000015010000,Hori Switch Split Pad Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00006e00000000010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00006600000000000000,Horipad FPS Plus 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
@@ -888,6 +889,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000790000000018000000000000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,
03000000790000000018000000010000,Mayflash Wii U Pro Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,
030000005e0400002800000002010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Mac OS X,
030000005e0400000300000006010000,Microsoft SideWinder,a:b0,b:b1,back:b9,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Mac OS X,
030000005e0400000700000006010000,Microsoft SideWinder,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Mac OS X,
030000005e0400002700000001010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Mac OS X,
03000000d62000007162000001000000,Moga Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,
03000000c62400002a89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,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,
@@ -904,7 +907,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b15,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000550900001472000025050000,NVIDIA Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,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:Mac OS X,
030000004b120000014d000000010000,Nyko Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Mac OS X,
030000006f0e00000901000002010000,PDP Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000006f0e00000901000002010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000008f0e00000300000000000000,Piranha Xtreme PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000666600006706000088020000,PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Mac OS X,
030000004c050000da0c000000010000,PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
@@ -947,6 +950,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000004c0500006802000002100000,Rii RK707,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b3,righttrigger:b9,rightx:a2,righty:a3,start:b1,x:b15,y:b12,platform:Mac OS X,
03000000c6240000fefa000000000000,Rock Candy PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000006f0e00008701000005010000,Rock Candy Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000e804000000a000001b010000,Samsung EIGP20,a:b1,b:b3,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b11,leftx:a1,lefty:a3,rightshoulder:b12,rightx:a4,righty:a5,start:b16,x:b7,y:b9,platform:Mac OS X,
03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Mac OS X,
03000000a30c00002500000006020000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Mac OS X,
03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X,
@@ -992,6 +996,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e040000130b000011050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000200b000011050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000200b000013050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000200b000015050000,Xbox One Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000d102000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000005e040000dd02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
030000005e040000e002000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,
@@ -1067,6 +1072,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00000760000011010000,8BitDo Ultimate Wireless,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d00001230000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00001330000011010000,8BitDo Ultimate Wireless,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00000121000011010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000c82d00000121000000010000,8BitDo Xbox One SN30 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000a00500003232000001000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,
05000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
@@ -1176,6 +1183,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000000d0f00006a00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00006b00000011010000,Hori Real Arcade Pro 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00001600000000010000,Hori Real Arcade Pro EXSE,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,
030000000d0f00008501000015010000,Hori Switch Split Pad Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000000d0f00006e00000011010000,Horipad 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f00006600000011010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
030000000d0f0000ee00000011010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
@@ -1251,26 +1259,28 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000b50700001203000010010000,Mega World Logic 3 Controller,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,
03000000780000000600000010010000,Microntek Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
030000005e0400002800000000010000,Microsoft Dual Strike,a:b3,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,rightx:a0,righty:a1~,start:b5,x:b1,y:b0,platform:Linux,
030000005e0400000e00000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,
030000005e0400000700000000010000,Microsoft SideWinder Gamepad,a:b0,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux,
030000005e0400000300000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux,
030000005e0400000700000000010000,Microsoft SideWinder,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Linux,
030000005e0400000e00000000010000,Microsoft SideWinder Freestyle Pro,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,
030000005e0400002700000000010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Linux,
030000005e0400008502000000010000,Microsoft Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,
030000005e0400008902000021010000,Microsoft Xbox,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,
030000005e0400008e02000001000000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.1,dpleft:h0.2,dpright:h0.8,dpup:h0.4,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e0400008e02000004010000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e0400008e02000056210000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e0400008e02000062230000,Microsoft Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b00000b050000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000d102000001010000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000d102000003020000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000120b000009050000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000dd02000003020000,Microsoft Xbox One 2015,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000dd02000003020000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000ea02000008040000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000120b000009050000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000e302000003020000,Microsoft Xbox One Elite,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000000b000008040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000005e040000050b000003090000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
030000005e040000ea02000008040000,Microsoft Xbox One S,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e0400008902000021010000,Microsoft Xbox pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,
030000005e040000120b00000b050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,
050000004d4f435554452d3035335800,Mocute 053X,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
05000000e80400006e0400001b010000,Mocute 053X M59,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000004d4f435554452d3035305800,Mocute 054X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000d6200000e589000001000000,Moga 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,
05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,
@@ -1301,7 +1311,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000d620000011a7000011010000,Nintendo Switch PowerA Core Plus Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000007e0500000920000000026803,Nintendo Switch Pro Controller,a:b1,b:b0,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Linux,
030000007e0500000920000011810000,Nintendo Switch Pro 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,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,
050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
050000007e0500000920000001800000,Nintendo Switch Pro 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,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,
050000007e0500000720000001800000,Nintendo Switch Right Joy-Con,a:b1,b:b2,back:b9,leftshoulder:b4,leftstick:b10,leftx:a1~,lefty:a0,rightshoulder:b6,start:b8,x:b0,y:b3,platform:Linux,
05000000010000000100000003000000,Nintendo Wii Remote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
@@ -1330,9 +1340,11 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000006f0e0000c802000012010000,PDP Kingdom Hearts Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000006f0e00008501000011010000,PDP Nintendo Switch Fightpad Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
030000006f0e00002801000011010000,PDP PS3 Rock Candy Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000006f0e00000901000011010000,PDP Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
030000006f0e00000901000011010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
03000000ad1b000004f9000000010000,PDP Xbox 360 Versus Fighting,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,
030000006f0e0000a802000023020000,PDP Xbox One Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
030000006f0e0000a702000023020000,PDP Xbox One Raven Black,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000006f0e0000d802000006640000,PDP Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000666600006706000000010000,PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,
030000004c050000da0c000011010000,PlayStation Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
03000000d9040000160f000000010000,PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,
@@ -1432,6 +1444,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,
03000000a30600000b04000000010000,Saitek P990 Dual Analog,a:b1,b:b2,back:b9,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:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux,
03000000a306000020f6000011010000,Saitek PS2700 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,
05000000e804000000a000001b010000,Samsung EIGP20,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000d81d00000e00000010010000,Savior,a:b0,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b9,x:b4,y:b5,platform:Linux,
03000000a30c00002500000011010000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Linux,
03000000790000001100000011010000,Sega Saturn,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Linux,
@@ -1525,14 +1538,12 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000ffff0000ffff000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,
0000000058626f782047616d65706100,Xbox Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
030000005e0400000a0b000005040000,Xbox One Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,
030000005e040000120b000009050000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000d102000002010000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000ea02000001030000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000005e040000e002000003090000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000005e040000fd02000003090000,Xbox One Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000fd02000030110000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000120b000007050000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000005e040000e302000002090000,Xbox One Elite,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000220b000013050000,Xbox One Elite 2 Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
@@ -1540,16 +1551,20 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
060000005e040000ea0200000d050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b000001050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b000005050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b000009050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b00000d050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000130b000007050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000130b000011050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000130b000015050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
060000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000120b00000b050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b000007050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000005e040000130b000007050000,Xbox Series X Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000130b000011050000,Xbox Series X Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000450c00002043000010010000,XEOX SL6556 BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
05000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,

View File

@@ -38,6 +38,7 @@
#define PS_POINT_SAMPLER 0
#define PS_SHUFFLE 0
#define PS_READ_BA 0
#define PS_READ16_SRC 0
#define PS_DFMT 0
#define PS_DEPTH_FMT 0
#define PS_PAL_FMT 0
@@ -52,6 +53,7 @@
#define PS_BLEND_C 0
#define PS_BLEND_D 0
#define PS_BLEND_MIX 0
#define PS_ROUND_INV 0
#define PS_FIXED_ONE_A 0
#define PS_PABE 0
#define PS_DITHER 0
@@ -740,7 +742,11 @@ void ps_dither(inout float3 C, float2 pos_xy)
else
fpos = int2(pos_xy / (float)PS_SCALE_FACTOR);
C += DitherMatrix[fpos.x & 3][fpos.y & 3];
float value = DitherMatrix[fpos.x & 3][fpos.y & 3];
if (PS_ROUND_INV != 0)
C -= value;
else
C += value;
}
}
@@ -750,6 +756,9 @@ void ps_color_clamp_wrap(inout float3 C)
// so we need to limit the color depth on dithered items
if (SW_BLEND || PS_DITHER || PS_FBMASK)
{
if (PS_DFMT == FMT_16 && PS_BLEND_MIX == 0 && PS_ROUND_INV != 0)
C += 7.0f; // Need to round up, not down since the shader will invert
// Standard Clamp
if (PS_COLCLIP == 0 && PS_HDR == 0)
C = clamp(C, (float3)0.0f, (float3)255.0f);
@@ -762,8 +771,10 @@ void ps_color_clamp_wrap(inout float3 C)
}
}
void ps_blend(inout float4 Color, inout float As, float2 pos_xy)
void ps_blend(inout float4 Color, inout float4 As_rgba, float2 pos_xy)
{
float As = As_rgba.a;
if (SW_BLEND)
{
// PABE
@@ -810,17 +821,15 @@ void ps_blend(inout float4 Color, inout float As, float2 pos_xy)
if (PS_CLR_HW == 1)
{
// Replace Af with As so we can do proper compensation for Alpha.
if (PS_BLEND_C == 2)
As = Af;
// As or Af
As_rgba.rgb = (float3)C;
// Subtract 1 for alpha to compensate for the changed equation,
// if c.rgb > 255.0f then we further need to adjust alpha accordingly,
// we pick the lowest overflow from all colors because it's the safest,
// we divide by 255 the color because we don't know Cd value,
// changed alpha should only be done for hw blend.
float min_color = min(min(Color.r, Color.g), Color.b);
float alpha_compensate = max(1.0f, min_color / 255.0f);
As -= alpha_compensate;
float3 alpha_compensate = max((float3)1.0f, Color.rgb / (float3)255.0f);
As_rgba.rgb -= alpha_compensate;
}
else if (PS_CLR_HW == 2)
{
@@ -832,6 +841,16 @@ void ps_blend(inout float4 Color, inout float As, float2 pos_xy)
float color_compensate = 1.0f * (C + 1.0f);
Color.rgb -= (float3)color_compensate;
}
else if (PS_CLR_HW == 3)
{
// As, Ad or Af clamped.
As_rgba.rgb = (float3)C_clamped;
// Cs*(Alpha + 1) might overflow, if it does then adjust alpha value
// that is sent on second output to compensate.
float3 overflow_check = (Color.rgb - (float3)255.0f) / 255.0f;
float3 alpha_compensate = max((float3)0.0f, overflow_check);
As_rgba.rgb -= alpha_compensate;
}
}
else
{
@@ -877,26 +896,37 @@ PS_OUTPUT ps_main(PS_INPUT input)
{
uint4 denorm_c = uint4(C);
uint2 denorm_TA = uint2(float2(TA.xy) * 255.0f + 0.5f);
// Mask will take care of the correct destination
if (PS_READ_BA)
C.rb = C.bb;
else
C.rb = C.rr;
if (PS_READ_BA)
if (PS_READ16_SRC)
{
C.rb = (float2)float((denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7u) << 5));
if (denorm_c.a & 0x80u)
C.ga = (float2)(float((denorm_c.a & 0x7Fu) | (denorm_TA.y & 0x80u)));
C.ga = (float2)float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.y & 0x80u));
else
C.ga = (float2)(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
C.ga = (float2)float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u));
}
else
{
if (denorm_c.g & 0x80u)
C.ga = (float2)(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
// Mask will take care of the correct destination
if (PS_READ_BA)
C.rb = C.bb;
else
C.ga = (float2)(float((denorm_c.g & 0x7Fu) | (denorm_TA.x & 0x80u)));
C.rb = C.rr;
if (PS_READ_BA)
{
if (denorm_c.a & 0x80u)
C.ga = (float2)(float((denorm_c.a & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = (float2)(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
}
else
{
if (denorm_c.g & 0x80u)
C.ga = (float2)(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = (float2)(float((denorm_c.g & 0x7Fu) | (denorm_TA.x & 0x80u)));
}
}
}
@@ -908,15 +938,15 @@ PS_OUTPUT ps_main(PS_INPUT input)
C.a = 128.0f;
}
float alpha_blend;
float4 alpha_blend;
if (PS_BLEND_C == 1 && PS_CLR_HW > 3)
{
float4 RT = trunc(RtTexture.Load(int3(input.p.xy, 0)) * 255.0f + 0.1f);
alpha_blend = RT.a / 128.0f;
alpha_blend = (float4)(RT.a / 128.0f);
}
else
{
alpha_blend = C.a / 128.0f;
alpha_blend = (float4)(C.a / 128.0f);
}
// Alpha correction
@@ -966,12 +996,12 @@ PS_OUTPUT ps_main(PS_INPUT input)
#if !PS_NO_COLOR
output.c0 = PS_HDR ? float4(C.rgb / 65535.0f, C.a / 255.0f) : C / 255.0f;
#if !PS_NO_COLOR1
output.c1 = (float4)(alpha_blend);
output.c1 = alpha_blend;
#endif
#if PS_NO_ABLEND
// write alpha blend factor into col0
output.c0.a = alpha_blend;
output.c0.a = alpha_blend.a;
#endif
#if PS_ONLY_ALPHA
// rgb isn't used

View File

@@ -654,7 +654,12 @@ void ps_dither(inout vec3 C)
#else
ivec2 fpos = ivec2(gl_FragCoord.xy / float(PS_SCALE_FACTOR));
#endif
C += DitherMatrix[fpos.y&3][fpos.x&3];
float value = DitherMatrix[fpos.y&3][fpos.x&3];
#if PS_ROUND_INV
C -= value;
#else
C += value;
#endif
#endif
}
@@ -664,6 +669,10 @@ void ps_color_clamp_wrap(inout vec3 C)
// so we need to limit the color depth on dithered items
#if SW_BLEND || PS_DITHER || PS_FBMASK
#if PS_DFMT == FMT_16 && PS_BLEND_MIX == 0
C += 7.f; // Need to round up, not down since the shader will invert
#endif
// Correct the Color value based on the output format
#if PS_COLCLIP == 0 && PS_HDR == 0
// Standard Clamp
@@ -686,8 +695,10 @@ void ps_color_clamp_wrap(inout vec3 C)
#endif
}
void ps_blend(inout vec4 Color, inout float As)
void ps_blend(inout vec4 Color, inout vec4 As_rgba)
{
float As = As_rgba.a;
#if SW_BLEND
// PABE
@@ -766,18 +777,15 @@ void ps_blend(inout vec4 Color, inout float As)
#endif
#if PS_CLR_HW == 1
// Replace Af with As so we can do proper compensation for Alpha.
#if PS_BLEND_C == 2
As = Af;
#endif
// As or Af
As_rgba.rgb = vec3(C);
// Subtract 1 for alpha to compensate for the changed equation,
// if c.rgb > 255.0f then we further need to adjust alpha accordingly,
// we pick the lowest overflow from all colors because it's the safest,
// we divide by 255 the color because we don't know Cd value,
// changed alpha should only be done for hw blend.
float min_color = min(min(Color.r, Color.g), Color.b);
float alpha_compensate = max(1.0f, min_color / 255.0f);
As -= alpha_compensate;
vec3 alpha_compensate = max(vec3(1.0f), Color.rgb / vec3(255.0f));
As_rgba.rgb -= alpha_compensate;
#elif PS_CLR_HW == 2
// Compensate slightly for Cd*(As + 1) - Cs*As.
// The initial factor we chose is 1 (0.00392)
@@ -786,6 +794,14 @@ void ps_blend(inout vec4 Color, inout float As)
// blended value it can be.
float color_compensate = 1.0f * (C + 1.0f);
Color.rgb -= vec3(color_compensate);
#elif PS_CLR_HW == 3
// As, Ad or Af clamped.
As_rgba.rgb = vec3(C_clamped);
// Cs*(Alpha + 1) might overflow, if it does then adjust alpha value
// that is sent on second output to compensate.
vec3 overflow_check = (Color.rgb - vec3(255.0f)) / 255.0f;
vec3 alpha_compensate = max(vec3(0.0f), overflow_check);
As_rgba.rgb -= alpha_compensate;
#endif
#else
@@ -871,7 +887,13 @@ void ps_main()
#if PS_SHUFFLE
uvec4 denorm_c = uvec4(C);
uvec2 denorm_TA = uvec2(vec2(TA.xy) * 255.0f + 0.5f);
#if PS_READ16_SRC
C.rb = vec2(float((denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7u) << 5)));
if (bool(denorm_c.a & 0x80u))
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u)));
#else
// Write RB part. Mask will take care of the correct destination
#if PS_READ_BA
C.rb = C.bb;
@@ -907,9 +929,10 @@ void ps_main()
// float sel = step(128.0f, c.g);
// vec2 c_shuffle = vec2((denorm_c.gg & 0x7Fu) | (denorm_TA & 0x80u));
// c.ga = mix(c_shuffle.xx, c_shuffle.yy, sel);
#endif
#endif // PS_READ_BA
#endif
#endif // READ16_SRC
#endif // PS_SHUFFLE
// Must be done before alpha correction
@@ -920,9 +943,9 @@ void ps_main()
#if SW_AD_TO_HW
vec4 RT = trunc(fetch_rt() * 255.0f + 0.1f);
float alpha_blend = RT.a / 128.0f;
vec4 alpha_blend = vec4(RT.a / 128.0f);
#else
float alpha_blend = C.a / 128.0f;
vec4 alpha_blend = vec4(C.a / 128.0f);
#endif
// Correct the ALPHA value based on the output format
@@ -962,12 +985,12 @@ void ps_main()
SV_Target0 = C / 255.0f;
#endif
#if !defined(DISABLE_DUAL_SOURCE) && !PS_NO_COLOR1
SV_Target1 = vec4(alpha_blend);
SV_Target1 = alpha_blend;
#endif
#if PS_NO_ABLEND
// write alpha blend factor into col0
SV_Target0.a = alpha_blend;
SV_Target0.a = alpha_blend.a;
#endif
#if PS_ONLY_ALPHA
// rgb isn't used

View File

@@ -23,7 +23,17 @@ layout(location = 0) in vec2 v_tex;
#if defined(ps_convert_rgba8_16bits) || defined(ps_convert_float32_32bits)
layout(location = 0) out uint o_col0;
#else
#elif !defined(ps_datm1) && \
!defined(ps_datm0) && \
!defined(ps_convert_rgba8_float32) && \
!defined(ps_convert_rgba8_float24) && \
!defined(ps_convert_rgba8_float16) && \
!defined(ps_convert_rgb5a1_float16) && \
!defined(ps_convert_rgba8_float32_biln) && \
!defined(ps_convert_rgba8_float24_biln) && \
!defined(ps_convert_rgba8_float16_biln) && \
!defined(ps_convert_rgb5a1_float16_biln) && \
!defined(ps_depth_copy)
layout(location = 0) out vec4 o_col0;
#endif
@@ -69,8 +79,6 @@ void ps_convert_rgba8_16bits()
#ifdef ps_datm1
void ps_datm1()
{
o_col0 = vec4(0, 0, 0, 0);
if(sample_c(v_tex).a < (127.5f / 255.0f)) // >= 0x80 pass
discard;
@@ -80,8 +88,6 @@ void ps_datm1()
#ifdef ps_datm0
void ps_datm0()
{
o_col0 = vec4(0, 0, 0, 0);
if((127.5f / 255.0f) < sample_c(v_tex).a) // < 0x80 pass (== 0x80 should not pass)
discard;
}

View File

@@ -328,6 +328,7 @@ void main()
#define PS_POINT_SAMPLER 0
#define PS_SHUFFLE 0
#define PS_READ_BA 0
#define PS_READ16_SRC 0
#define PS_DFMT 0
#define PS_DEPTH_FMT 0
#define PS_PAL_FMT 0
@@ -386,7 +387,7 @@ layout(location = 0) in VSOutput
#if !defined(DISABLE_DUAL_SOURCE) && !PS_NO_COLOR1
layout(location = 0, index = 0) out vec4 o_col0;
layout(location = 0, index = 1) out vec4 o_col1;
#else
#elif !PS_NO_COLOR
layout(location = 0) out vec4 o_col0;
#endif
@@ -437,22 +438,22 @@ vec4 sample_c(vec2 uv)
#endif
#if PS_AUTOMATIC_LOD == 1
return texture(Texture, uv);
return texture(Texture, uv);
#elif PS_MANUAL_LOD == 1
// FIXME add LOD: K - ( LOG2(Q) * (1 << L))
float K = MinMax.x;
float L = MinMax.y;
float bias = MinMax.z;
float max_lod = MinMax.w;
// FIXME add LOD: K - ( LOG2(Q) * (1 << L))
float K = MinMax.x;
float L = MinMax.y;
float bias = MinMax.z;
float max_lod = MinMax.w;
float gs_lod = K - log2(abs(vsIn.t.w)) * L;
// FIXME max useful ?
//float lod = max(min(gs_lod, max_lod) - bias, 0.0f);
float lod = min(gs_lod, max_lod) - bias;
float gs_lod = K - log2(abs(vsIn.t.w)) * L;
// FIXME max useful ?
//float lod = max(min(gs_lod, max_lod) - bias, 0.0f);
float lod = min(gs_lod, max_lod) - bias;
return textureLod(Texture, uv, lod);
return textureLod(Texture, uv, lod);
#else
return textureLod(Texture, uv, 0); // No lod
return textureLod(Texture, uv, 0); // No lod
#endif
#endif
}
@@ -969,105 +970,116 @@ void ps_dither(inout vec3 C)
fpos = ivec2(gl_FragCoord.xy / float(PS_SCALE_FACTOR));
#endif
C += DitherMatrix[fpos.y & 3][fpos.x & 3];
float value = DitherMatrix[fpos.y & 3][fpos.x & 3];
#if PS_ROUND_INV
C -= value;
#else
C += value;
#endif
#endif
}
void ps_color_clamp_wrap(inout vec3 C)
{
// When dithering the bottom 3 bits become meaningless and cause lines in the picture
// so we need to limit the color depth on dithered items
// When dithering the bottom 3 bits become meaningless and cause lines in the picture
// so we need to limit the color depth on dithered items
#if SW_BLEND || PS_DITHER || PS_FBMASK
// Correct the Color value based on the output format
#if PS_COLCLIP == 0 && PS_HDR == 0
// Standard Clamp
C = clamp(C, vec3(0.0f), vec3(255.0f));
#if PS_DFMT == FMT_16 && PS_BLEND_MIX == 0 && PS_ROUND_INV != 0
C += 7.0f; // Need to round up, not down since the shader will invert
#endif
// FIXME rouding of negative float?
// compiler uses trunc but it might need floor
// Correct the Color value based on the output format
#if PS_COLCLIP == 0 && PS_HDR == 0
// Standard Clamp
C = clamp(C, vec3(0.0f), vec3(255.0f));
#endif
// Warning: normally blending equation is mult(A, B) = A * B >> 7. GPU have the full accuracy
// GS: Color = 1, Alpha = 255 => output 1
// GPU: Color = 1/255, Alpha = 255/255 * 255/128 => output 1.9921875
// FIXME rouding of negative float?
// compiler uses trunc but it might need floor
// Warning: normally blending equation is mult(A, B) = A * B >> 7. GPU have the full accuracy
// GS: Color = 1, Alpha = 255 => output 1
// GPU: Color = 1/255, Alpha = 255/255 * 255/128 => output 1.9921875
#if PS_DFMT == FMT_16 && PS_BLEND_MIX == 0
// In 16 bits format, only 5 bits of colors are used. It impacts shadows computation of Castlevania
C = vec3(ivec3(C) & ivec3(0xF8));
// In 16 bits format, only 5 bits of colors are used. It impacts shadows computation of Castlevania
C = vec3(ivec3(C) & ivec3(0xF8));
#elif PS_COLCLIP == 1 || PS_HDR == 1
C = vec3(ivec3(C) & ivec3(0xFF));
C = vec3(ivec3(C) & ivec3(0xFF));
#endif
#endif
}
void ps_blend(inout vec4 Color, inout float As)
void ps_blend(inout vec4 Color, inout vec4 As_rgba)
{
float As = As_rgba.a;
#if SW_BLEND
// PABE
#if PS_PABE
// No blending so early exit
if (As < 1.0f)
return;
// No blending so early exit
if (As < 1.0f)
return;
#endif
#if PS_FEEDBACK_LOOP_IS_NEEDED
vec4 RT = trunc(sample_from_rt() * 255.0f + 0.1f);
vec4 RT = trunc(sample_from_rt() * 255.0f + 0.1f);
#else
// Not used, but we define it to make the selection below simpler.
vec4 RT = vec4(0.0f);
// Not used, but we define it to make the selection below simpler.
vec4 RT = vec4(0.0f);
#endif
// FIXME FMT_16 case
// FIXME Ad or Ad * 2?
float Ad = RT.a / 128.0f;
// FIXME FMT_16 case
// FIXME Ad or Ad * 2?
float Ad = RT.a / 128.0f;
// Let the compiler do its jobs !
vec3 Cd = RT.rgb;
vec3 Cs = Color.rgb;
// Let the compiler do its jobs !
vec3 Cd = RT.rgb;
vec3 Cs = Color.rgb;
#if PS_BLEND_A == 0
vec3 A = Cs;
vec3 A = Cs;
#elif PS_BLEND_A == 1
vec3 A = Cd;
vec3 A = Cd;
#else
vec3 A = vec3(0.0f);
vec3 A = vec3(0.0f);
#endif
#if PS_BLEND_B == 0
vec3 B = Cs;
vec3 B = Cs;
#elif PS_BLEND_B == 1
vec3 B = Cd;
vec3 B = Cd;
#else
vec3 B = vec3(0.0f);
vec3 B = vec3(0.0f);
#endif
#if PS_BLEND_C == 0
float C = As;
float C = As;
#elif PS_BLEND_C == 1
float C = Ad;
float C = Ad;
#else
float C = Af;
float C = Af;
#endif
#if PS_BLEND_D == 0
vec3 D = Cs;
vec3 D = Cs;
#elif PS_BLEND_D == 1
vec3 D = Cd;
vec3 D = Cd;
#else
vec3 D = vec3(0.0f);
vec3 D = vec3(0.0f);
#endif
// As/Af clamp alpha for Blend mix
// We shouldn't clamp blend mix with clr1 as we want alpha higher
float C_clamped = C;
#if PS_BLEND_MIX > 0 && PS_CLR_HW != 1
C_clamped = min(C_clamped, 1.0f);
C_clamped = min(C_clamped, 1.0f);
#endif
#if PS_BLEND_A == PS_BLEND_B
Color.rgb = D;
Color.rgb = D;
// In blend_mix, HW adds on some alpha factor * dst.
// Truncating here wouldn't quite get the right result because it prevents the <1 bit here from combining with a <1 bit in dst to form a ≥1 amount that pushes over the truncation.
// Instead, apply an offset to convert HW's round to a floor.
@@ -1084,26 +1096,31 @@ void ps_blend(inout vec4 Color, inout float As)
#endif
#if PS_CLR_HW == 1
// Replace Af with As so we can do proper compensation for Alpha.
#if PS_BLEND_C == 2
As = Af;
#endif
// Subtract 1 for alpha to compensate for the changed equation,
// if c.rgb > 255.0f then we further need to adjust alpha accordingly,
// we pick the lowest overflow from all colors because it's the safest,
// we divide by 255 the color because we don't know Cd value,
// changed alpha should only be done for hw blend.
float min_color = min(min(Color.r, Color.g), Color.b);
float alpha_compensate = max(1.0f, min_color / 255.0f);
As -= alpha_compensate;
// As or Af
As_rgba.rgb = vec3(C);
// Subtract 1 for alpha to compensate for the changed equation,
// if c.rgb > 255.0f then we further need to adjust alpha accordingly,
// we pick the lowest overflow from all colors because it's the safest,
// we divide by 255 the color because we don't know Cd value,
// changed alpha should only be done for hw blend.
vec3 alpha_compensate = max(vec3(1.0f), Color.rgb / vec3(255.0f));
As_rgba.rgb -= alpha_compensate;
#elif PS_CLR_HW == 2
// Compensate slightly for Cd*(As + 1) - Cs*As.
// The initial factor we chose is 1 (0.00392)
// as that is the minimum color Cd can be,
// then we multiply by alpha to get the minimum
// blended value it can be.
float color_compensate = 1.0f * (C + 1.0f);
Color.rgb -= vec3(color_compensate);
// Compensate slightly for Cd*(As + 1) - Cs*As.
// The initial factor we chose is 1 (0.00392)
// as that is the minimum color Cd can be,
// then we multiply by alpha to get the minimum
// blended value it can be.
float color_compensate = 1.0f * (C + 1.0f);
Color.rgb -= vec3(color_compensate);
#elif PS_CLR_HW == 3
// As, Ad or Af clamped.
As_rgba.rgb = vec3(C_clamped);
// Cs*(Alpha + 1) might overflow, if it does then adjust alpha value
// that is sent on second output to compensate.
vec3 overflow_check = (Color.rgb - vec3(255.0f)) / 255.0f;
vec3 alpha_compensate = max(vec3(0.0f), overflow_check);
As_rgba.rgb -= alpha_compensate;
#endif
#else
@@ -1134,40 +1151,40 @@ void main()
{
#if PS_SCANMSK & 2
// fail depth test on prohibited lines
if ((int(gl_FragCoord.y) & 1) == (PS_SCANMSK & 1))
if ((int(gl_FragCoord.y) & 1) == (PS_SCANMSK & 1))
discard;
#endif
#if PS_DATE >= 5
#if PS_WRITE_RG == 1
// Pseudo 16 bits access.
float rt_a = sample_from_rt().g;
// Pseudo 16 bits access.
float rt_a = sample_from_rt().g;
#else
float rt_a = sample_from_rt().a;
float rt_a = sample_from_rt().a;
#endif
#if (PS_DATE & 3) == 1
// DATM == 0: Pixel with alpha equal to 1 will failed
bool bad = (127.5f / 255.0f) < rt_a;
// DATM == 0: Pixel with alpha equal to 1 will failed
bool bad = (127.5f / 255.0f) < rt_a;
#elif (PS_DATE & 3) == 2
// DATM == 1: Pixel with alpha equal to 0 will failed
bool bad = rt_a < (127.5f / 255.0f);
// DATM == 1: Pixel with alpha equal to 0 will failed
bool bad = rt_a < (127.5f / 255.0f);
#endif
if (bad) {
discard;
}
if (bad) {
discard;
}
#endif // PS_DATE >= 5
#if PS_DATE == 3
int stencil_ceil = int(texelFetch(PrimMinTexture, ivec2(gl_FragCoord.xy), 0).r);
// Note gl_PrimitiveID == stencil_ceil will be the primitive that will update
// the bad alpha value so we must keep it.
int stencil_ceil = int(texelFetch(PrimMinTexture, ivec2(gl_FragCoord.xy), 0).r);
// Note gl_PrimitiveID == stencil_ceil will be the primitive that will update
// the bad alpha value so we must keep it.
if (gl_PrimitiveID > stencil_ceil) {
discard;
}
if (gl_PrimitiveID > stencil_ceil) {
discard;
}
#endif
vec4 C = ps_color();
@@ -1175,98 +1192,105 @@ void main()
#if PS_SHUFFLE
uvec4 denorm_c = uvec4(C);
uvec2 denorm_TA = uvec2(vec2(TA.xy) * 255.0f + 0.5f);
// Mask will take care of the correct destination
#if PS_READ_BA
C.rb = C.bb;
#else
C.rb = C.rr;
#endif
#if PS_READ_BA
#if PS_READ16_SRC
C.rb = vec2(float((denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7u) << 5)));
if ((denorm_c.a & 0x80u) != 0u)
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.y & 0x80u)));
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u)));
#else
if ((denorm_c.g & 0x80u) != 0u)
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.x & 0x80u)));
// Mask will take care of the correct destination
#if PS_READ_BA
C.rb = C.bb;
#else
C.rb = C.rr;
#endif
#if PS_READ_BA
if ((denorm_c.a & 0x80u) != 0u)
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.a & 0x7Fu) | (denorm_TA.x & 0x80u)));
#else
if ((denorm_c.g & 0x80u) != 0u)
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.y & 0x80u)));
else
C.ga = vec2(float((denorm_c.g & 0x7Fu) | (denorm_TA.x & 0x80u)));
#endif
#endif
#endif
// Must be done before alpha correction
// Must be done before alpha correction
// AA (Fixed one) will output a coverage of 1.0 as alpha
// AA (Fixed one) will output a coverage of 1.0 as alpha
#if PS_FIXED_ONE_A
C.a = 128.0f;
C.a = 128.0f;
#endif
#if (PS_BLEND_C == 1 && PS_CLR_HW > 3)
vec4 RT = trunc(subpassLoad(RtSampler) * 255.0f + 0.1f);
float alpha_blend = RT.a / 128.0f;
vec4 RT = trunc(subpassLoad(RtSampler) * 255.0f + 0.1f);
vec4 alpha_blend = vec4(RT.a / 128.0f);
#else
float alpha_blend = C.a / 128.0f;
vec4 alpha_blend = vec4(C.a / 128.0f);
#endif
// Correct the ALPHA value based on the output format
#if (PS_DFMT == FMT_16)
float A_one = 128.0f; // alpha output will be 0x80
C.a = (PS_FBA != 0) ? A_one : step(128.0f, C.a) * A_one;
float A_one = 128.0f; // alpha output will be 0x80
C.a = (PS_FBA != 0) ? A_one : step(128.0f, C.a) * A_one;
#elif (PS_DFMT == FMT_32) && (PS_FBA != 0)
if(C.a < 128.0f) C.a += 128.0f;
if(C.a < 128.0f) C.a += 128.0f;
#endif
// Get first primitive that will write a failling alpha value
// Get first primitive that will write a failling alpha value
#if PS_DATE == 1
// DATM == 0
// Pixel with alpha equal to 1 will failed (128-255)
// DATM == 0
// Pixel with alpha equal to 1 will failed (128-255)
o_col0 = (C.a > 127.5f) ? vec4(gl_PrimitiveID) : vec4(0x7FFFFFFF);
#elif PS_DATE == 2
// DATM == 1
// Pixel with alpha equal to 0 will failed (0-127)
o_col0 = (C.a < 127.5f) ? vec4(gl_PrimitiveID) : vec4(0x7FFFFFFF);
// DATM == 1
// Pixel with alpha equal to 0 will failed (0-127)
o_col0 = (C.a < 127.5f) ? vec4(gl_PrimitiveID) : vec4(0x7FFFFFFF);
#else
ps_blend(C, alpha_blend);
ps_dither(C.rgb);
ps_dither(C.rgb);
// Color clamp/wrap needs to be done after sw blending and dithering
ps_color_clamp_wrap(C.rgb);
// Color clamp/wrap needs to be done after sw blending and dithering
ps_color_clamp_wrap(C.rgb);
ps_fbmask(C);
ps_fbmask(C);
#if !PS_NO_COLOR
#if PS_HDR == 1
o_col0 = vec4(C.rgb / 65535.0f, C.a / 255.0f);
#else
o_col0 = C / 255.0f;
#endif
#if !defined(DISABLE_DUAL_SOURCE) && !PS_NO_COLOR1
o_col1 = vec4(alpha_blend);
#endif
#if !PS_NO_COLOR
#if PS_HDR == 1
o_col0 = vec4(C.rgb / 65535.0f, C.a / 255.0f);
#else
o_col0 = C / 255.0f;
#endif
#if !defined(DISABLE_DUAL_SOURCE) && !PS_NO_COLOR1
o_col1 = alpha_blend;
#endif
#if PS_NO_ABLEND
// write alpha blend factor into col0
o_col0.a = alpha_blend;
#endif
#if PS_ONLY_ALPHA
// rgb isn't used
o_col0.rgb = vec3(0.0f);
#endif
#endif
#if PS_NO_ABLEND
// write alpha blend factor into col0
o_col0.a = alpha_blend.a;
#endif
#if PS_ONLY_ALPHA
// rgb isn't used
o_col0.rgb = vec3(0.0f);
#endif
#endif
#if PS_ZCLAMP
gl_FragDepth = min(gl_FragCoord.z, MaxDepthPS);
#endif
#if PS_ZCLAMP
gl_FragDepth = min(gl_FragCoord.z, MaxDepthPS);
#endif
#endif // PS_DATE
#endif // PS_DATE
}
#endif

View File

@@ -10,8 +10,9 @@ function(detectOperatingSystem)
if(WIN32)
set(Windows TRUE PARENT_SCOPE)
elseif(UNIX AND APPLE)
# No easy way to filter out iOS.
message(WARNING "OS X/iOS isn't supported, the build will most likely fail")
if(IOS)
message(WARNING "iOS isn't supported, the build will most likely fail")
endif()
set(MacOSX TRUE PARENT_SCOPE)
elseif(UNIX)
if(CMAKE_SYSTEM_NAME MATCHES "Linux")

View File

@@ -445,7 +445,7 @@ ID3D12GraphicsCommandList4* Context::GetInitCommandList()
return res.command_lists[0].get();
}
void Context::ExecuteCommandList(WaitType wait_for_completion)
bool Context::ExecuteCommandList(WaitType wait_for_completion)
{
CommandListResources& res = m_command_lists[m_current_command_list];
HRESULT hr;
@@ -463,12 +463,21 @@ void Context::ExecuteCommandList(WaitType wait_for_completion)
if (res.init_command_list_used)
{
hr = res.command_lists[0]->Close();
pxAssertRel(SUCCEEDED(hr), "Close init command list");
if (FAILED(hr))
{
Console.Error("Closing init command list failed with HRESULT %08X", hr);
return false;
}
}
// Close and queue command list.
hr = res.command_lists[1]->Close();
pxAssertRel(SUCCEEDED(hr), "Close command list");
if (FAILED(hr))
{
Console.Error("Closing main command list failed with HRESULT %08X", hr);
return false;
}
if (res.init_command_list_used)
{
const std::array<ID3D12CommandList*, 2> execute_lists{res.command_lists[0].get(), res.command_lists[1].get()};
@@ -487,6 +496,8 @@ void Context::ExecuteCommandList(WaitType wait_for_completion)
MoveToNextCommandList();
if (wait_for_completion != WaitType::None)
WaitForFence(res.ready_fence_value, wait_for_completion == WaitType::Spin);
return true;
}
void Context::InvalidateSamplerGroups()

View File

@@ -130,7 +130,7 @@ namespace D3D12
};
/// Executes the current command list.
void ExecuteCommandList(WaitType wait_for_completion);
bool ExecuteCommandList(WaitType wait_for_completion);
/// Waits for a specific fence.
void WaitForFence(u64 fence, bool spin);

View File

@@ -119,7 +119,7 @@ namespace GL
if (!context)
return nullptr;
Console.WriteLn("Created a %s context", context->IsGLES() ? "OpenGL ES" : "OpenGL");
Console.WriteLn("Created an %s context", context->IsGLES() ? "OpenGL ES" : "OpenGL");
// NOTE: Not thread-safe. But this is okay, since we're not going to be creating more than one context at a time.
static Context* context_being_created;

View File

@@ -100,6 +100,8 @@ static void SysPageFaultSignalFilter(int signal, siginfo_t* siginfo, void* ctx)
#if defined(__APPLE__) && defined(__x86_64__)
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext->__ss.__rip);
#elif defined(__FreeBSD__) && defined(__x86_64__)
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext.mc_rip);
#elif defined(__x86_64__)
void* const exception_pc = reinterpret_cast<void*>(static_cast<ucontext_t*>(ctx)->uc_mcontext.gregs[REG_RIP]);
#else

View File

@@ -197,9 +197,9 @@ namespace Vulkan
{
u32 gpu_count = 0;
VkResult res = vkEnumeratePhysicalDevices(instance, &gpu_count, nullptr);
if (res != VK_SUCCESS || gpu_count == 0)
if ((res != VK_SUCCESS && res != VK_INCOMPLETE) || gpu_count == 0)
{
LOG_VULKAN_ERROR(res, "vkEnumeratePhysicalDevices failed: ");
LOG_VULKAN_ERROR(res, "vkEnumeratePhysicalDevices (1) failed: ");
return {};
}
@@ -207,12 +207,20 @@ namespace Vulkan
gpus.resize(gpu_count);
res = vkEnumeratePhysicalDevices(instance, &gpu_count, gpus.data());
if (res != VK_SUCCESS)
if (res == VK_INCOMPLETE)
{
LOG_VULKAN_ERROR(res, "vkEnumeratePhysicalDevices failed: ");
Console.Warning("First vkEnumeratePhysicalDevices() call returned %zu devices, but second returned %u", gpus.size(), gpu_count);
}
else if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkEnumeratePhysicalDevices (2) failed: ");
return {};
}
// Maybe we lost a GPU?
if (gpu_count < gpus.size())
gpus.resize(gpu_count);
return gpus;
}
@@ -1122,9 +1130,13 @@ namespace Vulkan
void Context::WaitForCommandBufferCompletion(u32 index)
{
// Wait for this command buffer to be completed.
VkResult res = vkWaitForFences(m_device, 1, &m_frame_resources[index].fence, VK_TRUE, UINT64_MAX);
const VkResult res = vkWaitForFences(m_device, 1, &m_frame_resources[index].fence, VK_TRUE, UINT64_MAX);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkWaitForFences failed: ");
m_last_submit_failed.store(true, std::memory_order_release);
return;
}
// Clean up any resources for command buffers between the last known completed buffer and this
// now-completed command buffer. If we use >2 buffers, this may be more than one buffer.
@@ -1266,11 +1278,12 @@ namespace Vulkan
submit_info.pSignalSemaphores = &m_spin_resources[index].semaphore;
}
VkResult res = vkQueueSubmit(m_graphics_queue, 1, &submit_info, resources.fence);
const VkResult res = vkQueueSubmit(m_graphics_queue, 1, &submit_info, resources.fence);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkQueueSubmit failed: ");
pxFailRel("Failed to submit command buffer.");
m_last_submit_failed.store(true, std::memory_order_release);
return;
}
if (spin_cycles != 0)
@@ -1286,14 +1299,14 @@ namespace Vulkan
present_swap_chain->ReleaseCurrentImage();
VkResult res = vkQueuePresentKHR(m_present_queue, &present_info);
const VkResult res = vkQueuePresentKHR(m_present_queue, &present_info);
if (res != VK_SUCCESS)
{
// VK_ERROR_OUT_OF_DATE_KHR is not fatal, just means we need to recreate our swap chain.
if (res != VK_ERROR_OUT_OF_DATE_KHR && res != VK_SUBOPTIMAL_KHR)
LOG_VULKAN_ERROR(res, "vkQueuePresentKHR failed: ");
m_last_present_failed.store(true);
m_last_present_failed.store(true, std::memory_order_release);
return;
}
@@ -1460,6 +1473,9 @@ namespace Vulkan
void Context::ExecuteCommandBuffer(WaitType wait_for_completion)
{
if (m_last_submit_failed.load(std::memory_order_acquire))
return;
// If we're waiting for completion, don't bother waking the worker thread.
const u32 current_frame = m_current_frame;
SubmitCommandBuffer();
@@ -1481,9 +1497,12 @@ namespace Vulkan
bool Context::CheckLastPresentFail()
{
bool res = m_last_present_failed;
m_last_present_failed = false;
return res;
return m_last_present_failed.exchange(false, std::memory_order_acq_rel);
}
bool Context::CheckLastSubmitFail()
{
return m_last_submit_failed.load(std::memory_order_acquire);
}
void Context::DeferBufferDestruction(VkBuffer object)
@@ -1596,7 +1615,7 @@ namespace Vulkan
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
DebugMessengerCallback, nullptr};
VkResult res =
const VkResult res =
vkCreateDebugUtilsMessengerEXT(m_instance, &messenger_info, nullptr, &m_debug_messenger_callback);
if (res != VK_SUCCESS)
{
@@ -1688,7 +1707,7 @@ namespace Vulkan
subpass_dependency_ptr};
VkRenderPass pass;
VkResult res = vkCreateRenderPass(m_device, &pass_info, nullptr, &pass);
const VkResult res = vkCreateRenderPass(m_device, &pass_info, nullptr, &pass);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateRenderPass failed: ");
@@ -1894,9 +1913,14 @@ void main()
SpinResources& resources = m_spin_resources[index];
if (!resources.in_progress)
return;
VkResult res = vkWaitForFences(m_device, 1, &resources.fence, VK_TRUE, UINT64_MAX);
const VkResult res = vkWaitForFences(m_device, 1, &resources.fence, VK_TRUE, UINT64_MAX);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkWaitForFences failed: ");
m_last_submit_failed.store(true, std::memory_order_release);
return;
}
SpinCommandCompleted(index);
}
@@ -1906,7 +1930,7 @@ void main()
resources.in_progress = false;
const u32 timestamp_base = (index + NUM_COMMAND_BUFFERS) * 2;
std::array<u64, 2> timestamps;
VkResult res = vkGetQueryPoolResults(m_device, m_timestamp_query_pool, timestamp_base, static_cast<u32>(timestamps.size()),
const VkResult res = vkGetQueryPoolResults(m_device, m_timestamp_query_pool, timestamp_base, static_cast<u32>(timestamps.size()),
sizeof(timestamps), timestamps.data(), sizeof(u64), VK_QUERY_RESULT_64_BIT);
if (res == VK_SUCCESS)
{
@@ -2014,7 +2038,7 @@ void main()
constexpr u64 MAX_MAX_DEVIATION = 100000; // 100µs
for (int i = 0; i < 4; i++) // 4 tries to get under MAX_MAX_DEVIATION
{
VkResult res = vkGetCalibratedTimestampsEXT(m_device, std::size(infos), infos, timestamps, &maxDeviation);
const VkResult res = vkGetCalibratedTimestampsEXT(m_device, std::size(infos), infos, timestamps, &maxDeviation);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkGetCalibratedTimestampsEXT failed: ");

View File

@@ -209,6 +209,7 @@ namespace Vulkan
// Was the last present submitted to the queue a failure? If so, we must recreate our swapchain.
bool CheckLastPresentFail();
bool CheckLastSubmitFail();
// Schedule a vulkan resource for destruction later on. This will occur when the command buffer
// is next re-used, and the GPU has finished working with the specified resource.
@@ -373,6 +374,7 @@ namespace Vulkan
StreamBuffer m_texture_upload_buffer;
std::atomic_bool m_last_submit_failed{false};
std::atomic_bool m_last_present_failed{false};
std::atomic_bool m_present_done{true};
std::mutex m_present_mutex;

View File

@@ -280,7 +280,7 @@ void Host::ReleaseHostDisplay(bool clear_state)
g_host_display.reset();
}
bool Host::BeginPresentFrame(bool frame_skip)
HostDisplay::PresentResult Host::BeginPresentFrame(bool frame_skip)
{
if (s_loop_number == 0)
{
@@ -291,12 +291,15 @@ bool Host::BeginPresentFrame(bool frame_skip)
std::string dump_path(fmt::format("{}_frame{}.png", s_output_prefix, s_dump_frame_number));
GSQueueSnapshot(dump_path);
}
if (g_host_display->BeginPresent(frame_skip))
return true;
// don't render imgui
ImGuiManager::NewFrame();
return false;
const HostDisplay::PresentResult result = g_host_display->BeginPresent(frame_skip);
if (result != HostDisplay::PresentResult::OK)
{
// don't render imgui
ImGuiManager::SkipFrame();
}
return result;
}
void Host::EndPresentFrame()

View File

@@ -495,6 +495,16 @@ void AutoUpdaterDialog::checkIfUpdateNeeded()
Console.WriteLn(Color_StrongRed, "Update needed.");
// Don't show the dialog if a game started while the update info was downloading. Some people have
// really slow connections, apparently. If we're a manual triggered update check, then display
// regardless. This will fall through and signal main to delete us.
if (!m_display_messages &&
(QtHost::IsVMValid() || (g_emu_thread->isRunningFullscreenUI() && g_emu_thread->isFullscreen())))
{
Console.WriteLn(Color_StrongRed, "Not showing update dialog due to active VM.");
return;
}
m_ui.currentVersion->setText(tr("Current Version: %1 (%2)").arg(getCurrentVersion()).arg(getCurrentVersionDate()));
m_ui.newVersion->setText(tr("New Version: %1 (%2)").arg(m_latest_version).arg(m_latest_version_timestamp.toString()));
m_ui.updateNotes->setText(tr("Loading..."));

View File

@@ -58,6 +58,7 @@ target_sources(pcsx2-qt PRIVATE
Settings/ControllerGlobalSettingsWidget.ui
Settings/ControllerMacroEditWidget.ui
Settings/ControllerMacroWidget.ui
Settings/ControllerMouseSettingsDialog.ui
Settings/ControllerSettingsDialog.cpp
Settings/ControllerSettingsDialog.h
Settings/ControllerSettingsDialog.ui

View File

@@ -254,13 +254,17 @@ void MainWindow::setupAdditionalUi()
m_status_vps_widget->setFixedSize(125, 16);
m_status_vps_widget->hide();
m_settings_toolbar_menu = new QMenu(m_ui.toolBar);
m_settings_toolbar_menu->addAction(m_ui.actionSettings);
m_settings_toolbar_menu->addAction(m_ui.actionViewGameProperties);
for (u32 scale = 0; scale <= 10; scale++)
{
QAction* action = m_ui.menuWindowSize->addAction((scale == 0) ? tr("Internal Resolution") : tr("%1x Scale").arg(scale));
connect(action, &QAction::triggered, [scale]() { g_emu_thread->requestDisplaySize(static_cast<float>(scale)); });
}
updateEmulationActions(false, false);
updateEmulationActions(false, false, false);
updateDisplayRelatedActions(false, false, false);
#ifdef ENABLE_RAINTEGRATION
@@ -316,6 +320,7 @@ void MainWindow::connectSignals()
connect(m_ui.menuLoadState, &QMenu::aboutToShow, this, &MainWindow::onLoadStateMenuAboutToShow);
connect(m_ui.menuSaveState, &QMenu::aboutToShow, this, &MainWindow::onSaveStateMenuAboutToShow);
connect(m_ui.actionSettings, &QAction::triggered, [this]() { doSettings(); });
connect(m_ui.actionSettings2, &QAction::triggered, this, &MainWindow::onSettingsTriggeredFromToolbar);
connect(m_ui.actionInterfaceSettings, &QAction::triggered, [this]() { doSettings("Interface"); });
connect(m_ui.actionGameListSettings, &QAction::triggered, [this]() { doSettings("Game List"); });
connect(m_ui.actionEmulationSettings, &QAction::triggered, [this]() { doSettings("Emulation"); });
@@ -737,6 +742,41 @@ void MainWindow::setStyleFromSettings()
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "CobaltSky")
{
// Custom palette by KamFretoZ, A soothing deep royal blue
// that are meant to be easy on the eyes as the main color.
// Alternative dark theme.
qApp->setStyle(QStyleFactory::create("Fusion"));
const QColor gray(192, 192, 192);
const QColor royalBlue(29, 41, 81);
const QColor darkishBlue(17, 30, 108);
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, royalBlue);
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, royalBlue.lighter());
darkPalette.setColor(QPalette::AlternateBase, royalBlue);
darkPalette.setColor(QPalette::ToolTipBase, darkishBlue);
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Button, royalBlue.darker());
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::Link, Qt::white);
darkPalette.setColor(QPalette::Highlight, darkishBlue.lighter());
darkPalette.setColor(QPalette::HighlightedText, Qt::white);
darkPalette.setColor(QPalette::Active, QPalette::Button, darkishBlue);
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, gray);
darkPalette.setColor(QPalette::Disabled, QPalette::Light, gray.darker());
qApp->setPalette(darkPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #2a82da; border: 1px solid white; }");
}
else if (theme == "VioletAngelPurple")
{
// Custom palette by RedDevilus, Blue as main color and Purple as complimentary.
@@ -978,32 +1018,45 @@ void MainWindow::onToolsVideoCaptureToggled(bool checked)
g_emu_thread->beginCapture(path);
}
void MainWindow::onSettingsTriggeredFromToolbar()
{
if (s_vm_valid)
{
m_settings_toolbar_menu->exec(QCursor::pos());
}
else
{
doSettings();
}
}
void MainWindow::saveStateToConfig()
{
if (!isVisible())
return;
bool changed = false;
const QByteArray geometry(saveGeometry());
const QByteArray geometry_b64(geometry.toBase64());
const std::string old_geometry_b64(Host::GetBaseStringSettingValue("UI", "MainWindowGeometry"));
if (old_geometry_b64 != geometry_b64.constData())
{
const QByteArray geometry = saveGeometry();
const QByteArray geometry_b64 = geometry.toBase64();
const std::string old_geometry_b64 = Host::GetBaseStringSettingValue("UI", "MainWindowGeometry");
if (old_geometry_b64 != geometry_b64.constData())
{
Host::SetBaseStringSettingValue("UI", "MainWindowGeometry", geometry_b64.constData());
Host::CommitBaseSettingChanges();
}
Host::SetBaseStringSettingValue("UI", "MainWindowGeometry", geometry_b64.constData());
changed = true;
}
const QByteArray state(saveState());
const QByteArray state_b64(state.toBase64());
const std::string old_state_b64(Host::GetBaseStringSettingValue("UI", "MainWindowState"));
if (old_state_b64 != state_b64.constData())
{
const QByteArray state = saveState();
const QByteArray state_b64 = state.toBase64();
const std::string old_state_b64 = Host::GetBaseStringSettingValue("UI", "MainWindowState");
if (old_state_b64 != state_b64.constData())
{
Host::SetBaseStringSettingValue("UI", "MainWindowState", state_b64.constData());
Host::CommitBaseSettingChanges();
}
Host::SetBaseStringSettingValue("UI", "MainWindowState", state_b64.constData());
changed = true;
}
if (changed)
Host::CommitBaseSettingChanges();
}
void MainWindow::restoreStateFromConfig()
@@ -1032,13 +1085,13 @@ void MainWindow::restoreStateFromConfig()
}
}
void MainWindow::updateEmulationActions(bool starting, bool running)
void MainWindow::updateEmulationActions(bool starting, bool running, bool stopping)
{
const bool starting_or_running = starting || running;
m_ui.actionStartFile->setDisabled(starting_or_running);
m_ui.actionStartDisc->setDisabled(starting_or_running);
m_ui.actionStartBios->setDisabled(starting_or_running);
m_ui.actionStartFile->setDisabled(starting_or_running || stopping);
m_ui.actionStartDisc->setDisabled(starting_or_running || stopping);
m_ui.actionStartBios->setDisabled(starting_or_running || stopping);
m_ui.actionPowerOff->setEnabled(running);
m_ui.actionPowerOffWithoutSaving->setEnabled(running);
@@ -1063,8 +1116,8 @@ void MainWindow::updateEmulationActions(bool starting, bool running)
m_ui.actionPause->setChecked(false);
// scanning needs to be disabled while running
m_ui.actionScanForNewGames->setDisabled(starting_or_running);
m_ui.actionRescanAllGames->setDisabled(starting_or_running);
m_ui.actionScanForNewGames->setDisabled(starting_or_running || stopping);
m_ui.actionRescanAllGames->setDisabled(starting_or_running || stopping);
}
void MainWindow::updateDisplayRelatedActions(bool has_surface, bool render_to_main, bool fullscreen)
@@ -1318,6 +1371,14 @@ bool MainWindow::requestShutdown(bool allow_confirm, bool allow_save_to_state, b
if (!isRenderingToMain() && isHidden() && !QtHost::InBatchMode() && !g_emu_thread->isRunningFullscreenUI())
updateWindowState(true);
// Clear the VM valid state early. That way we can't do anything in the UI if we take a while to shut down.
if (s_vm_valid)
{
s_vm_valid = false;
updateEmulationActions(false, false, true);
updateDisplayRelatedActions(false, false, false);
}
// Now we can actually shut down the VM.
g_emu_thread->shutdownVM(save_state);
return true;
@@ -1863,7 +1924,7 @@ void MainWindow::onInputRecOpenViewer()
void MainWindow::onVMStarting()
{
s_vm_valid = true;
updateEmulationActions(true, false);
updateEmulationActions(true, false, false);
updateWindowTitle();
// prevent loading state until we're fully initialized
@@ -1874,7 +1935,7 @@ void MainWindow::onVMStarted()
{
s_vm_valid = true;
m_was_disc_change_request = false;
updateEmulationActions(true, true);
updateEmulationActions(true, true, false);
updateWindowTitle();
updateStatusBarWidgetVisibility();
updateInputRecordingActions(true);
@@ -1923,7 +1984,7 @@ void MainWindow::onVMStopped()
s_vm_valid = false;
s_vm_paused = false;
m_last_fps_status = QString();
updateEmulationActions(false, false);
updateEmulationActions(false, false, false);
updateWindowTitle();
updateWindowState();
updateStatusBarWidgetVisibility();
@@ -1979,6 +2040,7 @@ void MainWindow::closeEvent(QCloseEvent* event)
// If there's no VM, we can just exit as normal.
if (!s_vm_valid)
{
saveStateToConfig();
QMainWindow::closeEvent(event);
return;
}
@@ -1992,7 +2054,6 @@ void MainWindow::closeEvent(QCloseEvent* event)
return;
// Application will be exited in VM stopped handler.
saveStateToConfig();
m_is_closing = true;
}
@@ -2004,7 +2065,7 @@ static QString getFilenameFromMimeData(const QMimeData* md)
// only one url accepted
const QList<QUrl> urls(md->urls());
if (urls.size() == 1)
filename = urls.front().toLocalFile();
filename = QDir::toNativeSeparators(urls.front().toLocalFile());
}
return filename;
@@ -2118,7 +2179,10 @@ DisplayWidget* MainWindow::createDisplay(bool fullscreen, bool render_to_main)
if (!g_host_display->CreateDevice(wi.value(), Host::GetEffectiveVSyncMode()))
{
QMessageBox::critical(this, tr("Error"), tr("Failed to create host display device context."));
QMessageBox::critical(this, tr("Error"),
tr("Failed to create host display device. This may be due to your GPU not supporting the chosen renderer (%1), or because your "
"graphics drivers need to be updated.")
.arg(QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(EmuConfig.GS.Renderer))));
destroyDisplayWidget(true);
return nullptr;
}
@@ -2846,6 +2910,9 @@ void MainWindow::doStartFile(std::optional<CDVD_SourceType> source, const QStrin
// we might still be saving a resume state...
VMManager::WaitForSaveStateFlush();
// GetSaveStateFileName() might temporarily mount the ISO to get the serial.
cancelGameListRefresh();
const std::optional<bool> resume(
promptForResumeState(QString::fromStdString(VMManager::GetSaveStateFileName(params->filename.c_str(), -1))));
if (!resume.has_value())
@@ -2858,11 +2925,23 @@ void MainWindow::doStartFile(std::optional<CDVD_SourceType> source, const QStrin
void MainWindow::doDiscChange(CDVD_SourceType source, const QString& path)
{
const bool is_gs_dump = VMManager::IsGSDumpFileName(path.toStdString());
if (is_gs_dump != GSDumpReplayer::IsReplayingDump())
{
QMessageBox::critical(this, tr("Error"), tr("Cannot switch from game to GS dump or vice versa."));
return;
}
else if (is_gs_dump)
{
Host::RunOnCPUThread([path = path.toStdString()]() { GSDumpReplayer::ChangeDump(path.c_str()); });
return;
}
bool reset_system = false;
if (!m_was_disc_change_request)
{
QMessageBox message(QMessageBox::Question, tr("Confirm Disc Change"),
tr("Do you want to swap discs or boot the new image (via system reset)?"));
tr("Do you want to swap discs or boot the new image (via system reset)?"), QMessageBox::NoButton, this);
message.addButton(tr("Swap Disc"), QMessageBox::ActionRole);
QPushButton* reset_button = message.addButton(tr("Reset"), QMessageBox::ActionRole);
QPushButton* cancel_button = message.addButton(QMessageBox::Cancel);

View File

@@ -19,6 +19,7 @@
#include <QtWidgets/QLabel>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenu>
#include <functional>
#include <optional>
@@ -170,6 +171,7 @@ private Q_SLOTS:
void onBlockDumpActionToggled(bool checked);
void onShowAdvancedSettingsToggled(bool checked);
void onToolsVideoCaptureToggled(bool checked);
void onSettingsTriggeredFromToolbar();
// Input Recording
void onInputRecNewActionTriggered();
@@ -211,7 +213,7 @@ private:
void saveStateToConfig();
void restoreStateFromConfig();
void updateEmulationActions(bool starting, bool running);
void updateEmulationActions(bool starting, bool running, bool stopping);
void updateDisplayRelatedActions(bool has_surface, bool render_to_main, bool fullscreen);
void updateStatusBarWidgetVisibility();
void updateWindowTitle();
@@ -283,6 +285,8 @@ private:
QLabel* m_status_vps_widget = nullptr;
QLabel* m_status_resolution_widget = nullptr;
QMenu* m_settings_toolbar_menu = nullptr;
QString m_current_disc_path;
QString m_current_elf_override;
QString m_current_game_serial;

View File

@@ -79,13 +79,14 @@
<addaction name="menuLoadState"/>
<addaction name="menuSaveState"/>
<addaction name="separator"/>
<addaction name="actionSettings"/>
<addaction name="actionExit"/>
</widget>
<widget class="QMenu" name="menuSettings">
<property name="title">
<string>S&amp;ettings</string>
</property>
<addaction name="actionViewGameProperties"/>
<addaction name="separator"/>
<addaction name="actionInterfaceSettings"/>
<addaction name="actionGameListSettings"/>
<addaction name="actionBIOSSettings"/>
@@ -156,7 +157,6 @@
<addaction name="actionViewGameList"/>
<addaction name="actionViewGameGrid"/>
<addaction name="actionViewSystemDisplay"/>
<addaction name="actionViewGameProperties"/>
<addaction name="separator"/>
<addaction name="actionFullscreen"/>
<addaction name="menuWindowSize"/>
@@ -245,7 +245,7 @@
<addaction name="separator"/>
<addaction name="actionFullscreen"/>
<addaction name="separator"/>
<addaction name="actionSettings"/>
<addaction name="actionSettings2"/>
<addaction name="actionControllerSettings"/>
</widget>
<widget class="QStatusBar" name="statusBar"/>
@@ -522,7 +522,19 @@
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Settings...</string>
<string>&amp;Settings</string>
</property>
<property name="menuRole">
<enum>QAction::PreferencesRole</enum>
</property>
</action>
<action name="actionSettings2">
<property name="icon">
<iconset theme="settings-3-line">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Settings</string>
</property>
<property name="menuRole">
<enum>QAction::PreferencesRole</enum>

View File

@@ -795,17 +795,22 @@ void EmuThread::onDisplayWindowResized(int width, int height, float scale)
void EmuThread::onApplicationStateChanged(Qt::ApplicationState state)
{
// NOTE: This is executed on the emu thread, not UI thread.
if (!m_pause_on_focus_loss || !VMManager::HasValidVM())
if (!VMManager::HasValidVM())
return;
const bool focus_loss = (state != Qt::ApplicationActive);
if (focus_loss)
{
if (!m_was_paused_by_focus_loss && VMManager::GetState() == VMState::Running)
if (m_pause_on_focus_loss && !m_was_paused_by_focus_loss && VMManager::GetState() == VMState::Running)
{
m_was_paused_by_focus_loss = true;
VMManager::SetPaused(true);
}
// Clear the state of all keyboard binds.
// That way, if we had a key held down, and lost focus, the bind won't be stuck enabled because we never
// got the key release message, because it happened in another window which "stole" the event.
InputManager::ClearBindStateFromSource(InputManager::MakeHostKeyboardKey(0));
}
else
{
@@ -967,17 +972,17 @@ void Host::ReleaseHostDisplay(bool clear_state)
g_emu_thread->releaseHostDisplay(clear_state);
}
bool Host::BeginPresentFrame(bool frame_skip)
HostDisplay::PresentResult Host::BeginPresentFrame(bool frame_skip)
{
if (!g_host_display->BeginPresent(frame_skip))
const HostDisplay::PresentResult result = g_host_display->BeginPresent(frame_skip);
if (result != HostDisplay::PresentResult::OK)
{
// if we're skipping a frame, we need to reset imgui's state, since
// we won't be calling EndPresentFrame().
ImGuiManager::NewFrame();
return false;
ImGuiManager::SkipFrame();
}
return true;
return result;
}
void Host::EndPresentFrame()

View File

@@ -72,7 +72,7 @@ AdvancedSettingsWidget::AdvancedSettingsWidget(SettingsDialog* dialog, QWidget*
dialog->registerWidgetHelp(m_ui.eeClampMode, tr("Clamping Mode"), tr("Normal (Default)"), tr(""));
dialog->registerWidgetHelp(m_ui.eeRecompiler, tr("Enable Recompiler"), tr("Checked"),
tr("Performs just - in - time binary translation of 64 - bit MIPS - IV machine code to x86."));
tr("Performs just-in-time binary translation of 64-bit MIPS-IV machine code to x86."));
dialog->registerWidgetHelp(m_ui.eeWaitLoopDetection, tr("Wait Loop Detection"), tr("Checked"),
tr("Moderate speedup for some games, with no known side effects."));
@@ -86,7 +86,7 @@ AdvancedSettingsWidget::AdvancedSettingsWidget(SettingsDialog* dialog, QWidget*
tr("Uses backpatching to avoid register flushing on every memory access."));
dialog->registerWidgetHelp(m_ui.pauseOnTLBMiss, tr("Pause On TLB Miss"), tr("Unchecked"),
tr("Pauses the virtual machine when a TLB miss occurs, instead of ignoring it and continuing. Note the the VM will pause after the "
tr("Pauses the virtual machine when a TLB miss occurs, instead of ignoring it and continuing. Note that the VM will pause after the "
"end of the block, not on the instruction which caused the exception. Refer to the console to see the address where the invalid "
"access occurred."));

View File

@@ -45,10 +45,10 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent,
#endif
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableMouseMapping, "UI", "EnableMouseMapping", false);
connect(m_ui.mouseSettings, &QToolButton::clicked, this, &ControllerGlobalSettingsWidget::mouseSettingsClicked);
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.multitapPort1, "Pad", "MultitapPort1", false);
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.multitapPort2, "Pad", "MultitapPort2", false);
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerXScale, "Pad", "PointerXScale", 8.0f);
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerYScale, "Pad", "PointerYScale", 8.0f);
#ifdef _WIN32
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableXInputSource, "InputSources", "XInput", false);
@@ -81,13 +81,6 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent,
for (QCheckBox* cb : {m_ui.multitapPort1, m_ui.multitapPort2})
connect(cb, &QCheckBox::stateChanged, this, [this]() { emit bindingSetupChanged(); });
connect(m_ui.pointerXScale, &QSlider::valueChanged, this,
[this](int value) { m_ui.pointerXScaleLabel->setText(QStringLiteral("%1").arg(value)); });
connect(m_ui.pointerYScale, &QSlider::valueChanged, this,
[this](int value) { m_ui.pointerYScaleLabel->setText(QStringLiteral("%1").arg(value)); });
m_ui.pointerXScaleLabel->setText(QStringLiteral("%1").arg(m_ui.pointerXScale->value()));
m_ui.pointerYScaleLabel->setText(QStringLiteral("%1").arg(m_ui.pointerYScale->value()));
updateSDLOptionsEnabled();
}
@@ -128,6 +121,12 @@ void ControllerGlobalSettingsWidget::ledSettingsClicked()
dialog.exec();
}
void ControllerGlobalSettingsWidget::mouseSettingsClicked()
{
ControllerMouseSettingsDialog dialog(this, m_dialog);
dialog.exec();
}
ControllerLEDSettingsDialog::ControllerLEDSettingsDialog(QWidget* parent, ControllerSettingsDialog* dialog)
: QDialog(parent)
, m_dialog(dialog)
@@ -156,3 +155,35 @@ void ControllerLEDSettingsDialog::linkButton(ColorPickerButton* button, u32 play
});
#endif
}
ControllerMouseSettingsDialog::ControllerMouseSettingsDialog(QWidget* parent, ControllerSettingsDialog* dialog)
: QDialog(parent)
{
m_ui.setupUi(this);
SettingsInterface* sif = dialog->getProfileSettingsInterface();
m_ui.icon->setPixmap(QIcon::fromTheme(QStringLiteral("mouse-line")).pixmap(32, 32));
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerXSpeedSlider, "Pad", "PointerXSpeed", 40.0f);
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerYSpeedSlider, "Pad", "PointerYSpeed", 40.0f);
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerXDeadZoneSlider, "Pad", "PointerXDeadZone", 20.0f);
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerYDeadZoneSlider, "Pad", "PointerYDeadZone", 20.0f);
ControllerSettingWidgetBinder::BindWidgetToInputProfileFloat(sif, m_ui.pointerInertiaSlider, "Pad", "PointerInertia", 10.0f);
connect(m_ui.pointerXSpeedSlider, &QSlider::valueChanged, this, [this](int value) { m_ui.pointerXSpeedVal->setText(QStringLiteral("%1").arg(value)); });
connect(m_ui.pointerYSpeedSlider, &QSlider::valueChanged, this, [this](int value) { m_ui.pointerYSpeedVal->setText(QStringLiteral("%1").arg(value)); });
connect(m_ui.pointerXDeadZoneSlider, &QSlider::valueChanged, this, [this](int value) { m_ui.pointerXDeadZoneVal->setText(QStringLiteral("%1").arg(value)); });
connect(m_ui.pointerYDeadZoneSlider, &QSlider::valueChanged, this, [this](int value) { m_ui.pointerYDeadZoneVal->setText(QStringLiteral("%1").arg(value)); });
connect(m_ui.pointerInertiaSlider, &QSlider::valueChanged, this, [this](int value) { m_ui.pointerInertiaVal->setText(QStringLiteral("%1").arg(value)); });
m_ui.pointerXSpeedVal->setText(QStringLiteral("%1").arg(m_ui.pointerXSpeedSlider->value()));
m_ui.pointerYSpeedVal->setText(QStringLiteral("%1").arg(m_ui.pointerYSpeedSlider->value()));
m_ui.pointerXDeadZoneVal->setText(QStringLiteral("%1").arg(m_ui.pointerXDeadZoneSlider->value()));
m_ui.pointerYDeadZoneVal->setText(QStringLiteral("%1").arg(m_ui.pointerYDeadZoneSlider->value()));
m_ui.pointerInertiaVal->setText(QStringLiteral("%1").arg(m_ui.pointerInertiaSlider->value()));
connect(m_ui.buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this, &QDialog::accept);
}
ControllerMouseSettingsDialog::~ControllerMouseSettingsDialog() = default;

View File

@@ -24,6 +24,7 @@
#include "ui_ControllerGlobalSettingsWidget.h"
#include "ui_ControllerLEDSettingsDialog.h"
#include "ui_ControllerMouseSettingsDialog.h"
class ControllerSettingsDialog;
@@ -44,6 +45,7 @@ Q_SIGNALS:
private Q_SLOTS:
void updateSDLOptionsEnabled();
void ledSettingsClicked();
void mouseSettingsClicked();
private:
Ui::ControllerGlobalSettingsWidget m_ui;
@@ -64,3 +66,15 @@ private:
Ui::ControllerLEDSettingsDialog m_ui;
ControllerSettingsDialog* m_dialog;
};
class ControllerMouseSettingsDialog : public QDialog
{
Q_OBJECT
public:
ControllerMouseSettingsDialog(QWidget* parent, ControllerSettingsDialog* dialog);
~ControllerMouseSettingsDialog();
private:
Ui::ControllerMouseSettingsDialog m_ui;
};

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>902</width>
<height>677</height>
<height>583</height>
</rect>
</property>
<property name="windowTitle">
@@ -26,6 +26,32 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item row="5" column="0">
<widget class="QGroupBox" name="profileSettings">
<property name="title">
<string>Profile Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>When this option is enabled, hotkeys can be set in this input profile, and will be used instead of the global hotkeys. By default, hotkeys are always shared between all profiles.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="useProfileHotkeyBindings">
<property name="text">
<string>Use Per-Profile Hotkeys</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="sdlGroup">
<property name="title">
@@ -65,8 +91,7 @@
</property>
<property name="icon">
<iconset theme="lightbulb-line">
<normaloff>.</normaloff>.
</iconset>
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
@@ -75,6 +100,19 @@
</layout>
</widget>
</item>
<item row="6" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>45</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="xinputGroup">
<property name="title">
@@ -101,71 +139,6 @@
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="dinputGroup">
<property name="title">
<string>DInput Source</string>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>The DInput source provides support for legacy controllers which do not support XInput. Accessing these controllers via SDL instead is recommended, but DirectInput can be used if they are not compatible with SDL.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="enableDInputSource">
<property name="text">
<string>Enable DInput Input Source</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="6" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>45</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="0">
<widget class="QGroupBox" name="profileSettings">
<property name="title">
<string>Profile Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>When this option is enabled, hotkeys can be set in this input profile, and will be used instead of the global hotkeys. By default, hotkeys are always shared between all profiles.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="useProfileHotkeyBindings">
<property name="text">
<string>Use Per-Profile Hotkeys</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="0">
<widget class="QGroupBox" name="multitapGroup">
<property name="title">
@@ -199,146 +172,6 @@
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="mouseGroup">
<property name="title">
<string>Mouse/Pointer Source</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Using raw input improves precision when you bind controller sticks to the mouse pointer. Also enables multiple mice to be used.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Horizontal Sensitivity:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSlider" name="pointerXScale">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>30</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="pointerXScaleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>10</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Vertical Sensitivity:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QSlider" name="pointerYScale">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>30</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="pointerYScaleLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>10</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="enableMouseMapping">
<property name="text">
<string>Enable Mouse Mapping</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="0" column="1" rowspan="7">
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
@@ -364,6 +197,72 @@
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="dinputGroup">
<property name="title">
<string>DInput Source</string>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>The DInput source provides support for legacy controllers which do not support XInput. Accessing these controllers via SDL instead is recommended, but DirectInput can be used if they are not compatible with SDL.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="enableDInputSource">
<property name="text">
<string>Enable DInput Input Source</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="mouseGroup">
<property name="title">
<string>Mouse/Pointer Source</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_7">
<property name="text">
<string>PCSX2 allows you to use your mouse to simulate analog stick movement.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="1,0">
<item>
<widget class="QCheckBox" name="enableMouseMapping">
<property name="text">
<string>Enable Mouse Mapping</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mouseSettings">
<property name="toolTip">
<string>Controller LED Settings</string>
</property>
<property name="text">
<string>Settings...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>

View File

@@ -0,0 +1,417 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ControllerMouseSettingsDialog</class>
<widget class="QDialog" name="ControllerMouseSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>654</width>
<height>169</height>
</rect>
</property>
<property name="windowTitle">
<string>Mouse Mapping Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="5" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="pointerYSpeed">
<item>
<widget class="QLabel" name="pointerYSpeedLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Y Speed</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="pointerYSpeedSlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="pointerYSpeedVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>10</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="pointerXSpeed">
<item>
<widget class="QLabel" name="pointerXSpeedLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>X Speed</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="pointerXSpeedSlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="pointerXSpeedVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>10</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="bottomMargin">
<number>10</number>
</property>
<item>
<widget class="QLabel" name="icon">
<property name="minimumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:700;&quot;&gt;Mouse Mapping Settings&lt;/span&gt;&lt;br/&gt;These settings fine-tune the behavior when mapping a mouse to the emulated controller.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0">
<layout class="QHBoxLayout" name="pointerInertia">
<item>
<widget class="QLabel" name="pointerInertiaLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Inertia</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="pointerInertiaSlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="pointerInertiaVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>10</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="pointerXDeadZone">
<item>
<widget class="QLabel" name="pointerXDeadZoneLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>X Dead Zone</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="pointerXDeadZoneSlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="pointerXDeadZoneVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>10</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="pointerYDeadZone">
<item>
<widget class="QLabel" name="pointerYDeadZoneLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Y Dead Zone</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="pointerYDeadZoneSlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="pointerYDeadZoneVal">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>10</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="../resources/resources.qrc"/>
</resources>
<connections/>
</ui>

View File

@@ -120,8 +120,8 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsDialog* dialog, QWidget
tr("Allows games and homebrew to access files / folders directly on the host computer."));
dialog->registerWidgetHelp(m_ui.optimalFramePacing, tr("Optimal Frame Pacing"), tr("Unchecked"),
tr("Sets the vsync queue size to 0, making every frame be completed and presented by the GS before input is polled, and the next frame begins. "
"Using this setting can reduce input lag, at the cost of measurably higher CPU and GPU requirements."));
tr("Sets the VSync queue size to 0, making every frame be completed and presented by the GS before input is polled and the next frame begins. "
"Using this setting can reduce input lag at the cost of measurably higher CPU and GPU requirements."));
dialog->registerWidgetHelp(m_ui.maxFrameLatency, tr("Maximum Frame Latency"), tr("2 Frames"),
tr("Sets the maximum number of frames that can be queued up to the GS, before the CPU thread will wait for one of them to complete before continuing. "
"Higher values can assist with smoothing out irregular frame times, but add additional input lag."));

View File

@@ -178,8 +178,11 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
&GraphicsSettingsWidget::onTrilinearFilteringChanged);
connect(m_ui.gpuPaletteConversion, QOverload<int>::of(&QCheckBox::stateChanged), this,
&GraphicsSettingsWidget::onGpuPaletteConversionChanged);
connect(m_ui.textureInsideRt, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&GraphicsSettingsWidget::onTextureInsideRtChanged);
onTrilinearFilteringChanged();
onGpuPaletteConversionChanged(m_ui.gpuPaletteConversion->checkState());
onTextureInsideRtChanged();
//////////////////////////////////////////////////////////////////////////
// HW Renderer Fixes
@@ -197,7 +200,11 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.preloadFrameData, "EmuCore/GS", "preload_frame_with_gs_data", false);
SettingWidgetBinder::BindWidgetToBoolSetting(
sif, m_ui.disablePartialInvalidation, "EmuCore/GS", "UserHacks_DisablePartialInvalidation", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.textureInsideRt, "EmuCore/GS", "UserHacks_TextureInsideRt", false);
SettingWidgetBinder::BindWidgetToIntSetting(
sif, m_ui.textureInsideRt, "EmuCore/GS", "UserHacks_TextureInsideRt", static_cast<int>(GSTextureInRtMode::Disabled));
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.readTCOnClose, "EmuCore/GS", "UserHacks_ReadTCOnClose", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.targetPartialInvalidation, "EmuCore/GS", "UserHacks_TargetPartialInvalidation", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.estimateTextureRegion, "EmuCore/GS", "UserHacks_EstimateTextureRegion", false);
//////////////////////////////////////////////////////////////////////////
// HW Upscaling Fixes
@@ -291,7 +298,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
// Remove texture offset and skipdraw range for global settings.
m_ui.upscalingFixesLayout->removeRow(2);
m_ui.hardwareFixesLayout->removeRow(3);
m_ui.hardwareFixesLayout->removeRow(5);
m_ui.skipDrawStart = nullptr;
m_ui.skipDrawEnd = nullptr;
m_ui.textureOffsetX = nullptr;
@@ -372,7 +379,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
dialog->registerWidgetHelp(m_ui.PCRTCOverscan, tr("Show Overscan"), tr("Unchecked"),
tr("Enables the option to show the overscan area on games which draw more than the safe area of the screen."));
dialog->registerWidgetHelp(m_ui.fmvAspectRatio, tr("FMV Aspect Ratio"), tr("Off (Default)"), tr("Overrides the FMV aspect ratio."));
dialog->registerWidgetHelp(m_ui.fmvAspectRatio, tr("FMV Aspect Ratio"), tr("Off (Default)"),
tr("Overrides the full-motion video (FMV) aspect ratio."));
dialog->registerWidgetHelp(m_ui.PCRTCAntiBlur, tr("Anti-Blur"), tr("Checked"),
tr("Enables internal Anti-Blur hacks. Less accurate to PS2 rendering but will make a lot of games look less blurry."));
@@ -402,24 +410,32 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
tr("Selects the quality at which screenshots will be compressed. Higher values preserve more detail for JPEG, and reduce file "
"size for PNG."));
dialog->registerWidgetHelp(m_ui.stretchY, tr("Stretch Height"), tr("100%"), tr(""));
dialog->registerWidgetHelp(m_ui.stretchY, tr("Stretch Height"), tr("100%"),
tr("Stretches (> 100%) or squashes (< 100%) the vertical component of the display"));
dialog->registerWidgetHelp(m_ui.fullscreenModes, tr("Fullscreen Mode"), tr("Borderless Fullscreen"),
tr("Chooses the fullscreen resolution and frequency."));
dialog->registerWidgetHelp(m_ui.cropLeft, tr("Left"), tr("0px"), tr(""));
dialog->registerWidgetHelp(m_ui.cropLeft, tr("Left"), tr("0px"),
tr("Changes number of pixels cropped from the left side of the display"));
dialog->registerWidgetHelp(m_ui.cropTop, tr("Top"), tr("0px"), tr(""));
dialog->registerWidgetHelp(m_ui.cropTop, tr("Top"), tr("0px"),
tr("Changes number of pixels cropped from the top of the display"));
dialog->registerWidgetHelp(m_ui.cropRight, tr("Right"), tr("0px"), tr(""));
dialog->registerWidgetHelp(m_ui.cropRight, tr("Right"), tr("0px"),
tr("Changes number of pixels cropped from the right side of the display"));
dialog->registerWidgetHelp(m_ui.cropBottom, tr("Bottom"), tr("0px"), tr(""));
dialog->registerWidgetHelp(m_ui.cropBottom, tr("Bottom"), tr("0px"),
tr("Changes number of pixels cropped from the bottom of the display"));
}
// Rendering tab
{
// Hardware
dialog->registerWidgetHelp(m_ui.upscaleMultiplier, tr("Internal Resolution"), tr("Native (PS2) (Default)"), tr(""));
dialog->registerWidgetHelp(m_ui.upscaleMultiplier, tr("Internal Resolution"), tr("Native (PS2) (Default)"),
tr("Control the resolution at which games are rendered. High resolutions can impact performance on"
"older or lower-end GPUs.<br>Non-native resolution may cause minor graphical issues in some games.<br>"
"FMV resolution will remain unchanged, as the video files are pre-rendered."));
dialog->registerWidgetHelp(
m_ui.mipmapping, tr("Mipmapping"), tr("Automatic (Default)"), tr("Control the accuracy level of the mipmapping emulation."));
@@ -491,7 +507,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
dialog->registerWidgetHelp(m_ui.cpuSpriteRenderBW, tr("CPU Sprite Renderer Size"), tr("0 (Disabled)"), tr(""));
dialog->registerWidgetHelp(m_ui.cpuCLUTRender, tr("Software Clut Render"), tr("0 (Disabled)"), tr(""));
dialog->registerWidgetHelp(m_ui.cpuCLUTRender, tr("Software CLUT Render"), tr("0 (Disabled)"), tr(""));
dialog->registerWidgetHelp(m_ui.skipDrawStart, tr("Skipdraw Range Start"), tr("0"),
tr("Completely skips drawing surfaces from the surface in the left box up to the surface specified in the box on the right."));
@@ -513,7 +529,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
"Disables accurate GS Memory Clearing to be done on the CPU, and let the GPU handle it, which can help Kingdom Hearts "
"games."));
dialog->registerWidgetHelp(m_ui.disablePartialInvalidation, tr("Disable Partial Invalidation"), tr("Unchecked"),
dialog->registerWidgetHelp(m_ui.disablePartialInvalidation, tr("Disable Partial Source Invalidation"), tr("Unchecked"),
tr("By default, the texture cache handles partial invalidations. Unfortunately it is very costly to compute CPU wise. "
"This hack replaces the partial invalidation with a complete deletion of the texture to reduce the CPU load. "
"It helps snowblind engine games."));
@@ -525,9 +541,19 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
tr("Uploads GS data when rendering a new frame to reproduce some effects accurately. "
"Fixes black screen issues in games like Armored Core: Last Raven."));
dialog->registerWidgetHelp(m_ui.textureInsideRt, tr("Texture Inside RT"), tr("Unchecked"),
tr("Allows the texture cache to reuse as an input texture the inner portion of a previous framebuffer. "
"In some selected games this is enabled by default regardless of this setting."));
dialog->registerWidgetHelp(m_ui.textureInsideRt, tr("Texture Inside RT"), tr("Disabled"),
tr("Allows the texture cache to reuse as an input texture the inner portion of a previous framebuffer."));
dialog->registerWidgetHelp(m_ui.readTCOnClose, tr("Read Targets When Closing"), tr("Unchecked"),
tr("Flushes all targets in the texture cache back to local memory when shutting down. Can prevent lost visuals when saving "
"state or switching renderers, but can also cause graphical corruption."));
dialog->registerWidgetHelp(m_ui.targetPartialInvalidation, tr("Target Partial Invalidation"), tr("Unchecked"),
tr("Allows partial invalidation of render targets, which can fix graphical errors in some games. Texture Inside Render Target "
"automatically enables this option."));
dialog->registerWidgetHelp(m_ui.estimateTextureRegion, tr("Estimate Texture Region"), tr("Unchecked"),
tr("Attempts to reduce the texture size when games do not set it themselves (e.g. Snowblind games)."));
}
// Upscaling Fixes tab
@@ -592,13 +618,14 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
dialog->registerWidgetHelp(m_ui.shadeBoostSaturation, tr("Saturation"), tr("50"), tr(""));
dialog->registerWidgetHelp(m_ui.tvShader, tr("TV Shader"), tr("None (Default)"), tr(""));
dialog->registerWidgetHelp(m_ui.tvShader, tr("TV Shader"), tr("None (Default)"),
tr("Applies a shader which replicates the visual effects of different styles of television set."));
}
// OSD tab
{
dialog->registerWidgetHelp(
m_ui.osdScale, tr("OSD Scale"), tr("100%"), tr("Scales the size of the onscreen OSD from 100% to 500%."));
m_ui.osdScale, tr("OSD Scale"), tr("100%"), tr("Scales the size of the onscreen OSD from 50% to 500%."));
dialog->registerWidgetHelp(m_ui.osdShowMessages, tr("Show OSD Messages"), tr("Checked"),
tr("Shows on-screen-display messages when events occur such as save states being "
@@ -654,7 +681,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
tr("Allows the GPU instead of just the CPU to transform lines into sprites. "
"This reduces CPU load and bandwidth requirement, but it is heavier on the GPU."));
dialog->registerWidgetHelp(m_ui.gsDumpCompression, tr("GS Dump Compression"), tr("Zstandard (zst)"), tr(""));
dialog->registerWidgetHelp(m_ui.gsDumpCompression, tr("GS Dump Compression"), tr("Zstandard (zst)"),
tr("Change the compression algorithm used when creating a GS dump."));
dialog->registerWidgetHelp(m_ui.useBlitSwapChain, tr("Use Blit Swap Chain"), tr("Unchecked"),
tr("Uses a blit presentation model instead of flipping when using the Direct3D 11 "
@@ -828,9 +856,18 @@ void GraphicsSettingsWidget::onEnableAudioCaptureArgumentsChanged()
void GraphicsSettingsWidget::onGpuPaletteConversionChanged(int state)
{
const bool enabled = state == Qt::CheckState::PartiallyChecked ? Host::GetBaseBoolSettingValue("EmuCore/GS", "paltex", false) : state;
const bool disabled =
state == Qt::CheckState::PartiallyChecked ? Host::GetBaseBoolSettingValue("EmuCore/GS", "paltex", false) : (state != 0);
m_ui.anisotropicFiltering->setEnabled(!enabled);
m_ui.anisotropicFiltering->setDisabled(disabled);
}
void GraphicsSettingsWidget::onTextureInsideRtChanged()
{
const bool disabled = static_cast<GSTextureInRtMode>(m_dialog->getEffectiveIntValue("EmuCore/GS", "UserHacks_TextureInsideRt",
static_cast<int>(GSTextureInRtMode::Disabled))) >= GSTextureInRtMode::InsideTargets;
m_ui.targetPartialInvalidation->setDisabled(disabled);
}
GSRendererType GraphicsSettingsWidget::getEffectiveRenderer() const

View File

@@ -41,6 +41,7 @@ private Q_SLOTS:
void onAdapterChanged(int index);
void onTrilinearFilteringChanged();
void onGpuPaletteConversionChanged(int state);
void onTextureInsideRtChanged();
void onFullscreenModeChanged(int index);
void onShadeBoostChanged();
void onCaptureContainerChanged();

View File

@@ -920,84 +920,13 @@
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_12">
<item row="2" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Skipdraw Range:</string>
<string>Software CLUT Render:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSpinBox" name="skipDrawStart">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="skipDrawEnd">
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="hwAutoFlush">
<property name="text">
<string>Auto Flush</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="frameBufferConversion">
<property name="text">
<string>Frame Buffer Conversion</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="disableDepthEmulation">
<property name="text">
<string>Disable Depth Emulation</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="disableSafeFeatures">
<property name="text">
<string>Disable Safe Features</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="disablePartialInvalidation">
<property name="text">
<string>Disable Partial Invalidation</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="preloadFrameData">
<property name="text">
<string>Preload Frame Data</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="textureInsideRt">
<property name="text">
<string>Texture Inside RT</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="cpuCLUTRender">
<property name="currentText">
@@ -1023,13 +952,6 @@
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Software CLUT Render:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_47">
<property name="text">
@@ -1056,6 +978,124 @@
</item>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Skipdraw Range:</string>
</property>
</widget>
</item>
<item row="5" 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="6" 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>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_45">
<property name="text">
<string>Texture Inside RT:</string>
</property>
</widget>
</item>
<item row="4" 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>
</layout>
</widget>
<widget class="QGroupBox" name="upscalingFixesTab">
@@ -1536,7 +1576,7 @@
<string>%</string>
</property>
<property name="minimum">
<number>100</number>
<number>50</number>
</property>
<property name="maximum">
<number>500</number>

View File

@@ -31,6 +31,7 @@ static const char* THEME_NAMES[] = {
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "PCSX2 (White/Blue) [Light]"),
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Scarlet Devil (Red/Purple) [Dark]"),
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Violet Angel (Blue/Purple) [Dark]"),
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Cobalt Sky (Royal Blue) [Dark]"),
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Ruby (Black/Red) [Dark]"),
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Sapphire (Black/Blue) [Dark]"),
QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Custom.qss [Drop in PCSX2 Folder]"),
@@ -46,6 +47,7 @@ static const char* THEME_VALUES[] = {
"PCSX2Blue",
"ScarletDevilRed",
"VioletAngelPurple",
"CobaltSky",
"Ruby",
"Sapphire",
"Custom",
@@ -129,7 +131,7 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsDialog* dialog, QWidget
m_ui.confirmShutdown, tr("Confirm Shutdown"), tr("Checked"),
tr("Determines whether a prompt will be displayed to confirm shutting down the virtual machine "
"when the hotkey is pressed."));
dialog->registerWidgetHelp(m_ui.saveStateOnShutdown, tr("Save State On Shutdown"), tr("Checked"),
dialog->registerWidgetHelp(m_ui.saveStateOnShutdown, tr("Save State On Shutdown"), tr("Unchecked"),
tr("Automatically saves the emulator state when powering down or exiting. You can then "
"resume directly from where you left off next time."));
dialog->registerWidgetHelp(m_ui.pauseOnStart, tr("Pause On Start"), tr("Unchecked"),

View File

@@ -58,7 +58,7 @@ MemoryCardConvertDialog::MemoryCardConvertDialog(QWidget* parent, QString select
SetType(MemoryCardType::File, MemoryCardFileType::PS2_8MB, "A standard, 8 MB memory card. Most compatible, but smallest capacity.");
break;
case 16:
SetType(MemoryCardType::File, MemoryCardFileType::PS2_16MB, "2x larger as a standard memory card. May have some compatibility issues.");
SetType(MemoryCardType::File, MemoryCardFileType::PS2_16MB, "2x larger than a standard memory card. May have some compatibility issues.");
break;
case 32:
SetType(MemoryCardType::File, MemoryCardFileType::PS2_32MB, "4x larger than a standard memory card. Likely to have compatibility issues.");

View File

@@ -115,7 +115,7 @@ void SettingsDialog::setupUi(const GameList::Entry* game)
{
addWidget(m_game_fix_settings_widget = new GameFixSettingsWidget(this, m_ui.settingsContainer), tr("Game Fix"),
QStringLiteral("close-line"),
tr("<strong>Game Fix Settings</strong><hr>Gamefixes can work around incorrect emulation in some titles<br>however they can "
tr("<strong>Game Fix Settings</strong><hr>Gamefixes can work around incorrect emulation in some titles.<br>However, they can "
"also cause problems in games if used incorrectly.<br>It is best to leave them all disabled unless advised otherwise."));
}

View File

@@ -423,6 +423,9 @@
<QtUi Include="Settings\ControllerLEDSettingsDialog.ui">
<FileType>Document</FileType>
</QtUi>
<QtUi Include="Settings\ControllerMouseSettingsDialog.ui">
<FileType>Document</FileType>
</QtUi>
<None Include="Settings\USBBindingWidget_DrivingForce.ui" />
<None Include="Settings\USBBindingWidget_GTForce.ui" />
<QtUi Include="Settings\USBDeviceWidget.ui">
@@ -439,4 +442,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="$(SolutionDir)common\vsprops\QtCompile.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
</Project>

View File

@@ -583,6 +583,9 @@
<QtUi Include="Settings\ControllerLEDSettingsDialog.ui">
<Filter>Settings</Filter>
</QtUi>
<QtUi Include="Settings\ControllerMouseSettingsDialog.ui">
<Filter>Settings</Filter>
</QtUi>
</ItemGroup>
<ItemGroup>
<None Include="Settings\FolderSettingsWidget.ui">

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.141 4c-1.582 0-2.387.169-3.128.565a3.453 3.453 0 0 0-1.448 1.448C6.169 6.753 6 7.559 6 9.14v5.718c0 1.582.169 2.387.565 3.128.337.63.818 1.111 1.448 1.448.74.396 1.546.565 3.128.565h1.718c1.582 0 2.387-.169 3.128-.565a3.453 3.453 0 0 0 1.448-1.448c.396-.74.565-1.546.565-3.128V9.14c0-1.582-.169-2.387-.565-3.128a3.453 3.453 0 0 0-1.448-1.448C15.247 4.169 14.441 4 12.86 4H11.14zm0-2h1.718c2.014 0 3.094.278 4.072.801a5.452 5.452 0 0 1 2.268 2.268c.523.978.801 2.058.801 4.072v5.718c0 2.014-.278 3.094-.801 4.072a5.452 5.452 0 0 1-2.268 2.268c-.978.523-2.058.801-4.072.801H11.14c-2.014 0-3.094-.278-4.072-.801a5.452 5.452 0 0 1-2.268-2.268C4.278 17.953 4 16.873 4 14.859V9.14c0-2.014.278-3.094.801-4.072A5.452 5.452 0 0 1 7.07 2.801C8.047 2.278 9.127 2 11.141 2zM11 6h2v5h-2V6z"/></svg>

After

Width:  |  Height:  |  Size: 918 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.141 4c-1.582 0-2.387.169-3.128.565a3.453 3.453 0 0 0-1.448 1.448C6.169 6.753 6 7.559 6 9.14v5.718c0 1.582.169 2.387.565 3.128.337.63.818 1.111 1.448 1.448.74.396 1.546.565 3.128.565h1.718c1.582 0 2.387-.169 3.128-.565a3.453 3.453 0 0 0 1.448-1.448c.396-.74.565-1.546.565-3.128V9.14c0-1.582-.169-2.387-.565-3.128a3.453 3.453 0 0 0-1.448-1.448C15.247 4.169 14.441 4 12.86 4H11.14zm0-2h1.718c2.014 0 3.094.278 4.072.801a5.452 5.452 0 0 1 2.268 2.268c.523.978.801 2.058.801 4.072v5.718c0 2.014-.278 3.094-.801 4.072a5.452 5.452 0 0 1-2.268 2.268c-.978.523-2.058.801-4.072.801H11.14c-2.014 0-3.094-.278-4.072-.801a5.452 5.452 0 0 1-2.268-2.268C4.278 17.953 4 16.873 4 14.859V9.14c0-2.014.278-3.094.801-4.072A5.452 5.452 0 0 1 7.07 2.801C8.047 2.278 9.127 2 11.141 2zM11 6h2v5h-2V6z" fill="#ffffff"/></svg>

After

Width:  |  Height:  |  Size: 933 B

View File

@@ -40,6 +40,7 @@
<file>icons/black/svg/lightbulb-line.svg</file>
<file>icons/black/svg/list-check.svg</file>
<file>icons/black/svg/login-box-line.svg</file>
<file>icons/black/svg/mouse-line.svg</file>
<file>icons/black/svg/pause-line.svg</file>
<file>icons/black/svg/play-line.svg</file>
<file>icons/black/svg/price-tag-3-line.svg</file>
@@ -100,6 +101,7 @@
<file>icons/white/svg/lightbulb-line.svg</file>
<file>icons/white/svg/list-check.svg</file>
<file>icons/white/svg/login-box-line.svg</file>
<file>icons/white/svg/mouse-line.svg</file>
<file>icons/white/svg/pause-line.svg</file>
<file>icons/white/svg/play-line.svg</file>
<file>icons/white/svg/price-tag-3-line.svg</file>

View File

@@ -66,6 +66,7 @@ bool IOCtlSrc::Reopen()
void IOCtlSrc::SetSpindleSpeed(bool restore_defaults) const
{
#ifdef __APPLE__
u16 speed = restore_defaults ? 0xFFFF : m_media_type >= 0 ? 5540 :
3600;
int ioctl_code = m_media_type >= 0 ? DKIOCDVDSETSPEED : DKIOCCDSETSPEED;
@@ -77,6 +78,10 @@ void IOCtlSrc::SetSpindleSpeed(bool restore_defaults) const
{
DevCon.WriteLn("CDVD: Spindle speed set to %d", speed);
}
#else
// FIXME: FreeBSD equivalent for DKIOCDVDSETSPEED DKIOCCDSETSPEED.
DevCon.Warning("CDVD: Setting spindle speed not supported!");
#endif
}
u32 IOCtlSrc::GetSectorCount() const

View File

@@ -357,6 +357,13 @@ enum class GSGPUTargetCLUTMode : u8
InsideTarget,
};
enum class GSTextureInRtMode : u8
{
Disabled,
InsideTargets,
MergeTargets,
};
// Template function for casting enumerations to their underlying type
template <typename Enumeration>
typename std::underlying_type<Enumeration>::type enum_cast(Enumeration E)
@@ -607,6 +614,7 @@ struct Pcsx2Config
{
static const char* AspectRatioNames[];
static const char* FMVAspectRatioSwitchNames[];
static const char* BlendingLevelNames[];
static const char* CaptureContainers[];
static const char* GetRendererName(GSRendererType type);
@@ -664,12 +672,14 @@ struct Pcsx2Config
UserHacks_AlignSpriteX : 1,
UserHacks_AutoFlush : 1,
UserHacks_CPUFBConversion : 1,
UserHacks_ReadTCOnClose : 1,
UserHacks_DisableDepthSupport : 1,
UserHacks_DisablePartialInvalidation : 1,
UserHacks_DisableSafeFeatures : 1,
UserHacks_MergePPSprite : 1,
UserHacks_WildHack : 1,
UserHacks_TextureInsideRt : 1,
UserHacks_TargetPartialInvalidation : 1,
UserHacks_EstimateTextureRegion : 1,
FXAA : 1,
ShadeBoost : 1,
DumpGSData : 1,
@@ -745,6 +755,7 @@ struct Pcsx2Config
int UserHacks_CPUSpriteRenderBW{0};
int UserHacks_CPUCLUTRender{ 0 };
GSGPUTargetCLUTMode UserHacks_GPUTargetCLUTMode{GSGPUTargetCLUTMode::Disabled};
GSTextureInRtMode UserHacks_TextureInsideRt{GSTextureInRtMode::Disabled};
TriFiltering TriFilter{TriFiltering::Automatic};
int OverrideTextureBarriers{-1};
int OverrideGeometryShaders{-1};

View File

@@ -543,12 +543,16 @@ static __fi void DoFMVSwitch()
RendererSwitched = false;
}
// Convenience function to update UI thread and set patches.
static __fi void frameLimitUpdateCore()
// Convenience function to update UI thread and set patches.
static __fi void VSyncUpdateCore()
{
DoFMVSwitch();
VMManager::Internal::VSyncOnCPUThread();
}
static __fi void VSyncCheckExit()
{
if (VMManager::Internal::IsExecutionInterrupted())
Cpu->ExitExecution();
}
@@ -559,10 +563,7 @@ static __fi void frameLimit()
{
// Framelimiter off in settings? Framelimiter go brrr.
if (EmuConfig.GS.LimitScalar == 0.0f || s_use_vsync_for_timing)
{
frameLimitUpdateCore();
return;
}
const u64 uExpectedEnd = m_iStart + m_iTicks; // Compute when we would expect this frame to end, assuming everything goes perfectly perfect.
const u64 iEnd = GetCPUTicks(); // The current tick we actually stopped on.
@@ -573,7 +574,6 @@ static __fi void frameLimit()
{
// ... Fudge the next frame start over a bit. Prevents fast forward zoomies.
m_iStart += (sDeltaTime / m_iTicks) * m_iTicks;
frameLimitUpdateCore();
return;
}
@@ -597,16 +597,17 @@ static __fi void frameLimit()
// Finally, set our next frame start to when this one ends
m_iStart = uExpectedEnd;
frameLimitUpdateCore();
}
static __fi void VSyncStart(u32 sCycle)
{
// Update vibration at the end of a frame.
VSyncUpdateCore();
PAD::Update();
frameLimit(); // limit FPS
gsPostVsyncStart(); // MUST be after framelimit; doing so before causes funk with frame times!
VSyncCheckExit();
if(EmuConfig.Trace.Enabled && EmuConfig.Trace.EE.m_EnableAll)
SysTrace.EE.Counters.Write( " ================ EE COUNTER VSYNC START (frame: %d) ================", g_FrameCount );

View File

@@ -169,8 +169,8 @@ The clamp modes are also numerically based.
* alignSprite [`0` or `1`] {Off or On} Default: Off (`0`)
* mergeSprite [`0` or `1`] {Off or On} Default: Off (`0`)
* wildArmsHack [`0` or `1`] {Off or On} Default: Off (`0`)
* skipDrawStart [Value between `0` to `100000`] {0-100000} Default: Off (`0`)
* skipDrawEnd [Value between `0` to `100000`] {0-100000} Default: Off (`0`)
* skipDrawStart [Value between `0` to `10000`] {0-10000} Default: Off (`0`)
* skipDrawEnd [Value between `0` to `10000`] {0-10000} Default: Off (`0`)
* halfPixelOffset [`0` or `1` or `2` or `3`] {Off, Normal Vertex, Special Texture or Special Texture Aggressive} Default: Off (`0`)
* roundSprite [`0` or `1` or `2`] {Off, Half or Full} Default: Off (`0`)

View File

@@ -126,6 +126,11 @@
"minimum": 0,
"maximum": 1
},
"readTCOnClose": {
"type": "integer",
"minimum": 0,
"maximum": 1
},
"disableDepthSupport": {
"type": "integer",
"minimum": 0,
@@ -146,11 +151,16 @@
"minimum": 0,
"maximum": 1
},
"textureInsideRT": {
"partialTargetInvalidation": {
"type": "integer",
"minimum": 0,
"maximum": 1
},
"textureInsideRT": {
"type": "integer",
"minimum": 0,
"maximum": 2
},
"alignSprite": {
"type": "integer",
"minimum": 0,
@@ -166,6 +176,21 @@
"minimum": 0,
"maximum": 1
},
"estimateTextureRegion": {
"type": "integer",
"minimum": 0,
"maximum": 1
},
"PCRTCOffsets": {
"type": "integer",
"minimum": 0,
"maximum": 1
},
"PCRTCOverscan": {
"type": "integer",
"minimum": 0,
"maximum": 1
},
"mipmap": {
"type": "integer",
"minimum": 0,
@@ -231,6 +256,21 @@
"minimum": 0,
"maximum": 2
},
"minimumBlendingLevel": {
"type": "integer",
"minimum": 0,
"maximum": 5
},
"maximumBlendingLevel": {
"type": "integer",
"minimum": 0,
"maximum": 5
},
"recommendedBlendingLevel": {
"type": "integer",
"minimum": 0,
"maximum": 5
},
"getSkipCount": {
"type": "string"
},

View File

@@ -160,7 +160,7 @@ void CommonHost::SetDataDirectory()
EmuFolders::DataRoot = Path::Combine(StringUtil::WideStringToUTF8String(documents_directory), "PCSX2");
CoTaskMemFree(documents_directory);
}
#elif defined(__linux__)
#elif defined(__linux__) || defined(__FreeBSD__)
// Use $XDG_CONFIG_HOME/PCSX2 if it exists.
const char* xdg_config_home = getenv("XDG_CONFIG_HOME");
if (xdg_config_home && Path::IsAbsolute(xdg_config_home))

View File

@@ -16,6 +16,7 @@
#include "PrecompiledHeader.h"
#include "Frontend/D3D11HostDisplay.h"
#include "GS/Renderers/DX11/D3D.h"
#include "imgui.h"
#include "imgui_impl_dx11.h"
@@ -439,49 +440,6 @@ void D3D11HostDisplay::DestroySurface()
m_swap_chain.reset();
}
static std::string GetDriverVersionFromLUID(const LUID& luid)
{
std::string ret;
HKEY hKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\DirectX", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
DWORD max_key_len = 0, adapter_count = 0;
if (RegQueryInfoKeyW(hKey, nullptr, nullptr, nullptr, &adapter_count, &max_key_len,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
{
std::vector<TCHAR> current_name(max_key_len + 1);
for (DWORD i = 0; i < adapter_count; ++i)
{
DWORD subKeyLength = static_cast<DWORD>(current_name.size());
if (RegEnumKeyExW(hKey, i, current_name.data(), &subKeyLength, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
{
LUID current_luid = {};
DWORD current_luid_size = sizeof(uint64_t);
if (RegGetValueW(hKey, current_name.data(), L"AdapterLuid", RRF_RT_QWORD, nullptr, &current_luid, &current_luid_size) == ERROR_SUCCESS &&
current_luid.HighPart == luid.HighPart && current_luid.LowPart == luid.LowPart)
{
LARGE_INTEGER driver_version = {};
DWORD driver_version_size = sizeof(driver_version);
if (RegGetValueW(hKey, current_name.data(), L"DriverVersion", RRF_RT_QWORD, nullptr, &driver_version, &driver_version_size) == ERROR_SUCCESS)
{
WORD nProduct = HIWORD(driver_version.HighPart);
WORD nVersion = LOWORD(driver_version.HighPart);
WORD nSubVersion = HIWORD(driver_version.LowPart);
WORD nBuild = LOWORD(driver_version.LowPart);
ret = StringUtil::StdStringFromFormat("%u.%u.%u.%u", nProduct, nVersion, nSubVersion, nBuild);
}
}
}
}
}
RegCloseKey(hKey);
}
return ret;
}
std::string D3D11HostDisplay::GetDriverInfo() const
{
std::string ret = "Unknown Feature Level";
@@ -518,7 +476,7 @@ std::string D3D11HostDisplay::GetDriverInfo() const
ret += StringUtil::WideStringToUTF8String(desc.Description);
ret += "\n";
const std::string driver_version(GetDriverVersionFromLUID(desc.AdapterLuid));
const std::string driver_version(D3D::GetDriverVersionFromLUID(desc.AdapterLuid));
if (!driver_version.empty())
{
ret += "Driver Version: ";
@@ -641,13 +599,10 @@ bool D3D11HostDisplay::UpdateImGuiFontTexture()
return true;
}
bool D3D11HostDisplay::BeginPresent(bool frame_skip)
HostDisplay::PresentResult D3D11HostDisplay::BeginPresent(bool frame_skip)
{
if (frame_skip || !m_swap_chain)
{
ImGui::EndFrame();
return 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
@@ -664,7 +619,7 @@ bool D3D11HostDisplay::BeginPresent(bool frame_skip)
const CD3D11_RECT scissor(0, 0, m_window_info.surface_width, m_window_info.surface_height);
m_context->RSSetViewports(1, &vp);
m_context->RSSetScissorRects(1, &scissor);
return true;
return PresentResult::OK;
}
void D3D11HostDisplay::EndPresent()

View File

@@ -65,7 +65,7 @@ public:
void SetVSync(VsyncMode mode) override;
bool BeginPresent(bool frame_skip) override;
PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override;
bool SetGPUTimingEnabled(bool enabled) override;

View File

@@ -16,6 +16,8 @@
#include "PrecompiledHeader.h"
#include "Frontend/D3D12HostDisplay.h"
#include "GS/Renderers/DX11/D3D.h"
#include "common/Assertions.h"
#include "common/Console.h"
#include "common/D3D12/Context.h"
@@ -343,49 +345,6 @@ void D3D12HostDisplay::DestroySurface()
m_swap_chain.reset();
}
static std::string GetDriverVersionFromLUID(const LUID& luid)
{
std::string ret;
HKEY hKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\DirectX", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
DWORD max_key_len = 0, adapter_count = 0;
if (RegQueryInfoKey(hKey, nullptr, nullptr, nullptr, &adapter_count, &max_key_len,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
{
std::vector<TCHAR> current_name(max_key_len + 1);
for (DWORD i = 0; i < adapter_count; ++i)
{
DWORD subKeyLength = static_cast<DWORD>(current_name.size());
if (RegEnumKeyExW(hKey, i, current_name.data(), &subKeyLength, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
{
LUID current_luid = {};
DWORD current_luid_size = sizeof(uint64_t);
if (RegGetValueW(hKey, current_name.data(), L"AdapterLuid", RRF_RT_QWORD, nullptr, &current_luid, &current_luid_size) == ERROR_SUCCESS &&
current_luid.HighPart == luid.HighPart && current_luid.LowPart == luid.LowPart)
{
LARGE_INTEGER driver_version = {};
DWORD driver_version_size = sizeof(driver_version);
if (RegGetValueW(hKey, current_name.data(), L"DriverVersion", RRF_RT_QWORD, nullptr, &driver_version, &driver_version_size) == ERROR_SUCCESS)
{
WORD nProduct = HIWORD(driver_version.HighPart);
WORD nVersion = LOWORD(driver_version.HighPart);
WORD nSubVersion = HIWORD(driver_version.LowPart);
WORD nBuild = LOWORD(driver_version.LowPart);
ret = StringUtil::StdStringFromFormat("%u.%u.%u.%u", nProduct, nVersion, nSubVersion, nBuild);
}
}
}
}
}
RegCloseKey(hKey);
}
return ret;
}
std::string D3D12HostDisplay::GetDriverInfo() const
{
std::string ret = "Unknown Feature Level";
@@ -417,7 +376,7 @@ std::string D3D12HostDisplay::GetDriverInfo() const
ret += StringUtil::WideStringToUTF8String(desc.Description);
ret += "\n";
const std::string driver_version(GetDriverVersionFromLUID(desc.AdapterLuid));
const std::string driver_version(D3D::GetDriverVersionFromLUID(desc.AdapterLuid));
if (!driver_version.empty())
{
ret += "Driver Version: ";
@@ -554,13 +513,13 @@ bool D3D12HostDisplay::UpdateImGuiFontTexture()
return ImGui_ImplDX12_CreateFontsTexture();
}
bool D3D12HostDisplay::BeginPresent(bool frame_skip)
HostDisplay::PresentResult D3D12HostDisplay::BeginPresent(bool frame_skip)
{
if (m_device_lost)
return HostDisplay::PresentResult::DeviceLost;
if (frame_skip || !m_swap_chain)
{
ImGui::EndFrame();
return false;
}
return PresentResult::FrameSkipped;
static constexpr std::array<float, 4> clear_color = {};
D3D12::Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
@@ -574,7 +533,7 @@ bool D3D12HostDisplay::BeginPresent(bool frame_skip)
const D3D12_RECT scissor{0, 0, static_cast<LONG>(m_window_info.surface_width), static_cast<LONG>(m_window_info.surface_height)};
cmdlist->RSSetViewports(1, &vp);
cmdlist->RSSetScissorRects(1, &scissor);
return true;
return PresentResult::OK;
}
void D3D12HostDisplay::EndPresent()
@@ -586,7 +545,11 @@ void D3D12HostDisplay::EndPresent()
m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast<u32>(m_swap_chain_buffers.size()));
swap_chain_buf.TransitionToState(g_d3d12_context->GetCommandList(), D3D12_RESOURCE_STATE_PRESENT);
g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::None);
if (!g_d3d12_context->ExecuteCommandList(D3D12::Context::WaitType::None))
{
m_device_lost = true;
return;
}
const bool vsync = static_cast<UINT>(m_vsync_mode != VsyncMode::Off);
if (!vsync && m_using_allow_tearing)

View File

@@ -71,7 +71,7 @@ public:
void SetVSync(VsyncMode mode) override;
bool BeginPresent(bool frame_skip) override;
PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override;
bool SetGPUTimingEnabled(bool enabled) override;
@@ -97,4 +97,5 @@ protected:
bool m_allow_tearing_supported = false;
bool m_using_allow_tearing = false;
bool m_device_lost = false;
};

View File

@@ -3166,6 +3166,7 @@ void FullscreenUI::DrawGraphicsSettingsPage()
"3 (192 Max Width)", "4 (256 Max Width)", "5 (320 Max Width)", "6 (384 Max Width)", "7 (448 Max Width)",
"8 (512 Max Width)", "9 (576 Max Width)", "10 (640 Max Width)"};
static constexpr const char* s_cpu_clut_render_options[] = {"0 (Disabled)", "1 (Normal)", "2 (Aggressive)"};
static constexpr const char* s_texture_inside_rt_options[] = {"Disabled", "Inside Target", "Merge Targets"};
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"};
@@ -3193,9 +3194,19 @@ void FullscreenUI::DrawGraphicsSettingsPage()
DrawToggleSetting(bsi, "Disable Partial Invalidation",
"Removes texture cache entries when there is any intersection, rather than only the intersected areas.", "EmuCore/GS",
"UserHacks_DisablePartialInvalidation", false, manual_hw_fixes);
DrawToggleSetting(bsi, "Texture Inside Render Target",
DrawIntListSetting(bsi, "Texture Inside Render Target",
"Allows the texture cache to reuse as an input texture the inner portion of a previous framebuffer.", "EmuCore/GS",
"UserHacks_TextureInsideRt", false, manual_hw_fixes);
"UserHacks_TextureInsideRt", 0, s_texture_inside_rt_options, std::size(s_texture_inside_rt_options), 0, manual_hw_fixes);
DrawToggleSetting(bsi, "Target Partial Invalidation",
"Allows partial invalidation of render targets, which can fix graphical errors in some games.", "EmuCore/GS",
"UserHacks_TargetPartialInvalidation", false,
!GetEffectiveBoolSetting(bsi, "EmuCore/GS", "UserHacks_TextureInsideRt", false));
DrawToggleSetting(bsi, "Read Targets When Closing",
"Flushes all targets in the texture cache back to local memory when shutting down.", "EmuCore/GS",
"UserHacks_ReadTCOnClose", false, manual_hw_fixes);
DrawToggleSetting(bsi, "Estimate Texture Region",
"Attempts to reduce the texture size when games do not set it themselves (e.g. Snowblind games).", "EmuCore/GS",
"UserHacks_EstimateTextureRegion", false, manual_hw_fixes);
MenuHeading("Upscaling Fixes");
DrawIntListSetting(bsi, "Half-Pixel Offset", "Adjusts vertices relative to upscaling.", "EmuCore/GS",

View File

@@ -90,7 +90,7 @@ bool ImGuiManager::Initialize()
return false;
}
s_global_scale = std::max(1.0f, g_host_display->GetWindowScale() * (EmuConfig.GS.OsdScale / 100.0f));
s_global_scale = std::max(0.5f, g_host_display->GetWindowScale() * (EmuConfig.GS.OsdScale / 100.0f));
ImGui::CreateContext();
@@ -185,7 +185,7 @@ void ImGuiManager::WindowResized()
void ImGuiManager::UpdateScale()
{
const float window_scale = g_host_display ? g_host_display->GetWindowScale() : 1.0f;
const float scale = std::max(window_scale * (EmuConfig.GS.OsdScale / 100.0f), 1.0f);
const float scale = std::max(window_scale * (EmuConfig.GS.OsdScale / 100.0f), 0.5f);
if (scale == s_global_scale && (!HasFullscreenFonts() || !ImGuiFullscreen::UpdateLayoutScale()))
return;
@@ -229,6 +229,12 @@ void ImGuiManager::NewFrame()
}
}
void ImGuiManager::SkipFrame()
{
ImGui::EndFrame();
NewFrame();
}
void ImGuiManager::SetStyle()
{
ImGuiStyle& style = ImGui::GetStyle();

View File

@@ -40,6 +40,9 @@ namespace ImGuiManager
/// Call at the beginning of the frame to set up ImGui state.
void NewFrame();
/// Call when skipping rendering a frame, to update internal state.
void SkipFrame();
/// Renders any on-screen display elements.
void RenderOSD();

View File

@@ -409,8 +409,8 @@ void ImGuiManager::DrawSettingsOverlay()
APPEND("CCD={} ", GSConfig.UserHacks_CPUCLUTRender);
if (GSConfig.SkipDrawStart != 0 || GSConfig.SkipDrawEnd != 0)
APPEND("SD={}/{} ", GSConfig.SkipDrawStart, GSConfig.SkipDrawEnd);
if (GSConfig.UserHacks_TextureInsideRt)
APPEND("TexRT ");
if (GSConfig.UserHacks_TextureInsideRt != GSTextureInRtMode::Disabled)
APPEND("TexRT={} ", static_cast<int>(GSConfig.UserHacks_TextureInsideRt));
if (GSConfig.UserHacks_WildHack)
APPEND("WA ");
if (GSConfig.UserHacks_MergePPSprite)
@@ -421,10 +421,14 @@ void ImGuiManager::DrawSettingsOverlay()
APPEND("AF ");
if (GSConfig.UserHacks_CPUFBConversion)
APPEND("FBC ");
if (GSConfig.UserHacks_ReadTCOnClose)
APPEND("FTC ");
if(GSConfig.UserHacks_DisableDepthSupport)
APPEND("DDE ");
if (GSConfig.UserHacks_DisablePartialInvalidation)
APPEND("DPIV ");
if (GSConfig.UserHacks_TargetPartialInvalidation)
APPEND("TPV ");
if (GSConfig.UserHacks_DisableSafeFeatures)
APPEND("DSF ");
if (GSConfig.WrapGSMem)

View File

@@ -160,7 +160,11 @@ struct PointerAxisState
static std::array<std::array<float, static_cast<u8>(InputPointerAxis::Count)>, InputManager::MAX_POINTER_DEVICES> s_host_pointer_positions;
static std::array<std::array<PointerAxisState, static_cast<u8>(InputPointerAxis::Count)>, InputManager::MAX_POINTER_DEVICES>
s_pointer_state;
static std::array<float, static_cast<u8>(InputPointerAxis::Count)> s_pointer_axis_scale;
static std::array<float, 2> s_pointer_axis_speed;
static std::array<float, 2> s_pointer_axis_dead_zone;
static std::array<float, 2> s_pointer_axis_range;
static std::array<float, 2> s_pointer_pos = {0.0f, 0.0f};
static float s_pointer_inertia = 0.0f;
using PointerMoveCallback = std::function<void(InputBindingKey key, float value)>;
using KeyboardEventCallback = std::function<void(InputBindingKey key, float value)>;
@@ -919,6 +923,71 @@ bool InputManager::ProcessEvent(InputBindingKey key, float value, bool skip_butt
return true;
}
void InputManager::ClearBindStateFromSource(InputBindingKey key)
{
// Why are we doing it this way? Because any of the bindings could cause a reload and invalidate our iterators :(.
// Axis handlers should be fine, so we'll do those as a first pass.
for (const auto& [match_key, binding] : s_binding_map)
{
if (key.source_type != match_key.source_type || key.source_subtype != match_key.source_subtype ||
key.source_index != match_key.source_index || !IsAxisHandler(binding->handler))
{
continue;
}
for (u32 i = 0; i < binding->num_keys; i++)
{
if (binding->keys[i].MaskDirection() != match_key)
continue;
std::get<InputAxisEventHandler>(binding->handler)(0.0f);
break;
}
}
// Now go through the button handlers, and pick them off.
bool matched;
do
{
matched = false;
for (const auto& [match_key, binding] : s_binding_map)
{
if (key.source_type != match_key.source_type || key.source_subtype != match_key.source_subtype ||
key.source_index != match_key.source_index || IsAxisHandler(binding->handler))
{
continue;
}
for (u32 i = 0; i < binding->num_keys; i++)
{
if (binding->keys[i].MaskDirection() != match_key)
continue;
// Skip if we weren't pressed.
const u8 bit = static_cast<u8>(1) << i;
if ((binding->current_mask & bit) == 0)
continue;
// Only fire handler if we're changing from active state.
const u8 current_mask = binding->current_mask;
binding->current_mask &= ~bit;
if (current_mask == binding->full_mask)
{
std::get<InputButtonEventHandler>(binding->handler)(0.0f);
matched = true;
break;
}
}
// Need to start again, might've reloaded.
if (matched)
break;
}
} while (matched);
}
bool InputManager::PreprocessEvent(InputBindingKey key, float value, GenericInputBinding generic_key)
{
// does imgui want the event?
@@ -950,15 +1019,34 @@ void InputManager::GenerateRelativeMouseEvents()
{
for (u32 axis = 0; axis < static_cast<u32>(static_cast<u8>(InputPointerAxis::Count)); axis++)
{
const InputBindingKey key(MakePointerAxisKey(device, static_cast<InputPointerAxis>(axis)));
PointerAxisState& state = s_pointer_state[device][axis];
const float delta = static_cast<float>(state.delta.exchange(0, std::memory_order_acquire)) / 65536.0f;
const float unclamped_value = delta * s_pointer_axis_scale[axis];
float value = 0.0f;
const InputBindingKey key(MakePointerAxisKey(device, static_cast<InputPointerAxis>(axis)));
if (axis >= static_cast<u32>(InputPointerAxis::WheelX) && ImGuiManager::ProcessPointerAxisEvent(key, unclamped_value))
continue;
if (axis <= static_cast<u32>(InputPointerAxis::Y))
{
s_pointer_pos[axis] += delta * s_pointer_axis_speed[axis];
value = std::clamp(s_pointer_pos[axis], -1.0f, 1.0f);
s_pointer_pos[axis] -= value;
s_pointer_pos[axis] *= s_pointer_inertia;
value *= s_pointer_axis_range[axis];
if (value > 0.0f)
value += s_pointer_axis_dead_zone[axis];
else if (value < 0.0f)
value -= s_pointer_axis_dead_zone[axis];
}
else
{
// ImGui can consume mouse wheel events when the mouse is over a UI element.
if (delta != 0.0f && ImGuiManager::ProcessPointerAxisEvent(key, delta))
continue;
value = std::clamp(delta, -1.0f, 1.0f);
}
const float value = std::clamp(unclamped_value, -1.0f, 1.0f);
if (value != state.last_value)
{
state.last_value = value;
@@ -1203,14 +1291,18 @@ void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& bind
for (u32 pad = 0; pad < PAD::NUM_CONTROLLER_PORTS; pad++)
AddPadBindings(binding_si, pad, PAD::GetDefaultPadType(pad));
for (u32 axis = 0; axis < static_cast<u32>(InputPointerAxis::Count); axis++)
constexpr float ui_ctrl_range = 100.0f;
constexpr float pointer_sensitivity = 0.05f;
for (u32 axis = 0; axis <= static_cast<u32>(InputPointerAxis::Y); axis++)
{
// From lilypad: 1 mouse pixel = 1/8th way down.
const float default_scale = (axis <= static_cast<u32>(InputPointerAxis::Y)) ? 8.0f : 1.0f;
s_pointer_axis_scale[axis] =
1.0f /
std::max(si.GetFloatValue("Pad", fmt::format("Pointer{}Scale", s_pointer_axis_names[axis]).c_str(), default_scale), 1.0f);
s_pointer_axis_speed[axis] = si.GetFloatValue("Pad", fmt::format("Pointer{}Speed", s_pointer_axis_names[axis]).c_str(), 40.0f) /
ui_ctrl_range * pointer_sensitivity;
s_pointer_axis_dead_zone[axis] = std::min(
si.GetFloatValue("Pad", fmt::format("Pointer{}DeadZone", s_pointer_axis_names[axis]).c_str(), 20.0f) / ui_ctrl_range, 1.0f);
s_pointer_axis_range[axis] = 1.0f - s_pointer_axis_dead_zone[axis];
}
s_pointer_inertia = si.GetFloatValue("Pad", "PointerInertia", 10.0f) / ui_ctrl_range;
s_pointer_pos = {};
for (u32 port = 0; port < USB::NUM_PORTS; port++)
AddUSBBindings(binding_si, port);

View File

@@ -257,6 +257,9 @@ namespace InputManager
/// Returns true if anything was bound to this key, otherwise false.
bool InvokeEvents(InputBindingKey key, float value, GenericInputBinding generic_key = GenericInputBinding::Unknown);
/// Clears internal state for any binds with a matching source/index.
void ClearBindStateFromSource(InputBindingKey key);
/// Sets a hook which can be used to intercept events before they're processed by the normal bindings.
/// This is typically used when binding new controls to detect what gets pressed.
void SetHook(InputInterceptHook::Callback callback);

View File

@@ -81,7 +81,7 @@ public:
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* data, u32 data_stride, bool dynamic = false) override;
void UpdateTexture(id<MTLTexture> texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride);
void UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, const void* data, u32 data_stride) override;
bool BeginPresent(bool frame_skip) override;
PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override;
void SetVSync(VsyncMode mode) override;

View File

@@ -265,7 +265,7 @@ void MetalHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y,
static bool s_capture_next = false;
bool MetalHostDisplay::BeginPresent(bool frame_skip)
HostDisplay::PresentResult MetalHostDisplay::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)
@@ -273,7 +273,7 @@ bool MetalHostDisplay::BeginPresent(bool frame_skip)
if (frame_skip || m_window_info.type == WindowInfo::Type::Surfaceless || !g_gs_device)
{
ImGui::EndFrame();
return false;
return PresentResult::FrameSkipped;
}
id<MTLCommandBuffer> buf = dev->GetRenderCmdBuf();
m_current_drawable = MRCRetain([m_layer nextDrawable]);
@@ -284,13 +284,13 @@ bool MetalHostDisplay::BeginPresent(bool frame_skip)
[buf popDebugGroup];
dev->FlushEncoders();
ImGui::EndFrame();
return false;
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);
return true;
return PresentResult::OK;
}}
void MetalHostDisplay::EndPresent()

View File

@@ -335,13 +335,10 @@ bool OpenGLHostDisplay::UpdateImGuiFontTexture()
return ImGui_ImplOpenGL3_CreateFontsTexture();
}
bool OpenGLHostDisplay::BeginPresent(bool frame_skip)
HostDisplay::PresentResult OpenGLHostDisplay::BeginPresent(bool frame_skip)
{
if (frame_skip || m_window_info.type == WindowInfo::Type::Surfaceless)
{
ImGui::EndFrame();
return false;
}
return PresentResult::FrameSkipped;
glDisable(GL_SCISSOR_TEST);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
@@ -350,7 +347,7 @@ bool OpenGLHostDisplay::BeginPresent(bool frame_skip)
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, m_window_info.surface_width, m_window_info.surface_height);
return true;
return PresentResult::OK;
}
void OpenGLHostDisplay::EndPresent()

View File

@@ -58,7 +58,7 @@ public:
void SetVSync(VsyncMode mode) override;
bool BeginPresent(bool frame_skip) override;
PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override;
bool SetGPUTimingEnabled(bool enabled) override;

View File

@@ -339,17 +339,18 @@ bool VulkanHostDisplay::DoneCurrent()
return true;
}
bool VulkanHostDisplay::BeginPresent(bool frame_skip)
HostDisplay::PresentResult VulkanHostDisplay::BeginPresent(bool frame_skip)
{
if (frame_skip || !m_swap_chain)
{
ImGui::EndFrame();
return false;
}
return PresentResult::FrameSkipped;
// Previous frame needs to be presented before we can acquire the swap chain.
g_vulkan_context->WaitForPresentComplete();
// Check if the device was lost.
if (g_vulkan_context->CheckLastSubmitFail())
return PresentResult::DeviceLost;
VkResult res = m_swap_chain->AcquireNextImage();
if (res != VK_SUCCESS)
{
@@ -367,7 +368,7 @@ bool VulkanHostDisplay::BeginPresent(bool frame_skip)
{
Console.Error("Failed to recreate surface after loss");
g_vulkan_context->ExecuteCommandBuffer(Vulkan::Context::WaitType::None);
return false;
return PresentResult::FrameSkipped;
}
res = m_swap_chain->AcquireNextImage();
@@ -380,7 +381,7 @@ bool VulkanHostDisplay::BeginPresent(bool frame_skip)
// Still submit the command buffer, otherwise we'll end up with several frames waiting.
LOG_VULKAN_ERROR(res, "vkAcquireNextImageKHR() failed: ");
g_vulkan_context->ExecuteCommandBuffer(Vulkan::Context::WaitType::None);
return false;
return PresentResult::FrameSkipped;
}
}
@@ -401,7 +402,7 @@ bool VulkanHostDisplay::BeginPresent(bool frame_skip)
const VkRect2D scissor{{0, 0}, {static_cast<u32>(swap_chain_texture.GetWidth()), static_cast<u32>(swap_chain_texture.GetHeight())}};
vkCmdSetViewport(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &vp);
vkCmdSetScissor(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &scissor);
return true;
return PresentResult::OK;
}
void VulkanHostDisplay::EndPresent()

View File

@@ -47,7 +47,7 @@ public:
void SetVSync(VsyncMode mode) override;
bool BeginPresent(bool frame_skip) override;
PresentResult BeginPresent(bool frame_skip) override;
void EndPresent() override;
bool SetGPUTimingEnabled(bool enabled) override;

View File

@@ -209,15 +209,15 @@ static bool DoGSOpen(GSRendererType renderer, u8* basemem)
return false;
}
try
if (!g_gs_device->Create())
{
if (!g_gs_device->Create())
{
g_gs_device->Destroy();
g_gs_device.reset();
return false;
}
g_gs_device->Destroy();
g_gs_device.reset();
return false;
}
if (!g_gs_renderer)
{
if (renderer == GSRendererType::Null)
{
g_gs_renderer = std::make_unique<GSRendererNull>();
@@ -230,55 +230,71 @@ static bool DoGSOpen(GSRendererType renderer, u8* basemem)
{
g_gs_renderer = std::unique_ptr<GSRenderer>(MULTI_ISA_SELECT(makeGSRendererSW)(GSConfig.SWExtraThreads));
}
g_gs_renderer->SetRegsMem(basemem);
g_gs_renderer->ResetPCRTC();
}
catch (std::exception& ex)
else
{
Host::ReportFormattedErrorAsync("GS", "GS error: Exception caught in GSopen: %s", ex.what());
g_gs_renderer.reset();
g_gs_device->Destroy();
g_gs_device.reset();
return false;
Console.Warning("(DoGSOpen) Using existing renderer.");
}
GSConfig.OsdShowGPU = EmuConfig.GS.OsdShowGPU && g_host_display->SetGPUTimingEnabled(true);
g_gs_renderer->SetRegsMem(basemem);
g_perfmon.Reset();
return true;
}
bool GSreopen(bool recreate_display, const Pcsx2Config::GSOptions& old_config)
bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::GSOptions& old_config)
{
Console.WriteLn("Reopening GS with %s display", recreate_display ? "new" : "existing");
g_gs_renderer->Flush(GSState::GSFlushReason::GSREOPEN);
if (recreate_renderer)
g_gs_renderer->Flush(GSState::GSFlushReason::GSREOPEN);
if (GSConfig.UserHacks_ReadTCOnClose)
g_gs_renderer->ReadbackTextureCache();
freezeData fd = {};
if (g_gs_renderer->Freeze(&fd, true) != 0)
std::unique_ptr<u8[]> fd_data;
if (recreate_renderer)
{
Console.Error("(GSreopen) Failed to get GS freeze size");
return false;
}
if (g_gs_renderer->Freeze(&fd, true) != 0)
{
Console.Error("(GSreopen) Failed to get GS freeze size");
return false;
}
std::unique_ptr<u8[]> fd_data = std::make_unique<u8[]>(fd.size);
fd.data = fd_data.get();
if (g_gs_renderer->Freeze(&fd, false) != 0)
fd_data = std::make_unique<u8[]>(fd.size);
fd.data = fd_data.get();
if (g_gs_renderer->Freeze(&fd, false) != 0)
{
Console.Error("(GSreopen) Failed to freeze GS");
return false;
}
}
else
{
Console.Error("(GSreopen) Failed to freeze GS");
return false;
// Make sure nothing is left over.
g_gs_renderer->PurgeTextureCache();
g_gs_renderer->PurgePool();
}
if (recreate_display)
{
g_gs_device->ResetAPIState();
if (Host::BeginPresentFrame(true))
if (Host::BeginPresentFrame(false) == HostDisplay::PresentResult::OK)
Host::EndPresentFrame();
}
u8* basemem = g_gs_renderer->GetRegsMem();
const u32 gamecrc = g_gs_renderer->GetGameCRC();
g_gs_renderer->Destroy();
g_gs_renderer.reset();
if (recreate_renderer)
{
g_gs_renderer->Destroy();
g_gs_renderer.reset();
}
g_gs_device->Destroy();
g_gs_device.reset();
@@ -327,13 +343,17 @@ bool GSreopen(bool recreate_display, const Pcsx2Config::GSOptions& old_config)
}
}
if (g_gs_renderer->Defrost(&fd) != 0)
if (recreate_renderer)
{
Console.Error("(GSreopen) Failed to defrost");
return false;
if (g_gs_renderer->Defrost(&fd) != 0)
{
Console.Error("(GSreopen) Failed to defrost");
return false;
}
g_gs_renderer->SetGameCRC(gamecrc);
}
g_gs_renderer->SetGameCRC(gamecrc);
return true;
}
@@ -698,7 +718,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
GSConfig.DisableShaderCache != old_config.DisableShaderCache ||
GSConfig.DisableThreadedPresentation != old_config.DisableThreadedPresentation
);
if (!GSreopen(do_full_restart, old_config))
if (!GSreopen(do_full_restart, true, old_config))
pxFailRel("Failed to do full GS reopen");
return;
}
@@ -709,7 +729,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
GSConfig.SWExtraThreads != old_config.SWExtraThreads ||
GSConfig.SWExtraThreadsHeight != old_config.SWExtraThreadsHeight)
{
if (!GSreopen(false, old_config))
if (!GSreopen(false, true, old_config))
pxFailRel("Failed to do quick GS reopen");
return;
@@ -745,6 +765,8 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
GSConfig.UserHacks_CPUCLUTRender != old_config.UserHacks_CPUCLUTRender ||
GSConfig.UserHacks_GPUTargetCLUTMode != old_config.UserHacks_GPUTargetCLUTMode)
{
if (GSConfig.UserHacks_ReadTCOnClose)
g_gs_renderer->ReadbackTextureCache();
g_gs_renderer->PurgeTextureCache();
g_gs_renderer->PurgePool();
}
@@ -787,7 +809,7 @@ void GSSwitchRenderer(GSRendererType new_renderer)
const bool recreate_display = (!is_software_switch && existing_api != GetAPIForRenderer(new_renderer));
const Pcsx2Config::GSOptions old_config(GSConfig);
GSConfig.Renderer = new_renderer;
if (!GSreopen(recreate_display, old_config))
if (!GSreopen(recreate_display, true, old_config))
pxFailRel("Failed to reopen GS for renderer switch.");
}

View File

@@ -53,7 +53,7 @@ s16 GSLookupBeforeDrawFunctionId(const std::string_view& name);
int GSinit();
void GSshutdown();
bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* basemem);
bool GSreopen(bool recreate_display, const Pcsx2Config::GSOptions& old_config);
bool GSreopen(bool recreate_display, bool recreate_renderer, const Pcsx2Config::GSOptions& old_config);
void GSreset(bool hardware_reset);
void GSclose();
void GSgifSoftReset(u32 mask);

View File

@@ -24,15 +24,6 @@ const CRC::Game CRC::m_games[] =
{
// Note: IDs 0x7ACF7E03, 0x7D4EA48F, 0x37C53760 - shouldn't be added as it's from the multiloaders when packing games.
{0x00000000, NoTitle /* NoRegion */},
{0x9AAC5309, FFX2 /* EU */},
{0x9AAC530C, FFX2 /* FR */},
{0x9AAC530A, FFX2 /* ES */},
{0x9AAC530D, FFX2 /* DE */},
{0x9AAC530B, FFX2 /* IT */},
{0x48FE0C71, FFX2 /* US */},
{0x8A6D7F14, FFX2 /* JP */},
{0xE1FD9A2D, FFX2 /* JP */}, // int.
{0x11624CD6, FFX2 /* KO */},
{0x08C1ED4D, HauntingGround /* EU */},
{0x2CD5794C, HauntingGround /* EU */},
{0x867BB945, HauntingGround /* JP */},

View File

@@ -23,7 +23,6 @@ public:
enum Title : u32
{
NoTitle,
FFX2,
GetawayGames,
HauntingGround,
ICO,

View File

@@ -180,6 +180,12 @@ public:
return BNHelper(*this, x, y).value();
}
/// Get the block number of the given pixel, without wrapping to MAX_BLOCKS
u32 bnNoWrap(int x, int y) const
{
return BNHelper(*this, x, y).valueNoWrap();
}
/// Get a helper class for efficiently calculating multiple block numbers
BNHelper bnMulti(int x, int y) const
{
@@ -481,7 +487,7 @@ public:
static psm_t m_psm[64];
static readImage m_readImageX;
static const int m_vmsize = 1024 * 1024 * 4;
static constexpr int m_vmsize = 1024 * 1024 * 4;
u8* m_vm8;
@@ -981,10 +987,15 @@ public:
off.loopPixels(r, vm32(), (u32*)src, pitch, [&](u32* dst, u32* src) { *dst = *src; });
}
void WritePixel32(u8* RESTRICT src, u32 pitch, const GSOffset& off, const GSVector4i& r, u32 write_mask)
{
off.loopPixels(r, vm32(), (u32*)src, pitch, [&](u32* dst, u32* src) { *dst = (*dst & ~write_mask) | (*src & write_mask); });
}
void WritePixel24(u8* RESTRICT src, u32 pitch, const GSOffset& off, const GSVector4i& r)
{
off.loopPixels(r, vm32(), (u32*)src, pitch,
[&](u32* dst, u32* src)
[&](u32* dst, u32* src)
{
*dst = (*dst & 0xff000000) | (*src & 0x00ffffff);
});

View File

@@ -26,6 +26,7 @@
#include <iomanip> // Dump Verticles
int GSState::s_n = 0;
int GSState::s_transfer_n = 0;
static __fi bool IsAutoFlushEnabled()
{
@@ -37,14 +38,61 @@ static __fi bool IsFirstProvokingVertex()
return (GSConfig.Renderer != GSRendererType::SW && !g_gs_device->Features().provoking_vertex_last);
}
constexpr int GSState::GetSaveStateSize()
{
int size = 0;
size += sizeof(STATE_VERSION);
size += sizeof(m_env.PRIM);
size += sizeof(m_env.PRMODECONT);
size += sizeof(m_env.TEXCLUT);
size += sizeof(m_env.SCANMSK);
size += sizeof(m_env.TEXA);
size += sizeof(m_env.FOGCOL);
size += sizeof(m_env.DIMX);
size += sizeof(m_env.DTHE);
size += sizeof(m_env.COLCLAMP);
size += sizeof(m_env.PABE);
size += sizeof(m_env.BITBLTBUF);
size += sizeof(m_env.TRXDIR);
size += sizeof(m_env.TRXPOS);
size += sizeof(m_env.TRXREG);
size += sizeof(m_env.TRXREG); // obsolete
for (int i = 0; i < 2; i++)
{
size += sizeof(m_env.CTXT[i].XYOFFSET);
size += sizeof(m_env.CTXT[i].TEX0);
size += sizeof(m_env.CTXT[i].TEX1);
size += sizeof(m_env.CTXT[i].CLAMP);
size += sizeof(m_env.CTXT[i].MIPTBP1);
size += sizeof(m_env.CTXT[i].MIPTBP2);
size += sizeof(m_env.CTXT[i].SCISSOR);
size += sizeof(m_env.CTXT[i].ALPHA);
size += sizeof(m_env.CTXT[i].TEST);
size += sizeof(m_env.CTXT[i].FBA);
size += sizeof(m_env.CTXT[i].FRAME);
size += sizeof(m_env.CTXT[i].ZBUF);
}
size += sizeof(m_v.RGBAQ);
size += sizeof(m_v.ST);
size += sizeof(m_v.UV);
size += sizeof(m_v.FOG);
size += sizeof(m_v.XYZ);
size += sizeof(GIFReg); // obsolete
size += sizeof(m_tr.x);
size += sizeof(m_tr.y);
size += GSLocalMemory::m_vmsize;
size += (sizeof(GIFPath::tag) + sizeof(GIFPath::reg)) * 4 /* std::size(GSState::m_path) */; // std::size won't work without an instance.
size += sizeof(m_q);
return size;
}
GSState::GSState()
: m_version(STATE_VERSION)
, m_q(1.0f)
, m_scanmask_used(0)
, tex_flushed(true)
, m_vt(this, IsFirstProvokingVertex())
, m_regs(NULL)
, m_crc(0)
: m_vt(this, IsFirstProvokingVertex())
{
// m_nativeres seems to be a hack. Unfortunately it impacts draw call number which make debug painful in the replayer.
// Let's keep it disabled to ease debug.
@@ -63,54 +111,6 @@ GSState::GSState()
m_draw_transfers.clear();
m_sssize = 0;
m_sssize += sizeof(m_version);
m_sssize += sizeof(m_env.PRIM);
m_sssize += sizeof(m_env.PRMODECONT);
m_sssize += sizeof(m_env.TEXCLUT);
m_sssize += sizeof(m_env.SCANMSK);
m_sssize += sizeof(m_env.TEXA);
m_sssize += sizeof(m_env.FOGCOL);
m_sssize += sizeof(m_env.DIMX);
m_sssize += sizeof(m_env.DTHE);
m_sssize += sizeof(m_env.COLCLAMP);
m_sssize += sizeof(m_env.PABE);
m_sssize += sizeof(m_env.BITBLTBUF);
m_sssize += sizeof(m_env.TRXDIR);
m_sssize += sizeof(m_env.TRXPOS);
m_sssize += sizeof(m_env.TRXREG);
m_sssize += sizeof(m_env.TRXREG); // obsolete
for (int i = 0; i < 2; i++)
{
m_sssize += sizeof(m_env.CTXT[i].XYOFFSET);
m_sssize += sizeof(m_env.CTXT[i].TEX0);
m_sssize += sizeof(m_env.CTXT[i].TEX1);
m_sssize += sizeof(m_env.CTXT[i].CLAMP);
m_sssize += sizeof(m_env.CTXT[i].MIPTBP1);
m_sssize += sizeof(m_env.CTXT[i].MIPTBP2);
m_sssize += sizeof(m_env.CTXT[i].SCISSOR);
m_sssize += sizeof(m_env.CTXT[i].ALPHA);
m_sssize += sizeof(m_env.CTXT[i].TEST);
m_sssize += sizeof(m_env.CTXT[i].FBA);
m_sssize += sizeof(m_env.CTXT[i].FRAME);
m_sssize += sizeof(m_env.CTXT[i].ZBUF);
}
m_sssize += sizeof(m_v.RGBAQ);
m_sssize += sizeof(m_v.ST);
m_sssize += sizeof(m_v.UV);
m_sssize += sizeof(m_v.FOG);
m_sssize += sizeof(m_v.XYZ);
m_sssize += sizeof(GIFReg); // obsolete
m_sssize += sizeof(m_tr.x);
m_sssize += sizeof(m_tr.y);
m_sssize += m_mem.m_vmsize;
m_sssize += (sizeof(m_path[0].tag) + sizeof(m_path[0].reg)) * std::size(m_path);
m_sssize += sizeof(m_q);
PRIM = &m_env.PRIM;
//CSR->rREV = 0x20;
m_env.PRMODECONT.AC = 1;
@@ -285,6 +285,14 @@ void GSState::ResetHandlers()
m_fpGIFRegHandlers[GIF_A_D_REG_LABEL] = &GSState::GIFRegHandlerNull;
}
void GSState::ResetPCRTC()
{
PCRTCDisplays.SetVideoMode(GetVideoMode());
PCRTCDisplays.EnableDisplays(m_regs->PMODE, m_regs->SMODE2, isReallyInterlaced());
PCRTCDisplays.SetRects(0, m_regs->DISP[0].DISPLAY, m_regs->DISP[0].DISPFB);
PCRTCDisplays.SetRects(1, m_regs->DISP[1].DISPLAY, m_regs->DISP[1].DISPFB);
}
void GSState::UpdateSettings(const Pcsx2Config::GSOptions& old_config)
{
m_mipmap = GSConfig.Mipmap;
@@ -340,264 +348,6 @@ GSVideoMode GSState::GetVideoMode()
__assume(0); // unreachable
}
bool GSState::IsAnalogue()
{
return GetVideoMode() == GSVideoMode::NTSC || GetVideoMode() == GSVideoMode::PAL || GetVideoMode() == GSVideoMode::HDTV_1080I;
}
GSVector4i GSState::GetFrameMagnifiedRect(int i)
{
GSVector4i rectangle = { 0, 0, 0, 0 };
if (!IsEnabled(i))
return rectangle;
const int videomode = static_cast<int>(GetVideoMode()) - 1;
const auto& DISP = m_regs->DISP[i].DISPLAY;
const bool ignore_offset = !GSConfig.PCRTCOffsets;
const u32 DW = DISP.DW + 1;
const u32 DH = DISP.DH + 1;
;
// The number of sub pixels to draw are given in DH and DW, the MAGH/V relates to the size of the original square in the FB
// but the size it's drawn to uses the default size of the display mode (for PAL/NTSC this is a MAGH of 3)
// so for example a game will have a DW of 2559 and a MAGH of 4 to make it 512 (from the FB), but because it's drawing 2560 subpixels
// it will cover the entire 640 wide of the screen (2560 / (3+1)).
int width;
int height;
if (ignore_offset)
{
width = (DW / (DISP.MAGH + 1));
height = (DH / (DISP.MAGV + 1));
}
else
{
width = (DW / (VideoModeDividers[videomode].x + 1));
height = (DH / (VideoModeDividers[videomode].y + 1));
}
int res_multi = 1;
if (isinterlaced() && m_regs->SMODE2.FFMD && height > 1)
res_multi = 2;
// Set up the display rectangle based on the values obtained from DISPLAY registers
rectangle.right = width;
rectangle.bottom = height / res_multi;
return rectangle;
}
int GSState::GetDisplayHMagnification()
{
// Pick one of the DISPLAY's and hope that they are both the same. Favour DISPLAY[1]
for (int i = 1; i >= 0; i--)
{
if (IsEnabled(i))
return m_regs->DISP[i].DISPLAY.MAGH + 1;
}
// If neither DISPLAY is enabled, fallback to resolution offset (should never happen)
const int videomode = static_cast<int>(GetVideoMode()) - 1;
return VideoModeDividers[videomode].x + 1;
}
GSVector4i GSState::GetDisplayRect(int i)
{
GSVector4i rectangle = { 0, 0, 0, 0 };
if (i == -1)
{
return GetDisplayRect(0).runion(GetDisplayRect(1));
}
if (!IsEnabled(i))
return rectangle;
const auto& DISP = m_regs->DISP[i].DISPLAY;
const u32 DW = DISP.DW + 1;
const u32 DH = DISP.DH + 1;
const u32 MAGH = DISP.MAGH + 1;
const u32 MAGV = DISP.MAGV + 1;
const GSVector2i magnification(MAGH, MAGV);
const int width = DW / magnification.x;
const int height = DH / magnification.y;
const GSVector2i offsets = GetResolutionOffset(i);
// Set up the display rectangle based on the values obtained from DISPLAY registers
rectangle.left = offsets.x;
rectangle.top = offsets.y;
rectangle.right = rectangle.left + width;
rectangle.bottom = rectangle.top + height;
return rectangle;
}
GSVector2i GSState::GetResolutionOffset(int i)
{
GSVector2i offset = { 0, 0 };
if (!IsEnabled(i))
return offset;
const int videomode = static_cast<int>(GetVideoMode()) - 1;
const auto& DISP = m_regs->DISP[i].DISPLAY;
const auto& SMODE2 = m_regs->SMODE2;
const int res_multi = (SMODE2.INT + 1);
const GSVector4i offsets = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode] : VideoModeOffsetsOverscan[videomode];
offset.x = (static_cast<int>(DISP.DX) - offsets.z) / (VideoModeDividers[videomode].x + 1);
offset.y = (static_cast<int>(DISP.DY) - (offsets.w * ((IsAnalogue() && res_multi) ? res_multi : 1))) / (VideoModeDividers[videomode].y + 1);
return offset;
}
GSVector2i GSState::GetResolution()
{
const int videomode = static_cast<int>(GetVideoMode()) - 1;
const bool ignore_offset = !GSConfig.PCRTCOffsets;
const GSVector4i offsets = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode] : VideoModeOffsetsOverscan[videomode];
GSVector2i resolution(offsets.x, offsets.y);
// The resolution of the framebuffer is double when in FRAME mode and interlaced.
// Also we need a special check because no-interlace patches like to render in the original height, but in non-interlaced mode
// which means it would normally go off the bottom of the screen. Advantages of emulation, i guess... Limited to Ignore Offsets + Deinterlacing = Off.
if ((isinterlaced() && !m_regs->SMODE2.FFMD) || (GSConfig.InterlaceMode == GSInterlaceMode::Off && !GSConfig.PCRTCOffsets && !isinterlaced()))
resolution.y *= 2;
if (ignore_offset)
{
// Ideally we'd just cut the width at the resolution, but of course we have to hack the hack...
// Some games (Mortal Kombat Armageddon) render the image at 834 pixels then shrink it to 624 pixels
// which does fit, but when we ignore offsets we go on framebuffer size and some other games
// such as Johnny Mosleys Mad Trix and Transformers render too much but design it to go off the screen.
int magnified_width = (VideoModeDividers[videomode].z + 1) / GetDisplayHMagnification();
// When viewing overscan allow up to the overscan size
if (GSConfig.PCRTCOverscan)
magnified_width = std::max(magnified_width, offsets.x);
GSVector4i total_rect = GetDisplayRect(0).runion(GetDisplayRect(1));
total_rect.z = total_rect.z - total_rect.x;
total_rect.w = total_rect.w - total_rect.y;
total_rect.z = std::min(total_rect.z, magnified_width);
total_rect.w = std::min(total_rect.w, resolution.y);
resolution.x = total_rect.z;
resolution.y = total_rect.w;
}
return resolution;
}
GSVector4i GSState::GetFrameRect(int i, bool ignore_off)
{
// If no specific context is requested then pass the merged rectangle as return value
if (i == -1)
return GetFrameRect(0, ignore_off).runion(GetFrameRect(1, ignore_off));
GSVector4i rectangle = { 0, 0, 0, 0 };
if (!IsEnabled(i))
return rectangle;
const auto& DISP = m_regs->DISP[i].DISPLAY;
const u32 DW = DISP.DW + 1;
const u32 DH = DISP.DH + 1;
const GSVector2i magnification(DISP.MAGH+1, DISP.MAGV + 1);
const u32 DBX = m_regs->DISP[i].DISPFB.DBX;
int DBY = m_regs->DISP[i].DISPFB.DBY;
const int w = DW / magnification.x;
const int h = DH / magnification.y;
// If the combined height overflows 2048, it's likely adding a bit of extra data before the picture for offsetting the interlace
// only game known to do this is NASCAR '08
if (!ignore_off && (DBY + h) >= 2048)
DBY = DBY - 2048;
rectangle.left = (ignore_off) ? 0 : DBX;
rectangle.top = (ignore_off) ? 0 : DBY;
rectangle.right = rectangle.left + w;
rectangle.bottom = rectangle.top + h;
if (isinterlaced() && m_regs->SMODE2.FFMD && h > 1)
{
rectangle.bottom += 1;
rectangle.bottom >>= 1;
}
return rectangle;
}
int GSState::GetFramebufferHeight()
{
// Framebuffer height is 11 bits max
constexpr int height_limit = (1 << 11);
const GSVector4i disp1_rect = GetFrameRect(0, true);
const GSVector4i disp2_rect = GetFrameRect(1, true);
const GSVector4i combined = disp1_rect.runion(disp2_rect);
// DBY isn't an offset to the frame memory but rather an offset to read output circuit inside
// the frame memory, hence the top offset should also be calculated for the total height of the
// frame memory. Also we need to wrap the value only when we're dealing with values with range of the
// frame memory (offset + read output circuit height, IOW bottom of merged_output)
const int max_height = std::max(disp1_rect.height(), disp2_rect.height());
const int frame_memory_height = std::max(max_height, combined.bottom % height_limit);
if (frame_memory_height > 1024)
GL_PERF("Massive framebuffer height detected! (height:%d)", frame_memory_height);
return frame_memory_height;
}
int GSState::GetFramebufferBitDepth()
{
if (IsEnabled(0))
return GSLocalMemory::m_psm[m_regs->DISP[0].DISPFB.PSM].bpp;
else if (IsEnabled(1))
return GSLocalMemory::m_psm[m_regs->DISP[1].DISPFB.PSM].bpp;
return 32;
}
int GSState::GetFramebufferWidth()
{
const GSVector4i disp1_rect = GetFrameRect(0, true);
const GSVector4i disp2_rect = GetFrameRect(1, true);
const int max_width = std::max(disp1_rect.width(), disp2_rect.width());
return max_width;
}
bool GSState::IsEnabled(int i)
{
ASSERT(i >= 0 && i < 2);
const auto& DISP = m_regs->DISP[i].DISPLAY;
const bool disp1_enabled = m_regs->PMODE.EN1;
const bool disp2_enabled = m_regs->PMODE.EN2;
if ((i == 0 && disp1_enabled) || (i == 1 && disp2_enabled))
return DISP.DW && DISP.DH;
return false;
}
float GSState::GetTvRefreshRate()
{
const GSVideoMode videomode = GetVideoMode();
@@ -1771,6 +1521,7 @@ void GSState::FlushWrite()
m_tr.start += len;
g_perfmon.Put(GSPerfMon::Swizzle, len);
s_transfer_n++;
}
// This function decides if the context has changed in a way which warrants flushing the draw.
@@ -2007,14 +1758,26 @@ void GSState::Write(const u8* mem, int len)
// Invalid the CLUT if it crosses paths.
m_mem.m_clut.InvalidateRange(write_start_bp, write_end_bp);
if (GSConfig.PreloadFrameWithGSData)
GSVector4i r;
r.left = m_env.TRXPOS.DSAX;
r.top = m_env.TRXPOS.DSAY;
r.right = r.left + m_env.TRXREG.RRW;
r.bottom = r.top + m_env.TRXREG.RRH;
// Store the transfer for preloading new RT's.
if ((m_draw_transfers.size() > 0 && blit.DBP == m_draw_transfers.back().blit.DBP))
{
// Store the transfer for preloading new RT's.
if (m_draw_transfers.size() == 0 || (m_draw_transfers.size() > 0 && blit.DBP != m_draw_transfers.back().blit.DBP))
{
GSUploadQueue new_transfer = { blit, s_n };
m_draw_transfers.push_back(new_transfer);
}
// Same BP, let's update the rect.
GSUploadQueue transfer = m_draw_transfers.back();
m_draw_transfers.pop_back();
transfer.rect = transfer.rect.runion(r);
m_draw_transfers.push_back(transfer);
}
else
{
GSUploadQueue new_transfer = { blit, r, s_n };
m_draw_transfers.push_back(new_transfer);
}
GL_CACHE("Write! ... => 0x%x W:%d F:%s (DIR %d%d), dPos(%d %d) size(%d %d)",
@@ -2025,12 +1788,6 @@ void GSState::Write(const u8* mem, int len)
if (m_tr.end == 0 && len >= m_tr.total)
{
// received all data in one piece, no need to buffer it
GSVector4i r;
r.left = m_env.TRXPOS.DSAX;
r.top = m_env.TRXPOS.DSAY;
r.right = r.left + m_env.TRXREG.RRW;
r.bottom = r.top + m_env.TRXREG.RRH;
ExpandTarget(m_env.BITBLTBUF, r);
InvalidateVideoMem(blit, r, true);
@@ -2039,6 +1796,7 @@ void GSState::Write(const u8* mem, int len)
m_tr.start = m_tr.end = m_tr.total;
g_perfmon.Put(GSPerfMon::Swizzle, len);
s_transfer_n++;
}
else
{
@@ -2105,6 +1863,7 @@ void GSState::Move()
{
// ffxii uses this to move the top/bottom of the scrolling menus offscreen and then blends them back over the text to create a shading effect
// guitar hero copies the far end of the board to do a similar blend too
s_transfer_n++;
int sx = m_env.TRXPOS.SSAX;
int sy = m_env.TRXPOS.SSAY;
@@ -2164,15 +1923,26 @@ void GSState::Move()
Flush(GSFlushReason::LOCALTOLOCALMOVE);
}
if (GSConfig.PreloadFrameWithGSData)
GSVector4i r;
r.left = m_env.TRXPOS.DSAX;
r.top = m_env.TRXPOS.DSAY;
r.right = r.left + m_env.TRXREG.RRW;
r.bottom = r.top + m_env.TRXREG.RRH;
// Store the transfer for preloading new RT's.
if ((m_draw_transfers.size() > 0 && m_env.BITBLTBUF.DBP == m_draw_transfers.back().blit.DBP))
{
// Store the transfer for preloading new RT's.
if (m_draw_transfers.size() == 0 || (m_draw_transfers.size() > 0 && dbp != m_draw_transfers.back().blit.DBP))
{
GSUploadQueue new_transfer = { m_env.BITBLTBUF, s_n };
m_draw_transfers.push_back(new_transfer);
}
// Same BP, let's update the rect.
GSUploadQueue transfer = m_draw_transfers.back();
m_draw_transfers.pop_back();
transfer.rect = transfer.rect.runion(r);
m_draw_transfers.push_back(transfer);
}
else
{
GSUploadQueue new_transfer = { m_env.BITBLTBUF, r, s_n };
m_draw_transfers.push_back(new_transfer);
}
// Invalid the CLUT if it crosses paths.
m_mem.m_clut.InvalidateRange(write_start_bp, write_end_bp);
@@ -2370,6 +2140,19 @@ void GSState::ReadLocalMemoryUnsync(u8* mem, int qwc, GIFRegBITBLTBUF BITBLTBUF,
m_mem.ReadImageX(tb.x, tb.y, mem, len, BITBLTBUF, TRXPOS, TRXREG);
}
void GSState::PurgePool()
{
g_gs_device->PurgePool();
}
void GSState::PurgeTextureCache()
{
}
void GSState::ReadbackTextureCache()
{
}
template void GSState::Transfer<0>(const u8* mem, u32 size);
template void GSState::Transfer<1>(const u8* mem, u32 size);
template void GSState::Transfer<2>(const u8* mem, u32 size);
@@ -2580,18 +2363,22 @@ int GSState::Freeze(freezeData* fd, bool sizeonly)
{
if (sizeonly)
{
fd->size = m_sssize;
fd->size = GetSaveStateSize();
return 0;
}
if (!fd->data || fd->size < m_sssize)
if (!fd->data || fd->size < GetSaveStateSize())
return -1;
Flush(GSFlushReason::SAVESTATE);
u8* data = fd->data;
if (GSConfig.UserHacks_ReadTCOnClose)
ReadbackTextureCache();
WriteState(data, &m_version);
u8* data = fd->data;
const u32 version = STATE_VERSION;
WriteState(data, &version);
WriteState(data, &m_env.PRIM);
WriteState(data, &m_env.PRMODECONT);
WriteState(data, &m_env.TEXCLUT);
@@ -2659,7 +2446,7 @@ int GSState::Defrost(const freezeData* fd)
if (!fd || !fd->data || fd->size == 0)
return -1;
if (fd->size < m_sssize)
if (fd->size < GetSaveStateSize())
return -1;
u8* data = fd->data;
@@ -2668,7 +2455,7 @@ int GSState::Defrost(const freezeData* fd)
ReadState(&version, data);
if (version > m_version)
if (version > STATE_VERSION)
{
Console.Error("GS: Savestate version is incompatible. Load aborted.");
return -1;
@@ -2775,6 +2562,8 @@ int GSState::Defrost(const freezeData* fd)
g_perfmon.SetFrame(5000);
ResetPCRTC();
return 0;
}
@@ -3164,7 +2953,7 @@ __forceinline void GSState::HandleAutoFlush()
const GSVector2i offset = GSVector2i(m_context->XYOFFSET.OFX, m_context->XYOFFSET.OFY);
const GSVector4i scissor = GSVector4i(m_context->scissor.in);
GSVector4i old_tex_rect = GSVector4i(0, 0, 0, 0);
GSVector4i old_tex_rect = GSVector4i::zero();
int current_draw_end = m_index.tail;
while (current_draw_end >= n)
@@ -4098,9 +3887,6 @@ GIFRegTEX0 GSState::GetTex0Layer(u32 lod)
GSState::GSTransferBuffer::GSTransferBuffer()
{
x = y = 0;
start = end = total = 0;
constexpr size_t alloc_size = 1024 * 1024 * 4;
buff = reinterpret_cast<u8*>(_aligned_malloc(alloc_size, 32));
}

View File

@@ -35,13 +35,15 @@ public:
GSState();
virtual ~GSState();
static constexpr int GetSaveStateSize();
private:
// RESTRICT prevents multiple loads of the same part of the register when accessing its bitfields (the compiler is happy to know that memory writes in-between will not go there)
typedef void (GSState::*GIFPackedRegHandler)(const GIFPackedReg* RESTRICT r);
GIFPackedRegHandler m_fpGIFPackedRegHandlers[16];
GIFPackedRegHandler m_fpGIFPackedRegHandlerXYZ[8][4];
GIFPackedRegHandler m_fpGIFPackedRegHandlers[16] = {};
GIFPackedRegHandler m_fpGIFPackedRegHandlerXYZ[8][4] = {};
void CheckFlushes();
@@ -58,14 +60,14 @@ private:
typedef void (GSState::*GIFRegHandler)(const GIFReg* RESTRICT r);
GIFRegHandler m_fpGIFRegHandlers[256];
GIFRegHandler m_fpGIFRegHandlerXYZ[8][4];
GIFRegHandler m_fpGIFRegHandlers[256] = {};
GIFRegHandler m_fpGIFRegHandlerXYZ[8][4] = {};
typedef void (GSState::*GIFPackedRegHandlerC)(const GIFPackedReg* RESTRICT r, u32 size);
GIFPackedRegHandlerC m_fpGIFPackedRegHandlersC[2];
GIFPackedRegHandlerC m_fpGIFPackedRegHandlerSTQRGBAXYZF2[8];
GIFPackedRegHandlerC m_fpGIFPackedRegHandlerSTQRGBAXYZ2[8];
GIFPackedRegHandlerC m_fpGIFPackedRegHandlersC[2] = {};
GIFPackedRegHandlerC m_fpGIFPackedRegHandlerSTQRGBAXYZF2[8] = {};
GIFPackedRegHandlerC m_fpGIFPackedRegHandlerSTQRGBAXYZ2[8] = {};
template<u32 prim, bool auto_flush, bool index_swap> void GIFPackedRegHandlerSTQRGBAXYZF2(const GIFPackedReg* RESTRICT r, u32 size);
template<u32 prim, bool auto_flush, bool index_swap> void GIFPackedRegHandlerSTQRGBAXYZ2(const GIFPackedReg* RESTRICT r, u32 size);
@@ -117,15 +119,12 @@ private:
template<bool auto_flush, bool index_swap>
void SetPrimHandlers();
u32 m_version;
int m_sssize;
struct GSTransferBuffer
{
int x, y;
int start, end, total;
u8* buff;
GIFRegBITBLTBUF m_blit;
int x = 0, y = 0;
int start = 0, end = 0, total = 0;
u8* buff = nullptr;
GIFRegBITBLTBUF m_blit = {};
GSTransferBuffer();
virtual ~GSTransferBuffer();
@@ -139,14 +138,14 @@ private:
void CalcAlphaMinMax();
protected:
GSVertex m_v;
float m_q;
GSVector4i m_scissor;
GSVector4i m_ofxy;
GSVertex m_v = {};
float m_q = 1.0f;
GSVector4i m_scissor = {};
GSVector4i m_ofxy = {};
u8 m_scanmask_used;
bool tex_flushed;
bool m_isPackedUV_HackFlag;
u8 m_scanmask_used = 0;
bool tex_flushed = true;
bool m_isPackedUV_HackFlag = false;
struct
{
@@ -154,13 +153,13 @@ protected:
u32 head, tail, next, maxcount; // head: first vertex, tail: last vertex + 1, next: last indexed + 1
u32 xy_tail;
u64 xy[4];
} m_vertex;
} m_vertex = {};
struct
{
u32* buff;
u32 tail;
} m_index;
} m_index = {};
void UpdateContext();
void UpdateScissor();
@@ -210,29 +209,31 @@ public:
struct GSUploadQueue
{
GIFRegBITBLTBUF blit;
GSVector4i rect;
int draw;
};
GIFPath m_path[4];
GIFRegPRIM* PRIM;
GSPrivRegSet* m_regs;
GIFPath m_path[4] = {};
GIFRegPRIM* PRIM = nullptr;
GSPrivRegSet* m_regs = nullptr;
GSLocalMemory m_mem;
GSDrawingEnvironment m_env;
GSDrawingEnvironment m_backup_env;
GSDrawingEnvironment m_prev_env;
GSVector4i temp_draw_rect;
GSDrawingContext* m_context;
u32 m_crc;
CRC::Game m_game;
GSDrawingEnvironment m_env = {};
GSDrawingEnvironment m_backup_env = {};
GSDrawingEnvironment m_prev_env = {};
GSVector4i temp_draw_rect = {};
GSDrawingContext* m_context = nullptr;
u32 m_crc = 0;
CRC::Game m_game = {};
std::unique_ptr<GSDumpBase> m_dump;
bool m_nativeres;
bool m_mipmap;
u32 m_dirty_gs_regs;
int m_backed_up_ctx;
bool m_nativeres = false;
bool m_mipmap = false;
u8 m_force_preload = 0;
u32 m_dirty_gs_regs = 0;
int m_backed_up_ctx = 0;
std::vector<GSUploadQueue> m_draw_transfers;
bool m_force_preload;
static int s_n;
static int s_transfer_n;
static constexpr u32 STATE_VERSION = 8;
@@ -278,7 +279,7 @@ public:
GSREOPEN = 1 << 13,
};
GSFlushReason m_state_flush_reason;
GSFlushReason m_state_flush_reason = UNKNOWN;
enum PRIM_OVERLAP
{
@@ -287,62 +288,592 @@ public:
PRIM_OVERLAP_NO
};
PRIM_OVERLAP m_prim_overlap;
PRIM_OVERLAP m_prim_overlap = PRIM_OVERLAP_UNKNOW;
std::vector<size_t> m_drawlist;
// The horizontal offset values (under z) for PAL and NTSC have been tweaked
// they should be apparently 632 and 652 respectively, but that causes a thick black line on the left
// these values leave a small black line on the right in a bunch of games, but it's not so bad.
// The only conclusion I can come to is there is horizontal overscan expected so there would normally
// be black borders either side anyway, or both sides slightly covered.
const GSVector4i VideoModeOffsets[6] = {
GSVector4i(640, 224, 642, 25),
GSVector4i(640, 256, 676, 36),
GSVector4i(640, 480, 276, 34),
GSVector4i(720, 480, 232, 35),
GSVector4i(1280, 720, 302, 24),
GSVector4i(1920, 540, 238, 40)
};
struct GSPCRTCRegs
{
// The horizontal offset values (under z) for PAL and NTSC have been tweaked
// they should be apparently 632 and 652 respectively, but that causes a thick black line on the left
// these values leave a small black line on the right in a bunch of games, but it's not so bad.
// The only conclusion I can come to is there is horizontal overscan expected so there would normally
// be black borders either side anyway, or both sides slightly covered.
static inline constexpr GSVector4i VideoModeOffsets[6] = {
GSVector4i::cxpr(640, 224, 642, 25),
GSVector4i::cxpr(640, 256, 676, 36),
GSVector4i::cxpr(640, 480, 276, 34),
GSVector4i::cxpr(720, 480, 232, 35),
GSVector4i::cxpr(1280, 720, 302, 24),
GSVector4i::cxpr(1920, 540, 238, 40)
};
const GSVector4i VideoModeOffsetsOverscan[6] = {
GSVector4i(711, 243, 498, 12),
GSVector4i(702, 288, 532, 18),
GSVector4i(640, 480, 276, 34),
GSVector4i(720, 480, 232, 35),
GSVector4i(1280, 720, 302, 24),
GSVector4i(1920, 540, 238, 40)
};
static inline constexpr GSVector4i VideoModeOffsetsOverscan[6] = {
GSVector4i::cxpr(711, 243, 498, 12),
GSVector4i::cxpr(702, 288, 532, 18),
GSVector4i::cxpr(640, 480, 276, 34),
GSVector4i::cxpr(720, 480, 232, 35),
GSVector4i::cxpr(1280, 720, 302, 24),
GSVector4i::cxpr(1920, 540, 238, 40)
};
const GSVector4i VideoModeDividers[6] = {
GSVector4i(3, 0, 2559, 239),
GSVector4i(3, 0, 2559, 287),
GSVector4i(1, 0, 1279, 479),
GSVector4i(1, 0, 1439, 479),
GSVector4i(0, 0, 1279, 719),
GSVector4i(0, 0, 1919, 1079)
};
static inline constexpr GSVector4i VideoModeDividers[6] = {
GSVector4i::cxpr(3, 0, 2559, 239),
GSVector4i::cxpr(3, 0, 2559, 287),
GSVector4i::cxpr(1, 0, 1279, 479),
GSVector4i::cxpr(1, 0, 1439, 479),
GSVector4i::cxpr(0, 0, 1279, 719),
GSVector4i::cxpr(0, 0, 1919, 1079)
};
struct PCRTCDisplay
{
bool enabled;
int FBP;
int FBW;
int PSM;
GSRegDISPFB prevFramebufferReg;
GSVector2i prevDisplayOffset;
GSVector2i displayOffset;
GSVector4i displayRect;
GSVector2i magnification;
GSVector2i prevFramebufferOffsets;
GSVector2i framebufferOffsets;
GSVector4i framebufferRect;
int Block()
{
return FBP << 5;
}
};
int videomode = 0;
int interlaced = 0;
int FFMD = 0;
bool PCRTCSameSrc = false;
bool toggling_field = false;
PCRTCDisplay PCRTCDisplays[2] = {};
bool IsAnalogue()
{
const GSVideoMode video = static_cast<GSVideoMode>(videomode + 1);
return video == GSVideoMode::NTSC || video == GSVideoMode::PAL || video == GSVideoMode::HDTV_1080I;
}
// Calculates which display is closest to matching zero offsets in either direction.
GSVector2i NearestToZeroOffset()
{
GSVector2i returnValue = { 1, 1 };
if (!PCRTCDisplays[0].enabled && !PCRTCDisplays[1].enabled)
return returnValue;
for (int i = 0; i < 2; i++)
{
if (!PCRTCDisplays[i].enabled)
{
returnValue.x = 1 - i;
returnValue.y = 1 - i;
return returnValue;
}
}
if (abs(PCRTCDisplays[0].displayOffset.x - VideoModeOffsets[videomode].z) <
abs(PCRTCDisplays[1].displayOffset.x - VideoModeOffsets[videomode].z))
returnValue.x = 0;
// When interlaced, the vertical base offset is doubled
const int verticalOffset = VideoModeOffsets[videomode].w * (1 << interlaced);
if (abs(PCRTCDisplays[0].displayOffset.y - verticalOffset) <
abs(PCRTCDisplays[1].displayOffset.y - verticalOffset))
returnValue.y = 0;
return returnValue;
}
void SetVideoMode(GSVideoMode videoModeIn)
{
videomode = static_cast<int>(videoModeIn) - 1;
}
// Enable each of the displays.
void EnableDisplays(GSRegPMODE pmode, GSRegSMODE2 smode2, bool smodetoggle)
{
PCRTCDisplays[0].enabled = pmode.EN1;
PCRTCDisplays[1].enabled = pmode.EN2;
interlaced = smode2.INT && IsAnalogue();
FFMD = smode2.FFMD;
toggling_field = smodetoggle && IsAnalogue();
}
void CheckSameSource()
{
if (PCRTCDisplays[0].enabled != PCRTCDisplays[1].enabled || (PCRTCDisplays[0].enabled | PCRTCDisplays[1].enabled) == false)
{
PCRTCSameSrc = false;
return;
}
PCRTCSameSrc = PCRTCDisplays[0].FBP == PCRTCDisplays[1].FBP &&
PCRTCDisplays[0].FBW == PCRTCDisplays[1].FBW &&
GSUtil::HasCompatibleBits(PCRTCDisplays[0].PSM, PCRTCDisplays[1].PSM);
}
bool FrameWrap()
{
const GSVector4i combined_rect = GSVector4i(PCRTCDisplays[0].framebufferRect.runion(PCRTCDisplays[1].framebufferRect));
return combined_rect.w >= 2048 || combined_rect.z >= 2048;
}
// If the start point of both frames match, we can do a single read
bool FrameRectMatch()
{
return PCRTCSameSrc;
}
GSVector2i GetResolution()
{
GSVector2i resolution;
const GSVector4i offsets = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode] : VideoModeOffsetsOverscan[videomode];
const bool is_full_height = interlaced || (toggling_field && GSConfig.InterlaceMode != GSInterlaceMode::Off) || GSConfig.InterlaceMode == GSInterlaceMode::Off;
if (!GSConfig.PCRTCOffsets)
{
if (PCRTCDisplays[0].enabled && PCRTCDisplays[1].enabled)
{
const GSVector4i combined_size = PCRTCDisplays[0].displayRect.runion(PCRTCDisplays[1].displayRect);
resolution = { combined_size.width(), combined_size.height() };
}
else if (PCRTCDisplays[0].enabled)
{
resolution = { PCRTCDisplays[0].displayRect.width(), PCRTCDisplays[0].displayRect.height() };
}
else
{
resolution = { PCRTCDisplays[1].displayRect.width(), PCRTCDisplays[1].displayRect.height() };
}
}
else
{
const int shift = is_full_height ? 1 : 0;
resolution = { offsets.x, offsets.y << shift };
}
resolution.x = std::min(resolution.x, offsets.x);
resolution.y = std::min(resolution.y, is_full_height ? offsets.y << 1 : offsets.y);
return resolution;
}
GSVector4i GetFramebufferRect(int display)
{
if (display == -1)
{
return GSVector4i(PCRTCDisplays[0].framebufferRect.runion(PCRTCDisplays[1].framebufferRect));
}
else
{
return PCRTCDisplays[display].framebufferRect;
}
}
int GetFramebufferBitDepth()
{
if (PCRTCDisplays[0].enabled)
return GSLocalMemory::m_psm[PCRTCDisplays[0].PSM].bpp;
else if (PCRTCDisplays[1].enabled)
return GSLocalMemory::m_psm[PCRTCDisplays[1].PSM].bpp;
return 32;
}
GSVector2i GetFramebufferSize(int display)
{
int max_height = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode].y : VideoModeOffsetsOverscan[videomode].y;
if (!(FFMD && interlaced))
{
max_height *= 2;
}
if (display == -1)
{
GSVector4i combined_rect = PCRTCDisplays[0].framebufferRect.runion(PCRTCDisplays[1].framebufferRect);
if (combined_rect.z >= 2048)
{
const int high_x = (PCRTCDisplays[0].framebufferRect.x > PCRTCDisplays[1].framebufferRect.x) ? PCRTCDisplays[0].framebufferRect.x : PCRTCDisplays[1].framebufferRect.x;
combined_rect.z -= GSConfig.UseHardwareRenderer() ? 2048 : high_x;
combined_rect.x = 0;
}
if (combined_rect.w >= 2048)
{
const int high_y = (PCRTCDisplays[0].framebufferRect.y > PCRTCDisplays[1].framebufferRect.y) ? PCRTCDisplays[0].framebufferRect.y : PCRTCDisplays[1].framebufferRect.y;
combined_rect.w -= GSConfig.UseHardwareRenderer() ? 2048 : high_y;
combined_rect.y = 0;
}
// Cap the framebuffer read to the maximum display height, otherwise the hardware renderer gets messy.
const int min_mag = std::max(1, std::min(PCRTCDisplays[0].magnification.y, PCRTCDisplays[1].magnification.y));
int offset = PCRTCDisplays[0].displayRect.runion(PCRTCDisplays[1].displayRect).y;
if (FFMD && interlaced)
{
offset = (offset - 1) / 2;
}
// Hardware mode needs a wider framebuffer as it can't offset the read.
if (GSConfig.UseHardwareRenderer())
{
combined_rect.z += std::max(PCRTCDisplays[0].framebufferOffsets.x, PCRTCDisplays[1].framebufferOffsets.x);
combined_rect.w += std::max(PCRTCDisplays[0].framebufferOffsets.y, PCRTCDisplays[1].framebufferOffsets.y);
}
offset = (max_height / min_mag) - offset;
combined_rect.w = std::min(combined_rect.w, offset);
return GSVector2i(combined_rect.z, combined_rect.w);
}
else
{
GSVector4i out_rect = PCRTCDisplays[display].framebufferRect;
if (out_rect.z >= 2048)
{
out_rect.z -= GSConfig.UseHardwareRenderer() ? 2048 : out_rect.x;
out_rect.x = 0;
}
if (out_rect.w >= 2048)
{
out_rect.w -= GSConfig.UseHardwareRenderer() ? 2048 : out_rect.y;
out_rect.y = 0;
}
// Cap the framebuffer read to the maximum display height, otherwise the hardware renderer gets messy.
const int min_mag = std::max(1, PCRTCDisplays[display].magnification.y);
int offset = PCRTCDisplays[display].displayRect.y;
if (FFMD && interlaced)
{
offset = (offset - 1) / 2;
}
offset = (max_height / min_mag) - offset;
out_rect.w = std::min(out_rect.w, offset);
// Hardware mode needs a wider framebuffer as it can't offset the read.
if (GSConfig.UseHardwareRenderer())
{
out_rect.z += PCRTCDisplays[display].framebufferOffsets.x;
out_rect.w += PCRTCDisplays[display].framebufferOffsets.y;
}
return GSVector2i(out_rect.z, out_rect.w);
}
}
// Sets up the rectangles for both the framebuffer read and the displays for the merge circuit.
void SetRects(int display, GSRegDISPLAY displayReg, GSRegDISPFB framebufferReg)
{
// Save framebuffer information first, while we're here.
PCRTCDisplays[display].FBP = framebufferReg.FBP;
PCRTCDisplays[display].FBW = framebufferReg.FBW;
PCRTCDisplays[display].PSM = framebufferReg.PSM;
PCRTCDisplays[display].prevFramebufferReg = framebufferReg;
// Probably not really enabled but will cause a mess.
// Q-Ball Billiards enables both circuits but doesn't set one of them up.
if (PCRTCDisplays[display].FBW == 0 && displayReg.DW == 0 && displayReg.DH == 0 && displayReg.MAGH == 0)
{
PCRTCDisplays[display].enabled = false;
return;
}
PCRTCDisplays[display].magnification = GSVector2i(displayReg.MAGH + 1, displayReg.MAGV + 1);
const u32 DW = displayReg.DW + 1;
const u32 DH = displayReg.DH + 1;
const int renderWidth = DW / PCRTCDisplays[display].magnification.x;
const int renderHeight = DH / PCRTCDisplays[display].magnification.y;
u32 finalDisplayWidth = renderWidth;
u32 finalDisplayHeight = renderHeight;
// When using screen offsets the screen gets squashed/resized in to the actual screen size.
if (GSConfig.PCRTCOffsets)
{
finalDisplayWidth = DW / (VideoModeDividers[videomode].x + 1);
finalDisplayHeight = DH / (VideoModeDividers[videomode].y + 1);
}
else
{
finalDisplayWidth = std::min(finalDisplayWidth ,DW / (VideoModeDividers[videomode].x + 1));
finalDisplayHeight = std::min(finalDisplayHeight, DH / (VideoModeDividers[videomode].y + 1));
}
// Framebuffer size and offsets.
PCRTCDisplays[display].prevFramebufferOffsets = PCRTCDisplays[display].framebufferOffsets;
PCRTCDisplays[display].framebufferRect.x = 0;
PCRTCDisplays[display].framebufferRect.y = 0;
PCRTCDisplays[display].framebufferRect.z = renderWidth;
if(FFMD && interlaced) // Round up the height as if it's an odd value, this will cause havok with the merge circuit.
PCRTCDisplays[display].framebufferRect.w = (renderHeight + 1) >> (FFMD * interlaced); // Half height read if FFMD + INT enabled.
else
PCRTCDisplays[display].framebufferRect.w = renderHeight;
PCRTCDisplays[display].framebufferOffsets.x = framebufferReg.DBX;
PCRTCDisplays[display].framebufferOffsets.y = framebufferReg.DBY;
const bool is_interlaced_resolution = interlaced || (toggling_field && GSConfig.InterlaceMode != GSInterlaceMode::Off);
// If the interlace flag isn't set, but it's still interlacing, the height is likely reported wrong.
// Q-Ball Billiards.
if (is_interlaced_resolution && !interlaced)
finalDisplayHeight *= 2;
// Display size and offsets.
PCRTCDisplays[display].displayRect.x = 0;
PCRTCDisplays[display].displayRect.y = 0;
PCRTCDisplays[display].displayRect.z = finalDisplayWidth;
PCRTCDisplays[display].displayRect.w = finalDisplayHeight;
PCRTCDisplays[display].prevDisplayOffset = PCRTCDisplays[display].displayOffset;
PCRTCDisplays[display].displayOffset.x = displayReg.DX;
PCRTCDisplays[display].displayOffset.y = displayReg.DY;
}
// Calculate framebuffer read offsets, should be considered if only one circuit is enabled, or difference is more than 1 line.
// Only considered if "Anti-blur" is enabled.
void CalculateFramebufferOffset()
{
if (GSConfig.PCRTCAntiBlur && PCRTCSameSrc)
{
if (abs(PCRTCDisplays[1].framebufferOffsets.y - PCRTCDisplays[0].framebufferOffsets.y) == 1
&& PCRTCDisplays[0].displayRect.y == PCRTCDisplays[1].displayRect.y)
{
if (PCRTCDisplays[1].framebufferOffsets.y < PCRTCDisplays[0].framebufferOffsets.y)
PCRTCDisplays[0].framebufferOffsets.y = PCRTCDisplays[1].framebufferOffsets.y;
else
PCRTCDisplays[1].framebufferOffsets.y = PCRTCDisplays[0].framebufferOffsets.y;
}
if (abs(PCRTCDisplays[1].framebufferOffsets.x - PCRTCDisplays[0].framebufferOffsets.x) == 1
&& PCRTCDisplays[0].displayRect.x == PCRTCDisplays[1].displayRect.x)
{
if (PCRTCDisplays[1].framebufferOffsets.x < PCRTCDisplays[0].framebufferOffsets.x)
PCRTCDisplays[0].framebufferOffsets.x = PCRTCDisplays[1].framebufferOffsets.x;
else
PCRTCDisplays[1].framebufferOffsets.x = PCRTCDisplays[0].framebufferOffsets.x;
}
}
PCRTCDisplays[0].framebufferRect.x += PCRTCDisplays[0].framebufferOffsets.x;
PCRTCDisplays[0].framebufferRect.z += PCRTCDisplays[0].framebufferOffsets.x;
PCRTCDisplays[0].framebufferRect.y += PCRTCDisplays[0].framebufferOffsets.y;
PCRTCDisplays[0].framebufferRect.w += PCRTCDisplays[0].framebufferOffsets.y;
PCRTCDisplays[1].framebufferRect.x += PCRTCDisplays[1].framebufferOffsets.x;
PCRTCDisplays[1].framebufferRect.z += PCRTCDisplays[1].framebufferOffsets.x;
PCRTCDisplays[1].framebufferRect.y += PCRTCDisplays[1].framebufferOffsets.y;
PCRTCDisplays[1].framebufferRect.w += PCRTCDisplays[1].framebufferOffsets.y;
}
// Used in software mode to align the buffer when reading. Offset is accounted for (block aligned) by GetOutput.
void RemoveFramebufferOffset(int display)
{
if (display >= 0)
{
// Hardware needs nothing but handling for wrapped framebuffers.
if (GSConfig.UseHardwareRenderer())
{
if (PCRTCDisplays[display].framebufferRect.z >= 2048)
{
PCRTCDisplays[display].framebufferRect.x = 0;
PCRTCDisplays[display].framebufferRect.z -= 2048;
}
if (PCRTCDisplays[display].framebufferRect.w >= 2048)
{
PCRTCDisplays[display].framebufferRect.y = 0;
PCRTCDisplays[display].framebufferRect.w -= 2048;
}
}
else
{
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[PCRTCDisplays[display].PSM];
// Software mode - See note below.
GSVector4i r = PCRTCDisplays[display].framebufferRect;
r = r.ralign<Align_Outside>(psm.bs);
PCRTCDisplays[display].framebufferRect.z -= r.x;
PCRTCDisplays[display].framebufferRect.w -= r.y;
PCRTCDisplays[display].framebufferRect.x -= r.x;
PCRTCDisplays[display].framebufferRect.y -= r.y;
}
}
else
{
// Software Mode Note:
// This code is to read the framebuffer nicely block aligned in software, then leave the remaining offset in to the block.
// In hardware mode this doesn't happen, it reads the whole framebuffer, so we need to keep the offset.
if (!GSConfig.UseHardwareRenderer())
{
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[PCRTCDisplays[1].PSM];
GSVector4i r = PCRTCDisplays[0].framebufferRect.runion(PCRTCDisplays[1].framebufferRect);
r = r.ralign<Align_Outside>(psm.bs);
PCRTCDisplays[0].framebufferRect.x -= r.x;
PCRTCDisplays[0].framebufferRect.y -= r.y;
PCRTCDisplays[0].framebufferRect.z -= r.x;
PCRTCDisplays[0].framebufferRect.w -= r.y;
PCRTCDisplays[1].framebufferRect.x -= r.x;
PCRTCDisplays[1].framebufferRect.y -= r.y;
PCRTCDisplays[1].framebufferRect.z -= r.x;
PCRTCDisplays[1].framebufferRect.w -= r.y;
}
}
}
// If the two displays are offset from each other, move them to the correct offsets.
// If using screen offsets, calculate the positions here.
void CalculateDisplayOffset(bool scanmask)
{
const bool both_enabled = PCRTCDisplays[0].enabled && PCRTCDisplays[1].enabled;
// Offsets are generally ignored, the "hacky" way of doing the displays, but direct to framebuffers.
if (!GSConfig.PCRTCOffsets)
{
const GSVector4i offsets = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode] : VideoModeOffsetsOverscan[videomode];
int int_off[2] = { 0, 0 };
GSVector2i zeroDisplay = NearestToZeroOffset();
GSVector2i baseOffset = PCRTCDisplays[zeroDisplay.y].displayOffset;
if (both_enabled)
{
int blurOffset = abs(PCRTCDisplays[1].displayOffset.y - PCRTCDisplays[0].displayOffset.y);
if (GSConfig.PCRTCAntiBlur && !scanmask && blurOffset < 4)
{
if (PCRTCDisplays[1].displayOffset.y > PCRTCDisplays[0].displayOffset.y)
PCRTCDisplays[1].displayOffset.y -= blurOffset;
else
PCRTCDisplays[0].displayOffset.y -= blurOffset;
}
}
// If there's a single pixel offset, account for it else it can throw interlacing out.
for (int i = 0; i < 2; i++)
{
if (!PCRTCDisplays[i].enabled)
continue;
// Should this be MAGV/H in the DISPLAY register rather than the "default" magnification?
const int offset = (PCRTCDisplays[i].displayOffset.y - (offsets.w * (interlaced + 1))) / (VideoModeDividers[videomode].y + 1);
if (offset > 4)
continue;
int_off[i] = offset & 1;
if (offset < 0)
int_off[i] = -int_off[i];
PCRTCDisplays[i].displayRect.y += int_off[i];
PCRTCDisplays[i].displayRect.w += int_off[i];
}
// Handle difference in offset between the two displays, used in games like DmC and Time Crisis 2 (for split screen).
// Offset is not screen based, but relative to each other.
if (both_enabled)
{
GSVector2i offset;
offset.x = (PCRTCDisplays[1 - zeroDisplay.x].displayOffset.x - PCRTCDisplays[zeroDisplay.x].displayOffset.x) / (VideoModeDividers[videomode].x + 1);
offset.y = (PCRTCDisplays[1 - zeroDisplay.y].displayOffset.y - PCRTCDisplays[zeroDisplay.y].displayOffset.y) / (VideoModeDividers[videomode].y + 1);
if (offset.x >= 4 || !GSConfig.PCRTCAntiBlur)
{
PCRTCDisplays[1 - zeroDisplay.x].displayRect.x += offset.x;
PCRTCDisplays[1 - zeroDisplay.x].displayRect.z += offset.x;
}
if (offset.y >= 4 || !GSConfig.PCRTCAntiBlur)
{
PCRTCDisplays[1 - zeroDisplay.y].displayRect.y += offset.y - int_off[1 - zeroDisplay.y];
PCRTCDisplays[1 - zeroDisplay.y].displayRect.w += offset.y - int_off[1 - zeroDisplay.y];
}
baseOffset = PCRTCDisplays[zeroDisplay.y].displayOffset;
}
// Handle any large vertical offset from the zero position on the screen.
// Example: Hokuto no Ken, does a rougly -14 offset to bring the screen up.
// Ignore the lowest bit, we've already accounted for this
int vOffset = ((static_cast<int>(baseOffset.y) - (offsets.w * (interlaced + 1))) / (VideoModeDividers[videomode].y + 1));
if(vOffset <= 4 && vOffset != 0)
{
PCRTCDisplays[0].displayRect.y += vOffset - int_off[0];
PCRTCDisplays[0].displayRect.w += vOffset - int_off[0];
PCRTCDisplays[1].displayRect.y += vOffset - int_off[1];
PCRTCDisplays[1].displayRect.w += vOffset - int_off[1];
}
}
else // We're using screen offsets, so just calculate the entire offset.
{
const GSVector4i offsets = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode] : VideoModeOffsetsOverscan[videomode];
GSVector2i offset = { 0, 0 };
GSVector2i zeroDisplay = NearestToZeroOffset();
if (both_enabled)
{
int blurOffset = abs(PCRTCDisplays[1].displayOffset.y - PCRTCDisplays[0].displayOffset.y);
if (GSConfig.PCRTCAntiBlur && !scanmask && blurOffset < 4)
{
if (PCRTCDisplays[1].displayOffset.y > PCRTCDisplays[0].displayOffset.y)
PCRTCDisplays[1].displayOffset.y -= blurOffset;
else
PCRTCDisplays[0].displayOffset.y -= blurOffset;
}
}
for (int i = 0; i < 2; i++)
{
// Should this be MAGV/H in the DISPLAY register rather than the "default" magnification?
offset.x = (static_cast<int>(PCRTCDisplays[i].displayOffset.x) - offsets.z) / (VideoModeDividers[videomode].x + 1);
offset.y = (static_cast<int>(PCRTCDisplays[i].displayOffset.y) - (offsets.w * (interlaced + 1))) / (VideoModeDividers[videomode].y + 1);
PCRTCDisplays[i].displayRect.x += offset.x;
PCRTCDisplays[i].displayRect.z += offset.x;
PCRTCDisplays[i].displayRect.y += offset.y;
PCRTCDisplays[i].displayRect.w += offset.y;
}
if (both_enabled)
{
GSVector2i offset;
offset.x = (PCRTCDisplays[1 - zeroDisplay.x].displayRect.x - PCRTCDisplays[zeroDisplay.x].displayRect.x);
offset.y = (PCRTCDisplays[1 - zeroDisplay.y].displayRect.y - PCRTCDisplays[zeroDisplay.y].displayRect.y);
if (offset.x > 0 && offset.x < 4 && GSConfig.PCRTCAntiBlur)
{
PCRTCDisplays[1 - zeroDisplay.x].displayRect.x -= offset.x;
PCRTCDisplays[1 - zeroDisplay.x].displayRect.z -= offset.x;
}
if (offset.y > 0 && offset.y < 4 && GSConfig.PCRTCAntiBlur)
{
PCRTCDisplays[1 - zeroDisplay.y].displayRect.y -= offset.y;
PCRTCDisplays[1 - zeroDisplay.y].displayRect.w -= offset.y;
}
}
}
}
} PCRTCDisplays;
public:
/// Returns the appropriate directory for draw dumping.
static std::string GetDrawDumpPath(const char* format, ...);
void ResetHandlers();
void ResetPCRTC();
int GetFramebufferHeight();
int GetFramebufferWidth();
int GetFramebufferBitDepth();
int GetDisplayHMagnification();
GSVector4i GetDisplayRect(int i = -1);
GSVector4i GetFrameMagnifiedRect(int i = -1);
GSVector2i GetResolutionOffset(int i = -1);
GSVector2i GetResolution();
GSVector4i GetFrameRect(int i = -1, bool ignore_off = false);
GSVideoMode GetVideoMode();
bool IsEnabled(int i);
bool isinterlaced();
bool isReallyInterlaced();
bool IsAnalogue();
float GetTvRefreshRate();
@@ -355,7 +886,9 @@ public:
bool TestDrawChanged();
void FlushWrite();
virtual void Draw() = 0;
virtual void PurgePool() = 0;
virtual void PurgePool();
virtual void PurgeTextureCache();
virtual void ReadbackTextureCache();
virtual void InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool eewrite = false) {}
virtual void InvalidateLocalMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r, bool clut = false) {}
virtual void ExpandTarget(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r) {}

View File

@@ -130,21 +130,44 @@ bool GSUtil::HasSharedBits(u32 spsm, const u32* RESTRICT ptr)
return (ptr[spsm >> 5] & (1 << (spsm & 0x1f))) == 0;
}
// Pixels can NOT coexist in the same 32bits of space.
// Example: Using PSMT8H or PSMT4HL/HH with CT24 would fail this check.
bool GSUtil::HasSharedBits(u32 spsm, u32 dpsm)
{
return (s_maps.SharedBitsField[dpsm][spsm >> 5] & (1 << (spsm & 0x1f))) == 0;
}
// Pixels can NOT coexist in the same 32bits of space.
// Example: Using PSMT8H or PSMT4HL/HH with CT24 would fail this check.
// SBP and DBO must match.
bool GSUtil::HasSharedBits(u32 sbp, u32 spsm, u32 dbp, u32 dpsm)
{
return ((sbp ^ dbp) | (s_maps.SharedBitsField[dpsm][spsm >> 5] & (1 << (spsm & 0x1f)))) == 0;
}
// Shares bit depths, only detects 16/24/32 bit formats.
// 24/32bit cross compatible, 16bit compatbile with 16bit.
bool GSUtil::HasCompatibleBits(u32 spsm, u32 dpsm)
{
return (s_maps.CompatibleBitsField[spsm][dpsm >> 5] & (1 << (dpsm & 0x1f))) != 0;
}
u32 GSUtil::GetChannelMask(u32 spsm)
{
switch (spsm)
{
case PSM_PSMCT24:
case PSM_PSMZ24:
return 0x7;
case PSM_PSMT8H:
case PSM_PSMT4HH: // This sucks, I'm sorry, but we don't have a way to do half channels
case PSM_PSMT4HL: // So uuhh TODO I guess.
return 0x8;
default:
return 0xf;
}
}
CRCHackLevel GSUtil::GetRecommendedCRCHackLevel(GSRendererType type)
{
return (type == GSRendererType::DX11 || type == GSRendererType::DX12) ? CRCHackLevel::Full : CRCHackLevel::Partial;
@@ -156,14 +179,19 @@ GSRendererType GSUtil::GetPreferredRenderer()
// Mac: Prefer Metal hardware.
return GSRendererType::Metal;
#elif defined(_WIN32)
if (D3D::ShouldPreferRenderer() == D3D::Renderer::Vulkan)
const u8 preferred = D3D::ShouldPreferRenderer();
#if defined(ENABLE_VULKAN)
if (preferred == D3D::Renderer::Vulkan)
return GSRendererType::VK;
#endif
#if defined(ENABLE_OPENGL)
else if (D3D::ShouldPreferRenderer() == D3D::Renderer::OpenGL)
if (preferred == D3D::Renderer::OpenGL)
return GSRendererType::OGL;
#endif
else
return GSRendererType::DX11;
if (preferred == D3D::Renderer::Direct3D12)
return GSRendererType::DX12;
return GSRendererType::DX11;
#else
// Linux: Prefer GL/Vulkan, whatever is available.
#if defined(ENABLE_OPENGL)

View File

@@ -32,6 +32,7 @@ public:
static bool HasSharedBits(u32 spsm, u32 dpsm);
static bool HasSharedBits(u32 sbp, u32 spsm, u32 dbp, u32 dpsm);
static bool HasCompatibleBits(u32 spsm, u32 dpsm);
static u32 GetChannelMask(u32 spsm);
static CRCHackLevel GetRecommendedCRCHackLevel(GSRendererType type);
static GSRendererType GetPreferredRenderer();

View File

@@ -20,6 +20,8 @@
#include "Host.h"
#include "common/StringUtil.h"
#include <algorithm>
const char* shaderName(ShaderConvert value)
{
switch (value)
@@ -262,6 +264,33 @@ void GSDevice::StretchRect(GSTexture* sTex, GSTexture* dTex, const GSVector4& dR
StretchRect(sTex, GSVector4(0, 0, 1, 1), dTex, dRect, shader, linear);
}
void GSDevice::DrawMultiStretchRects(
const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader)
{
for (u32 i = 0; i < num_rects; i++)
{
const MultiStretchRect& sr = rects[i];
pxAssert(shader == ShaderConvert::COPY || rects[0].wmask.wrgba == 0xf);
if (rects[0].wmask.wrgba != 0xf)
{
g_gs_device->StretchRect(sr.src, sr.src_rect, dTex, sr.dst_rect, rects[0].wmask.wr,
rects[0].wmask.wg, rects[0].wmask.wb, rects[0].wmask.wa);
}
else
{
g_gs_device->StretchRect(sr.src, sr.src_rect, dTex, sr.dst_rect, shader, sr.linear);
}
}
}
void GSDevice::SortMultiStretchRects(MultiStretchRect* rects, u32 num_rects)
{
// Depending on num_rects, insertion sort may be better here.
std::sort(rects, rects + num_rects, [](const MultiStretchRect& lhs, const MultiStretchRect& rhs) {
return lhs.src < rhs.src || lhs.linear < rhs.linear;
});
}
void GSDevice::ClearCurrent()
{
m_current = nullptr;
@@ -283,36 +312,8 @@ 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)
{
// KH:COM crashes at startup when booting *through the bios* due to m_merge being NULL.
// (texture appears to be non-null, and is being re-created at a size around like 1700x340,
// dunno if that's relevant) -- air
if (ResizeTarget(&m_merge, fs.x, fs.y))
{
GSTexture* tex[3] = {NULL, NULL, NULL};
for (size_t i = 0; i < std::size(tex); i++)
{
if (sTex[i] != NULL)
{
tex[i] = sTex[i];
}
}
DoMerge(tex, sRect, m_merge, dRect, PMODE, EXTBUF, c);
for (size_t i = 0; i < std::size(tex); i++)
{
if (tex[i] != sTex[i])
{
Recycle(tex[i]);
}
}
}
else
{
printf("GS: m_merge is NULL!\n");
}
DoMerge(sTex, sRect, m_merge, dRect, PMODE, EXTBUF, c, GSConfig.PCRTCOffsets);
m_current = m_merge;
}
@@ -356,6 +357,7 @@ void GSDevice::Interlace(const GSVector2i& ds, int field, int mode, float yoffse
break;
default:
m_current = m_merge;
break;
}
}

View File

@@ -314,6 +314,7 @@ struct alignas(16) GSHWDrawConfig
u32 ltf : 1;
// Shuffle and fbmask effect
u32 shuffle : 1;
u32 real16src: 1;
u32 read_ba : 1;
u32 write_rg : 1;
u32 fbmask : 1;
@@ -331,6 +332,7 @@ struct alignas(16) GSHWDrawConfig
u32 hdr : 1;
u32 colclip : 1;
u32 blend_mix : 2;
u32 round_inv : 1; // Blending will invert the value, so rounding needs to go the other way
u32 pabe : 1;
u32 no_color : 1; // disables color output entirely (depth only)
u32 no_color1 : 1; // disables second color output (when unnecessary)
@@ -708,6 +710,15 @@ public:
}
};
struct MultiStretchRect
{
GSVector4 src_rect;
GSVector4 dst_rect;
GSTexture* src;
bool linear;
GSHWDrawConfig::ColorMaskSelector wmask; // 0xf for all channels by default
};
enum BlendFactor : u8
{
// HW blend factors
@@ -758,7 +769,7 @@ protected:
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);
virtual void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) = 0;
virtual void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear) = 0;
virtual void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset, int bufIdx) = 0;
virtual void DoFXAA(GSTexture* sTex, GSTexture* dTex) {}
virtual void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) {}
@@ -826,6 +837,13 @@ public:
/// Performs a screen blit for display. If dTex is null, it assumes you are writing to the system framebuffer/swap chain.
virtual void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear) {}
/// Same as doing StretchRect for each item, except tries to batch together rectangles in as few draws as possible.
/// The provided list should be sorted by texture, the implementations only check if it's the same as the last.
virtual void DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader = ShaderConvert::COPY);
/// Sorts a MultiStretchRect list for optimal batching.
static void SortMultiStretchRects(MultiStretchRect* rects, u32 num_rects);
/// Updates a GPU CLUT texture from a source texture.
virtual void UpdateCLUTTexture(GSTexture* sTex, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) {}
@@ -857,8 +875,8 @@ public:
__fi static constexpr bool IsDualSourceBlendFactor(u8 factor)
{
return (factor == SRC1_ALPHA || factor == INV_SRC1_ALPHA
/*|| factor == SRC1_COLOR || factor == INV_SRC1_COLOR*/); // not used
return (factor == SRC1_ALPHA || factor == INV_SRC1_ALPHA || factor == SRC1_COLOR
/* || factor == INV_SRC1_COLOR*/); // not used
}
__fi static constexpr bool IsConstantBlendFactor(u16 factor)
{

View File

@@ -20,18 +20,20 @@
GSDirtyRect::GSDirtyRect() :
r(GSVector4i::zero()),
psm(PSM_PSMCT32),
bw(1)
bw(1),
rgba({})
{
}
GSDirtyRect::GSDirtyRect(GSVector4i& r, u32 psm, u32 bw) :
GSDirtyRect::GSDirtyRect(GSVector4i& r, u32 psm, u32 bw, RGBAMask rgba) :
r(r),
psm(psm),
bw(bw)
bw(bw),
rgba(rgba)
{
}
GSVector4i GSDirtyRect::GetDirtyRect(GIFRegTEX0& TEX0)
GSVector4i GSDirtyRect::GetDirtyRect(GIFRegTEX0 TEX0) const
{
GSVector4i _r;
@@ -54,7 +56,7 @@ GSVector4i GSDirtyRect::GetDirtyRect(GIFRegTEX0& TEX0)
return _r;
}
GSVector4i GSDirtyRectList::GetTotalRect(GIFRegTEX0& TEX0, const GSVector2i& size)
GSVector4i GSDirtyRectList::GetTotalRect(GIFRegTEX0 TEX0, const GSVector2i& size) const
{
if (!empty())
{
@@ -73,31 +75,27 @@ GSVector4i GSDirtyRectList::GetTotalRect(GIFRegTEX0& TEX0, const GSVector2i& siz
return GSVector4i::zero();
}
GSVector4i GSDirtyRectList::GetDirtyRect(GIFRegTEX0& TEX0, const GSVector2i& size, bool clear)
u32 GSDirtyRectList::GetDirtyChannels()
{
u32 channels = 0;
if (!empty())
{
const std::vector<GSDirtyRect>::iterator &it = begin();
const GSVector4i r = it[0].GetDirtyRect(TEX0);
if (clear)
erase(it);
GSVector2i bs = GSLocalMemory::m_psm[TEX0.PSM].bs;
return r.ralign<Align_Outside>(bs).rintersect(GSVector4i(0, 0, size.x, size.y));
for (auto& dirty_rect : *this)
{
channels |= dirty_rect.rgba._u32;
}
}
return GSVector4i::zero();
return channels;
}
GSVector4i GSDirtyRectList::GetDirtyRectAndClear(GIFRegTEX0& TEX0, const GSVector2i& size)
GSVector4i GSDirtyRectList::GetDirtyRect(size_t index, GIFRegTEX0 TEX0, const GSVector4i& clamp) const
{
const GSVector4i r = GetDirtyRect(TEX0, size, true);
return r;
const GSVector4i r = (*this)[index].GetDirtyRect(TEX0);
GSVector2i bs = GSLocalMemory::m_psm[TEX0.PSM].bs;
return r.ralign<Align_Outside>(bs).rintersect(clamp);
}
void GSDirtyRectList::ClearDirty()
{
clear();
}

View File

@@ -17,24 +17,36 @@
#include "GS/GSLocalMemory.h"
union RGBAMask
{
struct
{
u32 r : 1;
u32 g : 1;
u32 b : 1;
u32 a : 1;
} c;
u32 _u32;
};
class GSDirtyRect
{
public:
GSVector4i r;
u32 psm;
u32 bw;
RGBAMask rgba;
GSDirtyRect();
GSDirtyRect(GSVector4i& r, u32 psm, u32 bw);
GSVector4i GetDirtyRect(GIFRegTEX0& TEX0);
GSDirtyRect(GSVector4i& r, u32 psm, u32 bw, RGBAMask rgba);
GSVector4i GetDirtyRect(GIFRegTEX0 TEX0) const;
};
class GSDirtyRectList : public std::vector<GSDirtyRect>
{
public:
GSDirtyRectList() {}
GSVector4i GetTotalRect(GIFRegTEX0& TEX0, const GSVector2i& size);
GSVector4i GetDirtyRect(GIFRegTEX0& TEX0, const GSVector2i& size, bool clear = false);
GSVector4i GetDirtyRectAndClear(GIFRegTEX0& TEX0, const GSVector2i& size);
void ClearDirty();
GSVector4i GetTotalRect(GIFRegTEX0 TEX0, const GSVector2i& size) const;
u32 GetDirtyChannels();
GSVector4i GetDirtyRect(size_t index, GIFRegTEX0 TEX0, const GSVector4i& clamp) const;
};

View File

@@ -50,6 +50,8 @@ std::unique_ptr<GSRenderer> g_gs_renderer;
// we might be switching while the other thread reads it.
static GSVector4 s_last_draw_rect;
// Last time we reset the renderer due to a GPU crash, if any.
static Common::Timer::Value s_last_gpu_reset_time;
GSRenderer::GSRenderer()
: m_shader_time_start(Common::Timer::GetCurrentValue())
@@ -75,264 +77,108 @@ void GSRenderer::Destroy()
bool GSRenderer::Merge(int field)
{
bool en[2];
GSVector4i fr[2];
GSVector4i dr[2];
GSVector2i display_offsets[2];
GSVector2i display_baseline = {INT_MAX, INT_MAX};
GSVector2i frame_baseline = {INT_MAX, INT_MAX};
GSVector2i display_combined = {0, 0};
bool feedback_merge = m_regs->EXTWRITE.WRITE == 1;
bool display_offset = false;
for (int i = 0; i < 2; i++)
{
en[i] = IsEnabled(i) || (m_regs->EXTBUF.FBIN == i && feedback_merge);
if (en[i])
{
fr[i] = GetFrameRect(i);
dr[i] = GetDisplayRect(i);
display_offsets[i] = GetResolutionOffset(i);
display_combined.x = std::max(((dr[i].right) - dr[i].left) + display_offsets[i].x, display_combined.x);
display_combined.y = std::max((dr[i].bottom - dr[i].top) + display_offsets[i].y, display_combined.y);
display_baseline.x = std::min(display_offsets[i].x, display_baseline.x);
display_baseline.y = std::min(display_offsets[i].y, display_baseline.y);
frame_baseline.x = std::min(std::max(fr[i].left, 0), frame_baseline.x);
frame_baseline.y = std::min(std::max(fr[i].top, 0), frame_baseline.y);
display_offset |= std::abs(display_baseline.y - display_offsets[i].y) == 1;
/*DevCon.Warning("Read offset was X %d(left %d) Y %d(top %d)", display_baseline.x, dr[i].left, display_baseline.y, dr[i].top);
DevCon.Warning("[%d]: %d %d %d %d, %d %d %d %d\n", i, fr[i].x,fr[i].y,fr[i].z,fr[i].w , dr[i].x,dr[i].y,dr[i].z,dr[i].w);
DevCon.Warning("Offset X %d Offset Y %d", display_offsets[i].x, display_offsets[i].y);*/
}
}
if (!en[0] && !en[1])
{
return false;
}
GL_PUSH("Renderer Merge %d (0: enabled %d 0x%x, 1: enabled %d 0x%x)", s_n, en[0], m_regs->DISP[0].DISPFB.Block(), en[1], m_regs->DISP[1].DISPFB.Block());
// try to avoid fullscreen blur, could be nice on tv but on a monitor it's like double vision, hurts my eyes (persona 4, guitar hero)
//
// NOTE: probably the technique explained in graphtip.pdf (Antialiasing by Supersampling / 4. Reading Odd/Even Scan Lines Separately with the PCRTC then Blending)
const bool samesrc =
en[0] && en[1] &&
m_regs->DISP[0].DISPFB.FBP == m_regs->DISP[1].DISPFB.FBP &&
m_regs->DISP[0].DISPFB.FBW == m_regs->DISP[1].DISPFB.FBW &&
GSUtil::HasCompatibleBits(m_regs->DISP[0].DISPFB.PSM, m_regs->DISP[1].DISPFB.PSM);
bool single_fetch = false;
GSVector2i fs(0, 0);
GSVector2i ds(0, 0);
GSTexture* tex[3] = { nullptr, nullptr, nullptr };
int y_offset[3] = { 0, 0, 0 };
const bool feedback_merge = m_regs->EXTWRITE.WRITE == 1;
GSTexture* tex[3] = {NULL, NULL, NULL};
int y_offset[3] = {0, 0, 0};
PCRTCDisplays.SetVideoMode(GetVideoMode());
PCRTCDisplays.EnableDisplays(m_regs->PMODE, m_regs->SMODE2, isReallyInterlaced());
PCRTCDisplays.CheckSameSource();
s_n++;
if (!PCRTCDisplays.PCRTCDisplays[0].enabled && !PCRTCDisplays.PCRTCDisplays[1].enabled)
return false;
// Need to do this here, if the user has Anti-Blur enabled, these offsets can get wiped out/changed.
const bool game_deinterlacing = (m_regs->DISP[0].DISPFB.DBY != PCRTCDisplays.PCRTCDisplays[0].prevFramebufferReg.DBY) !=
(m_regs->DISP[1].DISPFB.DBY != PCRTCDisplays.PCRTCDisplays[1].prevFramebufferReg.DBY);
PCRTCDisplays.SetRects(0, m_regs->DISP[0].DISPLAY, m_regs->DISP[0].DISPFB);
PCRTCDisplays.SetRects(1, m_regs->DISP[1].DISPLAY, m_regs->DISP[1].DISPFB);
PCRTCDisplays.CalculateDisplayOffset(m_scanmask_used);
PCRTCDisplays.CalculateFramebufferOffset();
// Only need to check the right/bottom on software renderer, hardware always gets the full texture then cuts a bit out later.
if (samesrc && !feedback_merge && (GSConfig.UseHardwareRenderer() || (fr[0].right == fr[1].right && fr[0].bottom == fr[1].bottom)))
if (PCRTCDisplays.FrameRectMatch() && !PCRTCDisplays.FrameWrap() && !feedback_merge)
{
tex[0] = GetOutput(0, y_offset[0]);
tex[0] = GetOutput(-1, y_offset[0]);
tex[1] = tex[0]; // saves one texture fetch
y_offset[1] = y_offset[0];
single_fetch = true;
}
else
{
if (en[0])
if (PCRTCDisplays.PCRTCDisplays[0].enabled)
tex[0] = GetOutput(0, y_offset[0]);
if (en[1])
if (PCRTCDisplays.PCRTCDisplays[1].enabled)
tex[1] = GetOutput(1, y_offset[1]);
if (feedback_merge)
tex[2] = GetFeedbackOutput();
}
GSVector4 src_out_rect[2];
if (!tex[0] && !tex[1])
return false;
s_n++;
GSVector4 src_gs_read[2];
GSVector4 dst[3];
const bool slbg = m_regs->PMODE.SLBG;
GSVector2i resolution(GetResolution());
bool scanmask_frame = m_scanmask_used && !display_offset;
const bool ignore_offset = !GSConfig.PCRTCOffsets;
const bool is_bob = GSConfig.InterlaceMode == GSInterlaceMode::BobTFF || GSConfig.InterlaceMode == GSInterlaceMode::BobBFF;
// Use offset for bob deinterlacing always, extra offset added later for FFMD mode.
float offset = is_bob ? (tex[1] ? tex[1]->GetScale().y : tex[0]->GetScale().y) : 0.0f;
const bool scanmask_frame = m_scanmask_used && abs(PCRTCDisplays.PCRTCDisplays[0].displayRect.y - PCRTCDisplays.PCRTCDisplays[1].displayRect.y) != 1;
int field2 = 0;
int mode = 3;
int mode = 3; // If the game is manually deinterlacing then we need to bob (if we want to get away with no deinterlacing).
bool is_bob = GSConfig.InterlaceMode == GSInterlaceMode::BobTFF || GSConfig.InterlaceMode == GSInterlaceMode::BobBFF;
// FFMD (half frames) requires blend deinterlacing, so automatically use that. Same when SCANMSK is used but not blended in the merge circuit (Alpine Racer 3)
// FFMD (half frames) requires blend deinterlacing, so automatically use that. Same when SCANMSK is used but not blended in the merge circuit (Alpine Racer 3).
if (GSConfig.InterlaceMode != GSInterlaceMode::Automatic || (!m_regs->SMODE2.FFMD && !scanmask_frame))
{
field2 = ((static_cast<int>(GSConfig.InterlaceMode) - 2) & 1);
mode = ((static_cast<int>(GSConfig.InterlaceMode) - 2) >> 1);
// If the game is offsetting each frame itself and we're using full height buffers, we can offset this with Bob.
if (game_deinterlacing && !scanmask_frame && GSConfig.InterlaceMode == GSInterlaceMode::Automatic)
{
mode = 1; // Bob.
is_bob = true;
}
else
{
field2 = ((static_cast<int>(GSConfig.InterlaceMode) - 2) & 1);
mode = ((static_cast<int>(GSConfig.InterlaceMode) - 2) >> 1);
}
}
for (int i = 0; i < 2; i++)
{
if (!en[i] || !tex[i])
const GSPCRTCRegs::PCRTCDisplay& curCircuit = PCRTCDisplays.PCRTCDisplays[i];
if (!curCircuit.enabled || !tex[i])
continue;
GSVector4i r = GetFrameMagnifiedRect(i);
GSVector4 scale = GSVector4(tex[i]->GetScale()).xyxy();
GSVector2i off(ignore_offset ? 0 : display_offsets[i]);
GSVector2i display_diff(display_offsets[i].x - display_baseline.x, display_offsets[i].y - display_baseline.y);
GSVector2i frame_diff(fr[i].left - frame_baseline.x, fr[i].top - frame_baseline.y);
if (!GSConfig.UseHardwareRenderer())
{
// Clear any frame offsets, offset is already done in the software GetOutput.
fr[i].right -= fr[i].left;
fr[i].left = 0;
fr[i].bottom -= fr[i].top;
fr[i].top = 0;
// Put any frame offset difference back if we aren't anti-blurring on a single fetch (not offset).
if (!GSConfig.PCRTCAntiBlur && single_fetch)
{
fr[i].right += frame_diff.x;
fr[i].left += frame_diff.x;
fr[i].bottom += frame_diff.y;
fr[i].top += frame_diff.y;
}
}
// If using scanmsk we have to keep the single line offset, regardless of upscale
// so we handle this separately after the rect calculations.
float interlace_offset = 0.0f;
if ((!GSConfig.PCRTCAntiBlur || m_scanmask_used) && display_offset)
{
interlace_offset = static_cast<float>(display_diff.y & 1);
// When the displays are offset by 1 we need to adjust for upscale to handle it (reduces bounce in MGS2 when upscaling)
interlace_offset += (tex[i]->GetScale().y - 1.0f) / 2;
if (interlace_offset >= 1.0f)
{
if (!ignore_offset)
off.y -= 1;
display_diff.y -= 1;
}
}
// Start of Anti-Blur code.
if (!ignore_offset)
{
if (GSConfig.PCRTCAntiBlur)
{
if (samesrc)
{
// Offset by DISPLAY setting
if (display_diff.x < 4)
off.x -= display_diff.x;
if (display_diff.y < 4)
off.y -= display_diff.y;
// Only functional in HW mode, software clips/positions the framebuffer on read.
if (GSConfig.UseHardwareRenderer())
{
// Offset by DISPFB setting
if (abs(frame_diff.x) < 4)
off.x += frame_diff.x;
if (abs(frame_diff.y) < 4)
off.y += frame_diff.y;
}
}
}
}
else
{
if (!slbg || !feedback_merge)
{
// If the offsets between the two displays are quite large, it's probably intended for an effect.
if (display_diff.x >= 4 || !GSConfig.PCRTCAntiBlur)
off.x = display_diff.x;
if (display_diff.y >= 4 || !GSConfig.PCRTCAntiBlur)
off.y = display_diff.y;
// Need to check if only circuit 2 is enabled. Stuntman toggles circuit 1 on and off every other frame.
if (samesrc || m_regs->PMODE.EN == 2)
{
// Adjusting the screen offset when using a negative offset.
const int videomode = static_cast<int>(GetVideoMode()) - 1;
const GSVector4i offsets = !GSConfig.PCRTCOverscan ? VideoModeOffsets[videomode] : VideoModeOffsetsOverscan[videomode];
GSVector2i base_resolution(offsets.x, offsets.y);
if (isinterlaced() && !m_regs->SMODE2.FFMD)
base_resolution.y *= 2;
// Offset by DISPLAY setting
if (display_diff.x < 0)
{
off.x = 0;
if (base_resolution.x > resolution.x)
resolution.x -= display_diff.x;
}
if (display_diff.y < 0)
{
off.y = 0;
if (base_resolution.y > resolution.y)
resolution.y -= display_diff.y;
}
// Don't do X, we only care about height, this would need to be tailored for games using X (Black does -5).
// Mainly for Hokuto no Ken which does -14 Y offset.
if (display_baseline.y < -4)
off.y += display_baseline.y;
// Anti-Blur stuff
// Only functional in HW mode, software clips/positions the framebuffer on read.
if (GSConfig.PCRTCAntiBlur && GSConfig.UseHardwareRenderer())
{
// Offset by DISPFB setting
if (abs(frame_diff.x) < 4)
off.x += frame_diff.x;
if (abs(frame_diff.y) < 4)
off.y += frame_diff.y;
}
}
}
}
// End of Anti-Blur code.
if (isinterlaced() && m_regs->SMODE2.FFMD)
off.y >>= 1;
// dst is the final destination rect with offset on the screen.
dst[i] = scale * (GSVector4(off).xyxy() + GSVector4(r.rsize()));
dst[i] = scale * GSVector4(curCircuit.displayRect);
// src_gs_read is the size which we're really reading from GS memory.
src_gs_read[i] = ((GSVector4(fr[i]) + GSVector4(0, y_offset[i], 0, y_offset[i])) * scale) / GSVector4(tex[i]->GetSize()).xyxy();
// src_out_rect is the resized rect for output. (Not really used)
src_out_rect[i] = (GSVector4(r) * scale) / GSVector4(tex[i]->GetSize()).xyxy();
if (m_regs->SMODE2.FFMD && !is_bob && !GSConfig.DisableInterlaceOffset && GSConfig.InterlaceMode != GSInterlaceMode::Off)
src_gs_read[i] = ((GSVector4(curCircuit.framebufferRect) + GSVector4(0, y_offset[i], 0, y_offset[i])) * scale) / GSVector4(tex[i]->GetSize()).xyxy();
float interlace_offset = 0.0f;
if (isReallyInterlaced() && m_regs->SMODE2.FFMD && !is_bob && !GSConfig.DisableInterlaceOffset && GSConfig.InterlaceMode != GSInterlaceMode::Off)
{
// We do half because FFMD is a half sized framebuffer, then we offset by 1 in the shader for the actual interlace
if(GetUpscaleMultiplier() > 1.0f)
interlace_offset += ((((tex[1] ? tex[1]->GetScale().y : tex[0]->GetScale().y) + 0.5f) * 0.5f) - 1.0f) * static_cast<float>(field ^ field2);
offset = 1.0f;
interlace_offset = (scale.y) * static_cast<float>(field ^ field2);
}
// Restore manually offset "interlace" lines
// Scanmask frame offsets. It's gross, I'm sorry but it sucks.
if (m_scanmask_used)
{
int displayIntOffset = PCRTCDisplays.PCRTCDisplays[i].displayRect.y - PCRTCDisplays.PCRTCDisplays[1 - i].displayRect.y;
if (displayIntOffset > 0)
{
displayIntOffset &= 1;
dst[i].y -= displayIntOffset * scale.y;
dst[i].w -= displayIntOffset * scale.y;
interlace_offset += displayIntOffset;
}
}
dst[i] += GSVector4(0.0f, interlace_offset, 0.0f, interlace_offset);
}
@@ -349,42 +195,19 @@ bool GSRenderer::Merge(int field)
dst[2] = GSVector4(scale * GSVector4(feedback_rect.rsize()));
}
// Set the resolution to the height of the displays (kind of a saturate height)
if (ignore_offset && !feedback_merge)
{
GSVector2i max_resolution = GetResolution();
resolution.x = display_combined.x - display_baseline.x;
resolution.y = display_combined.y - display_baseline.y;
if (isinterlaced() && m_regs->SMODE2.FFMD)
{
resolution.y >>= 1;
}
resolution.x = std::min(max_resolution.x, resolution.x);
resolution.y = std::min(max_resolution.y, resolution.y);
}
GSVector2i resolution = PCRTCDisplays.GetResolution();
fs = GSVector2i(static_cast<int>(static_cast<float>(resolution.x) * GetUpscaleMultiplier()),
static_cast<int>(static_cast<float>(resolution.y) * GetUpscaleMultiplier()));
ds = fs;
// When interlace(FRAME) mode, the rect is half height, so it needs to be stretched.
const bool is_interlaced_resolution = m_regs->SMODE2.INT || (isReallyInterlaced() && IsAnalogue() && GSConfig.InterlaceMode != GSInterlaceMode::Off);
m_real_size = GSVector2i(fs.x, fs.y);
if (is_interlaced_resolution && m_regs->SMODE2.FFMD)
ds.y *= 2;
m_real_size = GSVector2i(fs.x, is_interlaced_resolution ? ds.y : fs.y);
if (!tex[0] && !tex[1])
return false;
if ((tex[0] == tex[1]) && (src_out_rect[0] == src_out_rect[1]).alltrue() && (dst[0] == dst[1]).alltrue() && !feedback_merge && !slbg)
if ((tex[0] == tex[1]) && (src_gs_read[0] == src_gs_read[1]).alltrue() && (dst[0] == dst[1]).alltrue() &&
(PCRTCDisplays.PCRTCDisplays[0].displayRect == PCRTCDisplays.PCRTCDisplays[1].displayRect).alltrue() &&
(PCRTCDisplays.PCRTCDisplays[0].framebufferRect == PCRTCDisplays.PCRTCDisplays[1].framebufferRect).alltrue() &&
!feedback_merge && !m_regs->PMODE.SLBG)
{
// the two outputs are identical, skip drawing one of them (the one that is alpha blended)
tex[0] = NULL;
tex[0] = nullptr;
}
GSVector4 c = GSVector4((int)m_regs->BGCOLOR.R, (int)m_regs->BGCOLOR.G, (int)m_regs->BGCOLOR.B, (int)m_regs->PMODE.ALP) / 255;
@@ -392,7 +215,11 @@ bool GSRenderer::Merge(int field)
g_gs_device->Merge(tex, src_gs_read, dst, fs, m_regs->PMODE, m_regs->EXTBUF, c);
if (isReallyInterlaced() && GSConfig.InterlaceMode != GSInterlaceMode::Off)
g_gs_device->Interlace(ds, field ^ field2, mode, offset);
{
const float offset = is_bob ? (tex[1] ? tex[1]->GetScale().y : tex[0]->GetScale().y) : 0.0f;
g_gs_device->Interlace(fs, field ^ field2, mode, offset);
}
if (GSConfig.ShadeBoost)
g_gs_device->ShadeBoost();
@@ -602,6 +429,40 @@ void GSJoinSnapshotThreads()
}
}
bool GSRenderer::BeginPresentFrame(bool frame_skip)
{
const HostDisplay::PresentResult result = Host::BeginPresentFrame(frame_skip);
if (result == HostDisplay::PresentResult::OK)
return true;
else if (result == HostDisplay::PresentResult::FrameSkipped)
return false;
// If we're constantly crashing on something in particular, we don't want to end up in an
// endless reset loop.. that'd probably end up leaking memory and/or crashing us for other
// reasons. So just abort in such case.
const Common::Timer::Value current_time = Common::Timer::GetCurrentValue();
if (s_last_gpu_reset_time != 0 &&
Common::Timer::ConvertValueToSeconds(current_time - s_last_gpu_reset_time) < 15.0f)
{
pxFailRel("Host GPU lost too many times, device is probably completely wedged.");
}
s_last_gpu_reset_time = current_time;
// Device lost, something went really bad.
// Let's just toss out everything, and try to hobble on.
if (!GSreopen(true, false, GSConfig))
{
pxFailRel("Failed to recreate GS device after loss.");
return false;
}
// First frame after reopening is definitely going to be trash, so skip it.
Host::AddIconOSDMessage("GSDeviceLost", ICON_FA_EXCLAMATION_TRIANGLE,
"Host GPU device encountered an error and was recovered. This may have broken rendering.",
Host::OSD_CRITICAL_ERROR_DURATION);
return false;
}
void GSRenderer::VSync(u32 field, bool registers_written)
{
Flush(GSFlushReason::VSYNC);
@@ -647,7 +508,7 @@ void GSRenderer::VSync(u32 field, bool registers_written)
if (skip_frame)
{
g_gs_device->ResetAPIState();
if (Host::BeginPresentFrame(true))
if (BeginPresentFrame(true))
Host::EndPresentFrame();
g_gs_device->RestoreAPIState();
PerformanceMetrics::Update(registers_written, fb_sprite_frame, true);
@@ -694,7 +555,7 @@ void GSRenderer::VSync(u32 field, bool registers_written)
}
g_gs_device->ResetAPIState();
if (Host::BeginPresentFrame(false))
if (BeginPresentFrame(false))
{
if (current && !blank_frame)
{
@@ -910,7 +771,7 @@ void GSRenderer::StopGSDump()
void GSRenderer::PresentCurrentFrame()
{
g_gs_device->ResetAPIState();
if (Host::BeginPresentFrame(false))
if (BeginPresentFrame(false))
{
GSTexture* current = g_gs_device->GetCurrent();
if (current)
@@ -967,15 +828,6 @@ void GSRenderer::EndCapture()
GSCapture::EndCapture();
}
void GSRenderer::PurgePool()
{
g_gs_device->PurgePool();
}
void GSRenderer::PurgeTextureCache()
{
}
GSTexture* GSRenderer::LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, const GSVector2i& size)
{
return nullptr;

View File

@@ -23,6 +23,7 @@ class GSRenderer : public GSState
{
private:
bool Merge(int field);
bool BeginPresentFrame(bool frame_skip);
u64 m_shader_time_start = 0;
@@ -33,6 +34,7 @@ private:
protected:
GSVector2i m_real_size{0, 0};
bool m_texture_shuffle = false;
bool m_copy_16bit_to_target_shuffle = false;
virtual GSTexture* GetOutput(int i, int& y_offset) = 0;
virtual GSTexture* GetFeedbackOutput() { return nullptr; }
@@ -51,9 +53,6 @@ public:
virtual GSVector2 GetTextureScaleFactor() { return { 1.0f, 1.0f }; }
GSVector2i GetInternalResolution();
virtual void PurgePool() override;
virtual void PurgeTextureCache();
virtual GSTexture* LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, const GSVector2i& size);
bool SaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,

View File

@@ -88,6 +88,7 @@ public:
int GetWidth() const { return m_size.x; }
int GetHeight() const { return m_size.y; }
GSVector2i GetSize() const { return m_size; }
GSVector4i GetRect() const { return GSVector4i(m_size).zwxy(); }
int GetMipmapLevels() const { return m_mipmap_levels; }
bool IsMipmap() const { return m_mipmap_levels > 1; }

View File

@@ -19,10 +19,8 @@
#include "GS/GSState.h"
GSVertexTrace::GSVertexTrace(const GSState* state, bool provoking_vertex_first)
: m_accurate_stq(false), m_state(state), m_primclass(GS_INVALID_CLASS)
: m_state(state)
{
memset(&m_alpha, 0, sizeof(m_alpha));
MULTI_ISA_SELECT(GSVertexTracePopulateFunctions)(*this, provoking_vertex_first);
}

View File

@@ -44,7 +44,7 @@ public:
int min, max;
bool valid;
};
bool m_accurate_stq;
bool m_accurate_stq = false;
protected:
const GSState* m_state;
@@ -54,25 +54,25 @@ protected:
FindMinMaxPtr m_fmm[2][2][2][2][4];
public:
GS_PRIM_CLASS m_primclass;
GS_PRIM_CLASS m_primclass = GS_INVALID_CLASS;
Vertex m_min;
Vertex m_max;
VertexAlpha m_alpha; // source alpha range after tfx, GSRenderer::GetAlphaMinMax() updates it
Vertex m_min = {};
Vertex m_max = {};
VertexAlpha m_alpha = {}; // source alpha range after tfx, GSRenderer::GetAlphaMinMax() updates it
union
{
u32 value;
struct { u32 r:4, g:4, b:4, a:4, x:1, y:1, z:1, f:1, s:1, t:1, q:1, _pad:1; };
struct { u32 rgba:16, xyzf:4, stq:4; };
} m_eq;
} m_eq = {};
union
{
struct { u32 mmag:1, mmin:1, linear:1, opt_linear:1; };
} m_filter;
} m_filter = {};
GSVector2 m_lod; // x = min, y = max
GSVector2 m_lod = {}; // x = min, y = max
public:
GSVertexTrace(const GSState* state, bool provoking_vertex_first);

View File

@@ -17,8 +17,12 @@
#include "GS/Renderers/DX11/D3D.h"
#include "GS/GSExtra.h"
#include "common/StringUtil.h"
#include <d3d11.h>
#include "fmt/format.h"
namespace D3D
{
wil::com_ptr_nothrow<IDXGIFactory2> CreateFactory(bool debug)
@@ -69,6 +73,52 @@ namespace D3D
return adapter;
}
std::string GetDriverVersionFromLUID(const LUID& luid)
{
std::string ret;
HKEY hKey;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\DirectX", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
DWORD max_key_len = 0, adapter_count = 0;
if (RegQueryInfoKeyW(hKey, nullptr, nullptr, nullptr, &adapter_count, &max_key_len, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
{
std::vector<TCHAR> current_name(max_key_len + 1);
for (DWORD i = 0; i < adapter_count; ++i)
{
DWORD subKeyLength = static_cast<DWORD>(current_name.size());
if (RegEnumKeyExW(hKey, i, current_name.data(), &subKeyLength, nullptr, nullptr, nullptr,
nullptr) == ERROR_SUCCESS)
{
LUID current_luid = {};
DWORD current_luid_size = sizeof(uint64_t);
if (RegGetValueW(hKey, current_name.data(), L"AdapterLuid", RRF_RT_QWORD, nullptr,
&current_luid, &current_luid_size) == ERROR_SUCCESS &&
current_luid.HighPart == luid.HighPart && current_luid.LowPart == luid.LowPart)
{
LARGE_INTEGER driver_version = {};
DWORD driver_version_size = sizeof(driver_version);
if (RegGetValueW(hKey, current_name.data(), L"DriverVersion", RRF_RT_QWORD, nullptr,
&driver_version, &driver_version_size) == ERROR_SUCCESS)
{
WORD nProduct = HIWORD(driver_version.HighPart);
WORD nVersion = LOWORD(driver_version.HighPart);
WORD nSubVersion = HIWORD(driver_version.LowPart);
WORD nBuild = LOWORD(driver_version.LowPart);
ret = fmt::format("{}.{}.{}.{}", nProduct, nVersion, nSubVersion, nBuild);
}
}
}
}
}
RegCloseKey(hKey);
}
return ret;
}
u8 Vendor()
{
auto factory = CreateFactory(false);
@@ -124,22 +174,37 @@ namespace D3D
switch (Vendor())
{
case VendorID::Nvidia:
{
if (feature_level == D3D_FEATURE_LEVEL_12_0)
return Renderer::Vulkan;
else if (feature_level == D3D_FEATURE_LEVEL_11_0)
return Renderer::OpenGL;
[[fallthrough]];
else
return Renderer::Direct3D11;
}
case VendorID::AMD:
{
if (feature_level == D3D_FEATURE_LEVEL_12_0)
return Renderer::Vulkan;
[[fallthrough]];
else
return Renderer::Direct3D11;
}
case VendorID::Intel:
if (feature_level == D3D_FEATURE_LEVEL_12_0)
return Renderer::OpenGL;
[[fallthrough]];
{
// Older Intel GPUs prior to Xe seem to have broken OpenGL drivers which choke
// on some of our shaders, causing what appears to be GPU timeouts+device removals.
// Vulkan has broken barriers, also prior to Xe. So just fall back to DX11 everywhere,
// unless we can find some way of differentiating Xe.
return Renderer::Direct3D11;
}
default:
{
// Default is D3D11
return Renderer::Default;
return Renderer::Direct3D11;
}
}
}
}

View File

@@ -32,6 +32,9 @@ namespace D3D
// however in the event that the adapter is not found due to the above, use the default
wil::com_ptr_nothrow<IDXGIAdapter1> GetAdapterFromIndex(IDXGIFactory2* factory, int index);
// returns the driver version from the registry as a string
std::string GetDriverVersionFromLUID(const LUID& luid);
// this is sort of a legacy thing that doesn't have much to do with d3d (just the easiest way)
// checks to see if the adapter at 0 is NV and thus we should prefer OpenGL
enum VendorID

View File

@@ -196,9 +196,11 @@ bool GSDevice11::Create()
memset(&bsd, 0, sizeof(bsd));
bsd.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
m_dev->CreateBlendState(&bsd, m_convert.bs.put());
for (u32 i = 0; i < static_cast<u32>(m_convert.bs.size()); i++)
{
bsd.RenderTarget[0].RenderTargetWriteMask = static_cast<u8>(i);
m_dev->CreateBlendState(&bsd, m_convert.bs[i].put());
}
// merge
@@ -608,26 +610,15 @@ void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ID3D11PixelShader* ps, ID3D11Buffer* ps_cb, bool linear)
{
StretchRect(sTex, sRect, dTex, dRect, ps, ps_cb, m_convert.bs.get(), linear);
StretchRect(sTex, sRect, dTex, dRect, ps, ps_cb, m_convert.bs[D3D11_COLOR_WRITE_ENABLE_ALL].get(), linear);
}
void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red, bool green, bool blue, bool alpha)
{
D3D11_BLEND_DESC bd = {};
u8 write_mask = 0;
if (red) write_mask |= D3D11_COLOR_WRITE_ENABLE_RED;
if (green) write_mask |= D3D11_COLOR_WRITE_ENABLE_GREEN;
if (blue) write_mask |= D3D11_COLOR_WRITE_ENABLE_BLUE;
if (alpha) write_mask |= D3D11_COLOR_WRITE_ENABLE_ALPHA;
bd.RenderTarget[0].RenderTargetWriteMask = write_mask;
wil::com_ptr_nothrow<ID3D11BlendState> bs;
m_dev->CreateBlendState(&bd, bs.put());
StretchRect(sTex, sRect, dTex, dRect, m_convert.ps[static_cast<int>(ShaderConvert::COPY)].get(), nullptr, bs.get(), false);
const u8 index = static_cast<u8>(red) | (static_cast<u8>(green) << 1) | (static_cast<u8>(blue) << 2) |
(static_cast<u8>(alpha) << 3);
StretchRect(sTex, sRect, dTex, dRect, m_convert.ps[static_cast<int>(ShaderConvert::COPY)].get(), nullptr,
m_convert.bs[index].get(), false);
}
void GSDevice11::StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ID3D11PixelShader* ps, ID3D11Buffer* ps_cb, ID3D11BlendState* bs, bool linear)
@@ -730,7 +721,7 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
// om
OMSetDepthStencilState(m_convert.dss.get(), 0);
OMSetBlendState(m_convert.bs.get(), 0);
OMSetBlendState(m_convert.bs[D3D11_COLOR_WRITE_ENABLE_ALL].get(), 0);
@@ -798,7 +789,91 @@ void GSDevice11::UpdateCLUTTexture(GSTexture* sTex, u32 offsetX, u32 offsetY, GS
StretchRect(sTex, GSVector4::zero(), dTex, dRect, m_convert.ps[static_cast<int>(shader)].get(), m_merge.cb.get(), nullptr, false);
}
void GSDevice11::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c)
void GSDevice11::DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader)
{
IASetInputLayout(m_convert.il.get());
IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
VSSetShader(m_convert.vs.get(), nullptr);
GSSetShader(nullptr, nullptr);
PSSetShader(m_convert.ps[static_cast<int>(shader)].get(), nullptr);
OMSetDepthStencilState(dTex->IsRenderTarget() ? m_convert.dss.get() : m_convert.dss_write.get(), 0);
OMSetRenderTargets(dTex->IsRenderTarget() ? dTex : nullptr, dTex->IsDepthStencil() ? dTex : nullptr);
const GSVector2 ds(static_cast<float>(dTex->GetWidth()), static_cast<float>(dTex->GetHeight()));
GSTexture* last_tex = rects[0].src;
bool last_linear = rects[0].linear;
u8 last_wmask = rects[0].wmask.wrgba;
u32 first = 0;
u32 count = 1;
for (u32 i = 1; i < num_rects; i++)
{
if (rects[i].src == last_tex && rects[i].linear == last_linear && rects[i].wmask.wrgba == last_wmask)
{
count++;
continue;
}
DoMultiStretchRects(rects + first, count, ds);
last_tex = rects[i].src;
last_linear = rects[i].linear;
last_wmask = rects[i].wmask.wrgba;
first += count;
count = 1;
}
DoMultiStretchRects(rects + first, count, ds);
}
void GSDevice11::DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, const GSVector2& ds)
{
// Don't use primitive restart here, it ends up slower on some drivers.
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);
u32 icount = 0;
u32 vcount = 0;
for (u32 i = 0; i < num_rects; i++)
{
const GSVector4& sRect = rects[i].src_rect;
const GSVector4& dRect = rects[i].dst_rect;
const float left = dRect.x * 2 / ds.x - 1.0f;
const float top = 1.0f - dRect.y * 2 / ds.y;
const float right = dRect.z * 2 / ds.x - 1.0f;
const float bottom = 1.0f - dRect.w * 2 / ds.y;
const u32 vstart = vcount;
verts[vcount++] = {GSVector4(left, top, 0.5f, 1.0f), GSVector2(sRect.x, sRect.y)};
verts[vcount++] = {GSVector4(right, top, 0.5f, 1.0f), GSVector2(sRect.z, sRect.y)};
verts[vcount++] = {GSVector4(left, bottom, 0.5f, 1.0f), GSVector2(sRect.x, sRect.w)};
verts[vcount++] = {GSVector4(right, bottom, 0.5f, 1.0f), GSVector2(sRect.z, sRect.w)};
if (i > 0)
idx[icount++] = vstart;
idx[icount++] = vstart;
idx[icount++] = vstart + 1;
idx[icount++] = vstart + 2;
idx[icount++] = vstart + 3;
idx[icount++] = vstart + 3;
};
IAUnmapVertexBuffer(sizeof(GSVertexPT1), vcount);
IAUnmapIndexBuffer(icount);
PSSetShaderResource(0, rects[0].src);
PSSetSamplerState(rects[0].linear ? m_convert.ln.get() : m_convert.pt.get());
OMSetBlendState(m_convert.bs[rects[0].wmask.wrgba].get(), 0.0f);
DrawIndexedPrimitive();
}
void GSDevice11::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear)
{
const GSVector4 full_r(0.0f, 0.0f, 1.0f, 1.0f);
const bool feedback_write_2 = PMODE.EN2 && sTex[2] != nullptr && EXTBUF.FBIN == 1;
@@ -821,14 +896,14 @@ void GSDevice11::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
{
// 2nd output is enabled and selected. Copy it to destination so we can blend it with 1st output
// Note: value outside of dRect must contains the background color (c)
StretchRect(sTex[1], sRect[1], dTex, PMODE.SLBG ? dRect[2] : dRect[1], ShaderConvert::COPY);
StretchRect(sTex[1], sRect[1], dTex, PMODE.SLBG ? dRect[2] : dRect[1], ShaderConvert::COPY, linear);
}
// Save 2nd output
if (feedback_write_2)
{
StretchRect(dTex, full_r, sTex[2], dRect[2], m_convert.ps[static_cast<int>(ShaderConvert::YUV)].get(),
m_merge.cb.get(), nullptr, true);
m_merge.cb.get(), nullptr, linear);
}
// Restore background color to process the normal merge
@@ -838,13 +913,13 @@ void GSDevice11::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
if (sTex[0])
{
// 1st output is enabled. It must be blended
StretchRect(sTex[0], sRect[0], dTex, dRect[0], m_merge.ps[PMODE.MMOD].get(), m_merge.cb.get(), m_merge.bs.get(), true);
StretchRect(sTex[0], sRect[0], dTex, dRect[0], m_merge.ps[PMODE.MMOD].get(), m_merge.cb.get(), m_merge.bs.get(), linear);
}
if (feedback_write_1)
{
StretchRect(sTex[0], full_r, sTex[2], dRect[2], m_convert.ps[static_cast<int>(ShaderConvert::YUV)].get(),
m_merge.cb.get(), nullptr, true);
m_merge.cb.get(), nullptr, linear);
}
}
@@ -991,11 +1066,11 @@ void GSDevice11::SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* vert
//
}
bool GSDevice11::IASetVertexBuffer(const void* vertex, u32 stride, u32 count)
void* GSDevice11::IAMapVertexBuffer(u32 stride, u32 count)
{
const u32 size = stride * count;
if (size > VERTEX_BUFFER_SIZE)
return false;
return nullptr;
D3D11_MAP type = D3D11_MAP_WRITE_NO_OVERWRITE;
@@ -1010,10 +1085,13 @@ bool GSDevice11::IASetVertexBuffer(const void* vertex, u32 stride, u32 count)
D3D11_MAPPED_SUBRESOURCE m;
if (FAILED(m_ctx->Map(m_vb.get(), 0, type, 0, &m)))
return false;
return nullptr;
GSVector4i::storent(static_cast<u8*>(m.pData) + m_vertex.start * stride, vertex, count * stride);
return static_cast<u8*>(m.pData) + (m_vertex.start * stride);
}
void GSDevice11::IAUnmapVertexBuffer(u32 stride, u32 count)
{
m_ctx->Unmap(m_vb.get(), 0);
if (m_state.vb_stride != stride)
@@ -1024,13 +1102,24 @@ bool GSDevice11::IASetVertexBuffer(const void* vertex, u32 stride, u32 count)
}
m_vertex.count = count;
}
bool GSDevice11::IASetVertexBuffer(const void* vertex, u32 stride, u32 count)
{
void* map = IAMapVertexBuffer(stride, count);
if (!map)
return false;
GSVector4i::storent(map, vertex, count * stride);
IAUnmapVertexBuffer(stride, count);
return true;
}
bool GSDevice11::IASetIndexBuffer(const void* index, u32 count)
u32* GSDevice11::IAMapIndexBuffer(u32 count)
{
if (count > (INDEX_BUFFER_SIZE / sizeof(u32)))
return false;
return nullptr;
D3D11_MAP type = D3D11_MAP_WRITE_NO_OVERWRITE;
@@ -1046,11 +1135,25 @@ bool GSDevice11::IASetIndexBuffer(const void* index, u32 count)
D3D11_MAPPED_SUBRESOURCE m;
if (FAILED(m_ctx->Map(m_ib.get(), 0, type, 0, &m)))
return false;
return nullptr;
std::memcpy((u8*)m.pData + m_index.start * sizeof(u32), index, count * sizeof(u32));
return static_cast<u32*>(m.pData) + m_index.start;
}
void GSDevice11::IAUnmapIndexBuffer(u32 count)
{
m_ctx->Unmap(m_ib.get(), 0);
m_index.count = count;
}
bool GSDevice11::IASetIndexBuffer(const void* index, u32 count)
{
u32* map = IAMapIndexBuffer(count);
if (!map)
return false;
std::memcpy(map, index, count * sizeof(u32));
IAUnmapIndexBuffer(count);
return true;
}

View File

@@ -126,7 +126,7 @@ private:
std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) final;
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) final;
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear) final;
void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0, int bufIdx = 0) final;
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final;
@@ -176,7 +176,7 @@ private:
wil::com_ptr_nothrow<ID3D11SamplerState> pt;
wil::com_ptr_nothrow<ID3D11DepthStencilState> dss;
wil::com_ptr_nothrow<ID3D11DepthStencilState> dss_write;
wil::com_ptr_nothrow<ID3D11BlendState> bs;
std::array<wil::com_ptr_nothrow<ID3D11BlendState>, 16> bs;
} m_convert;
struct
@@ -278,11 +278,19 @@ public:
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ID3D11PixelShader* ps, ID3D11Buffer* ps_cb, ID3D11BlendState* bs, bool linear = true);
void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear) override;
void UpdateCLUTTexture(GSTexture* sTex, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) override;
void DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader) override;
void DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, const GSVector2& ds);
void SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* vertices, bool datm);
void* IAMapVertexBuffer(u32 stride, u32 count);
void IAUnmapVertexBuffer(u32 stride, u32 count);
bool IASetVertexBuffer(const void* vertex, u32 stride, u32 count);
u32* IAMapIndexBuffer(u32 count);
void IAUnmapIndexBuffer(u32 count);
bool IASetIndexBuffer(const void* index, u32 count);
void IASetInputLayout(ID3D11InputLayout* layout);
void IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY topology);

View File

@@ -160,6 +160,7 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
sm.AddMacro("PS_POINT_SAMPLER", sel.point_sampler);
sm.AddMacro("PS_SHUFFLE", sel.shuffle);
sm.AddMacro("PS_READ_BA", sel.read_ba);
sm.AddMacro("PS_READ16_SRC", sel.real16src);
sm.AddMacro("PS_CHANNEL_FETCH", sel.channel);
sm.AddMacro("PS_TALES_OF_ABYSS_HLE", sel.tales_of_abyss_hle);
sm.AddMacro("PS_URBAN_CHAOS_HLE", sel.urban_chaos_hle);
@@ -173,6 +174,7 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
sm.AddMacro("PS_BLEND_C", sel.blend_c);
sm.AddMacro("PS_BLEND_D", sel.blend_d);
sm.AddMacro("PS_BLEND_MIX", sel.blend_mix);
sm.AddMacro("PS_ROUND_INV", sel.round_inv);
sm.AddMacro("PS_FIXED_ONE_A", sel.fixed_one_a);
sm.AddMacro("PS_PABE", sel.pabe);
sm.AddMacro("PS_DITHER", sel.dither);

View File

@@ -210,6 +210,7 @@ bool GSDevice12::CheckFeatures()
m_features.line_expand = false;
m_features.framebuffer_fetch = false;
m_features.dual_source_blend = true;
m_features.clip_control = true;
m_features.stencil_buffer = true;
m_features.dxt_textures = g_d3d12_context->SupportsTextureFormat(DXGI_FORMAT_BC1_UNORM) &&
@@ -477,10 +478,129 @@ void GSDevice12::UpdateCLUTTexture(GSTexture* sTex, u32 offsetX, u32 offsetY, GS
m_convert[static_cast<int>(shader)].get(), false);
}
void GSDevice12::BeginRenderPassForStretchRect(GSTexture12* dTex, const GSVector4i& dtex_rc, const GSVector4i& dst_rc)
void GSDevice12::DrawMultiStretchRects(
const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader)
{
const bool is_whole_target = dst_rc.eq(dtex_rc);
const D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE load_op = is_whole_target ? D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD : GetLoadOpForTexture(dTex);
GSTexture* last_tex = rects[0].src;
bool last_linear = rects[0].linear;
u8 last_wmask = rects[0].wmask.wrgba;
u32 first = 0;
u32 count = 1;
// Make sure all textures are in shader read only layout, so we don't need to break
// the render pass to transition.
for (u32 i = 0; i < num_rects; i++)
{
GSTexture12* const stex = static_cast<GSTexture12*>(rects[i].src);
stex->CommitClear();
if (stex->GetResourceState() != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)
{
EndRenderPass();
stex->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
}
}
for (u32 i = 1; i < num_rects; i++)
{
if (rects[i].src == last_tex && rects[i].linear == last_linear && rects[i].wmask.wrgba == last_wmask)
{
count++;
continue;
}
DoMultiStretchRects(rects + first, count, static_cast<GSTexture12*>(dTex), shader);
last_tex = rects[i].src;
last_linear = rects[i].linear;
last_wmask = rects[i].wmask.wrgba;
first += count;
count = 1;
}
DoMultiStretchRects(rects + first, count, static_cast<GSTexture12*>(dTex), shader);
}
void GSDevice12::DoMultiStretchRects(
const MultiStretchRect* rects, u32 num_rects, GSTexture12* dTex, ShaderConvert shader)
{
// Set up vertices first.
const u32 vertex_reserve_size = num_rects * 4 * sizeof(GSVertexPT1);
const u32 index_reserve_size = num_rects * 6 * sizeof(u32);
if (!m_vertex_stream_buffer.ReserveMemory(vertex_reserve_size, sizeof(GSVertexPT1)) ||
!m_index_stream_buffer.ReserveMemory(index_reserve_size, sizeof(u32)))
{
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)))
{
pxFailRel("Failed to reserve space for vertices");
}
}
// Pain in the arse because the primitive topology for the pipelines is all triangle strips.
// 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());
u32 icount = 0;
u32 vcount = 0;
for (u32 i = 0; i < num_rects; i++)
{
const GSVector4& sRect = rects[i].src_rect;
const GSVector4& dRect = rects[i].dst_rect;
const float left = dRect.x * 2 / ds.x - 1.0f;
const float top = 1.0f - dRect.y * 2 / ds.y;
const float right = dRect.z * 2 / ds.x - 1.0f;
const float bottom = 1.0f - dRect.w * 2 / ds.y;
const u32 vstart = vcount;
verts[vcount++] = {GSVector4(left, top, 0.5f, 1.0f), GSVector2(sRect.x, sRect.y)};
verts[vcount++] = {GSVector4(right, top, 0.5f, 1.0f), GSVector2(sRect.z, sRect.y)};
verts[vcount++] = {GSVector4(left, bottom, 0.5f, 1.0f), GSVector2(sRect.x, sRect.w)};
verts[vcount++] = {GSVector4(right, bottom, 0.5f, 1.0f), GSVector2(sRect.z, sRect.w)};
if (i > 0)
idx[icount++] = vstart;
idx[icount++] = vstart;
idx[icount++] = vstart + 1;
idx[icount++] = vstart + 2;
idx[icount++] = vstart + 3;
idx[icount++] = vstart + 3;
};
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.count = icount;
m_vertex_stream_buffer.CommitMemory(vcount * sizeof(GSVertexPT1));
m_index_stream_buffer.CommitMemory(icount * sizeof(u32));
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);
// Even though we're batching, a cmdbuffer submit could've messed this up.
const GSVector4i rc(dTex->GetRect());
OMSetRenderTargets(dTex->IsRenderTarget() ? dTex : nullptr, dTex->IsDepthStencil() ? dTex : nullptr, rc);
if (!InRenderPass())
BeginRenderPassForStretchRect(dTex, rc, rc, false);
SetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
SetUtilityTexture(rects[0].src, rects[0].linear ? m_linear_sampler_cpu : m_point_sampler_cpu);
pxAssert(shader == ShaderConvert::COPY || rects[0].wmask.wrgba == 0xf);
SetPipeline((rects[0].wmask.wrgba != 0xf) ? m_color_copy[rects[0].wmask.wrgba].get() :
m_convert[static_cast<int>(shader)].get());
if (ApplyUtilityState())
DrawIndexedPrimitive();
}
void GSDevice12::BeginRenderPassForStretchRect(
GSTexture12* dTex, const GSVector4i& dtex_rc, const GSVector4i& dst_rc, bool allow_discard)
{
const D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE load_op = (allow_discard && dst_rc.eq(dtex_rc)) ?
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD :
GetLoadOpForTexture(dTex);
dTex->SetState(GSTexture::State::Dirty);
if (dTex->GetType() != GSTexture::Type::DepthStencil)
@@ -494,8 +614,8 @@ void GSDevice12::BeginRenderPassForStretchRect(GSTexture12* dTex, const GSVector
else
{
const float clear_depth = dTex->GetClearDepth();
BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
load_op, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS,
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS, load_op, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
GSVector4::zero(), clear_depth);
}
@@ -567,7 +687,7 @@ void GSDevice12::DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect,
}
void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect,
const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c)
const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear)
{
GL_PUSH("DoMerge");
@@ -575,7 +695,7 @@ void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
const bool feedback_write_2 = PMODE.EN2 && sTex[2] != nullptr && EXTBUF.FBIN == 1;
const bool feedback_write_1 = PMODE.EN1 && sTex[2] != nullptr && EXTBUF.FBIN == 0;
const bool feedback_write_2_but_blend_bg = feedback_write_2 && PMODE.SLBG == 1;
const D3D12::DescriptorHandle& sampler = linear ? m_linear_sampler_cpu : m_point_sampler_cpu;
// Merge the 2 source textures (sTex[0],sTex[1]). Final results go to dTex. Feedback write will go to sTex[2].
// If either 2nd output is disabled or SLBG is 1, a background color will be used.
// Note: background color is also used when outside of the unit rectangle area
@@ -605,7 +725,7 @@ void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
{
static_cast<GSTexture12*>(sTex[1])->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
OMSetRenderTargets(dTex, nullptr, darea);
SetUtilityTexture(sTex[1], m_linear_sampler_cpu);
SetUtilityTexture(sTex[1], sampler);
BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS, c);
@@ -625,7 +745,7 @@ void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
EndRenderPass();
OMSetRenderTargets(sTex[2], nullptr, fbarea);
if (dcleared)
SetUtilityTexture(dTex, m_linear_sampler_cpu);
SetUtilityTexture(dTex, sampler);
// sTex[2] can be sTex[0], in which case it might be cleared (e.g. Xenosaga).
BeginRenderPassForStretchRect(static_cast<GSTexture12*>(sTex[2]), fbarea, GSVector4i(dRect[2]));
@@ -663,7 +783,7 @@ void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
{
// 1st output is enabled. It must be blended
SetUtilityRootSignature();
SetUtilityTexture(sTex[0], m_linear_sampler_cpu);
SetUtilityTexture(sTex[0], sampler);
SetPipeline(m_merge[PMODE.MMOD].get());
DrawStretchRect(sRect[0], dRect[0], dTex->GetSize());
}
@@ -673,7 +793,7 @@ void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
EndRenderPass();
SetUtilityRootSignature();
SetPipeline(m_convert[static_cast<int>(ShaderConvert::YUV)].get());
SetUtilityTexture(dTex, m_linear_sampler_cpu);
SetUtilityTexture(dTex, sampler);
OMSetRenderTargets(sTex[2], nullptr, fbarea);
BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE);
DrawStretchRect(full_r, dRect[2], dsize);
@@ -1501,6 +1621,7 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector&
sm.AddMacro("PS_POINT_SAMPLER", sel.point_sampler);
sm.AddMacro("PS_SHUFFLE", sel.shuffle);
sm.AddMacro("PS_READ_BA", sel.read_ba);
sm.AddMacro("PS_READ16_SRC", sel.real16src);
sm.AddMacro("PS_CHANNEL_FETCH", sel.channel);
sm.AddMacro("PS_TALES_OF_ABYSS_HLE", sel.tales_of_abyss_hle);
sm.AddMacro("PS_URBAN_CHAOS_HLE", sel.urban_chaos_hle);
@@ -1514,6 +1635,7 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector&
sm.AddMacro("PS_BLEND_C", sel.blend_c);
sm.AddMacro("PS_BLEND_D", sel.blend_d);
sm.AddMacro("PS_BLEND_MIX", sel.blend_mix);
sm.AddMacro("PS_ROUND_INV", sel.round_inv);
sm.AddMacro("PS_FIXED_ONE_A", sel.fixed_one_a);
sm.AddMacro("PS_PABE", sel.pabe);
sm.AddMacro("PS_DITHER", sel.dither);
@@ -1562,10 +1684,13 @@ GSDevice12::ComPtr<ID3D12PipelineState> GSDevice12::CreateTFXPipeline(const Pipe
gpb.SetRasterizationState(D3D12_FILL_MODE_SOLID, D3D12_CULL_MODE_NONE, false);
if (p.rt)
{
gpb.SetRenderTarget(0,
IsDATEModePrimIDInit(p.ps.date) ? DXGI_FORMAT_R32_FLOAT :
p.ps.hdr ? DXGI_FORMAT_R32G32B32A32_FLOAT :
DXGI_FORMAT_R8G8B8A8_UNORM);
const GSTexture::Format format = IsDATEModePrimIDInit(p.ps.date) ?
GSTexture::Format::PrimID :
(p.ps.hdr ? GSTexture::Format::HDRColor : GSTexture::Format::Color);
DXGI_FORMAT native_format;
LookupNativeFormat(format, nullptr, nullptr, &native_format, nullptr);
gpb.SetRenderTarget(0, native_format);
}
if (p.ds)
gpb.SetDepthStencilFormat(DXGI_FORMAT_D32_FLOAT_S8X24_UINT);
@@ -2346,13 +2471,12 @@ GSTexture12* GSDevice12::SetupPrimitiveTrackingDATE(GSHWDrawConfig& config, Pipe
OMSetRenderTargets(image, config.ds, config.drawarea);
// if the depth target has been cleared, we need to preserve that clear
BeginRenderPass(
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
GetLoadOpForTexture(static_cast<GSTexture12*>(config.ds)),
BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
config.ds ? GetLoadOpForTexture(static_cast<GSTexture12*>(config.ds)) :
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS,
config.ds ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE : D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
GSVector4::zero(),
static_cast<GSTexture12*>(config.ds)->GetClearDepth());
GSVector4::zero(), config.ds ? static_cast<GSTexture12*>(config.ds)->GetClearDepth() : 0.0f);
// draw the quad to prefill the image
const GSVector4 src = GSVector4(config.drawarea) / GSVector4(rtsize).xyxy();
@@ -2536,7 +2660,8 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
}
// avoid restarting the render pass just to switch from rt+depth to rt and vice versa
if (m_in_render_pass && !hdr_rt && !draw_ds && m_current_depth_target && m_current_render_target == draw_rt && config.tex != m_current_depth_target)
if (m_in_render_pass && !hdr_rt && !draw_ds && m_current_depth_target && m_current_render_target == draw_rt &&
config.tex != m_current_depth_target && m_current_depth_target->GetSize() == draw_rt->GetSize())
{
draw_ds = m_current_depth_target;
m_pipeline_selector.ds = true;

View File

@@ -183,7 +183,7 @@ private:
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE,
const GSRegEXTBUF& EXTBUF, const GSVector4& c) final;
const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear) final;
void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0, int bufIdx = 0) final;
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final;
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
@@ -254,8 +254,11 @@ public:
void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
PresentShader shader, float shaderTime, bool linear) override;
void UpdateCLUTTexture(GSTexture* sTex, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) override;
void DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader) override;
void DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTexture12* dTex, ShaderConvert shader);
void BeginRenderPassForStretchRect(GSTexture12* dTex, const GSVector4i& dtex_rc, const GSVector4i& dst_rc);
void BeginRenderPassForStretchRect(
GSTexture12* dTex, const GSVector4i& dtex_rc, const GSVector4i& dst_rc, bool allow_discard = true);
void DoStretchRect(GSTexture12* sTex, const GSVector4& sRect, GSTexture12* dTex, const GSVector4& dRect,
const ID3D12PipelineState* pipeline, bool linear);
void DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds);

View File

@@ -170,40 +170,6 @@ bool GSHwHack::GSC_SacredBlaze(GSRendererHW& r, const GSFrameInfo& fi, int& skip
return true;
}
bool GSHwHack::GSC_Spartan(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
{
if (skip == 0)
{
if (fi.TME)
{
// depth textures (bully, mgs3s1 intro, Front Mission 5)
if ((fi.TPSM == PSM_PSMZ32 || fi.TPSM == PSM_PSMZ24 || fi.TPSM == PSM_PSMZ16 || fi.TPSM == PSM_PSMZ16S) ||
// General, often problematic post processing
(GSUtil::HasSharedBits(fi.FBP, fi.FPSM, fi.TBP0, fi.TPSM)))
{
skip = 2;
}
}
}
return true;
}
bool GSHwHack::GSC_Oneechanbara2Special(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
{
if (skip == 0)
{
if (!s_nativeres && fi.TPSM == PSM_PSMCT24 && fi.TME && fi.FBP == 0x01180)
{
// Don't enable hack on native res if crc is below aggressive.
// Ghosting upscaling issue, bottom and right red lines also by upscaling.
skip = 1;
}
}
return true;
}
bool GSHwHack::GSC_SakuraTaisen(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
{
if (skip == 0)
@@ -499,21 +465,6 @@ bool GSHwHack::GSC_SakuraWarsSoLongMyLove(GSRendererHW& r, const GSFrameInfo& fi
return true;
}
bool GSHwHack::GSC_FightingBeautyWulong(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
{
if (skip == 0)
{
if (!s_nativeres && fi.TME && (fi.TBP0 == 0x0700 || fi.TBP0 == 0x0a80) && (fi.TPSM == PSM_PSMCT32 || fi.TPSM == PSM_PSMCT24))
{
// Don't enable hack on native res if crc is below aggressive.
// removes glow/blur which cause ghosting and other sprite issues similar to Tekken 5
skip = 1;
}
}
return true;
}
bool GSHwHack::GSC_GodHand(GSRendererHW& r, const GSFrameInfo& fi, int& skip)
{
if (skip == 0)
@@ -1036,30 +987,6 @@ bool GSHwHack::OI_FFX(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCa
return true;
}
bool GSHwHack::OI_MetalSlug6(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
// missing red channel fix (looks alright in pcsx2 r5000+)
GSVertex* RESTRICT v = r.m_vertex.buff;
for (size_t i = r.m_vertex.next; i > 0; i--, v++)
{
const u32 c = v->RGBAQ.U32[0];
const u32 r = (c >> 0) & 0xff;
const u32 g = (c >> 8) & 0xff;
const u32 b = (c >> 16) & 0xff;
if (r == 0 && g != 0 && b != 0)
{
v->RGBAQ.U32[0] = (c & 0xffffff00) | ((g + b + 1) >> 1);
}
}
r.m_vt.Update(r.m_vertex.buff, r.m_index.buff, r.m_vertex.tail, r.m_index.tail, r.m_vt.m_primclass);
return true;
}
bool GSHwHack::OI_RozenMaidenGebetGarden(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
@@ -1180,7 +1107,7 @@ bool GSHwHack::OI_ArTonelico2(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GST
bool GSHwHack::OI_JakGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
if (!(r.m_r == GSVector4i(0, 0, 16, 16)).alltrue())
if (RCONTEXT->FRAME.FBW != 1 || !(r.m_r == GSVector4i(0, 0, 16, 16)).alltrue())
return true; // Only 16x16 draws.
if (!r.CanUseSwSpriteRender())
@@ -1282,7 +1209,6 @@ const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_function
// Channel Effect
CRC_F(GSC_CrashBandicootWoC, CRCHackLevel::Partial),
CRC_F(GSC_GiTS, CRCHackLevel::Partial),
CRC_F(GSC_Spartan, CRCHackLevel::Partial),
CRC_F(GSC_SteambotChronicles, CRCHackLevel::Partial),
// Depth Issue
@@ -1296,8 +1222,6 @@ const GSHwHack::Entry<GSRendererHW::GSC_Ptr> GSHwHack::s_get_skip_count_function
CRC_F(GSC_DeathByDegreesTekkenNinaWilliams, CRCHackLevel::Partial), // + Upscaling issues
// Upscaling hacks
CRC_F(GSC_FightingBeautyWulong, CRCHackLevel::Partial),
CRC_F(GSC_Oneechanbara2Special, CRCHackLevel::Partial),
CRC_F(GSC_UltramanFightingEvolution, CRCHackLevel::Partial),
CRC_F(GSC_YakuzaGames, CRCHackLevel::Partial),
@@ -1322,7 +1246,6 @@ const GSHwHack::Entry<GSRendererHW::OI_Ptr> GSHwHack::s_before_draw_functions[]
CRC_F(OI_DBZBTGames, CRCHackLevel::Minimum),
CRC_F(OI_FFXII, CRCHackLevel::Minimum),
CRC_F(OI_FFX, CRCHackLevel::Minimum),
CRC_F(OI_MetalSlug6, CRCHackLevel::Minimum),
CRC_F(OI_RozenMaidenGebetGarden, CRCHackLevel::Minimum),
CRC_F(OI_SonicUnleashed, CRCHackLevel::Minimum),
CRC_F(OI_ArTonelico2, CRCHackLevel::Minimum),

View File

@@ -24,8 +24,6 @@ public:
static bool GSC_Manhunt2(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
static bool GSC_CrashBandicootWoC(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
static bool GSC_SacredBlaze(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
static bool GSC_Spartan(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
static bool GSC_Oneechanbara2Special(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
static bool GSC_SakuraTaisen(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
static bool GSC_SFEX3(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
static bool GSC_Tekken5(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
@@ -39,7 +37,6 @@ public:
static bool GSC_Kunoichi(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
static bool GSC_ZettaiZetsumeiToshi2(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
static bool GSC_SakuraWarsSoLongMyLove(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
static bool GSC_FightingBeautyWulong(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
static bool GSC_GodHand(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
static bool GSC_KnightsOfTheTemple2(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
static bool GSC_UltramanFightingEvolution(GSRendererHW& r, const GSFrameInfo& fi, int& skip);
@@ -63,7 +60,6 @@ public:
static bool OI_DBZBTGames(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_FFXII(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_FFX(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_MetalSlug6(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_RozenMaidenGebetGarden(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
static bool OI_ArTonelico2(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);

File diff suppressed because it is too large Load Diff

View File

@@ -75,6 +75,7 @@ private:
CLUTDrawOnGPU,
};
bool HasEEUpload(GSVector4i r);
CLUTDrawTestResult PossibleCLUTDraw();
CLUTDrawTestResult PossibleCLUTDrawAggressive();
bool CanUseSwPrimRender(bool no_rt, bool no_ds, bool draw_sprite_tex);
@@ -97,8 +98,8 @@ private:
void SetTCOffset();
GSTextureCache* m_tc;
GSVector4i m_r;
GSTextureCache::Source* m_src;
GSVector4i m_r = {};
GSTextureCache::Source* m_src = nullptr;
// CRC Hacks
bool IsBadFrame();
@@ -107,22 +108,27 @@ private:
int m_skip = 0;
int m_skip_offset = 0;
bool m_reset;
bool m_tex_is_fb;
bool m_channel_shuffle;
bool m_userhacks_tcoffset;
float m_userhacks_tcoffset_x;
float m_userhacks_tcoffset_y;
u32 m_last_channel_shuffle_fbmsk = 0;
bool m_channel_shuffle = false;
GSVector2i m_lod; // Min & Max level of detail
bool m_tex_is_fb = false;
bool m_userhacks_tcoffset = false;
float m_userhacks_tcoffset_x = 0.0f;
float m_userhacks_tcoffset_y = 0.0f;
GSHWDrawConfig m_conf;
GSVector2i m_lod = {}; // Min & Max level of detail
GSHWDrawConfig m_conf = {};
// software sprite renderer state
std::vector<GSVertexSW> m_sw_vertex_buffer;
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;
@@ -145,7 +151,6 @@ public:
GSVector4i ComputeBoundingBox(const GSVector2& rtscale, const GSVector2i& rtsize);
void MergeSprite(GSTextureCache::Source* tex);
GSVector2 GetTextureScaleFactor() override;
GSVector2i GetOutputSize(int real_h);
GSVector2i GetTargetSize(GSVector2i* unscaled_size = nullptr);
void Reset(bool hardware_reset) override;
@@ -161,6 +166,7 @@ public:
void Draw() override;
void PurgeTextureCache() override;
void ReadbackTextureCache() override;
GSTexture* LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, const GSVector2i& size) override;
// Called by the texture cache to know if current texture is useful

File diff suppressed because it is too large Load Diff

View File

@@ -132,6 +132,13 @@ public:
Surface();
virtual ~Surface();
/// Returns true if the target wraps around the end of GS memory.
bool Wraps() const { return (m_end_block < m_TEX0.TBP0); }
/// Returns the end block for the target, but doesn't wrap at 0x3FFF.
/// Can be used for overlap tests.
u32 UnwrappedEndBlock() const { return (m_end_block + (Wraps() ? MAX_BLOCKS : 0)); }
void UpdateAge();
bool Inside(u32 bp, u32 bw, u32 psm, const GSVector4i& rect);
bool Overlaps(u32 bp, u32 bw, u32 psm, const GSVector4i& rect);
@@ -234,19 +241,24 @@ public:
class Target : public Surface
{
public:
const int m_type;
bool m_used;
const int m_type = 0;
const bool m_depth_supported = false;
bool m_dirty_alpha = true;
bool m_is_frame = false;
bool m_used = false;
GSDirtyRectList m_dirty;
GSVector4i m_valid;
const bool m_depth_supported;
bool m_dirty_alpha;
bool m_is_frame;
GSVector4i m_valid{};
GSVector4i m_drawn_since_read{};
u32 m_valid_bits = 0;
int readbacks_since_draw = 0;
public:
Target(const GIFRegTEX0& TEX0, const bool depth_supported, const int type);
~Target();
void UpdateValidity(const GSVector4i& rect);
void ResizeValidity(const GSVector4i& rect);
void UpdateValidity(const GSVector4i& rect, bool can_resize = true);
void UpdateValidBits(u32 bits_written);
void Update(bool reset_age);
@@ -303,10 +315,10 @@ public:
struct
{
u32 fbp : 9;
u32 fbp : 14;
u32 fbw : 6;
u32 psm : 6;
u32 pad : 11;
u32 pad : 6;
};
};
@@ -382,6 +394,8 @@ protected:
// TODO: virtual void Write(Source* s, const GSVector4i& r) = 0;
// TODO: virtual void Write(Target* t, const GSVector4i& r) = 0;
Source* CreateMergedSource(GIFRegTEX0 TEX0, GIFRegTEXA TEXA, SourceRegion region, const GSVector2& scale);
public:
GSTextureCache();
~GSTextureCache();
@@ -395,15 +409,16 @@ public:
void Read(Target* t, const GSVector4i& r);
void Read(Source* t, const GSVector4i& r);
void RemoveAll();
void RemovePartial();
void AddDirtyRectTarget(Target* target, GSVector4i rect, u32 psm, u32 bw);
void ReadbackAll();
void AddDirtyRectTarget(Target* target, GSVector4i rect, u32 psm, u32 bw, RGBAMask rgba);
GSTexture* LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVector2i& offset, const GSVector2i& size);
Source* LookupSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, const GSVector2i* lod);
Source* LookupDepthSource(const GIFRegTEX0& TEX0, const GIFRegTEXA& TEXA, const GIFRegCLAMP& CLAMP, const GSVector4i& r, bool palette = false);
Target* LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask = 0, const bool is_frame = false, const int real_w = 0, const int real_h = 0, bool preload = GSConfig.PreloadFrameWithGSData);
Target* FindTargetOverlap(u32 bp, u32 end_block, int type, int psm);
Target* LookupTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, int type, bool used, u32 fbmask = 0, const bool is_frame = false, const int real_w = 0, const int real_h = 0, bool preload = GSConfig.PreloadFrameWithGSData, bool is_clear = false);
Target* LookupDisplayTarget(const GIFRegTEX0& TEX0, const GSVector2i& size, const int real_w, const int real_h);
/// Looks up a target in the cache, and only returns it if the BP/BW/PSM match exactly.

View File

@@ -287,7 +287,7 @@ public:
bool blend_color : 1;
bool pipeline_sel : 1;
bool sampler : 1;
} has;
} has = {};
DepthStencilSelector depth_sel = DepthStencilSelector::NoDepth();
// Clear line (Things below here are tracked by `has` and don't need to be cleared to reset)
SamplerSelector sampler_sel;
@@ -351,7 +351,7 @@ public:
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) override;
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear) override;
void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset, int bufIdx) override;
void DoFXAA(GSTexture* sTex, GSTexture* dTex) override;
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) override;

View File

@@ -542,7 +542,7 @@ GSTexture* GSDeviceMTL::CreateSurface(GSTexture::Type type, int width, int heigh
}
}}
void GSDeviceMTL::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c)
void GSDeviceMTL::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear)
{ @autoreleasepool {
id<MTLCommandBuffer> cmdbuf = GetRenderCmdBuf();
GSScopedDebugGroupMTL dbg(cmdbuf, @"DoMerge");
@@ -563,12 +563,12 @@ void GSDeviceMTL::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
{
// 2nd output is enabled and selected. Copy it to destination so we can blend it with 1st output
// Note: value outside of dRect must contains the background color (c)
StretchRect(sTex[1], sRect[1], dTex, dRect[1], ShaderConvert::COPY);
StretchRect(sTex[1], sRect[1], dTex, dRect[1], ShaderConvert::COPY, linear);
}
// Save 2nd output
if (feedback_write_2) // FIXME I'm not sure dRect[1] is always correct
DoStretchRect(dTex, full_r, sTex[2], dRect[1], m_convert_pipeline[static_cast<int>(ShaderConvert::YUV)], true, LoadAction::DontCareIfFull, &cb_yuv, sizeof(cb_yuv));
DoStretchRect(dTex, full_r, sTex[2], dRect[1], m_convert_pipeline[static_cast<int>(ShaderConvert::YUV)], linear, LoadAction::DontCareIfFull, &cb_yuv, sizeof(cb_yuv));
if (feedback_write_2_but_blend_bg)
ClearRenderTarget(dTex, c);
@@ -582,17 +582,17 @@ void GSDeviceMTL::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
if (PMODE.MMOD == 1)
{
// Blend with a constant alpha
DoStretchRect(sTex[0], sRect[0], dTex, dRect[0], pipeline, true, LoadAction::Load, &cb_c, sizeof(cb_c));
DoStretchRect(sTex[0], sRect[0], dTex, dRect[0], pipeline, linear, LoadAction::Load, &cb_c, sizeof(cb_c));
}
else
{
// Blend with 2 * input alpha
DoStretchRect(sTex[0], sRect[0], dTex, dRect[0], pipeline, true, LoadAction::Load, nullptr, 0);
DoStretchRect(sTex[0], sRect[0], dTex, dRect[0], pipeline, linear, LoadAction::Load, nullptr, 0);
}
}
if (feedback_write_1) // FIXME I'm not sure dRect[0] is always correct
StretchRect(dTex, full_r, sTex[2], dRect[0], ShaderConvert::YUV);
StretchRect(dTex, full_r, sTex[2], dRect[0], ShaderConvert::YUV, linear);
}}
void GSDeviceMTL::DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset, int bufIdx)
@@ -741,6 +741,7 @@ bool GSDeviceMTL::Create()
m_features.bptc_textures = true;
m_features.framebuffer_fetch = m_dev.features.framebuffer_fetch;
m_features.dual_source_blend = true;
m_features.clip_control = true;
m_features.stencil_buffer = true;
m_features.cas_sharpening = true;
@@ -1379,6 +1380,7 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
setFnConstantB(m_fn_constants, pssel.ltf, GSMTLConstantIndex_PS_LTF);
setFnConstantB(m_fn_constants, pssel.shuffle, GSMTLConstantIndex_PS_SHUFFLE);
setFnConstantB(m_fn_constants, pssel.read_ba, GSMTLConstantIndex_PS_READ_BA);
setFnConstantB(m_fn_constants, pssel.real16src, GSMTLConstantIndex_PS_READ16_SRC);
setFnConstantB(m_fn_constants, pssel.write_rg, GSMTLConstantIndex_PS_WRITE_RG);
setFnConstantB(m_fn_constants, pssel.fbmask, GSMTLConstantIndex_PS_FBMASK);
setFnConstantI(m_fn_constants, pssel.blend_a, GSMTLConstantIndex_PS_BLEND_A);
@@ -1389,6 +1391,7 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
setFnConstantB(m_fn_constants, pssel.hdr, GSMTLConstantIndex_PS_HDR);
setFnConstantB(m_fn_constants, pssel.colclip, GSMTLConstantIndex_PS_COLCLIP);
setFnConstantI(m_fn_constants, pssel.blend_mix, GSMTLConstantIndex_PS_BLEND_MIX);
setFnConstantB(m_fn_constants, pssel.round_inv, GSMTLConstantIndex_PS_ROUND_INV);
setFnConstantB(m_fn_constants, pssel.fixed_one_a, GSMTLConstantIndex_PS_FIXED_ONE_A);
setFnConstantB(m_fn_constants, pssel.pabe, GSMTLConstantIndex_PS_PABE);
setFnConstantB(m_fn_constants, pssel.no_color, GSMTLConstantIndex_PS_NO_COLOR);

View File

@@ -175,6 +175,7 @@ enum GSMTLFnConstants
GSMTLConstantIndex_PS_LTF,
GSMTLConstantIndex_PS_SHUFFLE,
GSMTLConstantIndex_PS_READ_BA,
GSMTLConstantIndex_PS_READ16_SRC,
GSMTLConstantIndex_PS_WRITE_RG,
GSMTLConstantIndex_PS_FBMASK,
GSMTLConstantIndex_PS_BLEND_A,
@@ -185,6 +186,7 @@ enum GSMTLFnConstants
GSMTLConstantIndex_PS_HDR,
GSMTLConstantIndex_PS_COLCLIP,
GSMTLConstantIndex_PS_BLEND_MIX,
GSMTLConstantIndex_PS_ROUND_INV,
GSMTLConstantIndex_PS_FIXED_ONE_A,
GSMTLConstantIndex_PS_PABE,
GSMTLConstantIndex_PS_NO_COLOR,

View File

@@ -42,6 +42,7 @@ constant bool PS_ADJT [[function_constant(GSMTLConstantIndex_PS_AD
constant bool PS_LTF [[function_constant(GSMTLConstantIndex_PS_LTF)]];
constant bool PS_SHUFFLE [[function_constant(GSMTLConstantIndex_PS_SHUFFLE)]];
constant bool PS_READ_BA [[function_constant(GSMTLConstantIndex_PS_READ_BA)]];
constant bool PS_READ16_SRC [[function_constant(GSMTLConstantIndex_PS_READ16_SRC)]];
constant bool PS_WRITE_RG [[function_constant(GSMTLConstantIndex_PS_WRITE_RG)]];
constant bool PS_FBMASK [[function_constant(GSMTLConstantIndex_PS_FBMASK)]];
constant uint PS_BLEND_A [[function_constant(GSMTLConstantIndex_PS_BLEND_A)]];
@@ -52,6 +53,7 @@ constant uint PS_CLR_HW [[function_constant(GSMTLConstantIndex_PS_CL
constant bool PS_HDR [[function_constant(GSMTLConstantIndex_PS_HDR)]];
constant bool PS_COLCLIP [[function_constant(GSMTLConstantIndex_PS_COLCLIP)]];
constant uint PS_BLEND_MIX [[function_constant(GSMTLConstantIndex_PS_BLEND_MIX)]];
constant bool PS_ROUND_INV [[function_constant(GSMTLConstantIndex_PS_ROUND_INV)]];
constant bool PS_FIXED_ONE_A [[function_constant(GSMTLConstantIndex_PS_FIXED_ONE_A)]];
constant bool PS_PABE [[function_constant(GSMTLConstantIndex_PS_PABE)]];
constant bool PS_NO_COLOR [[function_constant(GSMTLConstantIndex_PS_NO_COLOR)]];
@@ -792,7 +794,11 @@ struct PSMain
fpos = ushort2(in.p.xy);
else
fpos = ushort2(in.p.xy / SCALING_FACTOR);
C.rgb += cb.dither_matrix[fpos.y & 3][fpos.x & 3];
float value = cb.dither_matrix[fpos.y & 3][fpos.x & 3];;
if (PS_ROUND_INV)
C.rgb -= value;
else
C.rgb += value;
}
void ps_color_clamp_wrap(thread float4& C)
@@ -801,6 +807,9 @@ struct PSMain
if (!SW_BLEND && !PS_DITHER && !PS_FBMASK)
return;
if (PS_DFMT == FMT_16 && PS_BLEND_MIX == 0 && PS_ROUND_INV)
C.rgb += 7.f; // Need to round up, not down since the shader will invert
// Correct the Color value based on the output format
if (!PS_COLCLIP && !PS_HDR)
C.rgb = clamp(C.rgb, 0.f, 255.f); // Standard Clamp
@@ -824,8 +833,10 @@ struct PSMain
return selector == 0 ? zero : selector == 1 ? one : two;
}
void ps_blend(thread float4& Color, thread float& As)
void ps_blend(thread float4& Color, thread float4& As_rgba)
{
float As = As_rgba.a;
if (SW_BLEND)
{
// PABE
@@ -870,17 +881,15 @@ struct PSMain
if (PS_CLR_HW == 1)
{
// Replace Af with As so we can do proper compensation for Alpha.
if (PS_BLEND_C == 2)
As = cb.alpha_fix;
// As or Af
As_rgba.rgb = float3(C);
// Subtract 1 for alpha to compensate for the changed equation,
// if c.rgb > 255.0f then we further need to adjust alpha accordingly,
// we pick the lowest overflow from all colors because it's the safest,
// we divide by 255 the color because we don't know Cd value,
// changed alpha should only be done for hw blend.
float min_color = min(min(Color.r, Color.g), Color.b);
float alpha_compensate = max(1.f, min_color / 255.f);
As -= alpha_compensate;
float3 alpha_compensate = max(float3(1.f), Color.rgb / float3(255.f));
As_rgba.rgb -= alpha_compensate;
}
else if (PS_CLR_HW == 2)
{
@@ -892,6 +901,16 @@ struct PSMain
float color_compensate = 1.f * (C + 1.f);
Color.rgb -= float3(color_compensate);
}
else if (PS_CLR_HW == 3)
{
// As, Ad or Af clamped.
As_rgba.rgb = float3(C_clamped);
// Cs*(Alpha + 1) might overflow, if it does then adjust alpha value
// that is sent on second output to compensate.
float3 overflow_check = (Color.rgb - float3(255.f)) / 255.f;
float3 alpha_compensate = max(float3(0.f), overflow_check);
As_rgba.rgb -= alpha_compensate;
}
}
else
{
@@ -950,11 +969,22 @@ struct PSMain
uint4 denorm_c = uint4(C);
uint2 denorm_TA = uint2(cb.ta * 255.5f);
C.rb = PS_READ_BA ? C.bb : C.rr;
if (PS_READ_BA)
C.ga = (denorm_c.a & 0x7F) | (denorm_c.a & 0x80 ? denorm_TA.y & 0x80 : denorm_TA.x & 0x80);
if (PS_READ16_SRC)
{
C.rb = (denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7u) << 5);
if (denorm_c.a & 0x80)
C.ga = (denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.y & 0x80);
else
C.ga = (denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80);
}
else
C.ga = (denorm_c.g & 0x7F) | (denorm_c.g & 0x80 ? denorm_TA.y & 0x80 : denorm_TA.x & 0x80);
{
C.rb = PS_READ_BA ? C.bb : C.rr;
if (PS_READ_BA)
C.ga = (denorm_c.a & 0x7F) | (denorm_c.a & 0x80 ? denorm_TA.y & 0x80 : denorm_TA.x & 0x80);
else
C.ga = (denorm_c.g & 0x7F) | (denorm_c.g & 0x80 ? denorm_TA.y & 0x80 : denorm_TA.x & 0x80);
}
}
// Must be done before alpha correction
@@ -965,7 +995,7 @@ struct PSMain
C.a = 128.0f;
}
float alpha_blend = SW_AD_TO_HW ? (trunc(current_color.a * 255.5f) / 128.f) : (C.a / 128.f);
float4 alpha_blend = SW_AD_TO_HW ? float4(trunc(current_color.a * 255.5f) / 128.f) : float4(C.a / 128.f);
if (PS_DFMT == FMT_16)
{

View File

@@ -23,7 +23,7 @@ class GSDeviceNull : public GSDevice
private:
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format);
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) {}
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear) {}
void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0, int bufIdx = 0) {}
u16 ConvertBlendEnum(u16 generic) { return 0xFFFF; }

View File

@@ -17,6 +17,7 @@
#include "GLLoader.h"
#include "GS/GS.h"
#include "Host.h"
#include "HostSettings.h"
namespace ReplaceGL
{
@@ -112,8 +113,8 @@ namespace GLLoader
bool vendor_id_amd = false;
bool vendor_id_nvidia = false;
bool vendor_id_intel = false;
bool mesa_driver = false;
bool buggy_pbo = false;
bool disable_download_pbo = false;
bool is_gles = false;
bool has_dual_source_blend = false;
@@ -131,14 +132,8 @@ namespace GLLoader
vendor_id_amd = true;
else if (strstr(vendor, "NVIDIA Corporation"))
vendor_id_nvidia = true;
#ifdef _WIN32
else if (strstr(vendor, "Intel"))
vendor_id_intel = true;
#else
// On linux assumes the free driver if it isn't nvidia or amd pro driver
mesa_driver = !vendor_id_nvidia && !vendor_id_amd;
#endif
if (GSConfig.OverrideGeometryShaders != -1)
{
@@ -225,10 +220,16 @@ namespace GLLoader
// Don't use PBOs when we don't have ARB_buffer_storage, orphaning buffers probably ends up worse than just
// using the normal texture update routines and letting the driver take care of it.
GLLoader::buggy_pbo = !GLAD_GL_VERSION_4_4 && !GLAD_GL_ARB_buffer_storage && !GLAD_GL_EXT_buffer_storage;
if (GLLoader::buggy_pbo)
buggy_pbo = !GLAD_GL_VERSION_4_4 && !GLAD_GL_ARB_buffer_storage && !GLAD_GL_EXT_buffer_storage;
if (buggy_pbo)
Console.Warning("Not using PBOs for texture uploads because buffer_storage is unavailable.");
// Give the user the option to disable PBO usage for downloads.
// Most drivers seem to be faster with PBO.
disable_download_pbo = Host::GetBoolSettingValue("EmuCore/GS", "DisableGLDownloadPBO", false);
if (disable_download_pbo)
Console.Warning("Not using PBOs for texture downloads, this may reduce performance.");
return true;
}

View File

@@ -29,9 +29,8 @@ namespace GLLoader
extern bool vendor_id_amd;
extern bool vendor_id_nvidia;
extern bool vendor_id_intel;
extern bool mesa_driver;
extern bool buggy_pbo;
extern bool in_replayer;
extern bool disable_download_pbo;
// GL
extern bool is_gles;

View File

@@ -42,8 +42,8 @@ namespace GLState
GLuint ps_ss;
GLuint rt;
GLuint ds;
GSTextureOGL* rt = nullptr;
GSTextureOGL* ds = nullptr;
GLuint tex_unit[8];
GLuint64 tex_handle[8];
@@ -72,8 +72,8 @@ namespace GLState
ps_ss = 0;
rt = 0;
ds = 0;
rt = nullptr;
ds = nullptr;
std::fill(std::begin(tex_unit), std::end(tex_unit), 0);
std::fill(std::begin(tex_handle), std::end(tex_handle), 0);

View File

@@ -18,6 +18,8 @@
#include "GS/Renderers/OpenGL/GLLoader.h"
#include "GS/GSVector.h"
class GSTextureOGL;
namespace GLState
{
extern GLuint fbo; // frame buffer object
@@ -44,8 +46,8 @@ namespace GLState
extern GLuint ps_ss; // sampler
extern GLuint rt; // render target
extern GLuint ds; // Depth-Stencil
extern GSTextureOGL* rt; // render target
extern GSTextureOGL* ds; // Depth-Stencil
extern GLuint tex_unit[8]; // shader input texture
extern GLuint64 tex_handle[8]; // shader input texture

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