Compare commits

...
131 Commits
Author SHA1 Message Date
TheLastRarandlightningterror 38ee1e1e22 GS/DX: Specify shader model directly instead of via feature level 2026-05-11 22:29:45 +02:00
PCSX2 BotandTy 2b02a5dc78 [ci skip] PAD: Update to latest controller database. 2026-05-11 13:30:19 -04:00
descawedandlightningterror dead00eb62 GameDB: Enable Instant DMA Hack for SLPM-74405 to fix hang 2026-05-10 19:07:21 +02:00
TJnotJTandlightningterror 4fa2b8e45d GS/HW: Fixes/improvements to early scissor/bbox test. 2026-05-09 13:48:42 +02:00
refractionpcsx2andlightningterror fc90973509 GS/HW/COLCLIP: Check tex mapping is enabled when checking for recursion 2026-05-09 13:47:58 +02:00
Immersion95andlightningterror ff9176f83e GameDB: fixes wrong title for SLPM-65496 - Hyper Street Fighter II - The Anniversary Edition 2026-05-09 13:46:48 +02:00
refractionpcsx2andlightningterror 63d3dd9978 GS/TC: Update dirty target linked to source when dirty overlaps the read 2026-05-09 13:46:25 +02:00
JordanTheToasterandlightningterror b704c6caaa GameDB: Pump It Up Exceed Fixes 2026-05-08 18:56:51 +02:00
lightningterror a64465681c GS/HW: Disable texture shuffle if there's no tex. 2026-05-08 18:29:05 +02:00
refractionpcsx2andlightningterror 539357436c Build: Clean up a few warnings 2026-05-08 18:17:31 +02:00
refractionpcsx2andlightningterror 22cb6b0c9c GS/HW: Remove zero clear from GSUploadQueue, use transfer_type instead 2026-05-08 18:15:18 +02:00
PCSX2 Botandlightningterror 81df4e5def [ci skip] Qt: Update Base Translation. 2026-05-07 02:15:53 +02:00
ZiemasandTy a3f3e8733d GS: fix build with libstdc++ 16 2026-05-06 18:05:51 -04:00
SternXDandlightningterror 6fc84f76bb FullscreenUI: Fix RA login state, and add dismiss dialog 2026-05-06 20:55:44 +02:00
TJnotJTandlightningterror 0949f193b8 GS/SW: Adjust bounds in assertion to present false positive.
Account for the fact that bottom/right coordinates are exclusive.
2026-05-06 20:46:51 +02:00
TJnotJTandlightningterror 5f3dc1a76e GS/SW: Early exit optimization for multithreaded horizontal lines. 2026-05-06 20:46:51 +02:00
TJnotJTandlightningterror 9afea55bb6 GS/SW: Adjust bbox to account for AA1 and points/line rounding. 2026-05-06 20:46:51 +02:00
TJnotJTandlightningterror 433614ae85 GS: Adjust primitive bbox in vertex kick to account for AA1. 2026-05-06 20:46:51 +02:00
TJnotJTandlightningterror 00c85c4a8f GS/TC: Use temporary source on invalid TEX0. 2026-05-06 19:18:04 +02:00
PCSX2 Botandlightningterror f9885315bd [ci skip] Qt: Update Base Translation. 2026-05-06 11:59:56 +02:00
TheLastRarandlightningterror 26563dbae2 GS/HW: Clamp large UVs in shader anisotropic filtering 2026-05-06 11:59:32 +02:00
Ty 53e3838c1d Qt: Add MainWindowMaximized to ini for human readable maximized override 2026-05-05 16:01:35 -04:00
Ty 4509d11daf Qt: Add MainWindowFullscreen to ini for human readable fullscreen override 2026-05-05 16:01:35 -04:00
JordanTheToasterandlightningterror 37d5bf33e8 Deps: Update SDL3 to v3.4.8 2026-05-05 20:01:47 +02:00
PCSX2 Botandlightningterror 76842401ee [ci skip] PAD: Update to latest controller database. 2026-05-04 23:33:20 +02:00
PCSX2 BotandTy bf9d38e86a [ci skip] Qt: Update Base Translation. 2026-05-03 21:08:21 -04:00
Vishrut SachanandTy 7de2aad05a Qt: Fix Big Picture Mode mnemonic shortcut lost after state change 2026-05-03 19:41:23 -04:00
Florin9doiandTy 7d11e1f963 USB: Support for Olympus storage devices
Also the commands were incorrectly handled as file R/W
operations when the host did not provide unique tags,
causing f.buf to be overwritten with file content
2026-05-03 19:38:07 -04:00
TellowKrinkleandlightningterror 452e239eb4 GS:MTL: Fix AA1 depth clears 2026-05-03 23:51:30 +02:00
TJnotJTandlightningterror 8b82fbb449 GS/HW: Add constant buffer dumping. 2026-05-03 18:05:35 +02:00
lightningterror de84c41757 GS: Check if current tex is valid before resizing. 2026-05-03 15:25:58 +02:00
PCSX2 Botandlightningterror 3000e113e2 [ci skip] Qt: Update Base Translation. 2026-05-02 05:39:03 +02:00
refractionpcsx2andlightningterror c5d6b28be4 GS/HW: Correct buffered rect size of corrected EE->GS transfers 2026-05-02 05:38:48 +02:00
chaoticgdandlightningterror c0dfb8cfa0 Debugger: Call QWidget::update instead of QWidget::repaint 2026-05-01 20:47:40 +02:00
lightningterror f9feca4f4d GS/DX11/GL: Further cleanup to the copy behavior and some features checks. 2026-05-01 20:35:30 +02:00
lightningterror 4fa9c4e382 GS/HW: Check if alpha second pass copies are feedback loops. 2026-05-01 20:35:30 +02:00
lightningterror 93f1fcbe88 GS/HW: Sync rt copy behaviour across renderers. 2026-05-01 20:35:30 +02:00
TJnotJTandlightningterror 3d71e310c0 GSRunner: Add -debugdevice command line parameter. 2026-04-30 02:21:35 +02:00
TJnotJTandlightningterror 02d0f613ba GS/HW: Fix brackets in texture shuffle detection. 2026-04-30 02:03:44 +02:00
PCSX2 Botandlightningterror 3091407e9e [ci skip] Qt: Update Base Translation. 2026-04-29 22:53:37 +02:00
ZiemasandTy 70f6169eff StackWalker: Fix bugs.
Fixes incorrect stack tracing in cases where we are stoppen on certain instructions.
2026-04-28 19:58:38 -04:00
ZiemasandTy a1a3cd9d1b DebugData: Remove stack getters
They're not used and the naming is confused, we can readd these if we
actually want to use them.
2026-04-28 19:57:48 -04:00
ZiemasandTy 86c5d2ae2a DebugInterface: Allow stack tracing any IOP thread 2026-04-28 19:57:48 -04:00
ZiemasandTy d759055231 DebugInterface: Enable stack tracing any EE thread 2026-04-28 19:57:48 -04:00
ZiemasandTy c6474852d5 DebugData: Add function for getting register state 2026-04-28 19:57:48 -04:00
ZiemasandTy a2ac576711 DebugData: Add struct for the saved thread context 2026-04-28 19:57:48 -04:00
ZiemasandTy 1b6924a033 DebugData: Rename EE sleep state to match IOP 2026-04-28 19:57:48 -04:00
ZiemasandTy 8e5e053b81 DebugData: Fix EE thread struct 2026-04-28 19:57:48 -04:00
TheLastRarandlightningterror 6891511a1d GS/HW: Reject malformed UVs in shader anisotropic filtering 2026-04-29 00:14:51 +02:00
chaoticgdandTy c186f8d366 Qt: Fix crash when closing OSD font picker dialog 2026-04-28 12:09:23 -04:00
KamFretoZandlightningterror eed53d19a0 Qt: Move Cheevos Account Box to the top 2026-04-28 03:33:16 +02:00
PCSX2 Botandlightningterror abb218c5cf [ci skip] PAD: Update to latest controller database. 2026-04-27 18:30:02 +02:00
PCSX2 Botandlightningterror 249ac51827 [ci skip] Qt: Update Base Translation. 2026-04-27 02:33:55 +02:00
jasaavedandTy 3da22f126e Qt: Fix Fullscreen not on the correct display
Replace setPosition with setGeometry. This makes sure when rendering to main window, it stays on the current display the main window is currently positioned at.
2026-04-26 16:17:09 -04:00
Ty a509350646 FullscreenUI: Use Host::TranslateTo(C)String directly for non-string literals 2026-04-26 16:13:13 -04:00
KamFretoZandTy bd486f1729 Qt: Add -datapath launch argument 2026-04-26 16:13:04 -04:00
lightningterror f7ef43b155 GS/HW: Match TEXA behavior on shuffles between renderers. 2026-04-26 21:07:44 +02:00
lightningterror 282129030f GS/HW: Exclude shuffles for dst no rgb blend and no rt check. 2026-04-26 21:07:44 +02:00
lightningterror 5734403ffc GS/HW: Several barrier fixes to sw blend and barrier date.
- Accumulation blend: Require 32 bit RT is only when texture barriers/multidraw fb copies are on/supported.

- Barrier DATE:
Always use barrier date if texture barrier/multidraw fb copies are supported.
Check if texture barriers/multidraw fb copies are supported before enabling barrier date for AA1.
2026-04-26 21:07:44 +02:00
lightningterror 8d4d9ce026 GS/HW: Allow Date barrier when texture barriers/multidraw fb copies are disabled. 2026-04-26 21:07:44 +02:00
lightningterror 390e3994ad GS/HW: Clean up date stencil temp ds texture creation.
Add barrier fallback method.
2026-04-26 21:07:44 +02:00
Pyra Drakeandlightningterror 6c41c125d8 GameDB: Fixes for SLES-52674 & SLES-52563
The two 2005 releases, like their 2004 versions, have a cross-save feature allowing you to manage team in TCM and play matches in FIFA. This has been made available via memcardFilters for FolderMC users.
The name for SLES-52674 has also been changed from its German name to its English name as it is PAL-M5.
2026-04-26 20:52:51 +02:00
TellowKrinkleandlightningterror 83da1b6479 GS:MTL: AA1 Support 2026-04-26 13:06:57 +02:00
lightningterror 226b4d25dc GS/HW: More AA1 fixes and optimizations.
Do not enable AA1 if we aren't drawing to the RT.
Check if RT is written for IsCoverageAlphaSupported().
2026-04-26 13:05:39 +02:00
SternXDandlightningterror 27736ef47f Achievements: Block debugger in hardcore mode when using RAIntegration 2026-04-25 22:27:23 +02:00
TJnotJTandlightningterror f95b2ce24d GS/HW: Reorganize AA1 setup and fix some performance bugs.
Separate depth feedback setup it is not duplicated in both AA1 and alpha test setup.

Try to optimize out depth feedback in AA1 if triangle interiors are discarded.

Fix barriers not being properly enabled for AA1 triangles (was only activating if depth test was used).
2026-04-25 22:26:09 +02:00
chaoticgdandlightningterror f1d83f8c65 Qt: Automatically reflow settings check boxes 2026-04-24 13:32:24 +02:00
lightningterror 84c0cd8890 GS: Fix AA1 coverage regression on sw and cleanup. 2026-04-24 13:09:54 +02:00
lightningterror 971382f11e GS/DX12/Debug: Enable dx12 support for debug purposes on Haswell. 2026-04-24 01:30:59 +02:00
TellowKrinkleandlightningterror ddf6fe3264 GS: Add helper to check if devices support feedback loops 2026-04-24 01:19:27 +02:00
TellowKrinkleandlightningterror e5f0c99c76 GS: Remove depth feedback none option
Depth feedback doesn't get used most of the time anyways
2026-04-24 01:19:27 +02:00
TellowKrinkleandlightningterror af81d8b7da GS:MTL: Faster fbfetch depth feedback 2026-04-24 01:19:27 +02:00
TellowKrinkleandlightningterror 8f23e31ae4 GS:MTL: Implement depth feedback 2026-04-24 01:19:27 +02:00
TellowKrinkleandlightningterror 3737b1e06a GS: Move DS as RT texture creation to GSDevice 2026-04-24 01:19:27 +02:00
JordanTheToasterandlightningterror de848fe468 GameDB: Various fixes 2026-04-23 22:01:58 +02:00
ce2a859ad1 GS/HW: Use shader anisotropic filtering
Metal port by @TellowKrinkle

Co-Authored-By: TellowKrinkle <tellowkrinkle@gmail.com>
2026-04-23 18:19:57 +02:00
TJnotJTandlightningterror 8cfd583605 GS/HW: Use correct draw functions in backend passes.
Fixes broken primid and blend multipass draws.

Regression from AA1 changes to draw functions.
2026-04-23 02:32:33 +02:00
chaoticgdandlightningterror 8477679096 Qt: Make sure the game list table view cover column remains hidden 2026-04-22 15:37:01 +02:00
TJnotJTandlightningterror d0ee5b859a GS/VK: Fix flat specifier in VK shader. 2026-04-21 23:28:00 +02:00
lightningterror 16f2c262a3 GS/DX11: Clean up pre binding and srv conflict unbinding.
Move conflict check inside OMSetRenderTargets, it's easier to track and gets rid of duplicate code.

Move shader resource binding after rtv/dsv binding, easier to track and manage srv conflicts.

Get rid of shader resource pre binding, while it may work it's more of a headache to track so many states and things can go wrong easily, so just get rid of it, especially with shader binding after rtv/dsv binding.
2026-04-21 19:31:49 +02:00
lightningterror e8c750ee88 GS/DX11: Make sure we commit a clear for read only dsv too. 2026-04-21 19:31:49 +02:00
lightningterror 20bbb99516 GS/HW: Fix shader compile errors and warnings from AA1 on GL. 2026-04-21 02:20:13 +02:00
chaoticgdandTy d9e0cd55c7 Hotkeys: Check if the memory card is busy before resetting 2026-04-20 19:43:55 -04:00
Mrlinkwiiandlightningterror 9c64627b07 OSD : add OSD message for HW AA1 2026-04-20 19:49:00 +02:00
lightningterror 3cbc7f3f09 GS/DX11: Properly null out rtv/dsv during CAS.
This way we ensure the current states are also updated.
2026-04-20 19:10:31 +02:00
PCSX2 Botandlightningterror 38bcdf65e3 [ci skip] PAD: Update to latest controller database. 2026-04-20 19:09:41 +02:00
TJnotJTandlightningterror 2ae8f1f23f GS/HW: Support HW AA1 for lines/triangles.
Credits: concept by TellowKrinkle.
2026-04-20 11:16:27 +02:00
jasaavedandTy ed796ee3db Qt: Fix Exclusive Fullscreen Mode Saving to Config
Add a variable to keep track if the window was in exclusive fullscreen. This helps prevent saving geometry to config which is for windows rendered separate from main.

The variable is set back to false when render window is released.
2026-04-18 09:59:21 -04:00
jasaavedandTy b1962d22b0 Qt: Fix main window not being hidden when using exclusive fullscreen with rendering to main
Fixed an issue where enabling both render‑to‑main and exclusive fullscreen caused the main window to remain visible when switching from windowed to fullscreen. This occurred because shouldHideMainWindow() relied solely on isRenderingFullscreen() to detect fullscreen state, but exclusive fullscreen is controlled by the GS thread and never sets Qt’s WindowFullScreen flag. The fix adds an additional check for isExclusiveFullscreen().
2026-04-18 09:59:21 -04:00
TJnotJTandlightningterror 3722d392d8 GS/TC: Remove debug code. 2026-04-18 13:30:19 +02:00
TheLastRarandTellowKrinkle bda58fb0c1 GHActions: Check msbuild compile succeeds 2026-04-18 02:08:04 -05:00
SternXDandlightningterror 6f1f4e81d0 Qt: Add missing winwil include path 2026-04-18 02:10:53 +02:00
KamFretoZandlightningterror 928bf313df Qt/Shortcut: Use WIL instead of WRL for shortcut COM initialization 2026-04-17 23:43:11 +02:00
KamFretoZandlightningterror 7b2fa803de Qt: Add Icon Selection and Preview to the Shortcut Creator 2026-04-17 23:43:11 +02:00
TellowKrinkleandlightningterror 2dbae3bec9 Misc: Fix GCC arm linux build 2026-04-17 21:19:28 +02:00
TellowKrinkleandlightningterror fa26937885 CMake: Disable Wattributes on gcc 2026-04-17 21:19:28 +02:00
TellowKrinkleandlightningterror c610d4d2d4 Common: Fix arm64 linux build 2026-04-17 21:19:28 +02:00
TellowKrinkleandlightningterror 2f9d7a2901 Deps: Fix build on Xcode 26.4 2026-04-17 21:19:28 +02:00
TellowKrinkleandlightningterror 640a30a25b Deps: Don't remove downloaded files while building
That way we don't need to redownload if a deps build fails partway though and is restarted
2026-04-17 21:19:28 +02:00
refractionpcsx2andlightningterror bf94ee1c8e GS: Simplify Overlaps check 2026-04-17 21:13:30 +02:00
refractionpcsx2andlightningterror 1fa3c6aa4a GS: Move new Draw Buffering to a UI option 2026-04-17 21:13:30 +02:00
refractionpcsx2andlightningterror 76c401b0fc GS: Implement draw buffering 2026-04-17 21:13:30 +02:00
Pyra Drakeandlightningterror 1249846e87 GameDB: Fix name of SLES-54961
It was of low quality.
2026-04-17 20:51:36 +02:00
TJnotJTandlightningterror 09fcc4d60e GS/TC: Break LookupTarget() into 3 functions.
Separate draw lookup from frame lookup. Put common code in another processing function.
2026-04-17 20:17:46 +02:00
KamFretoZandlightningterror fa9c99a948 Redump: Update Database 2026-04-17 19:46:48 +02:00
TheLastRarandlightningterror f85b20eb00 GS/GL: Initialise stencil ref/mask during device creation 2026-04-17 11:46:24 +02:00
lightningterror 420f4177c9 GS/GL/DX11: Don't bind source tex on shader resource if it's tex is fb with no barrier.
Texture will be copied, the copy will be used as a shader resource. Avoids useless binds.
2026-04-15 21:53:40 +02:00
TellowKrinkleandlightningterror 4f5bde2cef GS:MTL: 32K texture support for M5+ 2026-04-15 21:52:24 +02:00
TellowKrinkleandlightningterror 601b0e60ea GS:MTL: Move texture size calculation to dedicated function 2026-04-15 21:52:24 +02:00
lightningterror a2e354c629 GS/GL: Better handle shader resource null. 2026-04-15 21:51:20 +02:00
lightningterror 241a929748 GS/DX11: Better handle shader resource null. 2026-04-15 21:51:20 +02:00
SternXDandlightningterror 1bf80dbabd FullscreenUI: Add missing icons to settings
Signed-off-by: SternXD <stern@sidestore.io>
2026-04-15 21:00:48 +02:00
SternXDandlightningterror e6e9408f95 FullscreenUI: Reduce nav selection opacity from 100% to 70%
Signed-off-by: SternXD <stern@sidestore.io>
2026-04-15 21:00:48 +02:00
SternXDandlightningterror 1c01f8486d FullscreenUI: Add Preview/Reset buttons to Achievement Sounds
Signed-off-by: SternXD <stern@sidestore.io>
2026-04-15 21:00:48 +02:00
SternXDandlightningterror 8700ae00b5 FullscreenUI: center about version under banner
Signed-off-by: SternXD <stern@sidestore.io>
2026-04-15 21:00:48 +02:00
SternXDandlightningterror 427d53d3c4 FullscreenUI: Add configurable gamepad glyph style
Signed-off-by: SternXD <stern@sidestore.io>
2026-04-15 21:00:48 +02:00
SternXDandlightningterror b1a588cc50 FullscreenUI: Fix clipped summary text
Signed-off-by: SternXD <stern@sidestore.io>
2026-04-15 21:00:48 +02:00
SternXDandlightningterror 19738064f6 FullscreenUI: Reduce summary text opacity
Signed-off-by: SternXD <stern@sidestore.io>
2026-04-15 21:00:48 +02:00
SternXDandlightningterror 0eb4441dac FullscreenUI: Add text shadows for improved readability
Signed-off-by: SternXD <stern@sidestore.io>
2026-04-15 21:00:48 +02:00
SternXDandlightningterror fee5ab8e99 FullscreenUI: Align toggles with text
Signed-off-by: SternXD <stern@sidestore.io>
2026-04-15 21:00:48 +02:00
SternXDandlightningterror 6477a2d62e FullscreenUI: Add rounded corners
Signed-off-by: SternXD <stern@sidestore.io>
2026-04-15 21:00:48 +02:00
TJnotJTandlightningterror c26c2236c5 GS/TC: Fix alpha write masking check in target update.
Make sure we detect when FB mask is such that only alpha is written. Avoid incorrectly updating RGB to a target when we don't need it and possibly nuking other depth targets in the process.
2026-04-15 20:58:08 +02:00
TJnotJTandlightningterror f3496e3858 GS/HW: Set valid flags if display target has exact match with recent transfer. 2026-04-15 20:56:23 +02:00
JordanTheToasterandlightningterror 177318b353 GameDB: Various fixes 2026-04-15 16:36:21 +02:00
KamFretoZandlightningterror 93695ce623 Achievements: Formatting Cleanup 2026-04-15 16:05:13 +02:00
KamFretoZandlightningterror cc8292753f Achievement: Fix default custom sound effect behavior
Achievements: Preview custom sounds directly instead of using/opening the system's default audio player.
2026-04-15 16:05:13 +02:00
lightningterror 87319a2d2f GS/DX12: Replace the in render pass member check with function check.
Consitency.
2026-04-15 13:08:12 +02:00
lightningterror bf5be8ed4f GS/DX12: Fix render pass optimization not being properly hit.
The depth target render pass optimization wasn't being hit properly, so let's synch it with the other renderers.
2026-04-15 13:08:12 +02:00
0fc1b66a9b Qt: Update Base Translation (#14297)
Daily update of base translation sources.

Co-authored-by: PCSX2 Bot <PCSX2Bot@users.noreply.github.com>
2026-04-13 21:35:31 -04:00
lightningterror cb63269d9e GS: Fix a bunch of compiler warnings.
Type comparisons, unused variables, missing return value.
2026-04-14 00:01:19 +02:00
lightningterror 903e859433 FSUI: Fix unused variable compiler warning. 2026-04-14 00:01:19 +02:00
125 changed files with 10706 additions and 6381 deletions
@@ -27,7 +27,7 @@ LIBJPEGTURBO=3.1.4.1
LIBPNG=1.6.56
LIBWEBP=1.6.0
NVENC=13.0.19.0
SDL=SDL3-3.4.4
SDL=SDL3-3.4.8
LZ4=1.10.0
VULKAN=1.4.328.1
ZSTD=1.5.7
@@ -60,7 +60,7 @@ b072aed6871998cce9b36e7774033105ca29e33632be5b6347f3206898e0756a ffmpeg-$FFMPEG
ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 libjpeg-turbo-$LIBJPEGTURBO.tar.gz
f7d8bf1601b7804f583a254ab343a6549ca6cf27d255c302c47af2d9d36a6f18 libpng-$LIBPNG.tar.xz
e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWEBP.tar.gz
ee712dbe6a89bb140bbfc2ce72358fb5ee5cc2240abeabd54855012db30b3864 $SDL.tar.gz
e9fff7467fb60f037e6708da18b25560649e4c63edc2a69bb871b960d9cbfbba $SDL.tar.gz
9ce32d4a2763a2ac5f258726ba2f49e9011327c1ee8c30862a32d0f30889fbe8 libpng-$LIBPNG-apng.patch.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
13da39edb3a40ed9713ae390ca89faa2f1202c9dda869ef306a8d4383e242bee nv-codec-headers-$NVENC.tar.gz
@@ -152,7 +152,7 @@ cd ..
echo "Building libpng..."
rm -fr "libpng-$LIBPNG"
tar xf "libpng-$LIBPNG.tar.xz"
gunzip -d -f "libpng-$LIBPNG-apng.patch.gz"
gzip -kd -f "libpng-$LIBPNG-apng.patch.gz"
cd "libpng-$LIBPNG"
patch -p1 < "../libpng-$LIBPNG-apng.patch"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_STATIC=OFF -DPNG_SHARED=ON -DPNG_TOOLS=OFF -B build -G Ninja
@@ -87,7 +87,7 @@ cd ..
echo "Building libpng..."
rm -fr "libpng-$LIBPNG"
tar xf "libpng-$LIBPNG.tar.xz"
gunzip -d -f "libpng-$LIBPNG-apng.patch.gz"
gzip -kd -f "libpng-$LIBPNG-apng.patch.gz"
cd "libpng-$LIBPNG"
patch -p1 < "../libpng-$LIBPNG-apng.patch"
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_STATIC=OFF -DPNG_SHARED=ON -DPNG_TOOLS=OFF -B build -G Ninja
@@ -14,8 +14,8 @@
"sources": [
{
"type": "archive",
"url": "https://libsdl.org/release/SDL3-3.4.4.tar.gz",
"sha256": "ee712dbe6a89bb140bbfc2ce72358fb5ee5cc2240abeabd54855012db30b3864"
"url": "https://libsdl.org/release/SDL3-3.4.8.tar.gz",
"sha256": "e9fff7467fb60f037e6708da18b25560649e4c63edc2a69bb871b960d9cbfbba"
}
],
"cleanup": [
@@ -47,7 +47,7 @@ QT=6.11.0
QTAPNG=1.3.0
FREETYPE=2.14.3
SDL=SDL3-3.4.4
SDL=SDL3-3.4.8
HARFBUZZ=14.0.0
ZSTD=1.5.7
LZ4=1.10.0
@@ -93,7 +93,7 @@ cfb1993d7a10848965b01b9cf33a54b8a4ba4e5e3a6d28d59483e73f10d9fc76 qttools-everyw
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
36bc4f1cc413335368ee656c42afca65c5a3987e8768cc28cf11ba775e785a5f freetype-$FREETYPE.tar.xz
ee712dbe6a89bb140bbfc2ce72358fb5ee5cc2240abeabd54855012db30b3864 $SDL.tar.gz
e9fff7467fb60f037e6708da18b25560649e4c63edc2a69bb871b960d9cbfbba $SDL.tar.gz
f29db9470e0ca5cef484e04e27baeec233aa428e8fdabe9e51b0f706c0809d24 harfbuzz-$HARFBUZZ.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
@@ -218,7 +218,7 @@ cd ..
echo "Installing libpng..."
rm -fr "libpng-$LIBPNG"
tar xf "libpng-$LIBPNG.tar.xz"
gunzip -d -f "libpng-$LIBPNG-apng.patch.gz"
gzip -kd -f "libpng-$LIBPNG-apng.patch.gz"
cd "libpng-$LIBPNG"
patch -p1 < "../libpng-$LIBPNG-apng.patch"
cmake "${CMAKE_COMMON[@]}" "$CMAKE_ARCH_X64" -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_FRAMEWORK=OFF -B build
@@ -301,6 +301,8 @@ cd "qtbase-everywhere-src-$QT"
# Patch Qt to support macOS 11
patch -p1 < "$SCRIPTDIR/qt-macos11compat.patch"
# Backport fix build on Xcode 26.4 (https://codereview.qt-project.org/c/qt/qtbase/+/724619)
patch -p1 < "$SCRIPTDIR/qt110-xcode264.patch"
# since we don't have a direct reference to QtSvg, it doesn't deployed directly from the main binary
# (only indirectly from iconengines), and the libqsvg.dylib imageformat plugin does not get deployed.
@@ -24,7 +24,7 @@ QT=6.11.0
QTAPNG=1.3.0
FREETYPE=2.14.3
SDL=SDL3-3.4.4
SDL=SDL3-3.4.8
HARFBUZZ=14.0.0
ZSTD=1.5.7
LZ4=1.10.0
@@ -69,7 +69,7 @@ cfb1993d7a10848965b01b9cf33a54b8a4ba4e5e3a6d28d59483e73f10d9fc76 qttools-everyw
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
36bc4f1cc413335368ee656c42afca65c5a3987e8768cc28cf11ba775e785a5f freetype-$FREETYPE.tar.xz
ee712dbe6a89bb140bbfc2ce72358fb5ee5cc2240abeabd54855012db30b3864 $SDL.tar.gz
e9fff7467fb60f037e6708da18b25560649e4c63edc2a69bb871b960d9cbfbba $SDL.tar.gz
f29db9470e0ca5cef484e04e27baeec233aa428e8fdabe9e51b0f706c0809d24 harfbuzz-$HARFBUZZ.tar.gz
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b lz4-$LZ4.tar.gz
@@ -170,7 +170,7 @@ cd ..
echo "Installing libpng..."
rm -fr "libpng-$LIBPNG"
tar xf "libpng-$LIBPNG.tar.xz"
gunzip -d -f "libpng-$LIBPNG-apng.patch.gz"
gzip -kd -f "libpng-$LIBPNG-apng.patch.gz"
cd "libpng-$LIBPNG"
patch -p1 < "../libpng-$LIBPNG-apng.patch"
cmake "${CMAKE_COMMON[@]}" -DBUILD_SHARED_LIBS=ON -DPNG_TESTS=OFF -DPNG_FRAMEWORK=OFF -B build
@@ -244,6 +244,8 @@ cd "qtbase-everywhere-src-$QT"
# Patch Qt to support macOS 11
patch -p1 < "$SCRIPTDIR/qt-macos11compat.patch"
# Backport fix build on Xcode 26.4 (https://codereview.qt-project.org/c/qt/qtbase/+/724619)
patch -p1 < "$SCRIPTDIR/qt110-xcode264.patch"
# since we don't have a direct reference to QtSvg, it doesn't deployed directly from the main binary
# (only indirectly from iconengines), and the libqsvg.dylib imageformat plugin does not get deployed.
@@ -0,0 +1,25 @@
diff --git a/src/corelib/thread/qyieldcpu.h b/src/corelib/thread/qyieldcpu.h
index ab710ea..954c49a 100644
--- a/src/corelib/thread/qyieldcpu.h
+++ b/src/corelib/thread/qyieldcpu.h
@@ -33,7 +33,9 @@
noexcept
#endif
{
-#if __has_builtin(__yield)
+#if __has_builtin(__builtin_arm_yield)
+ __builtin_arm_yield();
+#elif __has_builtin(__yield)
__yield(); // Generic
#elif defined(_YIELD_PROCESSOR) && defined(Q_CC_MSVC)
_YIELD_PROCESSOR(); // Generic; MSVC's <atomic>
@@ -47,9 +49,6 @@
_mm_pause();
#elif defined(Q_PROCESSOR_X86)
__asm__("pause"); // hopefully asm() works in this compiler
-
-#elif __has_builtin(__builtin_arm_yield)
- __builtin_arm_yield();
#elif defined(Q_PROCESSOR_ARM) && Q_PROCESSOR_ARM >= 7 && defined(Q_CC_GNU)
__asm__("yield"); // this works everywhere
@@ -60,7 +60,7 @@ set QTAPNG=1.3.0
set FREETYPE=2.14.3
set HARFBUZZ=14.0.0
set SDL=SDL3-3.4.4
set SDL=SDL3-3.4.8
set LIBJPEGTURBO=3.1.4.1
set LIBPNG=1656
set LIBPNGLONG=1.6.56
@@ -94,7 +94,7 @@ call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lp
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz 9ce32d4a2763a2ac5f258726ba2f49e9011327c1ee8c30862a32d0f30889fbe8 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 6bd4fbb665f77899a488b381c5b6e9681fc57c60b669738f985fea714f3456c5 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 506206e02f90c1f37e048eacaf9e8a3a7dc682fd27783eb0ff15a7d2dcc9c2af || goto error
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/refs/tags/v%LZ4%.zip" 3224b4c80f351f194984526ef396f6079bd6332dd9825c72ac0d7a37b3cdc565 || goto error
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://github.com/madler/zlib/releases/download/v%ZLIB%/zlib%ZLIBSHORT%.zip" e8bf55f3017aa181690990cb58a994e77885da140609fc8f94abe9b65d2cae28 || goto error
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 7897bc5d620580d9b7cd3539c44b59d78f3657d33663fe97a145e07b4ebd69a4 || goto error
@@ -77,7 +77,7 @@ set LIBX264=b35605ace3ddf7c1a5d67a2eb553f034aef41d55
set FREETYPE=2.14.3
set HARFBUZZ=14.0.0
set SDL=SDL3-3.4.4
set SDL=SDL3-3.4.8
set LIBJPEGTURBO=3.1.4.1
set LIBPNG=1656
set LIBPNGLONG=1.6.56
@@ -121,7 +121,7 @@ call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lp
call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net/libpng-apng/libpng-%LIBPNGLONG%-apng.patch.gz 9ce32d4a2763a2ac5f258726ba2f49e9011327c1ee8c30862a32d0f30889fbe8 || goto error
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" ecae8008e2cc9ade2f2c1bb9d5e6d4fb73e7c433866a056bd82980741571a022 || goto error
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 6bd4fbb665f77899a488b381c5b6e9681fc57c60b669738f985fea714f3456c5 || goto error
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 506206e02f90c1f37e048eacaf9e8a3a7dc682fd27783eb0ff15a7d2dcc9c2af || goto error
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/refs/tags/v%LZ4%.zip" 3224b4c80f351f194984526ef396f6079bd6332dd9825c72ac0d7a37b3cdc565 || goto error
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://github.com/madler/zlib/releases/download/v%ZLIB%/zlib%ZLIBSHORT%.zip" e8bf55f3017aa181690990cb58a994e77885da140609fc8f94abe9b65d2cae28 || goto error
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 7897bc5d620580d9b7cd3539c44b59d78f3657d33663fe97a145e07b4ebd69a4 || goto error
+1 -1
View File
@@ -150,7 +150,7 @@ jobs:
cmake --build build --config Release || exit /b
cmake --install build --config Release || exit /b
) else (
msbuild "PCSX2_qt.sln" /m /v:m /p:Configuration="${{ inputs.configuration }}" /p:Platform="${{ inputs.platform }}"
msbuild "PCSX2_qt.sln" /m /v:m /p:Configuration="${{ inputs.configuration }}" /p:Platform="${{ inputs.platform }}" || exit /b
)
REM We can use Segoe UI Emoji so we don't need to bundle this
del bin\resources\fonts\Twemoji*
+59 -25
View File
@@ -17558,7 +17558,7 @@ SLES-51257:
name: "The Sims"
name-sort: "Sims, The"
region: "PAL-M8"
compat: 3
compat: 5
speedHacks:
mtvu: 0 # Fixes bad graphics due to bad T-Bit handling.
SLES-51258:
@@ -17719,7 +17719,7 @@ SLES-51315:
compat: 5
SLES-51316:
name: "Grand Theft Auto - Vice City"
region: "PAL-E"
region: "PAL-A"
SLES-51317:
name: "Minority Report - Le Futur vous Rattrape"
region: "PAL-F"
@@ -20719,7 +20719,7 @@ SLES-52558:
name: "Crimson Sea 2"
region: "PAL-G"
SLES-52559:
name: "FIFA 2005"
name: "FIFA Football 2005"
region: "PAL-E"
gameFixes:
- GIFFIFOHack # Partially fixes graphical issues also needs EE cycle rate + 3.
@@ -20728,9 +20728,10 @@ SLES-52559:
gsHWFixes:
halfPixelOffset: 2 # Fixes misaligned bloom and edge garbage.
nativeScaling: 1 # Fixes misaligned bloom.
mipmap: 0 # Currently totally breaks player uniform rendering if mipmapping is enabled.
cpuSpriteRenderBW: 2 # Fixes player outfit rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SLES-52560:
name: "FIFA 2005"
name: "FIFA Football 2005"
region: "PAL-G"
gameFixes:
- GIFFIFOHack # Partially fixes graphical issues also needs EE cycle rate + 3.
@@ -20739,9 +20740,10 @@ SLES-52560:
gsHWFixes:
halfPixelOffset: 2 # Fixes misaligned bloom and edge garbage.
nativeScaling: 1 # Fixes misaligned bloom.
mipmap: 0 # Currently totally breaks player uniform rendering if mipmapping is enabled.
cpuSpriteRenderBW: 2 # Fixes player outfit rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SLES-52561:
name: "FIFA 2005"
name: "FIFA Football 2005"
region: "PAL-P-S"
gameFixes:
- GIFFIFOHack # Partially fixes graphical issues also needs EE cycle rate + 3.
@@ -20750,9 +20752,10 @@ SLES-52561:
gsHWFixes:
halfPixelOffset: 2 # Fixes misaligned bloom and edge garbage.
nativeScaling: 1 # Fixes misaligned bloom.
mipmap: 0 # Currently totally breaks player uniform rendering if mipmapping is enabled.
cpuSpriteRenderBW: 2 # Fixes player outfit rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SLES-52562:
name: "FIFA 2005"
name: "FIFA Football 2005"
region: "PAL-I"
gameFixes:
- GIFFIFOHack # Partially fixes graphical issues also needs EE cycle rate + 3.
@@ -20761,7 +20764,8 @@ SLES-52562:
gsHWFixes:
halfPixelOffset: 2 # Fixes misaligned bloom and edge garbage.
nativeScaling: 1 # Fixes misaligned bloom.
mipmap: 0 # Currently totally breaks player uniform rendering if mipmapping is enabled.
cpuSpriteRenderBW: 2 # Fixes player outfit rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SLES-52563:
name: "FIFA Football 2005"
region: "PAL-M6"
@@ -20772,7 +20776,11 @@ SLES-52563:
gsHWFixes:
halfPixelOffset: 2 # Fixes misaligned bloom and edge garbage.
nativeScaling: 1 # Fixes misaligned bloom.
mipmap: 0 # Currently totally breaks player uniform rendering if mipmapping is enabled.
cpuSpriteRenderBW: 2 # Fixes player outfit rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
memcardFilters: # Football Fusion with Total Club Manager 2005
- "SLES-52674"
- "SLES-52563"
SLES-52567:
name: "Catwoman"
region: "PAL-M7"
@@ -21167,10 +21175,13 @@ SLES-52673:
name: "NHL 2005"
region: "PAL-M5"
SLES-52674:
name: "Fussball Manager 2005"
name: "Total Club Manager 2005"
region: "PAL-M5"
clampModes:
vu1ClampMode: 3 # Fixes 3D rendering.
memcardFilters: # Football Fusion with Total Club Manager 2005
- "SLES-52674"
- "SLES-52563"
SLES-52675:
name: "Hugo - Cannon Cruise"
region: "PAL-Unk"
@@ -21839,28 +21850,32 @@ SLES-52909:
gameFixes:
- EETimingHack # Fixes graphical corruption.
gsHWFixes:
mipmap: 0 # Currently totally breaks player uniform rendering if mipmapping is enabled.
cpuSpriteRenderBW: 2 # Fixes player outfit rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SLES-52910:
name: "UEFA Champions League 2005"
name: "UEFA Champions League 2004-2005"
region: "PAL-F-G"
gameFixes:
- EETimingHack # Fixes graphical corruption.
gsHWFixes:
mipmap: 0 # Currently totally breaks player uniform rendering if mipmapping is enabled.
cpuSpriteRenderBW: 2 # Fixes player outfit rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SLES-52911:
name: "UEFA Champions League 2005"
name: "UEFA Champions League 2004-2005"
region: "PAL-I-S"
gameFixes:
- EETimingHack # Fixes graphical corruption.
gsHWFixes:
mipmap: 0 # Currently totally breaks player uniform rendering if mipmapping is enabled.
cpuSpriteRenderBW: 2 # Fixes player outfit rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SLES-52912:
name: "UEFA Champions League 2005"
name: "UEFA Champions League 2004-2005"
region: "PAL-E-DU"
gameFixes:
- EETimingHack # Fixes graphical corruption.
gsHWFixes:
mipmap: 0 # Currently totally breaks player uniform rendering if mipmapping is enabled.
cpuSpriteRenderBW: 2 # Fixes player outfit rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SLES-52913:
name: "Suikoden IV"
region: "PAL-M5"
@@ -27045,6 +27060,9 @@ SLES-54522:
SLES-54527:
name: "DreamWorks & Aardman Flushed Away"
region: "PAL-M5"
gsHWFixes:
halfPixelOffset: 5 # Fixes post alignment.
nativeScaling: 2 # Fixes post effects.
SLES-54532:
name: "Home Alone"
region: "PAL-PL-P"
@@ -28404,7 +28422,7 @@ SLES-54960:
name: "Godzilla - Unleashed"
region: "PAL-M5"
SLES-54961:
name: "World series of poker 2008"
name: "World Series of Poker 2008 - Battle for the Bracelets"
region: "PAL-E"
SLES-54962:
name: "Guitar Hero III - Legends of Rock"
@@ -31951,6 +31969,9 @@ SLKA-25176:
name: "펌프 잇 업 익시드"
name-en: "Pump It Up - Exceed"
region: "NTSC-K"
gsHWFixes:
cpuSpriteRenderBW: 4 # Fixes graphical corruption in musical notes.
cpuSpriteRenderLevel: 2 # Needed for above.
SLKA-25180:
name: "스플린터 셀 - 판도라 투머로우"
name-en: "Tom Clancy's Splinter Cell - Pandora Tomorrow"
@@ -32125,7 +32146,8 @@ SLKA-25209:
gsHWFixes:
halfPixelOffset: 2 # Fixes misaligned bloom and edge garbage.
nativeScaling: 1 # Fixes misaligned bloom.
mipmap: 0 # Currently totally breaks player uniform rendering if mipmapping is enabled.
cpuSpriteRenderBW: 2 # Fixes player outfit rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SLKA-25210:
name: "서머 히트 비치 발리볼" # Undumped on ReDump as of 2025-08-28
name-en: "Summer Heat Beach Volleyball"
@@ -44020,7 +44042,7 @@ SLPM-65495:
SLPM-65496:
name: "ハイパーストリートファイターⅡ アニバーサリーエディション"
name-sort: "はいぱーすとりーとふぁいたー2 あにばーさりーえでぃしょん"
name-en: "Hyper Street Fighter"
name-en: "Hyper Street Fighter II - The Anniversary Edition"
region: "NTSC-J"
SLPM-65497:
name: "ワールドサッカーウイニングイレブン7 インターナショナル プレミアムパッケージ supported by アディダス"
@@ -54487,6 +54509,7 @@ SLPM-74405:
region: "NTSC-J"
gameFixes:
- EETimingHack # Stops crash after intro movie.
- InstantDMAHack # Stops hang when entering Sword Select screen.
SLPM-74406:
name: "WRC WORLD RALLY CHAMPIONSHIP [PlayStation2 the Best]"
name-sort: "WRC ~わーるど らりー ちゃんぴおんしっぷ~ [PlayStation2 the Best]"
@@ -69652,7 +69675,7 @@ SLUS-21050:
getSkipCount: "GSC_BurnoutGames"
beforeDraw: "OI_BurnoutGames"
SLUS-21051:
name: "FIFA 2005"
name: "FIFA Soccer 2005"
region: "NTSC-U"
compat: 5
gameFixes:
@@ -69662,7 +69685,8 @@ SLUS-21051:
gsHWFixes:
halfPixelOffset: 2 # Fixes misaligned bloom and edge garbage.
nativeScaling: 1 # Fixes misaligned bloom.
mipmap: 0 # Currently totally breaks player uniform rendering if mipmapping is enabled.
cpuSpriteRenderBW: 2 # Fixes player outfit rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SLUS-21052:
name: "Disney/Pixar Monsters, Inc."
region: "NTSC-U"
@@ -70095,6 +70119,9 @@ SLUS-21131:
name: "Pump It Up - Exceed"
region: "NTSC-U"
compat: 5
gsHWFixes:
cpuSpriteRenderBW: 4 # Fixes graphical corruption in musical notes.
cpuSpriteRenderLevel: 2 # Needed for above.
SLUS-21132:
name: "Stella Deus - The Gate of Eternity"
region: "NTSC-U"
@@ -72499,6 +72526,9 @@ SLUS-21484:
name: "DreamWorks & Aardman Flushed Away"
region: "NTSC-U"
compat: 5
gsHWFixes:
halfPixelOffset: 5 # Fixes post alignment.
nativeScaling: 2 # Fixes post effects.
SLUS-21485:
name: "Backyard Sports - Basketball 2007"
region: "NTSC-U"
@@ -75569,7 +75599,8 @@ SLUS-29127:
gsHWFixes:
halfPixelOffset: 2 # Fixes misaligned bloom and edge garbage.
nativeScaling: 1 # Fixes misaligned bloom.
mipmap: 0 # Currently totally breaks player uniform rendering if mipmapping is enabled.
cpuSpriteRenderBW: 2 # Fixes player outfit rendering.
cpuSpriteRenderLevel: 2 # Needed for above.
SLUS-29129:
name: "25 to Life [Public Beta Vol.1.0]"
region: "NTSC-U"
@@ -75930,6 +75961,9 @@ SLUS-29196:
SLUS-29197:
name: "DreamWorks & Aardman Flushed Away [Demo]"
region: "NTSC-U"
gsHWFixes:
halfPixelOffset: 5 # Fixes post alignment.
nativeScaling: 2 # Fixes post effects.
SLUS-29198:
name: "Guitar Hero II [Demo]"
region: "NTSC-U"
File diff suppressed because it is too large Load Diff
+10 -1
View File
@@ -654,6 +654,7 @@
030000006f0e00001e01000000000000,Rock Candy 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,
030000006f0e00002801000000000000,Rock Candy 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,
030000006f0e00002f01000000000000,Rock Candy 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,
03000000830500000052000000000000,Rockfire CosmoVoyager,a:b0,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b10,leftshoulder:b6,lefttrigger:b7,misc1:b11,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
03000000830500007030000000000000,Rockfire Space Ranger,a:b0,b:b1,back:b5,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b9,righttrigger:b8,start:b2,x:b3,y:b4,platform:Windows,
03000000050b0000e318000000000000,ROG Chakram,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,
03000000050b0000e518000000000000,ROG Chakram,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,
@@ -682,6 +683,7 @@
03000000a306000020f6000000000000,Saitek PS2700 PlayStation 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:a4,start:b9,x:b0,y:b3,platform:Windows,
03000000300f00001101000000000000,Saitek Rumble,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,
03000000e804000000a0000000000000,Samsung EIGP20,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000bd12000010d1000000000000,Sanwa 4Button,a:b0,b:b1,x:b2,y:b3,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,platform:Windows,
03000000c01100000252000000000000,Sanwa Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
03000000c01100004350000000000000,Sanwa Micro Grip P3,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,x:b3,y:b2,platform:Windows,
03000000411200004550000000000000,Sanwa Micro Grip Pro,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a1,righty:a2,start:b9,x:b1,y:b3,platform:Windows,
@@ -773,6 +775,7 @@
03000000d60600002a00000000000000,Trust 850F,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,x:b3,y:b4,platform:Windows,
030000005f140000c501000000000000,Trust Gamepad,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:Windows,
03000000b80500000210000000000000,Trust Gamepad,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:Windows,
030000005f1400002a01000000000000,Trust Predator GM1200,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,x:b3,y:b4,platform:Windows,
030000004f04000087b6000000000000,TWCS Throttle,dpdown:b8,dpleft:b9,dpright:b7,dpup:b6,leftstick:b5,lefttrigger:-a5,leftx:a0,lefty:a1,righttrigger:+a5,platform:Windows,
03000000411200000450000000000000,Twin Shock,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a4,start:b11,x:b3,y:b0,platform:Windows,
03000000d90400000200000000000000,TwinShock PS2 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:Windows,
@@ -1008,6 +1011,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000006d04000018c2000000000000,Logitech F510,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,
030000006d04000019c2000005030000,Logitech F710,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,
030000006d0400001fc2000000000000,Logitech F710,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,
030000006d0400001ac2000004000000,Logitech Precision,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,
030000006d04000018c2000000010000,Logitech RumblePad 2,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,
03000000380700005032000000010000,Mad Catz PS3 Fightpad 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,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000380700008433000000010000,Mad Catz PS3 Fightstick TE S Plus,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,
@@ -1334,7 +1338,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000b40400001124000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000b40400001224000011010000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b2,paddle1:b16,paddle2:b17,paddle3:b14,paddle4:b15,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000151900004000000001000000,Flydigi Vader 2,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b12,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b14,paddle1:b2,paddle2:b5,paddle3:b16,paddle4:b17,rightshoulder:b7,rightstick:b13,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
06000000d73700000124000000000000,Flydigi Vader 5 Pro,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,guide:b8,leftstick:b9,rightstick:b10,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,leftx:a0,lefty:a1,lefttrigger:a2,rightx:a3,righty:a4,righttrigger:a5,misc1:b11,misc2:b12,misc3:b13,misc4:b14,paddle1:b15,paddle2:b17,paddle3:b16,paddle4:b18,misc5:b19,platform:Linux,
06000000d73700000124000000000000,Flydigi Vader 5 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,misc1:b11,misc2:b12,misc3:b13,misc4:b14,misc5:b19,paddle1:b15,paddle2:b17,paddle3:b16,paddle4:b18,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000007e0500003703000000000000,GameCube Adapter,a:b0,b:b1,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,
19000000030000000300000002030000,GameForce Controller,a:b1,b:b0,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b5,rightstick:b15,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,
03000000373500000b10000019010000,GameSir Cyclone 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,
@@ -1440,6 +1444,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000bd12000003c0000010010000,Joypad Alpha Shock,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,
03000000242f00002d00000011010000,JYS Adapter,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,
03000000242f00008a00000011010000,JYS Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,
03000000314100001935000000010000,LeadJoy Xeno Plus,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:a4,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000006f0e00000103000000020000,Logic3 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,
030000006d040000d1ca000000000000,Logitech Chillstream,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,
030000006d040000d1ca000011010000,Logitech Chillstream,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,
@@ -1533,6 +1538,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000853200000503000000010000,Nacon MGX 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,
0300000085320000170d000011010000,Nacon Revolution 5 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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
0300000085320000190d000011010000,Nacon Revolution 5 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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
0300000085320000180d000011010000,Nacon Revolution 5 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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
0300000085320000160d000011010000,Nacon Revolution 5 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:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
030000000d0f00000900000010010000,Natec Genesis P44,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,
030000004f1f00000800000011010000,NeoGeo PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
0300000092120000474e000000010000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,platform:Linux,
@@ -1790,6 +1797,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000f00600000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux,
030000005f140000c501000010010000,Trust Gamepad,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,
06000000f51000000870000003010000,Turtle Beach Recon,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,
03000000f51000001370000000010000,Turtle Beach ReactR,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
03000000f51000001370000000000000,Turtle Beach ReactR,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux,
03000000100800000100000010010000,Twin PlayStation 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,
03000000c0160000e105000010010000,Ultimate Atari Fight Stick,a:b1,b:b2,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b8,x:b0,y:b3,platform:Linux,
03000000151900005678000010010000,Uniplay U6,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,
+311 -33
View File
@@ -43,6 +43,13 @@
#define PS_ATST_NOTEQUAL 4
#endif
#ifndef PS_AA1_NONE
#define PS_AA1_NONE 0
#define PS_AA1_LINE 1
#define PS_AA1_TRIANGLE 2
#define PS_AA1_TRIANGLE_SW_Z 3
#endif
#ifndef PS_FST
#define PS_IIP 0
#define PS_FST 0
@@ -102,6 +109,17 @@
#define PS_NO_COLOR1 0
#define PS_DATE 0
#define PS_TEX_IS_FB 0
#define PS_AA1 0
#define PS_ABE 0
#endif
#ifndef VS_EXPAND_NONE
#define VS_EXPAND_NONE 0
#define VS_EXPAND_POINT 1
#define VS_EXPAND_LINE 2
#define VS_EXPAND_SPRITE 3
#define VS_EXPAND_LINE_AA1 4
#define VS_EXPAND_TRIANGLE_AA1 5
#endif
#define SW_BLEND (PS_BLEND_A || PS_BLEND_B || PS_BLEND_D)
@@ -110,7 +128,8 @@
#define NEEDS_RT_FOR_AFAIL (PS_AFAIL == AFAIL_ZB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
#define NEEDS_DEPTH_FOR_AFAIL (PS_AFAIL == AFAIL_FB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
#define NEEDS_DEPTH_FOR_ZTST (PS_ZTST == ZTST_GEQUAL || PS_ZTST == ZTST_GREATER)
#define SW_DEPTH (NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST)
#define NEEDS_DEPTH_FOR_AA1 (PS_AA1 == PS_AA1_TRIANGLE_SW_Z)
#define SW_DEPTH (NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST || NEEDS_DEPTH_FOR_AA1)
#define ZWRITE (PS_ZFLOOR || PS_ZCLAMP || SW_DEPTH)
struct VS_INPUT
@@ -135,6 +154,9 @@ struct VS_OUTPUT
#else
nointerpolation float4 c : COLOR0;
#endif
float inv_cov : COLOR1; // We use the inverse to make it simpler to interpolate.
nointerpolation uint interior : COLOR2; // 1 for triangle interior; 0 for edge;
};
struct PS_INPUT
@@ -147,6 +169,8 @@ struct PS_INPUT
#else
nointerpolation float4 c : COLOR0;
#endif
float inv_cov : COLOR1; // We use the inverse to make it simpler to interpolate.
nointerpolation uint interior : COLOR2; // 1 for triangle interior; 0 for edge;
#if (PS_DATE >= 1 && PS_DATE <= 3) || GS_FORWARD_PRIMID
uint primid : SV_PrimitiveID;
#endif
@@ -220,6 +244,155 @@ cbuffer cb1
float RcpScaleFactor;
};
#if (PS_AUTOMATIC_LOD != 1) && (PS_MANUAL_LOD == 1)
float manual_lod(float uv_w)
{
// FIXME add LOD: K - ( LOG2(Q) * (1 << L))
float K = LODParams.x;
float L = LODParams.y;
float bias = LODParams.z;
float max_lod = LODParams.w;
float gs_lod = K - log2(abs(uv_w)) * L;
// FIXME max useful ?
//return max(min(gs_lod, max_lod) - bias, 0.0f);
return min(gs_lod, max_lod) - bias;
}
#endif
#if PS_ANISOTROPIC_FILTERING > 1
float4 sample_c_af(float2 uv, float uv_w)
{
// HW sampler will reject bad UVs, match that here.
uv = any(isnan(uv) | isinf(uv)) ? float2(0, 0) : uv;
// Large floating point values risk NaN/Inf values.
// Above this value floats lose decimal precision, so seems a resonable limit for UVs.
uv = clamp(uv, -8388608.0f, 8388608.0f);
// Below taken from https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#7.18.11%20LOD%20Calculations
// With guidance from https://pema.dev/2025/05/09/mipmaps-too-much-detail/
float2 sz;
Texture.GetDimensions(sz.x, sz.y);
float2 dX = ddx(uv) * sz;
float2 dY = ddy(uv) * sz;
// Calculate Ellipse Transform
bool d_zero = length(dX) == 0 || length(dY) == 0;
bool d_par = (dX.x * dY.y - dY.x * dX.y) == 0;
bool d_per = dot(dX, dY) == 0;
bool d_inf_nan = any(isinf(dX) | isinf(dY) | isnan(dX) | isnan(dY));
if (!(d_zero || d_par || d_per || d_inf_nan))
{
float A = dX.y * dX.y + dY.y * dY.y;
float B = -2 * (dX.x * dX.y + dY.x * dY.y);
float C = dX.x * dX.x + dY.x * dY.x;
float f = (dX.x * dY.y - dY.x * dX.y);
float F = f * f;
float p = A - C;
float q = A + C;
float t = sqrt(p * p + B * B);
float2 new_dX = float2(
sqrt(F * (t + p) / (t * (q + t))),
sqrt(F * (t - p) / (t * (q + t))) * sign(B)
);
float2 new_dY = float2(
sqrt(F * (t - p) / (t * (q - t))) * -sign(B),
sqrt(F * (t + p) / (t * (q - t)))
);
d_inf_nan = any(isinf(new_dX) | isinf(new_dY) | isnan(new_dX) | isnan(new_dY));
if (!d_inf_nan)
{
dX = new_dX;
dY = new_dY;
}
}
// Compute AF values
float squared_length_x = dX.x * dX.x + dX.y * dX.y;
float squared_length_y = dY.x * dY.x + dY.y * dY.y;
float determinant = abs(dX.x * dY.y - dX.y * dY.x);
bool is_major_x = squared_length_x > squared_length_y;
float squared_length_major = is_major_x ? squared_length_x : squared_length_y;
float length_major = sqrt(squared_length_major);
float aniso_ratio;
float length_lod;
float2 aniso_line;
if (length_major <= 1.0f)
{
// A zero length_major would result in NaN Lod and break sampling.
// A small length_major would result in aniso_ratio getting clamped to 1.
// Perform isotropic filtering instead.
aniso_ratio = 1.0f;
length_lod = length_major;
aniso_line = float2(0, 0);
}
else
{
float norm_major = 1.0f / length_major;
float2 aniso_line_dir = float2(
(is_major_x ? dX.x : dY.x) * norm_major,
(is_major_x ? dX.y : dY.y) * norm_major
);
aniso_ratio = squared_length_major / determinant;
// Calculate the minor length of the ellipse for Lod, while also clamping the ratio of anisotropy.
if (aniso_ratio > PS_ANISOTROPIC_FILTERING)
{
// ratio is clamped - Lod is based on ratio (preserves area)
aniso_ratio = PS_ANISOTROPIC_FILTERING;
length_lod = length_major / PS_ANISOTROPIC_FILTERING;
}
else
{
// ratio not clamped - Lod is based on area
length_lod = determinant / length_major;
}
// clamp to top Lod
if (length_lod < 1.0f)
aniso_ratio = max(1.0f, aniso_ratio * length_lod);
aniso_ratio = round(aniso_ratio);
aniso_line = aniso_line_dir * 0.5f * length_major * (1.0f / sz);
}
#if PS_AUTOMATIC_LOD == 1
float lod = log2(length_lod);
#elif PS_MANUAL_LOD == 1
float lod = manual_lod(uv_w);
#else
float lod = 0; // No Lod
#endif
float4 colour;
if (aniso_ratio == 1.0f)
colour = Texture.SampleLevel(TextureSampler, uv, lod);
else
{
float4 num = float4(0, 0, 0, 0);
for (int i = 0; i < aniso_ratio; i++)
{
float2 d = -aniso_line + (0.5f + i) * (2.0f * aniso_line) / aniso_ratio;
float2 uv_sample = uv + d;
float4 sample_colour = Texture.SampleLevel(TextureSampler, uv_sample, lod);
num += sample_colour;
}
colour = num / aniso_ratio;
}
return colour;
}
#endif
float4 sample_c(float2 uv, float uv_w, int2 xy)
{
#if PS_TEX_IS_FB == 1
@@ -252,21 +425,12 @@ float4 sample_c(float2 uv, float uv_w, int2 xy)
#endif
#endif
#if PS_AUTOMATIC_LOD == 1
#if PS_ANISOTROPIC_FILTERING > 1
return sample_c_af(uv, uv_w);
#elif PS_AUTOMATIC_LOD == 1
return Texture.Sample(TextureSampler, uv);
#elif PS_MANUAL_LOD == 1
// FIXME add LOD: K - ( LOG2(Q) * (1 << L))
float K = LODParams.x;
float L = LODParams.y;
float bias = LODParams.z;
float max_lod = LODParams.w;
float gs_lod = K - log2(abs(uv_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 Texture.SampleLevel(TextureSampler, uv, lod);
return Texture.SampleLevel(TextureSampler, uv, manual_lod(uv_w));
#else
return Texture.SampleLevel(TextureSampler, uv, 0); // No lod
#endif
@@ -1077,7 +1241,15 @@ PS_OUTPUT ps_main(PS_INPUT input)
#endif
float4 C = ps_color(input);
#if PS_FIXED_ONE_A
#if PS_AA1
float cov = clamp(1.0f - abs(input.inv_cov), 0.0f, 1.0f);
#if PS_ABE
if (floor(C.a) == 128.0f) // The coverage is only used if the fragment alpha is 128.
C.a = 128.0f * cov;
#else
C.a = 128.0f * cov;
#endif
#elif PS_FIXED_ONE_A
// AA (Fixed one) will output a coverage of 1.0 as alpha
C.a = 128.0f;
#endif
@@ -1210,10 +1382,7 @@ PS_OUTPUT ps_main(PS_INPUT input)
uint4 denorm_c = uint4(C);
uint2 denorm_TA = uint2(float2(TA.xy) * 255.0f + 0.5f);
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.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.y & 0x80u));
else
C.ga = (float2)float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u));
C.ga = (float2)float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u));
}
else if (PS_SHUFFLE_ACROSS)
{
@@ -1279,6 +1448,11 @@ PS_OUTPUT ps_main(PS_INPUT input)
input.p.z = min(input.p.z, MaxDepthPS);
#endif
#if PS_AA1 == PS_AA1_TRIANGLE_SW_Z
if (!bool(input.interior))
input.p.z = DepthTexture.Load(int3(input.p.xy, 0)).r; // No depth update for triangle edges.
#endif
#if ZWRITE
#if SW_DEPTH && PS_NO_COLOR1 && DX12
// Output color clone for feedback as well as real depth.
@@ -1310,7 +1484,19 @@ cbuffer cb0
float2 TextureOffset;
float2 PointSize;
uint MaxDepth;
uint BaseVertex; // Only used in DX11.
uint _cb0_pad0;
};
#ifdef DX12
cbuffer cb2 : register(b2)
#else
cbuffer cb2
#endif
{
uint BaseVertex;
uint BaseIndex;
uint _cb2_pad0;
uint _cb2_pad1;
};
VS_OUTPUT vs_main(VS_INPUT input)
@@ -1362,10 +1548,14 @@ VS_OUTPUT vs_main(VS_INPUT input)
output.c = input.c;
output.t.z = input.f.r;
// Silence compiler warnings; should be optimized out when not needed.
output.inv_cov = 0.0f;
output.interior = 0;
return output;
}
#if VS_EXPAND != 0
#if VS_EXPAND != VS_EXPAND_NONE
struct VS_RAW_INPUT
{
@@ -1379,14 +1569,19 @@ struct VS_RAW_INPUT
};
StructuredBuffer<VS_RAW_INPUT> vertices : register(t0);
StructuredBuffer<uint> IndexBuffer : register(t5);
uint load_index(uint _i)
{
uint i = _i + BaseIndex;
// i is even => load lower 16 bits; i odd => load upper 16 bits.
uint shift = (i & 1u) << 4u;
return (IndexBuffer.Load(i >> 1u) >> shift) & 0xFFFFu;
}
VS_INPUT load_vertex(uint index)
{
#ifdef DX12
VS_RAW_INPUT raw = vertices.Load(index);
#else
VS_RAW_INPUT raw = vertices.Load(BaseVertex + index);
#endif
VS_INPUT vert;
vert.st = raw.ST;
@@ -1401,7 +1596,7 @@ VS_INPUT load_vertex(uint index)
VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
{
#if VS_EXPAND == 1 // Point
#if VS_EXPAND == VS_EXPAND_POINT
VS_OUTPUT vtx = vs_main(load_vertex(vid >> 2));
@@ -1410,7 +1605,13 @@ VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
return vtx;
#elif VS_EXPAND == 2 // Line
#elif (VS_EXPAND == VS_EXPAND_LINE) || (VS_EXPAND == VS_EXPAND_LINE_AA1)
// The difference between EXPAND_LINE and EXPAND_LINE_AA1
// is that EXPAND_LINE expands in the perpendicular direction while
// EXPAND_LINE_AA1 expands in the Y direction for shallow lines (X dominant)
// and the X direction for steep lines (Y dominant).
// EXPAND_LINE_AA1 also adds coverage to the output.
uint vid_base = vid >> 2;
bool is_bottom = vid & 2;
@@ -1419,20 +1620,31 @@ VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
VS_OUTPUT vtx = vs_main(load_vertex(vid_base));
VS_OUTPUT other = vs_main(load_vertex(vid_other));
float2 line_vector = normalize(vtx.p.xy - other.p.xy);
float2 line_normal = float2(line_vector.y, -line_vector.x);
float2 line_width = (line_normal * PointSize) / 2;
// line_normal is inverted for bottom point
float2 offset = (is_bottom ^ is_right) ? line_width : -line_width;
// Use bottom minus top for delta regardless of which vertex we are expanding.
float2 line_delta = is_bottom ? (vtx.p.xy - other.p.xy) : (other.p.xy - vtx.p.xy);
float2 line_vector = normalize(line_delta);
#if VS_EXPAND == VS_EXPAND_LINE
float2 line_expand = float2(line_vector.y, -line_vector.x);
#elif VS_EXPAND == VS_EXPAND_LINE_AA1
// Expand in y direction for shallow lines and x direction for steep lines.
line_delta /= VertexScale;
float2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? float2(0.0f, 2.0f) : float2(2.0f, 0.0f);
#endif
float2 line_width = (line_expand * PointSize) / 2;
float2 offset = is_right ? line_width : -line_width;
vtx.p.xy += offset;
#if VS_EXPAND == VS_EXPAND_LINE_AA1
vtx.inv_cov = is_right ? 1.0f : -1.0f;
#endif
// Lines will be run as (0 1 2) (1 2 3)
// This means that both triangles will have a point based off the top line point as their first point
// So we don't have to do anything for !IIP
return vtx;
#elif VS_EXPAND == 3 // Sprite
#elif VS_EXPAND == VS_EXPAND_SPRITE
// Sprite points are always in pairs
uint vid_base = vid >> 1;
@@ -1455,6 +1667,72 @@ VS_OUTPUT vs_main_expand(uint vid : SV_VertexID)
return vtx;
#elif VS_EXPAND == VS_EXPAND_TRIANGLE_AA1
// Triangles with AA1 are expanded as follows:
// - Vertices 0-2: Interior of triangle (1 triangle).
// - Vertices 3-8: First edge expanded (2 triangles).
// - Vertices 9-14: Second edge expanded (2 triangles).
// - Vertices 15-20: Third edge expanded (2 triangles).
uint prim_id = vid / 21;
uint prim_offset = vid - 21 * prim_id; // range: 0-20
bool interior = prim_offset < 3;
VS_OUTPUT vtx;
if (interior)
{
vtx = vs_main(load_vertex(load_index(3 * prim_id + prim_offset)));
vtx.inv_cov = 0.0f; // Full coverage
vtx.interior = 1;
}
else
{
// Vertex indices for this edge. We need all 3 for determining exterior/interior.
uint prim_offset_edges = prim_offset - 3; // range: 0-17
uint i0 = prim_offset_edges / 6;
uint i1 = (i0 >= 2) ? i0 - 2 : i0 + 1;
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
uint edge_offset = prim_offset_edges - 6 * i0; // range: 0-5
// Note: order of top/bottom, inside/outside order is arbitrary,
// as long as it assembles into two triangles forming a quad.
bool is_bottom = (2 <= edge_offset) && (edge_offset <= 4);
bool is_outside = edge_offset & 1;
vtx = vs_main(load_vertex(load_index(3 * prim_id + i0)));
VS_OUTPUT other = vs_main(load_vertex(load_index(3 * prim_id + i1)));
VS_OUTPUT opposite = vs_main(load_vertex(load_index(3 * prim_id + i2)));
// Similar expansion to line AA1 except instead of expanding on both sides of
// the line we expand on on the side towards the outside of the triangle.
float2 line_delta = vtx.p.xy - other.p.xy;
float2 line_normal = normalize(float2(line_delta.y, -line_delta.x));
float2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? float2(0.0f, 2.0f) : float2(2.0f, 0.0f);
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(opposite.p.xy - vtx.p.xy, line_normal) >= 0.0f))
{
// Expand direction point towards the interior so flip it.
line_expand = -line_expand;
}
float2 line_width = (line_expand * PointSize) / 2;
if (is_bottom)
vtx = other;
if (is_outside)
{
vtx.p.xy += line_width;
vtx.inv_cov = 1.0f; // No coverage
}
else
{
vtx.inv_cov = 0.0f; // Full coverage
}
vtx.interior = 0;
}
return vtx;
#endif
}
+180 -20
View File
@@ -33,6 +33,13 @@
#define PS_ATST_NOTEQUAL 4
#endif
#ifndef PS_AA1_NONE
#define PS_AA1_NONE 0
#define PS_AA1_LINE 1
#define PS_AA1_TRIANGLE 2
#define PS_AA1_TRIANGLE_SW_Z 3
#endif
// TEX_COORD_DEBUG output the uv coordinate as color. It is useful
// to detect bad sampling due to upscaling
//#define TEX_COORD_DEBUG
@@ -50,10 +57,11 @@
#define NEEDS_RT_FOR_AFAIL (PS_AFAIL == PS_ZB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
#define NEEDS_DEPTH_FOR_AFAIL (PS_AFAIL == AFAIL_FB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
#define NEEDS_DEPTH_FOR_ZTST (PS_ZTST == ZTST_GEQUAL || PS_ZTST == ZTST_GREATER)
#define NEEDS_DEPTH_FOR_AA1 (PS_AA1 == PS_AA1_TRIANGLE_SW_Z)
#define NEEDS_RT (NEEDS_RT_EARLY || NEEDS_RT_FOR_AFAIL || (!PS_PRIMID_INIT && (PS_FBMASK || SW_BLEND_NEEDS_RT || SW_AD_TO_HW)) || PS_COLOR_FEEDBACK)
#define NEEDS_TEX (PS_TFX != 4)
#define SW_DEPTH (NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST)
#define SW_DEPTH (NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST || NEEDS_DEPTH_FOR_AA1)
#define ZWRITE (SW_DEPTH || PS_ZCLAMP || PS_ZFLOOR)
layout(std140, binding = 0) uniform cb21
@@ -97,6 +105,9 @@ in SHADER
#else
flat vec4 c;
#endif
float inv_cov; // We use the inverse to make it simpler to interpolate.
flat uint interior; // 1 for triangle interior; 0 for edge;
} PSin;
#define TARGET_0_QUALIFIER out
@@ -182,6 +193,154 @@ vec4 sample_from_depth()
#if NEEDS_TEX
#if (PS_AUTOMATIC_LOD != 1) && (PS_MANUAL_LOD == 1)
float manual_lod(float uv_w)
{
// FIXME add LOD: K - ( LOG2(Q) * (1 << L))
float K = LODParams.x;
float L = LODParams.y;
float bias = LODParams.z;
float max_lod = LODParams.w;
float gs_lod = K - log2(abs(uv_w)) * L;
// FIXME max useful ?
//return max(min(gs_lod, max_lod) - bias, 0.0f);
return min(gs_lod, max_lod) - bias;
}
#endif
#if PS_ANISOTROPIC_FILTERING > 1
vec4 sample_c_af(vec2 uv, float uv_w)
{
// HW sampler will reject bad UVs, match that here.
uv = (any(isnan(uv)) || any(isinf(uv))) ? vec2(0, 0) : uv;
// Large floating point values risk NaN/Inf values.
// Above this value floats lose decimal precision, so seems a resonable limit for UVs.
uv = clamp(uv, -8388608.0f, 8388608.0f);
// Below taken from https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#7.18.11%20LOD%20Calculations
// With guidance from https://pema.dev/2025/05/09/mipmaps-too-much-detail/
vec2 sz = textureSize(TextureSampler, 0);
vec2 dX = dFdx(uv) * sz;
vec2 dY = dFdy(uv) * sz;
// Calculate Ellipse Transform
bool d_zero = length(dX) == 0 || length(dY) == 0;
bool d_par = (dX.x * dY.y - dY.x * dX.y) == 0;
bool d_per = dot(dX, dY) == 0;
bool d_inf_nan = any(isinf(dX)) || any(isinf(dY)) || any(isnan(dX)) || any(isnan(dY));
if (!(d_zero || d_par || d_per || d_inf_nan))
{
float A = dX.y * dX.y + dY.y * dY.y;
float B = -2 * (dX.x * dX.y + dY.x * dY.y);
float C = dX.x * dX.x + dY.x * dY.x;
float f = (dX.x * dY.y - dY.x * dX.y);
float F = f * f;
float p = A - C;
float q = A + C;
float t = sqrt(p * p + B * B);
vec2 new_dX = vec2(
sqrt(F * (t + p) / (t * (q + t))),
sqrt(F * (t - p) / (t * (q + t))) * sign(B)
);
vec2 new_dY = vec2(
sqrt(F * (t - p) / (t * (q - t))) * -sign(B),
sqrt(F * (t + p) / (t * (q - t)))
);
d_inf_nan = any(isinf(new_dX)) || any(isinf(new_dY)) || any(isnan(new_dX)) || any(isnan(new_dY));
if (!d_inf_nan)
{
dX = new_dX;
dY = new_dY;
}
}
// Compute AF values
float squared_length_x = dX.x * dX.x + dX.y * dX.y;
float squared_length_y = dY.x * dY.x + dY.y * dY.y;
float determinant = abs(dX.x * dY.y - dX.y * dY.x);
bool is_major_x = squared_length_x > squared_length_y;
float squared_length_major = is_major_x ? squared_length_x : squared_length_y;
float length_major = sqrt(squared_length_major);
float aniso_ratio;
float length_lod;
vec2 aniso_line;
if (length_major <= 1.0f)
{
// A zero length_major would result in NaN Lod and break sampling.
// A small length_major would result in aniso_ratio getting clamped to 1.
// Perform isotropic filtering instead.
aniso_ratio = 1.0f;
length_lod = length_major;
aniso_line = vec2(0, 0);
}
else
{
float norm_major = 1.0f / length_major;
vec2 aniso_line_dir = vec2(
(is_major_x ? dX.x : dY.x) * norm_major,
(is_major_x ? dX.y : dY.y) * norm_major
);
aniso_ratio = squared_length_major / determinant;
// Calculate the minor length of the ellipse for Lod, while also clamping the ratio of anisotropy.
if (aniso_ratio > PS_ANISOTROPIC_FILTERING)
{
// ratio is clamped - Lod is based on ratio (preserves area)
aniso_ratio = PS_ANISOTROPIC_FILTERING;
length_lod = length_major / PS_ANISOTROPIC_FILTERING;
}
else
{
// ratio not clamped - Lod is based on area
length_lod = determinant / length_major;
}
// clamp to top Lod
if (length_lod < 1.0f)
aniso_ratio = max(1.0f, aniso_ratio * length_lod);
aniso_ratio = round(aniso_ratio);
aniso_line = aniso_line_dir * 0.5f * length_major * (1.0f / sz);
}
#if PS_AUTOMATIC_LOD == 1
float lod = log2(length_lod);
#elif PS_MANUAL_LOD == 1
float lod = manual_lod(uv_w);
#else
float lod = 0; // No Lod
#endif
vec4 colour;
if (aniso_ratio == 1.0f)
colour = textureLod(TextureSampler, uv, lod);
else
{
vec4 num = vec4(0, 0, 0, 0);
for (int i = 0; i < aniso_ratio; i++)
{
vec2 d = -aniso_line + (0.5f + i) * (2.0f * aniso_line) / aniso_ratio;
vec2 uv_sample = uv + d;
vec4 sample_colour = textureLod(TextureSampler, uv_sample, lod);
num += sample_colour;
}
colour = num / aniso_ratio;
}
return colour;
}
#endif
vec4 sample_c(vec2 uv)
{
#if PS_TEX_IS_FB == 1
@@ -205,21 +364,12 @@ vec4 sample_c(vec2 uv)
#endif
#endif
#if PS_AUTOMATIC_LOD == 1
#if PS_ANISOTROPIC_FILTERING > 1
return sample_c_af(uv, PSin.t_float.w);
#elif PS_AUTOMATIC_LOD == 1
return texture(TextureSampler, uv);
#elif PS_MANUAL_LOD == 1
// FIXME add LOD: K - ( LOG2(Q) * (1 << L))
float K = LODParams.x;
float L = LODParams.y;
float bias = LODParams.z;
float max_lod = LODParams.w;
float gs_lod = K - log2(abs(PSin.t_float.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(TextureSampler, uv, lod);
return textureLod(TextureSampler, uv, manual_lod(PSin.t_float.w));
#else
return textureLod(TextureSampler, uv, 0.0f); // No lod
#endif
@@ -1099,7 +1249,15 @@ void ps_main()
vec4 C = ps_color();
#if PS_FIXED_ONE_A
#if PS_AA1
float cov = clamp(1.0f - abs(PSin.inv_cov), 0.0f, 1.0f);
#if PS_ABE
if (floor(C.a) == 128.0f) // The coverage is only used if the fragment alpha is 128.
C.a = 128.0f * cov;
#else
C.a = 128.0f * cov;
#endif
#elif PS_FIXED_ONE_A
// AA (Fixed one) will output a coverage of 1.0 as alpha
C.a = 128.0f;
#endif
@@ -1172,10 +1330,7 @@ void ps_main()
uvec2 denorm_TA = uvec2(vec2(TA.xy) * 255.0f + 0.5f);
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)));
C.ga = vec2(float((denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80u)));
#elif PS_SHUFFLE_ACROSS
#if(PS_PROCESS_BA == SHUFFLE_READWRITE && PS_PROCESS_RG == SHUFFLE_READWRITE)
C.br = C.rb;
@@ -1224,7 +1379,7 @@ void ps_main()
#elif (PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
if (!atst_pass)
{
C.a = sample_from_rt().a;
C.a = sample_from_rt().a;
#if PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
input_z = sample_from_depth().r;
#endif
@@ -1244,6 +1399,11 @@ void ps_main()
input_z = min(input_z, MaxDepthPS);
#endif
#if PS_AA1 == PS_AA1_TRIANGLE_SW_Z
if (!bool(PSin.interior))
input_z = sample_from_depth().r; // No depth update for triangle edges.
#endif
#if ZWRITE
#if SW_DEPTH && PS_NO_COLOR1 && (DEPTH_FEEDBACK_SUPPORT == 2)
// Depth as color write. For depth as color feedback we write to both
+116 -10
View File
@@ -18,6 +18,15 @@ layout(std140, binding = 1) uniform cb20
#ifdef VERTEX_SHADER
#ifndef VS_EXPAND_NONE
#define VS_EXPAND_NONE 0
#define VS_EXPAND_POINT 1
#define VS_EXPAND_LINE 2
#define VS_EXPAND_SPRITE 3
#define VS_EXPAND_LINE_AA1 4
#define VS_EXPAND_TRIANGLE_AA1 5
#endif
out SHADER
{
vec4 t_float;
@@ -27,11 +36,13 @@ out SHADER
#else
flat vec4 c;
#endif
float inv_cov; // We use the inverse to make it simpler to interpolate.
flat uint interior; // 1 for triangle interior; 0 for edge.
} VSout;
const float exp_min32 = exp2(-32.0f);
#if VS_EXPAND == 0
#if VS_EXPAND == VS_EXPAND_NONE
layout(location = 0) in vec2 i_st;
layout(location = 2) in vec4 i_c;
@@ -98,10 +109,23 @@ struct RawVertex
uint FOG;
};
layout(std140, binding = 4) uniform cb22
{
uint BaseVertex;
uint BaseIndex;
uint pad_cb22_0;
uint pad_cb22_1;
};
layout(std140, binding = 2) readonly buffer VertexBuffer {
RawVertex vertex_buffer[];
};
// Warning: use std430 instead of std140 so that the ints are tightly packed.
layout(std430, binding = 3) readonly buffer IndexBuffer {
uint index_buffer[];
};
struct ProcessedVertex
{
vec4 p;
@@ -110,9 +134,17 @@ struct ProcessedVertex
vec4 c;
};
uint load_index(uint _i)
{
uint i = _i + BaseIndex;
// i is even => load lower 16 bits; i odd => load upper 16 bits.
uint shift = (i & 1u) << 4u;
return (index_buffer[i >> 1u] >> shift) & 0xFFFFu;
}
ProcessedVertex load_vertex(uint index)
{
RawVertex rvtx = vertex_buffer[index];
RawVertex rvtx = vertex_buffer[BaseVertex + index];
vec2 i_st = rvtx.ST;
vec4 i_c = vec4(uvec4(bitfieldExtract(rvtx.RGBA, 0, 8), bitfieldExtract(rvtx.RGBA, 8, 8),
@@ -156,14 +188,14 @@ void main()
uint vid = uint(gl_VertexID);
#if VS_EXPAND == 1 // Point
#if VS_EXPAND == VS_EXPAND_POINT
vtx = load_vertex(vid >> 2);
vtx.p.x += ((vid & 1u) != 0u) ? PointSize.x : 0.0f;
vtx.p.y += ((vid & 2u) != 0u) ? PointSize.y : 0.0f;
#elif VS_EXPAND == 2 // Line
#elif (VS_EXPAND == VS_EXPAND_LINE) || (VS_EXPAND == VS_EXPAND_LINE_AA1)
uint vid_base = vid >> 2;
bool is_bottom = (vid & 2u) != 0u;
@@ -172,18 +204,29 @@ void main()
vtx = load_vertex(vid_base);
ProcessedVertex other = load_vertex(vid_other);
vec2 line_vector = normalize(vtx.p.xy - other.p.xy);
vec2 line_normal = vec2(line_vector.y, -line_vector.x);
vec2 line_width = (line_normal * PointSize) / 2;
// line_normal is inverted for bottom point
vec2 offset = ((uint(is_bottom) ^ uint(is_right)) != 0u) ? line_width : -line_width;
// Use bottom minus top for delta regardless of which vertex we are expanding.
vec2 line_delta = is_bottom ? (vtx.p.xy - other.p.xy) : (other.p.xy - vtx.p.xy);
vec2 line_vector = normalize(line_delta);
#if VS_EXPAND == VS_EXPAND_LINE
vec2 line_expand = vec2(line_vector.y, -line_vector.x);
#elif VS_EXPAND == VS_EXPAND_LINE_AA1
// Expand in y direction for shallow lines and x direction for steep lines.
line_delta /= VertexScale;
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
#endif
vec2 line_width = (line_expand * PointSize) / 2;
vec2 offset = is_right ? line_width : -line_width;
vtx.p.xy += offset;
#if VS_EXPAND == VS_EXPAND_LINE_AA1
VSout.inv_cov = is_right ? 1.0f : -1.0f;
#endif
// Lines will be run as (0 1 2) (1 2 3)
// This means that both triangles will have a point based off the top line point as their first point
// So we don't have to do anything for !IIP
#elif VS_EXPAND == 3 // Sprite
#elif VS_EXPAND == VS_EXPAND_SPRITE
// Sprite points are always in pairs
uint vid_base = vid >> 1;
@@ -204,6 +247,69 @@ void main()
vtx.t_float.y = is_bottom ? lt.t_float.y : vtx.t_float.y;
vtx.t_int.yw = is_bottom ? lt.t_int.yw : vtx.t_int.yw;
#elif VS_EXPAND == VS_EXPAND_TRIANGLE_AA1
// Triangles with AA1 are expanded as follows:
// - Vertices 0-2: Interior of triangle (1 triangle).
// - Vertices 3-8: First edge expanded (2 triangles).
// - Vertices 9-14: Second edge expanded (2 triangles).
// - Vertices 15-20: Third edge expanded (2 triangles).
uint prim_id = vid / 21;
uint prim_offset = vid - 21 * prim_id; // range: 0-20
bool interior = prim_offset < 3;
if (interior)
{
vtx = load_vertex(load_index(3 * prim_id + prim_offset));
VSout.inv_cov = 0.0f; // Full coverage
VSout.interior = 1;
}
else
{
// Vertex indices for this edge. We need all 3 for determining exterior/interior.
uint prim_offset_edges = prim_offset - 3; // range: 0-17
uint i0 = prim_offset_edges / 6;
uint i1 = (i0 >= 2) ? i0 - 2 : i0 + 1;
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
uint edge_offset = prim_offset_edges - 6 * i0; // range: 0-5
// Note: order of top/bottom, inside/outside order is arbitrary,
// as long as it assembles into two triangles forming a quad.
bool is_bottom = (2 <= edge_offset) && (edge_offset <= 4);
bool is_outside = (edge_offset & 1) != 0;
vtx = load_vertex(load_index(3 * prim_id + i0));
ProcessedVertex other = load_vertex(load_index(3 * prim_id + i1));
ProcessedVertex opposite = load_vertex(load_index(3 * prim_id + i2));
// Similar expansion to line AA1 except instead of expanding on both sides of
// the line we expand on on the side towards the outside of the triangle.
vec2 line_delta = vtx.p.xy - other.p.xy;
vec2 line_normal = normalize(vec2(line_delta.y, -line_delta.x));
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(opposite.p.xy - vtx.p.xy, line_normal) >= 0.0f))
{
// Expand direction point towards the interior so flip it.
line_expand = -line_expand;
}
vec2 line_width = (line_expand * PointSize) / 2;
if (is_bottom)
vtx = other;
if (is_outside)
{
vtx.p.xy += line_width;
VSout.inv_cov = 1.0f; // No coverage
}
else
{
VSout.inv_cov = 0.0f; // Full coverage
}
VSout.interior = 0;
}
#endif
gl_Position = vtx.p;
+297 -28
View File
@@ -7,6 +7,15 @@
#if defined(VERTEX_SHADER)
#ifndef VS_EXPAND_NONE
#define VS_EXPAND_NONE 0
#define VS_EXPAND_POINT 1
#define VS_EXPAND_LINE 2
#define VS_EXPAND_SPRITE 3
#define VS_EXPAND_LINE_AA1 4
#define VS_EXPAND_TRIANGLE_AA1 5
#endif
layout(std140, set = 0, binding = 0) uniform cb0
{
vec2 VertexScale;
@@ -28,9 +37,12 @@ layout(location = 0) out VSOutput
#else
flat vec4 c;
#endif
float inv_cov; // We use the inverse to make it simpler to interpolate.
flat uint interior; // 1 for triangle interior; 0 for edge;
} vsOut;
#if VS_EXPAND == 0
#if VS_EXPAND == VS_EXPAND_NONE
layout(location = 0) in vec2 a_st;
layout(location = 1) in uvec4 a_c;
@@ -99,10 +111,23 @@ struct RawVertex
uint FOG;
};
layout(push_constant) uniform cb2
{
uint BaseVertex;
uint BaseIndex;
uint pad_cb2_0;
uint pad_cb2_1;
};
layout(std140, set = 0, binding = 2) readonly buffer VertexBuffer {
RawVertex vertex_buffer[];
};
// Warning: use std430 instead of std140 so that the ints are tightly packed.
layout(std430, set = 0, binding = 3) readonly buffer IndexBuffer {
uint index_buffer[];
};
struct ProcessedVertex
{
vec4 p;
@@ -111,9 +136,17 @@ struct ProcessedVertex
vec4 c;
};
uint load_index(uint _i)
{
uint i = _i + BaseIndex;
// i is even => load lower 16 bits; i odd => load upper 16 bits.
uint shift = (i & 1u) << 4u;
return (index_buffer[i >> 1u] >> shift) & 0xFFFFu;
}
ProcessedVertex load_vertex(uint index)
{
RawVertex rvtx = vertex_buffer[index];
RawVertex rvtx = vertex_buffer[BaseVertex + index];
vec2 a_st = rvtx.ST;
uvec4 a_c = uvec4(bitfieldExtract(rvtx.RGBA, 0, 8), bitfieldExtract(rvtx.RGBA, 8, 8),
@@ -161,14 +194,14 @@ void main()
ProcessedVertex vtx;
uint vid = uint(gl_VertexIndex);
#if VS_EXPAND == 1 // Point
#if VS_EXPAND == VS_EXPAND_POINT
vtx = load_vertex(vid >> 2);
vtx.p.x += ((vid & 1u) != 0u) ? PointSize.x : 0.0f;
vtx.p.y += ((vid & 2u) != 0u) ? PointSize.y : 0.0f;
#elif VS_EXPAND == 2 // Line
#elif (VS_EXPAND == VS_EXPAND_LINE) || (VS_EXPAND == VS_EXPAND_LINE_AA1)
uint vid_base = vid >> 2;
@@ -179,18 +212,29 @@ void main()
vtx = load_vertex(vid_base);
ProcessedVertex other = load_vertex(vid_other);
vec2 line_vector = normalize(vtx.p.xy - other.p.xy);
vec2 line_normal = vec2(line_vector.y, -line_vector.x);
vec2 line_width = (line_normal * PointSize) / 2;
// line_normal is inverted for bottom point
vec2 offset = ((uint(is_bottom) ^ uint(is_right)) != 0u) ? line_width : -line_width;
// Use bottom minus top for delta regardless of which vertex we are expanding.
vec2 line_delta = is_bottom ? (vtx.p.xy - other.p.xy) : (other.p.xy - vtx.p.xy);
vec2 line_vector = normalize(line_delta);
#if VS_EXPAND == VS_EXPAND_LINE
vec2 line_expand = vec2(line_vector.y, -line_vector.x);
#elif VS_EXPAND == VS_EXPAND_LINE_AA1
// Expand in y direction for shallow lines and x direction for steep lines.
line_delta /= VertexScale;
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
#endif
vec2 line_width = (line_expand * PointSize) / 2;
vec2 offset = is_right ? line_width : -line_width;
vtx.p.xy += offset;
#if VS_EXPAND == VS_EXPAND_LINE_AA1
vsOut.inv_cov = is_right ? 1.0f : -1.0f;
#endif
// Lines will be run as (0 1 2) (1 2 3)
// This means that both triangles will have a point based off the top line point as their first point
// So we don't have to do anything for !IIP
#elif VS_EXPAND == 3 // Sprite
#elif VS_EXPAND == VS_EXPAND_SPRITE
// Sprite points are always in pairs
uint vid_base = vid >> 1;
@@ -211,6 +255,69 @@ void main()
vtx.t.y = is_bottom ? lt.t.y : vtx.t.y;
vtx.ti.yw = is_bottom ? lt.ti.yw : vtx.ti.yw;
#elif VS_EXPAND == VS_EXPAND_TRIANGLE_AA1
// Triangles with AA1 are expanded as follows:
// - Vertices 0-2: Interior of triangle (1 triangle).
// - Vertices 3-8: First edge expanded (2 triangles).
// - Vertices 9-14: Second edge expanded (2 triangles).
// - Vertices 15-20: Third edge expanded (2 triangles).
uint prim_id = vid / 21;
uint prim_offset = vid - 21 * prim_id; // range: 0-20
bool interior = prim_offset < 3;
if (interior)
{
vtx = load_vertex(load_index(3 * prim_id + prim_offset));
vsOut.inv_cov = 0.0f; // Full coverage
vsOut.interior = 1;
}
else
{
// Vertex indices for this edge. We need all 3 for determining exterior/interior.
uint prim_offset_edges = prim_offset - 3; // range: 0-17
uint i0 = prim_offset_edges / 6;
uint i1 = (i0 >= 2) ? i0 - 2 : i0 + 1;
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
uint edge_offset = prim_offset_edges - 6 * i0; // range: 0-5
// Note: order of top/bottom, inside/outside order is arbitrary,
// as long as it assembles into two triangles forming a quad.
bool is_bottom = (2 <= edge_offset) && (edge_offset <= 4);
bool is_outside = (edge_offset & 1) != 0;
vtx = load_vertex(load_index(3 * prim_id + i0));
ProcessedVertex other = load_vertex(load_index(3 * prim_id + i1));
ProcessedVertex opposite = load_vertex(load_index(3 * prim_id + i2));
// Similar expansion to line AA1 except instead of expanding on both sides of
// the line we expand on on the side towards the outside of the triangle.
vec2 line_delta = vtx.p.xy - other.p.xy;
vec2 line_normal = normalize(vec2(line_delta.y, -line_delta.x));
vec2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? vec2(0.0f, 2.0f) : vec2(2.0f, 0.0f);
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(opposite.p.xy - vtx.p.xy, line_normal) >= 0.0f))
{
// Expand direction point towards the interior so flip it.
line_expand = -line_expand;
}
vec2 line_width = (line_expand * PointSize) / 2;
if (is_bottom)
vtx = other;
if (is_outside)
{
vtx.p.xy += line_width;
vsOut.inv_cov = 1.0f; // No coverage
}
else
{
vsOut.inv_cov = 0.0f; // Full coverage
}
vsOut.interior = 0;
}
#endif
gl_Position = vtx.p;
@@ -267,6 +374,13 @@ void main()
#define PS_ATST_NOTEQUAL 4
#endif
#ifndef PS_AA1_NONE
#define PS_AA1_NONE 0
#define PS_AA1_LINE 1
#define PS_AA1_TRIANGLE 2
#define PS_AA1_TRIANGLE_SW_Z 3
#endif
#ifndef PS_FST
#define PS_FST 0
#define PS_WMS 0
@@ -327,9 +441,10 @@ void main()
#define AFAIL_NEEDS_RT (PS_AFAIL == AFAIL_ZB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
#define AFAIL_NEEDS_DEPTH (PS_AFAIL == AFAIL_FB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
#define ZTST_NEEDS_DEPTH (PS_ZTST == ZTST_GEQUAL || PS_ZTST == ZTST_GREATER)
#define AA1_NEEDS_DEPTH (PS_AA1 == PS_AA1_TRIANGLE_SW_Z)
#define PS_FEEDBACK_LOOP_IS_NEEDED_RT (PS_TEX_IS_FB == 1 || AFAIL_NEEDS_RT || PS_FBMASK || SW_BLEND_NEEDS_RT || SW_AD_TO_HW || (PS_DATE >= 5) || AFAIL_NEEDS_RT)
#define PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH (AFAIL_NEEDS_DEPTH || ZTST_NEEDS_DEPTH)
#define PS_FEEDBACK_LOOP_IS_NEEDED_RT (PS_TEX_IS_FB == 1 || AFAIL_NEEDS_RT || PS_FBMASK || SW_BLEND_NEEDS_RT || SW_AD_TO_HW || (PS_DATE >= 5))
#define PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH (AFAIL_NEEDS_DEPTH || ZTST_NEEDS_DEPTH || AA1_NEEDS_DEPTH)
#define ZWRITE (PS_ZCLAMP || PS_ZFLOOR || PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH)
#define NEEDS_TEX (PS_TFX != 4)
@@ -365,6 +480,8 @@ layout(location = 0) in VSOutput
#else
flat vec4 c;
#endif
float inv_cov; // We use the inverse to make it simpler to interpolate.
flat uint interior; // 1 for triangle interior; 0 for edge;
} vsIn;
#if !PS_NO_COLOR && !PS_NO_COLOR1
@@ -416,6 +533,154 @@ layout(depth_less) out float gl_FragDepth;
#if NEEDS_TEX
#if (PS_AUTOMATIC_LOD != 1) && (PS_MANUAL_LOD == 1)
float manual_lod(float uv_w)
{
// FIXME add LOD: K - ( LOG2(Q) * (1 << L))
float K = LODParams.x;
float L = LODParams.y;
float bias = LODParams.z;
float max_lod = LODParams.w;
float gs_lod = K - log2(abs(uv_w)) * L;
// FIXME max useful ?
//return max(min(gs_lod, max_lod) - bias, 0.0f);
return min(gs_lod, max_lod) - bias;
}
#endif
#if PS_ANISOTROPIC_FILTERING > 1
vec4 sample_c_af(vec2 uv, float uv_w)
{
// HW sampler will reject bad UVs, match that here.
uv = (any(isnan(uv)) || any(isinf(uv))) ? vec2(0, 0) : uv;
// Large floating point values risk NaN/Inf values.
// Above this value floats lose decimal precision, so seems a resonable limit for UVs.
uv = clamp(uv, -8388608.0f, 8388608.0f);
// Below taken from https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#7.18.11%20LOD%20Calculations
// With guidance from https://pema.dev/2025/05/09/mipmaps-too-much-detail/
vec2 sz = textureSize(Texture, 0);
vec2 dX = dFdx(uv) * sz;
vec2 dY = dFdy(uv) * sz;
// Calculate Ellipse Transform
bool d_zero = length(dX) == 0 || length(dY) == 0;
bool d_par = (dX.x * dY.y - dY.x * dX.y) == 0;
bool d_per = dot(dX, dY) == 0;
bool d_inf_nan = any(isinf(dX)) || any(isinf(dY)) || any(isnan(dX)) || any(isnan(dY));
if (!(d_zero || d_par || d_per || d_inf_nan))
{
float A = dX.y * dX.y + dY.y * dY.y;
float B = -2 * (dX.x * dX.y + dY.x * dY.y);
float C = dX.x * dX.x + dY.x * dY.x;
float f = (dX.x * dY.y - dY.x * dX.y);
float F = f * f;
float p = A - C;
float q = A + C;
float t = sqrt(p * p + B * B);
vec2 new_dX = vec2(
sqrt(F * (t + p) / (t * (q + t))),
sqrt(F * (t - p) / (t * (q + t))) * sign(B)
);
vec2 new_dY = vec2(
sqrt(F * (t - p) / (t * (q - t))) * -sign(B),
sqrt(F * (t + p) / (t * (q - t)))
);
d_inf_nan = any(isinf(new_dX)) || any(isinf(new_dY)) || any(isnan(new_dX)) || any(isnan(new_dY));
if (!d_inf_nan)
{
dX = new_dX;
dY = new_dY;
}
}
// Compute AF values
float squared_length_x = dX.x * dX.x + dX.y * dX.y;
float squared_length_y = dY.x * dY.x + dY.y * dY.y;
float determinant = abs(dX.x * dY.y - dX.y * dY.x);
bool is_major_x = squared_length_x > squared_length_y;
float squared_length_major = is_major_x ? squared_length_x : squared_length_y;
float length_major = sqrt(squared_length_major);
float aniso_ratio;
float length_lod;
vec2 aniso_line;
if (length_major <= 1.0f)
{
// A zero length_major would result in NaN Lod and break sampling.
// A small length_major would result in aniso_ratio getting clamped to 1.
// Perform isotropic filtering instead.
aniso_ratio = 1.0f;
length_lod = length_major;
aniso_line = vec2(0, 0);
}
else
{
float norm_major = 1.0f / length_major;
vec2 aniso_line_dir = vec2(
(is_major_x ? dX.x : dY.x) * norm_major,
(is_major_x ? dX.y : dY.y) * norm_major
);
aniso_ratio = squared_length_major / determinant;
// Calculate the minor length of the ellipse for Lod, while also clamping the ratio of anisotropy.
if (aniso_ratio > PS_ANISOTROPIC_FILTERING)
{
// ratio is clamped - Lod is based on ratio (preserves area)
aniso_ratio = PS_ANISOTROPIC_FILTERING;
length_lod = length_major / PS_ANISOTROPIC_FILTERING;
}
else
{
// ratio not clamped - Lod is based on area
length_lod = determinant / length_major;
}
// clamp to top Lod
if (length_lod < 1.0f)
aniso_ratio = max(1.0f, aniso_ratio * length_lod);
aniso_ratio = round(aniso_ratio);
aniso_line = aniso_line_dir * 0.5f * length_major * (1.0f / sz);
}
#if PS_AUTOMATIC_LOD == 1
float lod = log2(length_lod);
#elif PS_MANUAL_LOD == 1
float lod = manual_lod(uv_w);
#else
float lod = 0; // No Lod
#endif
vec4 colour;
if (aniso_ratio == 1.0f)
colour = textureLod(Texture, uv, lod);
else
{
vec4 num = vec4(0, 0, 0, 0);
for (int i = 0; i < aniso_ratio; i++)
{
vec2 d = -aniso_line + (0.5f + i) * (2.0f * aniso_line) / aniso_ratio;
vec2 uv_sample = uv + d;
vec4 sample_colour = textureLod(Texture, uv_sample, lod);
num += sample_colour;
}
colour = num / aniso_ratio;
}
return colour;
}
#endif
vec4 sample_c(vec2 uv)
{
#if PS_TEX_IS_FB
@@ -439,21 +704,12 @@ vec4 sample_c(vec2 uv)
#endif
#endif
#if PS_AUTOMATIC_LOD == 1
#if PS_ANISOTROPIC_FILTERING > 1
return sample_c_af(uv, vsIn.t.w);
#elif PS_AUTOMATIC_LOD == 1
return texture(Texture, uv);
#elif PS_MANUAL_LOD == 1
// FIXME add LOD: K - ( LOG2(Q) * (1 << L))
float K = LODParams.x;
float L = LODParams.y;
float bias = LODParams.z;
float max_lod = LODParams.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;
return textureLod(Texture, uv, lod);
return textureLod(Texture, uv, manual_lod(vsIn.t.w));
#else
return textureLod(Texture, uv, 0); // No lod
#endif
@@ -1346,7 +1602,15 @@ void main()
vec4 C = ps_color();
#if PS_FIXED_ONE_A
#if PS_AA1
float cov = clamp(1.0f - abs(vsIn.inv_cov), 0.0f, 1.0f);
#if PS_ABE
if (floor(C.a) == 128.0f) // The coverage is only used if the fragment alpha is 128.
C.a = 128.0f * cov;
#else
C.a = 128.0f * cov;
#endif
#elif PS_FIXED_ONE_A
// AA (Fixed one) will output a coverage of 1.0 as alpha
C.a = 128.0f;
#endif
@@ -1472,7 +1736,7 @@ void main()
#elif (PS_AFAIL == AFAIL_RGB_ONLY || PS_AFAIL == AFAIL_RGB_ONLY_SW_Z)
if (!atst_pass)
{
o_col0.a = sample_from_rt().a;
o_col0.a = sample_from_rt().a;
#if PS_AFAIL == AFAIL_RGB_ONLY_SW_Z
input_z = sample_from_depth().r;
#endif
@@ -1483,7 +1747,12 @@ void main()
#if PS_ZCLAMP
input_z = min(input_z, MaxDepthPS);
#endif
#if PS_AA1 == PS_AA1_TRIANGLE_SW_Z
if (!bool(vsIn.interior))
input_z = sample_from_depth().r; // No depth update for triangle edges.
#endif
#if ZWRITE
gl_FragDepth = input_z;
#endif
+3
View File
@@ -247,6 +247,9 @@ if (MSVC)
else()
set(DEFAULT_WARNINGS -Wall -Wextra -Wno-unused-function -Wno-unused-parameter -Wno-missing-field-initializers)
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
list(APPEND DEFAULT_WARNINGS -Wno-attributes)
endif()
if (USE_PGO_GENERATE OR USE_PGO_OPTIMIZE)
add_compile_options("-fprofile-dir=${CMAKE_SOURCE_DIR}/profile")
-5
View File
@@ -226,11 +226,6 @@ namespace PageFaultHandler
#ifdef ARCH_ARM64
void HostSys::FlushInstructionCache(void* address, u32 size)
{
__builtin___clear_cache(reinterpret_cast<char*>(address), reinterpret_cast<char*>(address) + size);
}
[[maybe_unused]] static bool IsStoreInstruction(const void* ptr)
{
u32 bits;
+6
View File
@@ -799,6 +799,12 @@ bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& pa
s_perf_enable = true;
continue;
}
else if (CHECK_ARG("-debugdevice"))
{
Console.WriteLn("Enable debug device");
s_settings_interface.SetBoolValue("EmuCore/GS", "UseDebugDevice", true);
continue;
}
else if (CHECK_ARG("--"))
{
no_more_args = true;
+4 -5
View File
@@ -450,7 +450,7 @@ void DebuggerWindow::onStepInto()
cpu->resumeCpu();
});
repaint();
update();
}
void DebuggerWindow::onStepOver()
@@ -500,7 +500,7 @@ void DebuggerWindow::onStepOver()
cpu->resumeCpu();
});
this->repaint();
update();
}
void DebuggerWindow::onStepOut()
@@ -525,8 +525,7 @@ void DebuggerWindow::onStepOut()
cpu->getPC(),
cpu->getRegister(0, 31),
cpu->getRegister(0, 29),
thread->EntryPoint(),
thread->StackTop());
thread->EntryPoint());
break;
}
}
@@ -541,7 +540,7 @@ void DebuggerWindow::onStepOut()
cpu->resumeCpu();
});
this->repaint();
update();
}
void DebuggerWindow::changeEvent(QEvent* event)
+9 -9
View File
@@ -86,7 +86,7 @@ bool DisassemblyView::fromJson(const JsonValueWrapper& json)
if (show_instruction_bytes != json.value().MemberEnd() && show_instruction_bytes->value.IsBool())
m_showInstructionBytes = show_instruction_bytes->value.GetBool();
repaint();
update();
return true;
}
@@ -222,7 +222,7 @@ void DisassemblyView::contextRunToCursor()
void DisassemblyView::contextJumpToCursor()
{
cpu().setPc(m_selectedAddressStart);
this->repaint();
update();
}
void DisassemblyView::contextToggleBreakpoint()
@@ -378,7 +378,7 @@ void DisassemblyView::contextRestoreFunction()
void DisassemblyView::contextShowInstructionBytes()
{
m_showInstructionBytes = !m_showInstructionBytes;
this->repaint();
update();
}
QString DisassemblyView::GetLineDisasm(u32 address)
@@ -610,7 +610,7 @@ void DisassemblyView::mousePressEvent(QMouseEvent* event)
m_selectedAddressEnd = selectedAddress;
}
}
this->repaint();
update();
}
}
@@ -637,7 +637,7 @@ void DisassemblyView::wheelEvent(QWheelEvent* event)
{
m_visibleStart -= 4;
}
this->repaint();
update();
}
void DisassemblyView::keyPressEvent(QKeyEvent* event)
@@ -709,7 +709,7 @@ void DisassemblyView::keyPressEvent(QKeyEvent* event)
break;
}
this->repaint();
update();
}
void DisassemblyView::openContextMenu(QPoint pos)
@@ -1014,9 +1014,9 @@ void DisassemblyView::gotoAddress(u32 address, bool should_set_focus)
m_selectedAddressStart = destAddress;
m_selectedAddressEnd = destAddress;
this->repaint();
update();
if (should_set_focus)
this->setFocus();
setFocus();
}
void DisassemblyView::toggleBreakpoint(u32 address)
@@ -1034,7 +1034,7 @@ void DisassemblyView::toggleBreakpoint(u32 address)
QtHost::RunOnUIThread([view, cpu]() {
BreakpointModel::getInstance(*cpu)->refreshData();
if (view)
view->repaint();
view->update();
});
});
}
@@ -27,7 +27,6 @@ MemorySearchView::MemorySearchView(const DebuggerViewParameters& parameters)
: DebuggerView(parameters, MONOSPACE_FONT)
{
m_ui.setupUi(this);
this->repaint();
m_ui.listSearchResults->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_ui.btnSearch, &QPushButton::clicked, this, &MemorySearchView::onSearchButtonClicked);
+7 -7
View File
@@ -741,7 +741,7 @@ bool MemoryView::fromJson(const JsonValueWrapper& json)
if (little_endian != json.value().MemberEnd() && little_endian->value.IsBool())
m_table.SetLittleEndian(little_endian->value.GetBool());
repaint();
update();
return true;
}
@@ -764,7 +764,7 @@ void MemoryView::mousePressEvent(QMouseEvent* event)
return;
m_table.SelectAt(event->pos());
repaint();
update();
}
void MemoryView::openContextMenu(QPoint pos)
@@ -859,7 +859,7 @@ void MemoryView::openContextMenu(QPoint pos)
menu->popup(this->mapToGlobal(pos));
this->repaint();
update();
return;
}
@@ -934,7 +934,7 @@ void MemoryView::wheelEvent(QWheelEvent* event)
{
m_table.UpdateStartAddress(m_table.startAddress - 0x10);
}
this->repaint();
update();
}
void MemoryView::keyPressEvent(QKeyEvent* event)
@@ -954,7 +954,7 @@ void MemoryView::keyPressEvent(QKeyEvent* event)
break;
}
}
this->repaint();
update();
DebuggerView::broadcastEvent(DebuggerEvents::VMUpdate());
}
@@ -962,8 +962,8 @@ void MemoryView::gotoAddress(u32 address)
{
m_table.UpdateStartAddress(address & ~0xF);
m_table.selectedAddress = address;
this->repaint();
this->setFocus();
update();
setFocus();
}
#include "moc_MemoryView.cpp"
+7 -6
View File
@@ -36,7 +36,7 @@ RegisterView::RegisterView(const DebuggerViewParameters& parameters)
ui.registerTabs->addTab(cpu().getRegisterCategoryName(i));
}
connect(ui.registerTabs, &QTabBar::currentChanged, [this]() { this->repaint(); });
connect(ui.registerTabs, &QTabBar::currentChanged, [this]() { update(); });
receiveEvent<DebuggerEvents::Refresh>([this](const DebuggerEvents::Refresh& event) -> bool {
update();
@@ -69,7 +69,7 @@ bool RegisterView::fromJson(const JsonValueWrapper& json)
if (show_fpr_float != json.value().MemberEnd() && show_fpr_float->value.IsBool())
m_showFPRFloat = show_fpr_float->value.GetBool();
repaint();
update();
return true;
}
@@ -209,7 +209,8 @@ void RegisterView::mousePressEvent(QMouseEvent* event)
}
}
}
this->repaint();
update();
}
void RegisterView::wheelEvent(QWheelEvent* event)
@@ -223,7 +224,7 @@ void RegisterView::wheelEvent(QWheelEvent* event)
m_rowStart -= 1;
}
this->repaint();
update();
}
void RegisterView::mouseDoubleClickEvent(QMouseEvent* event)
@@ -259,7 +260,7 @@ void RegisterView::customMenuRequested(QPoint pos)
action->setChecked(m_showFPRFloat);
connect(action, &QAction::triggered, this, [this]() {
m_showFPRFloat = !m_showFPRFloat;
repaint();
update();
});
menu->addSeparator();
@@ -272,7 +273,7 @@ void RegisterView::customMenuRequested(QPoint pos)
action->setChecked(m_showVU0FFloat);
connect(action, &QAction::triggered, this, [this]() {
m_showVU0FFloat = !m_showVU0FFloat;
repaint();
update();
});
menu->addSeparator();
+1 -2
View File
@@ -111,8 +111,7 @@ void StackModel::refreshData()
{
if (thread->Status() == ThreadStatus::THS_RUN)
{
m_stackFrames = MipsStackWalk::Walk(&m_cpu, m_cpu.getPC(), m_cpu.getRegister(0, 31), m_cpu.getRegister(0, 29),
thread->EntryPoint(), thread->StackTop());
m_stackFrames = m_cpu.StackTrace(*thread);
break;
}
}
-2
View File
@@ -71,8 +71,6 @@ private:
//: Refers to a Thread Wait State in the Debugger.
{WaitState::NONE, tr("NONE")},
//: Refers to a Thread Wait State in the Debugger.
{WaitState::WAKEUP_REQ, tr("WAKEUP REQUEST")},
//: Refers to a Thread Wait State in the Debugger.
{WaitState::SEMA, tr("SEMAPHORE")},
//: Refers to a Thread Wait State in the Debugger.
{WaitState::SLEEP, tr("SLEEP")},
+8 -5
View File
@@ -294,11 +294,6 @@ void GameListWidget::initialize()
if (Host::ContainsBaseSettingValue("GameListTableView", "HeaderState"))
{
loadTableHeaderState();
// Enforce at least one column is visible immediately after loading.
// This handles cases where a config (perhaps from an older version) has 0 columns and
// no games are visible to be changed (such as per-game config) or played as you can't click on any.
// Will automatically repair a broken header state from config (PCSX2.ini) file.
ensureMinimumOneColumnVisible();
}
else
{
@@ -811,6 +806,14 @@ void GameListWidget::loadTableHeaderState()
QSignalBlocker blocker(header);
header->restoreState(QByteArray::fromBase64(QByteArray::fromStdString(state_setting)));
header->setSectionHidden(GameListModel::Column_Cover, true);
// Enforce at least one column is visible immediately after loading.
// This handles cases where a config (perhaps from an older version) has 0 columns and
// no games are visible to be changed (such as per-game config) or played as you can't click on any.
// Will automatically repair a broken header state from config (PCSX2.ini) file.
ensureMinimumOneColumnVisible();
}
void GameListWidget::ensureMinimumOneColumnVisible()
+44 -4
View File
@@ -873,6 +873,26 @@ void MainWindow::saveStateToConfig()
changed = true;
}
if (Host::ContainsBaseSettingValue("UI", "MainWindowMaximized"))
{
const bool maximized = Host::GetBaseBoolSettingValue("UI", "MainWindowMaximized");
if (maximized != isMaximized())
{
Host::SetBaseBoolSettingValue("UI", "MainWindowMaximized", isMaximized());
changed = true;
}
}
if (Host::ContainsBaseSettingValue("UI", "MainWindowFullscreen"))
{
const bool fullscreen = Host::GetBaseBoolSettingValue("UI", "MainWindowFullscreen");
if (fullscreen != isFullScreen())
{
Host::SetBaseBoolSettingValue("UI", "MainWindowFullscreen", isFullScreen());
changed = true;
}
}
if (changed)
Host::CommitBaseSettingChanges();
}
@@ -901,6 +921,24 @@ void MainWindow::restoreStateFromConfig()
m_ui.actionViewStatusBar->setChecked(!m_ui.statusBar->isHidden());
}
}
{
if (Host::ContainsBaseSettingValue("UI", "MainWindowMaximized"))
{
const bool maximized = Host::GetBaseBoolSettingValue("UI", "MainWindowMaximized");
if (maximized)
showMaximized();
}
}
{
if (Host::ContainsBaseSettingValue("UI", "MainWindowFullscreen"))
{
const bool fullscreen = Host::GetBaseBoolSettingValue("UI", "MainWindowFullscreen");
if (fullscreen)
showFullScreen();
}
}
}
void MainWindow::updateEmulationActions(bool starting, bool running, bool stopping)
@@ -1114,7 +1152,7 @@ bool MainWindow::shouldHideMainWindow() const
{
// NOTE: We can't use isRenderingToMain() here, because this happens post-fullscreen-switch.
return (Host::GetBoolSettingValue("UI", "HideMainWindowWhenRunning", false) && !g_emu_thread->shouldRenderToMain()) ||
(g_emu_thread->shouldRenderToMain() && (isRenderingFullscreen() || m_is_temporarily_windowed)) ||
(g_emu_thread->shouldRenderToMain() && (isRenderingFullscreen() || g_emu_thread->isExclusiveFullscreen() || m_is_temporarily_windowed)) ||
Host::InNoGUIMode();
}
@@ -1714,7 +1752,7 @@ void MainWindow::onStartFullscreenUITriggered()
void MainWindow::onFullscreenUIStateChange(bool running)
{
m_ui.actionStartFullscreenUI->setText(running ? tr("Stop Big Picture Mode") : tr("Start Big Picture Mode"));
m_ui.actionStartFullscreenUI->setText(running ? tr("Stop Big Picture Mode") : tr("Start Big Picture &Mode"));
m_ui.actionToolbarStartFullscreenUI->setText(running ? tr("Exit Big Picture", "In Toolbar") : tr("Big Picture", "In Toolbar"));
}
@@ -2538,6 +2576,7 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool recreate_window,
return std::nullopt;
}
m_display_is_exclusive_fullscreen = g_emu_thread->isExclusiveFullscreen();
g_emu_thread->connectDisplaySignals(m_display_surface);
updateWindowTitle();
@@ -2582,7 +2621,7 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main)
#ifdef DISPLAY_SURFACE_WINDOW
if (isVisible() && g_emu_thread->shouldRenderToMain())
m_display_surface->setPosition(screen()->availableGeometry().topLeft());
m_display_surface->setGeometry(screen()->geometry());
else
restoreDisplayWindowGeometryFromConfig();
@@ -2709,6 +2748,7 @@ void MainWindow::releaseRenderWindow()
// Now we can safely destroy the display window.
destroyDisplayWidget(true);
m_display_created = false;
m_display_is_exclusive_fullscreen = false;
m_ui.actionViewSystemDisplay->setEnabled(false);
m_ui.actionFullscreen->setEnabled(false);
@@ -2719,7 +2759,7 @@ void MainWindow::destroyDisplayWidget(bool show_game_list)
if (!m_display_surface)
return;
if (!m_display_surface->isFullScreen() && !isRenderingToMain())
if (!(m_display_surface->isFullScreen() || m_display_is_exclusive_fullscreen) && !isRenderingToMain())
saveDisplayWindowGeometryToConfig();
if (isRenderingToMain())
+1
View File
@@ -317,6 +317,7 @@ private:
QMenu* m_settings_toolbar_menu = nullptr;
bool m_display_created = false;
bool m_display_is_exclusive_fullscreen = false;
bool m_relative_mouse_mode = false;
bool m_hide_mouse_cursor = false;
bool m_was_paused_on_surface_loss = false;
+9 -2
View File
@@ -2086,7 +2086,8 @@ void QtHost::PrintCommandLineHelp(const std::string_view progname)
std::fprintf(stderr, " -version: Displays version information and exits.\n");
std::fprintf(stderr, " -batch: Enables batch mode (exits after shutting down).\n");
std::fprintf(stderr, " -nogui: Hides main window while running (implies batch mode).\n");
std::fprintf(stderr, " -portable: Force enable portable mode to store data in local PCSX2 path instead of the default configuration path.\n");
std::fprintf(stderr, " -portable: Force enable portable mode to store data in local PCSX2 path instead of the default configuration path. Overrides '-datapath'.\n");
std::fprintf(stderr, " -datapath <path>: Specify the directory to be used for all application data.\n");
std::fprintf(stderr, " -elf <file>: Overrides the boot ELF with the specified filename.\n");
std::fprintf(stderr, " -gameargs <string>: passes the specified quoted space-delimited string of launch arguments.\n");
std::fprintf(stderr, " -disc <path>: Uses the specified host DVD drive as a source.\n");
@@ -2165,6 +2166,12 @@ bool QtHost::ParseCommandLineOptions(const QStringList& args, std::shared_ptr<VM
EmuConfig.IsPortableMode = true;
continue;
}
else if (CHECK_ARG_PARAM(QStringLiteral("-datapath")))
{
std::string path = (++it)->toStdString();
EmuConfig.CustomDataPath = path;
continue;
}
else if (CHECK_ARG(QStringLiteral("-fastboot")))
{
AutoBoot(autoboot)->fast_boot = true;
@@ -2297,7 +2304,7 @@ bool QtHost::ParseCommandLineOptions(const QStringList& args, std::shared_ptr<VM
Console.Warning("Skipping autoboot due to no boot parameters.");
autoboot.reset();
}
if(autoboot && autoboot->start_turbo.value_or(false) && autoboot->start_unlimited.value_or(false))
{
Console.Warning("Both turbo and unlimited frame limit modes requested. Using unlimited.");
+15 -26
View File
@@ -23,6 +23,7 @@
#include <QtWidgets/QSpinBox>
#include "common/FileSystem.h"
#include "common/HostSys.h"
#include "common/Path.h"
#include "pcsx2/Config.h"
@@ -1261,8 +1262,8 @@ namespace SettingWidgetBinder
widget->connect(widget, &QLineEdit::editingFinished, widget, std::move(value_changed));
}
static inline void BindWidgetToFileSetting(SettingsInterface* sif, QLineEdit* widget, QAbstractButton* browse_button,
QAbstractButton* open_button, QAbstractButton* reset_button, std::string section, std::string key, std::string default_value,
static inline void BindWidgetToAudioFileSetting(SettingsInterface* sif, QLineEdit* widget, QAbstractButton* browse_button,
QAbstractButton* preview_button, QAbstractButton* reset_button, std::string section, std::string key, std::string default_value,
const QString& filter, bool allow_pergame = false, bool use_relative = true)
{
using Accessor = SettingAccessor<QLineEdit>;
@@ -1296,40 +1297,25 @@ namespace SettingWidgetBinder
Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), relative_path.c_str());
}
else
{
Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), new_value.c_str());
}
if (!FileSystem::FileExists(new_value.c_str()))
{
QMessageBox::critical(QtUtils::GetRootWidget(widget), qApp->translate("SettingWidgetBinder", "Error"),
qApp->translate("SettingWidgetBinder", "File cannot be found."));
}
Host::CommitBaseSettingChanges();
return;
}
else
{
QMessageBox::critical(QtUtils::GetRootWidget(widget), qApp->translate("SettingWidgetBinder", "Error"),
qApp->translate("SettingWidgetBinder", "File path cannot be empty."));
}
Host::RemoveBaseSettingValue(section.c_str(), key.c_str());
// reset to old value
std::string current_path(Host::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value.c_str()));
if (current_path.empty())
current_path = default_value;
else if (use_relative && !Path::IsAbsolute(current_path))
current_path = Path::Canonicalize(Path::Combine(EmuFolders::DataRoot, current_path));
widget->setText(QString::fromStdString(current_path));
};
if (browse_button)
{
QObject::connect(browse_button, &QAbstractButton::clicked, browse_button, [widget, key, value_changed, filter]() {
const QString path(QDir::toNativeSeparators(QFileDialog::getOpenFileName(QtUtils::GetRootWidget(widget),
qApp->translate("SettingWidgetBinder", "Select File"), QString(), filter)));
qApp->translate("SettingWidgetBinder", "Select Audio File"), QString(), filter)));
if (path.isEmpty())
return;
@@ -1337,19 +1323,22 @@ namespace SettingWidgetBinder
value_changed();
});
}
if (open_button)
if (preview_button)
{
QObject::connect(open_button, &QAbstractButton::clicked, open_button, [widget]() {
QString path(Accessor::getStringValue(widget));
if (!path.isEmpty())
QtUtils::OpenURL(QtUtils::GetRootWidget(widget), QUrl::fromLocalFile(path));
QObject::connect(preview_button, &QAbstractButton::clicked, preview_button, [widget, default_value = std::move(default_value)]() {
const QByteArray path = widget->text().toUtf8();
Common::PlaySoundAsync(
(path.isEmpty()
? default_value
: path.constData()).c_str()
);
});
}
if (reset_button)
{
QObject::connect(
reset_button, &QAbstractButton::clicked, reset_button, [widget, default_value = std::move(default_value), value_changed]() {
widget->setText(QString::fromStdString(default_value));
reset_button, &QAbstractButton::clicked, reset_button, [widget, value_changed]() {
widget->clear();
value_changed();
});
}
@@ -17,7 +17,10 @@
#include <QtCore/QDateTime>
#include <QtWidgets/QMessageBox>
const char* AchievementSettingsWidget::AUDIO_FILE_FILTER = QT_TRANSLATE_NOOP("AchievementSettingsWidget", "WAV Audio Files (*.wav)");
static constexpr const char* AUDIO_FILE_FILTER = QT_TRANSLATE_NOOP("AchievementSettingsWidget", "WAV Audio Files (*.wav)");
static constexpr const char* DEFAULT_INFO_SOUND_NAME = "sounds/achievements/message.wav";
static constexpr const char* DEFAULT_UNLOCK_SOUND_NAME = "sounds/achievements/unlock.wav";
static constexpr const char* DEFAULT_LBSUBMIT_SOUND_NAME = "sounds/achievements/lbsubmit.wav";
AchievementSettingsWidget::AchievementSettingsWidget(SettingsWindow* settings_dialog, QWidget* parent)
: SettingsWidget(settings_dialog, parent)
@@ -44,9 +47,9 @@ AchievementSettingsWidget::AchievementSettingsWidget(SettingsWindow* settings_di
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.achievementNotificationsDuration, "Achievements", "NotificationsDuration", Pcsx2Config::AchievementsOptions::DEFAULT_NOTIFICATION_DURATION);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.leaderboardNotificationsDuration, "Achievements", "LeaderboardsDuration", Pcsx2Config::AchievementsOptions::DEFAULT_LEADERBOARD_DURATION);
SettingWidgetBinder::BindWidgetToFileSetting(sif, m_ui.notificationSoundPath, m_ui.notificationSoundBrowse, m_ui.notificationSoundOpen, m_ui.notificationSoundReset, "Achievements", "InfoSoundName", Path::Combine(EmuFolders::Resources, EmuConfig.Achievements.DEFAULT_INFO_SOUND_NAME), qApp->translate("AchievementSettingsWidget", AUDIO_FILE_FILTER), true, false);
SettingWidgetBinder::BindWidgetToFileSetting(sif, m_ui.unlockSoundPath, m_ui.unlockSoundBrowse, m_ui.unlockSoundOpen, m_ui.unlockSoundReset, "Achievements", "UnlockSoundName", Path::Combine(EmuFolders::Resources, EmuConfig.Achievements.DEFAULT_UNLOCK_SOUND_NAME), qApp->translate("AchievementSettingsWidget", AUDIO_FILE_FILTER), true, false);
SettingWidgetBinder::BindWidgetToFileSetting(sif, m_ui.lbSoundPath, m_ui.lbSoundBrowse, m_ui.lbSoundOpen, m_ui.lbSoundReset, "Achievements", "LBSubmitSoundName", Path::Combine(EmuFolders::Resources, EmuConfig.Achievements.DEFAULT_LBSUBMIT_SOUND_NAME), qApp->translate("AchievementSettingsWidget", AUDIO_FILE_FILTER), true, false);
SettingWidgetBinder::BindWidgetToAudioFileSetting(sif, m_ui.notificationSoundPath, m_ui.notificationSoundBrowse, m_ui.notificationSoundOpen, m_ui.notificationSoundReset, "Achievements", "InfoSoundName", Path::Combine(EmuFolders::Resources, DEFAULT_INFO_SOUND_NAME), qApp->translate("AchievementSettingsWidget", AUDIO_FILE_FILTER), true, false);
SettingWidgetBinder::BindWidgetToAudioFileSetting(sif, m_ui.unlockSoundPath, m_ui.unlockSoundBrowse, m_ui.unlockSoundOpen, m_ui.unlockSoundReset, "Achievements", "UnlockSoundName", Path::Combine(EmuFolders::Resources, DEFAULT_UNLOCK_SOUND_NAME), qApp->translate("AchievementSettingsWidget", AUDIO_FILE_FILTER), true, false);
SettingWidgetBinder::BindWidgetToAudioFileSetting(sif, m_ui.lbSoundPath, m_ui.lbSoundBrowse, m_ui.lbSoundOpen, m_ui.lbSoundReset, "Achievements", "LBSubmitSoundName", Path::Combine(EmuFolders::Resources, DEFAULT_LBSUBMIT_SOUND_NAME), qApp->translate("AchievementSettingsWidget", AUDIO_FILE_FILTER), true, false);
dialog()->registerWidgetHelp(m_ui.enable, tr("Enable Achievements"), tr("Unchecked"), tr("When enabled and logged in, PCSX2 will scan for achievements on startup."));
dialog()->registerWidgetHelp(m_ui.hardcoreMode, tr("Enable Hardcore Mode"), tr("Unchecked"), tr("\"Challenge\" mode for achievements, including leaderboard tracking. Disables save state, cheats, and slowdown functions."));
@@ -117,6 +120,10 @@ void AchievementSettingsWidget::updateEnableState()
const bool info = enabled && sound && dialog()->getEffectiveBoolValue("Achievements", "InfoSound", true);
const bool unlock = enabled && sound && dialog()->getEffectiveBoolValue("Achievements", "UnlockSound", true);
const bool lbsound = enabled && sound && dialog()->getEffectiveBoolValue("Achievements", "LBSubmitSound", true);
m_ui.viewProfile->setEnabled(enabled);
m_ui.loginButton->setEnabled(enabled);
m_ui.hardcoreMode->setEnabled(enabled);
m_ui.achievementNotifications->setEnabled(enabled);
m_ui.leaderboardNotifications->setEnabled(enabled);
@@ -26,7 +26,6 @@ private Q_SLOTS:
private:
void updateLoginState();
static const char* AUDIO_FILE_FILTER;
Ui::AchievementSettingsWidget m_ui;
};
+128 -106
View File
@@ -11,13 +11,118 @@
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="loginBox">
<property name="title">
<string>Account</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0">
<item>
<layout class="QGridLayout" name="acccountLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="enable">
<property name="text">
<string>Enable Achievements</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="loginStatus">
<property name="text">
<string>Username:
Login token generated at:</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
</property>
<property name="textInteractionFlags">
<set>Qt::TextInteractionFlag::TextBrowserInteraction</set>
</property>
<property name="buddy">
<cstring>viewProfile</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="achievementButtons">
<item>
<widget class="QPushButton" name="viewProfile">
<property name="text">
<string>View Profile...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="loginButton">
<property name="text">
<string>Login...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gameInfoBox">
<property name="minimumSize">
<size>
<width>0</width>
<height>75</height>
</size>
</property>
<property name="title">
<string>Game Info</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="gameInfo">
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
</property>
<property name="textInteractionFlags">
<set>Qt::TextInteractionFlag::TextBrowserInteraction</set>
</property>
<property name="buddy">
<cstring>viewProfile</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="rcheevosDisclaimer">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;PCSX2 uses RetroAchievements as an achievement database and for tracking progress. To use achievements, please sign up for an account at &lt;a href=&quot;https://retroachievements.org/&quot;&gt;retroachievements.org&lt;/a&gt;.&lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;To view the achievement list in-game, press the hotkey for &lt;span style=&quot; font-weight:600;&quot;&gt;Open Pause Menu&lt;/span&gt; and select &lt;span style=&quot; font-weight:600;&quot;&gt;Achievements&lt;/span&gt; from the menu.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::TextFormat::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="margin">
<number>8</number>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="buddy">
<cstring>enable</cstring>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="settingsBox">
<property name="title">
<string>Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="2" column="0">
<item row="1" column="1">
<widget class="QCheckBox" name="unofficialAchievements">
<property name="text">
<string>Test Unofficial Achievements</string>
@@ -25,23 +130,16 @@
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="hardcoreMode">
<widget class="QCheckBox" name="spectatorMode">
<property name="text">
<string>Enable Hardcore Mode</string>
<string>Enable Spectator Mode</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="enable">
<widget class="QCheckBox" name="hardcoreMode">
<property name="text">
<string>Enable Achievements</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="spectatorMode">
<property name="text">
<string>Enable Spectator Mode</string>
<string>Enable Hardcore Mode</string>
</property>
</widget>
</item>
@@ -316,10 +414,18 @@
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="5" column="0">
<widget class="QLineEdit" name="lbSoundPath"/>
<widget class="QLineEdit" name="lbSoundPath">
<property name="placeholderText">
<string>Default Sound Effect</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLineEdit" name="notificationSoundPath"/>
<widget class="QLineEdit" name="notificationSoundPath">
<property name="placeholderText">
<string>Default Sound Effect</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QPushButton" name="unlockSoundBrowse">
@@ -350,7 +456,11 @@
</widget>
</item>
<item row="3" column="0">
<widget class="QLineEdit" name="unlockSoundPath"/>
<widget class="QLineEdit" name="unlockSoundPath">
<property name="placeholderText">
<string>Default Sound Effect</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QPushButton" name="lbSoundBrowse">
@@ -411,94 +521,6 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="loginBox">
<property name="title">
<string>Account</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
<item>
<widget class="QLabel" name="loginStatus">
<property name="text">
<string>Username:
Login token generated at:</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
</property>
<property name="buddy">
<cstring>viewProfile</cstring>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="achievementButtons">
<item>
<widget class="QPushButton" name="viewProfile">
<property name="text">
<string>View Profile...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="loginButton">
<property name="text">
<string>Login...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gameInfoBox">
<property name="minimumSize">
<size>
<width>0</width>
<height>75</height>
</size>
</property>
<property name="title">
<string>Game Info</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="gameInfo">
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="rcheevosDisclaimer">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;PCSX2 uses RetroAchievements as an achievement database and for tracking progress. To use achievements, please sign up for an account at &lt;a href=&quot;https://retroachievements.org/&quot;&gt;retroachievements.org&lt;/a&gt;.&lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;To view the achievement list in-game, press the hotkey for &lt;span style=&quot; font-weight:600;&quot;&gt;Open Pause Menu&lt;/span&gt; and select &lt;span style=&quot; font-weight:600;&quot;&gt;Achievements&lt;/span&gt; from the menu.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::TextFormat::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="margin">
<number>8</number>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
@@ -516,9 +538,11 @@ Login token generated at:</string>
</widget>
<tabstops>
<tabstop>enable</tabstop>
<tabstop>viewProfile</tabstop>
<tabstop>loginButton</tabstop>
<tabstop>hardcoreMode</tabstop>
<tabstop>encoreMode</tabstop>
<tabstop>spectatorMode</tabstop>
<tabstop>encoreMode</tabstop>
<tabstop>unofficialAchievements</tabstop>
<tabstop>achievementNotifications</tabstop>
<tabstop>achievementNotificationsDuration</tabstop>
@@ -544,8 +568,6 @@ Login token generated at:</string>
<tabstop>lbSoundBrowse</tabstop>
<tabstop>lbSoundOpen</tabstop>
<tabstop>lbSoundReset</tabstop>
<tabstop>viewProfile</tabstop>
<tabstop>loginButton</tabstop>
</tabstops>
<resources>
<include location="../resources/resources.qrc"/>
+6 -18
View File
@@ -70,23 +70,9 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* settings_dialog
std::clamp(Host::GetBaseIntSettingValue("EmuCore/Speedhacks", "EECycleRate", DEFAULT_EE_CYCLE_RATE) - MINIMUM_EE_CYCLE_RATE,
0, MAXIMUM_EE_CYCLE_RATE - MINIMUM_EE_CYCLE_RATE))));
// Disable cheats, use the cheats panel instead (move fastcvd up in its spot).
const int count = m_ui.systemSettingsLayout->count();
for (int i = 0; i < count; i++)
{
QLayoutItem* item = m_ui.systemSettingsLayout->itemAt(i);
if (item && item->widget() == m_ui.cheats)
{
int row, col, rowSpan, colSpan;
m_ui.systemSettingsLayout->getItemPosition(i, &row, &col, &rowSpan, &colSpan);
delete m_ui.systemSettingsLayout->takeAt(i);
m_ui.systemSettingsLayout->removeWidget(m_ui.fastCDVD);
m_ui.systemSettingsLayout->addWidget(m_ui.fastCDVD, row, col);
delete m_ui.cheats;
m_ui.cheats = nullptr;
break;
}
}
// Disable cheats, use the cheats panel instead
m_ui.systemSettingsLayout->removeWidget(m_ui.cheats);
m_ui.cheats->hide();
}
else
{
@@ -96,9 +82,11 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* settings_dialog
// Allow for FastCDVD for per-game settings only
m_ui.systemSettingsLayout->removeWidget(m_ui.fastCDVD);
m_ui.fastCDVD->deleteLater();
m_ui.fastCDVD->hide();
}
reflowCheckBoxes(m_ui.systemSettingsLayout);
const std::optional<int> cycle_rate =
dialog()->getIntValue("EmuCore/Speedhacks", "EECycleRate", sif ? std::nullopt : std::optional<int>(DEFAULT_EE_CYCLE_RATE));
m_ui.eeCycleRate->setCurrentIndex(cycle_rate.has_value() ? (std::clamp(cycle_rate.value(), MINIMUM_EE_CYCLE_RATE, MAXIMUM_EE_CYCLE_RATE) + (0 - MINIMUM_EE_CYCLE_RATE) + static_cast<int>(dialog()->isPerGameSettings())) : 0);
@@ -270,7 +270,14 @@
</widget>
</item>
<item row="7" column="0" colspan="2">
<layout class="QGridLayout" name="hwFixesLayout">
<layout class="QGridLayout" name="hwFixesLayout">
<item row="4" column="1">
<widget class="QCheckBox" name="drawBuffering">
<property name="text">
<string>Draw Buffering</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="estimateTextureRegion">
<property name="text">
@@ -65,6 +65,13 @@
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="hwAA1">
<property name="text">
<string>AA1</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="enableHWFixes">
<property name="text">
<string>Manual Hardware Renderer Fixes</string>
@@ -235,6 +242,7 @@
<tabstop>blending</tabstop>
<tabstop>mipmapping</tabstop>
<tabstop>accurateAlphaTest</tabstop>
<tabstop>hwAA1</tabstop>
<tabstop>enableHWFixes</tabstop>
</tabstops>
<resources/>
@@ -112,6 +112,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_hw.dithering, "EmuCore/GS", "dithering_ps2", 2);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_hw.mipmapping, "EmuCore/GS", "hw_mipmap", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_hw.accurateAlphaTest, "EmuCore/GS", "HWAccurateAlphaTest", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_hw.hwAA1, "EmuCore/GS", "HWAA1", false);
SettingWidgetBinder::BindWidgetToIntSetting(
sif, m_hw.blending, "EmuCore/GS", "accurate_blending_unit", static_cast<int>(AccBlendLevel::Basic));
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_hw.enableHWFixes, "EmuCore/GS", "UserHacks", false);
@@ -152,6 +153,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
sif, m_fixes.limit24BitDepth, "EmuCore/GS", "UserHacks_Limit24BitDepth", static_cast<int>(GSLimit24BitDepth::Disabled));
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_fixes.readTCOnClose, "EmuCore/GS", "UserHacks_ReadTCOnClose", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_fixes.estimateTextureRegion, "EmuCore/GS", "UserHacks_EstimateTextureRegion", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_fixes.drawBuffering, "EmuCore/GS", "UserHacks_DrawBuffering", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_fixes.gpuPaletteConversion, "EmuCore/GS", "paltex", false);
connect(m_fixes.cpuSpriteRenderBW, &QComboBox::currentIndexChanged, this,
&GraphicsSettingsWidget::onCPUSpriteRenderBWChanged);
@@ -491,6 +493,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
dialog()->registerWidgetHelp(
m_hw.accurateAlphaTest, tr("Accurate Alpha Test"), tr("Unchecked"), tr("Enables accurate alpha testing, which some games require to render correctly. This may require more draw calls and result in a speed penalty."));
dialog()->registerWidgetHelp(
m_hw.hwAA1, tr("AA1"), tr("Unchecked"), tr("Enables AA1 (PS2 antialiasing), which some games require to render correctly. This may result in a heavy performance penalty."));
dialog()->registerWidgetHelp(
m_hw.textureFiltering, tr("Texture Filtering"), tr("Bilinear (PS2)"),
tr("Changes what filtering algorithm is used to map textures to surfaces.<br> "
@@ -610,6 +615,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* settings_dialog,
dialog()->registerWidgetHelp(m_fixes.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)."));
dialog()->registerWidgetHelp(m_fixes.drawBuffering, tr("Draw Buffering"), tr("Unchecked"),
tr("Attempts to reduce draw calls in games which do heavy context switching for blending purposes."));
}
// Upscaling Fixes tab
+1 -5
View File
@@ -184,10 +184,6 @@ void OSDSettingsWidget::onBrowseOsdFontPathClicked()
saveOsdFontPathSetting(QDir::toNativeSeparators(selected));
});
connect(m_font_picker, &QObject::destroyed, this, [this]() {
m_font_picker = nullptr;
});
m_font_picker->show();
}
@@ -313,4 +309,4 @@ void OSDSettingsWidget::onDeselectAllClicked()
m_ui.warnAboutUnsafeSettings->setChecked(true);
}
#include "moc_OSDSettingsWidget.cpp"
#include "moc_OSDSettingsWidget.cpp"
+26
View File
@@ -3,6 +3,7 @@
#include "SettingsWidget.h"
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QScrollArea>
#include <QtWidgets/QScrollBar>
#include <QtWidgets/QTabBar>
@@ -115,4 +116,29 @@ void SettingsWidget::updateTabMargins(QScrollArea* scroll_area)
}
}
void SettingsWidget::reflowCheckBoxes(QGridLayout* layout)
{
std::vector<QWidget*> widgets;
for (int row = 0; row < layout->rowCount(); row++)
{
for (int column = 0; column < layout->columnCount(); column++)
{
QLayoutItem* item = layout->itemAtPosition(row, column);
if (item && item->widget())
{
widgets.push_back(item->widget());
layout->removeWidget(item->widget());
}
}
}
for (size_t i = 0; i < widgets.size(); i++)
{
int row = static_cast<int>(i / 2);
int column = static_cast<int>(i % 2);
layout->addWidget(widgets[i], row, column);
}
}
#include "moc_SettingsWidget.cpp"
+5
View File
@@ -7,6 +7,7 @@
#include <QtWidgets/QWidget>
class QGridLayout;
class QScrollArea;
class QTabWidget;
@@ -45,6 +46,10 @@ protected:
QWidget* addTab(QString name, QWidget* contents, bool custom_margins = false);
void setTabVisible(QWidget* tab, bool is_visible, QWidget* switch_to = nullptr);
// Rearrange the checkboxes in a group so that there aren't any gaps. To be
// used if checkboxes have been hidden programmatically.
void reflowCheckBoxes(QGridLayout* layout);
private:
void updateTabMargins(QScrollArea* scroll_area);
+103 -58
View File
@@ -14,16 +14,10 @@
#include "VMManager.h"
#if defined(_WIN32)
#include "common/RedtapeWindows.h"
#include "common/RedtapeWilCom.h"
#include <shlobj.h>
#include <winnls.h>
#include <shobjidl.h>
#include <objbase.h>
#include <objidl.h>
#include <shlguid.h>
#include <comdef.h>
#include <wrl/client.h>
#endif
ShortcutCreationDialog::ShortcutCreationDialog(QWidget* parent, const QString& title, const QString& path)
@@ -41,10 +35,10 @@ ShortcutCreationDialog::ShortcutCreationDialog(QWidget* parent, const QString& t
m_ui.shortcutStartMenu->setText(tr("Application Launcher"));
#endif
QButtonGroup* buttonGroup = new QButtonGroup(this);
buttonGroup->setExclusive(true);
buttonGroup->addButton(m_ui.fastForwardTurboOption);
buttonGroup->addButton(m_ui.fastForwardUnlimitedOption);
QButtonGroup* speedGroup = new QButtonGroup(this);
speedGroup->setExclusive(true);
speedGroup->addButton(m_ui.fastForwardTurboOption);
speedGroup->addButton(m_ui.fastForwardUnlimitedOption);
m_ui.fastForwardTurboOption->setChecked(true);
connect(m_ui.overrideBootELFButton, &QPushButton::clicked, [&]() {
@@ -75,6 +69,37 @@ ShortcutCreationDialog::ShortcutCreationDialog(QWidget* parent, const QString& t
m_ui.loadStateIndex->setMaximum(VMManager::NUM_SAVE_STATE_SLOTS);
m_ui.iconPreview->setPixmap(QtHost::GetAppIcon().pixmap(m_ui.iconPreview->size()));
m_ui.resetIconButton->setEnabled(false);
connect(m_ui.browseIconButton, &QPushButton::clicked, [&]() {
#if defined(_WIN32)
const QString filter = tr("Icon Files (*.ico);;All Files (*.*)");
#else
const QString filter = tr("Image Files (*.png *.jpg *.svg *.webp);;All Files (*.*)");
#endif
const QString icon_file = QFileDialog::getOpenFileName(this, tr("Select Icon"), QString(), filter);
if (!icon_file.isEmpty())
{
QPixmap pixmap(icon_file);
if (pixmap.isNull())
{
QMessageBox::critical(this, tr("Invalid Icon"), tr("The selected file could not be loaded as an icon."));
return;
}
m_ui.iconPath->setText(Path::ToNativePath(icon_file.toStdString()).c_str());
m_ui.iconPreview->setPixmap(pixmap.scaled(m_ui.iconPreview->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
m_ui.resetIconButton->setEnabled(true);
}
});
connect(m_ui.resetIconButton, &QPushButton::clicked, [&]() {
m_ui.iconPath->clear();
m_ui.iconPreview->setPixmap(QtHost::GetAppIcon().pixmap(m_ui.iconPreview->size()));
m_ui.resetIconButton->setEnabled(false);
});
if (std::getenv("container"))
{
m_ui.portableModeToggle->setEnabled(false);
@@ -107,7 +132,7 @@ ShortcutCreationDialog::ShortcutCreationDialog(QWidget* parent, const QString& t
if (m_ui.loadStateIndexToggle->isChecked())
{
const s32 load_state_index = m_ui.loadStateIndex->value();
if (load_state_index >= 1 && load_state_index <= VMManager::NUM_SAVE_STATE_SLOTS)
if (load_state_index > 0 && load_state_index <= VMManager::NUM_SAVE_STATE_SLOTS)
{
args.push_back("-state");
args.push_back(StringUtil::ToChars(load_state_index));
@@ -135,14 +160,15 @@ ShortcutCreationDialog::ShortcutCreationDialog(QWidget* parent, const QString& t
}
std::string custom_args = m_ui.customArgsInput->text().toStdString();
std::string icon_path = m_ui.iconPath->text().toStdString();
ShortcutCreationDialog::CreateShortcut(title.toStdString(), path.toStdString(), args, custom_args, m_ui.shortcutDesktop->isChecked());
ShortcutCreationDialog::CreateShortcut(title.toStdString(), path.toStdString(), args, custom_args, icon_path, m_ui.shortcutDesktop->isChecked());
accept();
});
}
void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::string game_path, std::vector<std::string> passed_cli_args, std::string custom_args, bool is_desktop)
void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::string game_path, std::vector<std::string> passed_cli_args, std::string custom_args, const std::string icon_path, bool is_desktop)
{
#if defined(_WIN32)
if (name.empty())
@@ -165,10 +191,9 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
// https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath
// https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid
std::string link_file;
if (PWSTR directory; SUCCEEDED(SHGetKnownFolderPath(is_desktop ? FOLDERID_Desktop : FOLDERID_Programs, 0, NULL, &directory)))
if (wil::unique_cotaskmem_string directory; SUCCEEDED(SHGetKnownFolderPath(is_desktop ? FOLDERID_Desktop : FOLDERID_Programs, 0, NULL, &directory)))
{
std::string directory_utf8 = StringUtil::WideStringToUTF8String(directory);
CoTaskMemFree(directory);
std::string directory_utf8 = StringUtil::WideStringToUTF8String(directory.get());
if (is_desktop)
link_file = Path::ToNativePath(fmt::format("{}/{}.lnk", directory_utf8, clean_name));
@@ -186,7 +211,6 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
}
else
{
CoTaskMemFree(directory);
QMessageBox::critical(this, tr("Failed to create shortcut"), is_desktop ? tr("'Desktop' directory not found") : tr("User's 'Start Menu\\Programs' directory not found"), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
return;
}
@@ -205,7 +229,7 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
if (!lossless)
{
QMessageBox::warning(this, tr("Failed to create shortcut"), tr("File path contains invalid character(s)."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
QMessageBox::critical(this, tr("Failed to create shortcut"), tr("File path contains invalid character(s)."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
return;
}
@@ -231,23 +255,19 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
return;
}
Microsoft::WRL::ComPtr<IShellLink> pShellLink;
Microsoft::WRL::ComPtr<IPersistFile> pPersistFile;
wil::unique_couninitialize_call co_cleanup;
const auto cleanup = [&](bool return_value, const QString& fail_reason) -> bool {
if (!return_value)
{
Console.ErrorFmt("Failed to create shortcut: {}", fail_reason.toStdString());
QMessageBox::critical(this, tr("Failed to create shortcut"), fail_reason, QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
}
CoUninitialize();
return return_value;
const auto report_error = [&](const QString& reason) {
Console.ErrorFmt("Failed to create shortcut: {}", reason.toStdString());
QMessageBox::critical(this, tr("Failed to create shortcut"), reason, QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
};
res = CoCreateInstance(__uuidof(ShellLink), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pShellLink));
if (FAILED(res))
wil::com_ptr_nothrow<IShellLink> pShellLink = wil::CoCreateInstanceNoThrow<IShellLink>(CLSID_ShellLink);
wil::com_ptr_nothrow<IPersistFile> pPersistFile;
if (!pShellLink)
{
cleanup(false, tr("CoCreateInstance failed"));
report_error(tr("CoCreateInstance failed"));
return;
}
@@ -256,7 +276,7 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
res = pShellLink->SetPath(target_file.c_str());
if (FAILED(res))
{
cleanup(false, tr("SetPath failed (%1)").arg(str_error(res)));
report_error(tr("SetPath failed (%1)").arg(str_error(res)));
return;
}
@@ -265,7 +285,7 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
res = pShellLink->SetWorkingDirectory(working_dir.c_str());
if (FAILED(res))
{
cleanup(false, tr("SetWorkingDirectory failed (%1)").arg(str_error(res)));
report_error(tr("SetWorkingDirectory failed (%1)").arg(str_error(res)));
return;
}
@@ -276,26 +296,40 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
res = pShellLink->SetArguments(target_cli_args.c_str());
if (FAILED(res))
{
cleanup(false, tr("SetArguments failed (%1)").arg(str_error(res)));
report_error(tr("SetArguments failed (%1)").arg(str_error(res)));
return;
}
}
// Set the icon
std::string icon_path = Path::ToNativePath(Path::Combine(Path::GetDirectory(FileSystem::GetProgramPath()), "resources/icons/AppIconLarge.ico"));
const std::wstring w_icon_path = StringUtil::UTF8StringToWideString(icon_path);
std::string final_icon_path;
if (!icon_path.empty())
{
final_icon_path = Path::ToNativePath(icon_path);
if (!FileSystem::FileExists(final_icon_path.c_str()))
{
QMessageBox::critical(this, tr("Failed to create shortcut"), tr("The selected icon file does not exist."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
return;
}
}
else
{
final_icon_path = Path::ToNativePath(Path::Combine(Path::GetDirectory(FileSystem::GetProgramPath()), "resources/icons/AppIconLarge.ico"));
}
const std::wstring w_icon_path = StringUtil::UTF8StringToWideString(final_icon_path);
res = pShellLink->SetIconLocation(w_icon_path.c_str(), 0);
if (FAILED(res))
{
cleanup(false, tr("SetIconLocation failed (%1)").arg(str_error(res)));
report_error(tr("SetIconLocation failed (%1)").arg(str_error(res)));
return;
}
// Use the IPersistFile object to save the shell link
res = pShellLink.As(&pPersistFile);
res = pShellLink.query_to(&pPersistFile);
if (FAILED(res))
{
cleanup(false, tr("QueryInterface failed (%1)").arg(str_error(res)));
report_error(tr("QueryInterface failed (%1)").arg(str_error(res)));
return;
}
@@ -304,12 +338,10 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
res = pPersistFile->Save(w_link_file.c_str(), TRUE);
if (FAILED(res))
{
cleanup(false, tr("Failed to save the shortcut (%1)").arg(str_error(res)));
report_error(tr("Failed to save the shortcut (%1)").arg(str_error(res)));
return;
}
cleanup(true, {});
#else
if (name.empty())
@@ -365,25 +397,38 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
return;
}
// Copy PCSX2 icon
std::string icon_dest;
if (xdg_data_home)
icon_dest = fmt::format("{}/icons/hicolor/512x512/apps/", xdg_data_home);
else
icon_dest = fmt::format("{}/.local/share/icons/hicolor/512x512/apps/", home);
std::string icon_name;
if (is_flatpak) // Flatpak
if (!icon_path.empty())
{
executable_path = "flatpak run net.pcsx2.PCSX2";
icon_name = "net.pcsx2.PCSX2";
if (!FileSystem::FileExists(icon_path.c_str()))
{
QMessageBox::critical(this, tr("Failed to create shortcut"), tr("The selected icon file does not exist."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
return;
}
icon_name = icon_path;
}
else
{
icon_name = "PCSX2";
std::string icon_path = fmt::format("{}/{}.png", icon_dest, icon_name).c_str();
if (FileSystem::EnsureDirectoryExists(icon_dest.c_str(), true))
FileSystem::CopyFilePath(Path::Combine(EmuFolders::Resources, "icons/AppIconLarge.png").c_str(), icon_path.c_str(), false);
// Copy PCSX2 icon
std::string icon_dest;
if (xdg_data_home)
icon_dest = fmt::format("{}/icons/hicolor/512x512/apps/", xdg_data_home);
else
icon_dest = fmt::format("{}/.local/share/icons/hicolor/512x512/apps/", home);
if (is_flatpak) // Flatpak
{
executable_path = "flatpak run net.pcsx2.PCSX2";
icon_name = "net.pcsx2.PCSX2";
}
else
{
icon_name = "PCSX2";
std::string icon_path_dest = fmt::format("{}/{}.png", icon_dest, icon_name).c_str();
if (FileSystem::EnsureDirectoryExists(icon_dest.c_str(), true))
if (!FileSystem::FileExists(icon_path_dest.c_str()))
FileSystem::CopyFilePath(Path::Combine(EmuFolders::Resources, "icons/AppIconLarge.png").c_str(), icon_path_dest.c_str(), false);
}
}
// Shortcut CmdLine Args
@@ -393,7 +438,7 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
if (!lossless)
{
QMessageBox::warning(this, tr("Failed to create shortcut"), tr("File path contains invalid character(s)."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
QMessageBox::critical(this, tr("Failed to create shortcut"), tr("File path contains invalid character(s)."), QMessageBox::StandardButton::Ok, QMessageBox::StandardButton::Ok);
return;
}
+1 -1
View File
@@ -15,7 +15,7 @@ public:
~ShortcutCreationDialog() = default;
/// Create desktop shortcut for games
void CreateShortcut(const std::string name, const std::string game_path, std::vector<std::string> passed_cli_args, std::string custom_args, bool is_desktop);
void CreateShortcut(const std::string name, const std::string game_path, std::vector<std::string> passed_cli_args, std::string custom_args, const std::string icon_path, bool is_desktop);
/// Escapes the given string for use with command line arguments.
/// Returns a bool that indicates whether the escaping operation are lossless or not.
+97 -5
View File
@@ -10,11 +10,11 @@
<x>0</x>
<y>0</y>
<width>500</width>
<height>700</height>
<height>825</height>
</rect>
</property>
<layout class="QGridLayout" name="shortcutLayout">
<item row="4" column="0" colspan="2">
<item row="6" column="0" colspan="2">
<widget class="QGroupBox" name="launchArgsGroup">
<property name="title">
<string/>
@@ -291,7 +291,7 @@
</property>
</widget>
</item>
<item row="6" column="1">
<item row="8" column="1">
<widget class="QDialogButtonBox" name="dialogButtons">
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
@@ -301,7 +301,7 @@
</property>
</widget>
</item>
<item row="3" column="0">
<item row="5" column="0">
<widget class="QLabel" name="instruction">
<property name="text">
<string>Launch Options</string>
@@ -334,7 +334,7 @@
</layout>
</widget>
</item>
<item row="5" column="0">
<item row="7" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
@@ -347,11 +347,100 @@
</property>
</spacer>
</item>
<item row="3" column="0">
<widget class="QLabel" name="iconLabel">
<property name="text">
<string>Icon</string>
</property>
<property name="buddy">
<cstring>browseIconButton</cstring>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QGroupBox" name="iconGroup">
<property name="title">
<string/>
</property>
<layout class="QGridLayout" name="iconLayout">
<item row="1" column="2">
<widget class="QPushButton" name="resetIconButton">
<property name="toolTip">
<string>Reset to default application icon</string>
</property>
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="browseIconButton">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="2">
<widget class="QLabel" name="iconPreview">
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="contextMenuPolicy">
<enum>Qt::ContextMenuPolicy::NoContextMenu</enum>
</property>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::TextInteractionFlag::NoTextInteraction</set>
</property>
<property name="buddy">
<cstring>iconPath</cstring>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="iconPath">
<property name="readOnly">
<bool>true</bool>
</property>
<property name="placeholderText">
<string>Default PCSX2 Icon</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>shortcutDesktop</tabstop>
<tabstop>shortcutStartMenu</tabstop>
<tabstop>iconPath</tabstop>
<tabstop>browseIconButton</tabstop>
<tabstop>resetIconButton</tabstop>
<tabstop>portableModeToggle</tabstop>
<tabstop>overrideBootELFToggle</tabstop>
<tabstop>overrideBootELFPath</tabstop>
@@ -360,6 +449,9 @@
<tabstop>gameArgs</tabstop>
<tabstop>bootOptionToggle</tabstop>
<tabstop>bootOptionDropdown</tabstop>
<tabstop>fastForwardOptionToggle</tabstop>
<tabstop>fastForwardTurboOption</tabstop>
<tabstop>fastForwardUnlimitedOption</tabstop>
<tabstop>fullscreenMode</tabstop>
<tabstop>fullscreenModeDropdown</tabstop>
<tabstop>bigPictureModeToggle</tabstop>
File diff suppressed because it is too large Load Diff
+1
View File
@@ -39,6 +39,7 @@
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DepsIncludeDir)\SDL3</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DepsIncludeDir)\kddockwidgets-qt6</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\fmt\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\winwil\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\lzma\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)3rdparty\simpleini\include</AdditionalIncludeDirectories>
+112 -61
View File
@@ -63,6 +63,10 @@ namespace Achievements
static constexpr float LEADERBOARD_STARTED_NOTIFICATION_TIME = 3.0f;
static constexpr float LEADERBOARD_FAILED_NOTIFICATION_TIME = 3.0f;
static constexpr const char* DEFAULT_INFO_SOUND_NAME = "sounds/achievements/message.wav";
static constexpr const char* DEFAULT_UNLOCK_SOUND_NAME = "sounds/achievements/unlock.wav";
static constexpr const char* DEFAULT_LBSUBMIT_SOUND_NAME = "sounds/achievements/lbsubmit.wav";
static constexpr float INDICATOR_FADE_IN_TIME = 0.1f;
static constexpr float INDICATOR_FADE_OUT_TIME = 0.5f;
@@ -455,9 +459,9 @@ bool Achievements::Initialize()
SettingsInterface* secretsInterface = Host::Internal::GetSecretsSettingsLayer();
secretsInterface->SetStringValue("Achievements", "Token", oldToken.c_str());
secretsInterface->Save();
oldToken.clear();
auto baseLock = Host::GetSettingsLock();
SettingsInterface* baseInterface = Host::Internal::GetBaseSettingsLayer();
baseInterface->DeleteValue("Achievements", "Token");
@@ -540,7 +544,7 @@ void Achievements::UpdateNotificationPosition()
{
// Set notification position based on achievement settings
float horizontal_position, vertical_position, direction;
// Determine horizontal alignment
switch (EmuConfig.Achievements.NotificationPosition)
{
@@ -549,13 +553,13 @@ void Achievements::UpdateNotificationPosition()
case OsdOverlayPos::BottomLeft:
horizontal_position = 0.0f; // Left
break;
case OsdOverlayPos::TopCenter:
case OsdOverlayPos::Center:
case OsdOverlayPos::BottomCenter:
horizontal_position = 0.5f; // Center
break;
case OsdOverlayPos::TopRight:
case OsdOverlayPos::CenterRight:
case OsdOverlayPos::BottomRight:
@@ -563,7 +567,7 @@ void Achievements::UpdateNotificationPosition()
horizontal_position = 1.0f; // Right
break;
}
// Determine vertical alignment and stacking direction
switch (EmuConfig.Achievements.NotificationPosition)
{
@@ -573,14 +577,14 @@ void Achievements::UpdateNotificationPosition()
vertical_position = 0.15f; // Top area
direction = 1.0f; // Stack downward
break;
case OsdOverlayPos::CenterLeft:
case OsdOverlayPos::Center:
case OsdOverlayPos::CenterRight:
vertical_position = 0.5f; // Center
direction = 1.0f; // Stack downward
break;
case OsdOverlayPos::BottomLeft:
case OsdOverlayPos::BottomCenter:
case OsdOverlayPos::BottomRight:
@@ -589,7 +593,7 @@ void Achievements::UpdateNotificationPosition()
direction = -1.0f; // Stack upward
break;
}
ImGuiFullscreen::SetNotificationPosition(horizontal_position, vertical_position, direction);
}
@@ -1142,7 +1146,11 @@ void Achievements::DisplayAchievementSummary()
}
if (EmuConfig.Achievements.SoundEffects && EmuConfig.Achievements.InfoSound)
Common::PlaySoundAsync(EmuConfig.Achievements.InfoSoundName.c_str());
Common::PlaySoundAsync(
(EmuConfig.Achievements.InfoSoundName.empty()
? Path::Combine(EmuFolders::Resources, DEFAULT_INFO_SOUND_NAME)
: EmuConfig.Achievements.InfoSoundName).c_str()
);
}
void Achievements::DisplayHardcoreDeferredMessage()
@@ -1194,7 +1202,11 @@ void Achievements::HandleUnlockEvent(const rc_client_event_t* event)
}
if (EmuConfig.Achievements.SoundEffects && EmuConfig.Achievements.UnlockSound)
Common::PlaySoundAsync(EmuConfig.Achievements.UnlockSoundName.c_str());
Common::PlaySoundAsync(
(EmuConfig.Achievements.UnlockSoundName.empty()
? Path::Combine(EmuFolders::Resources, DEFAULT_UNLOCK_SOUND_NAME)
: EmuConfig.Achievements.UnlockSoundName).c_str()
);
}
void Achievements::HandleGameCompleteEvent(const rc_client_event_t* event)
@@ -1315,7 +1327,11 @@ void Achievements::HandleLeaderboardSubmittedEvent(const rc_client_event_t* even
}
if (EmuConfig.Achievements.SoundEffects && EmuConfig.Achievements.LBSubmitSound)
Common::PlaySoundAsync(EmuConfig.Achievements.LBSubmitSoundName.c_str());
Common::PlaySoundAsync(
(EmuConfig.Achievements.LBSubmitSoundName.empty()
? Path::Combine(EmuFolders::Resources, DEFAULT_LBSUBMIT_SOUND_NAME)
: EmuConfig.Achievements.LBSubmitSoundName).c_str()
);
}
void Achievements::HandleLeaderboardScoreboardEvent(const rc_client_event_t* event)
@@ -1524,6 +1540,11 @@ void Achievements::DisableHardcoreMode()
if (RA_HardcoreModeIsActive())
RA_DisableHardcore();
if (s_hardcore_mode && !RA_HardcoreModeIsActive())
{
s_hardcore_mode = false;
Host::OnAchievementsHardcoreModeChanged(false);
}
return;
}
#endif
@@ -1537,6 +1558,18 @@ bool Achievements::ResetHardcoreMode(bool is_booting)
if (!IsActive())
return false;
#ifdef ENABLE_RAINTEGRATION
if (IsUsingRAIntegration())
{
const bool ra_hardcore = (RA_HardcoreModeIsActive() != 0);
if (ra_hardcore == s_hardcore_mode)
return false;
s_hardcore_mode = ra_hardcore;
Host::OnAchievementsHardcoreModeChanged(ra_hardcore);
return true;
}
#endif
const auto lock = GetLock();
// If we're not logged in, don't apply hardcore mode restrictions.
@@ -1833,7 +1866,7 @@ void Achievements::ClientLoginWithPasswordCallback(int result, const char* error
Host::SetBaseStringSettingValue("Achievements", "Username", params->username);
Host::SetBaseStringSettingValue("Achievements", "LoginTimestamp", fmt::format("{}", std::time(nullptr)).c_str());
Host::CommitBaseSettingChanges();
SettingsInterface* secretsInterface = Host::Internal::GetSecretsSettingsLayer();
secretsInterface->SetStringValue("Achievements", "Token", user->token);
secretsInterface->Save();
@@ -2032,7 +2065,7 @@ static ImVec2 CalculateOverlayPosition(const ImGuiIO& io, float padding, Achieve
static ImVec2 AdjustPositionForAlignment(const ImVec2& base_position, const ImVec2& element_size, AchievementOverlayPosition alignment)
{
ImVec2 adjusted = base_position;
// Adjust for horizontal alignment
switch (alignment)
{
@@ -2041,14 +2074,14 @@ static ImVec2 AdjustPositionForAlignment(const ImVec2& base_position, const ImVe
case AchievementOverlayPosition::BottomLeft:
// Left aligned no adjustment needed for x
break;
case AchievementOverlayPosition::TopCenter:
case AchievementOverlayPosition::Center:
case AchievementOverlayPosition::BottomCenter:
// Center aligned offset by half element width
adjusted.x -= element_size.x * 0.5f;
break;
case AchievementOverlayPosition::TopRight:
case AchievementOverlayPosition::CenterRight:
case AchievementOverlayPosition::BottomRight:
@@ -2057,7 +2090,7 @@ static ImVec2 AdjustPositionForAlignment(const ImVec2& base_position, const ImVe
adjusted.x -= element_size.x;
break;
}
// Adjust for vertical alignment
switch (alignment)
{
@@ -2066,14 +2099,14 @@ static ImVec2 AdjustPositionForAlignment(const ImVec2& base_position, const ImVe
case AchievementOverlayPosition::TopRight:
// Top aligned no adjustment needed for y
break;
case AchievementOverlayPosition::CenterLeft:
case AchievementOverlayPosition::Center:
case AchievementOverlayPosition::CenterRight:
// Center aligned offset by half element height
adjusted.y -= element_size.y * 0.5f;
break;
case AchievementOverlayPosition::BottomLeft:
case AchievementOverlayPosition::BottomCenter:
case AchievementOverlayPosition::BottomRight:
@@ -2082,7 +2115,7 @@ static ImVec2 AdjustPositionForAlignment(const ImVec2& base_position, const ImVe
adjusted.y -= element_size.y;
break;
}
return adjusted;
}
@@ -2094,19 +2127,19 @@ static ImVec2 GetStackingDirection(AchievementOverlayPosition alignment)
case AchievementOverlayPosition::TopCenter:
case AchievementOverlayPosition::TopRight:
return ImVec2(0.0f, 1.0f); // Stack downward
case AchievementOverlayPosition::BottomLeft:
case AchievementOverlayPosition::BottomCenter:
case AchievementOverlayPosition::BottomRight:
default:
return ImVec2(0.0f, -1.0f); // Stack upward
case AchievementOverlayPosition::CenterLeft:
return ImVec2(1.0f, 0.0f); // Stack rightward
case AchievementOverlayPosition::CenterRight:
return ImVec2(-1.0f, 0.0f); // Stack leftward
case AchievementOverlayPosition::Center:
return ImVec2(0.0f, -1.0f); // Stack upward for center
}
@@ -2147,7 +2180,7 @@ void Achievements::DrawGameOverlays()
{
dl->AddImage(reinterpret_cast<ImTextureID>(badge->GetNativeHandle()),
current_position, current_position + image_size, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), col);
// For horizontal layouts, go horizontally for vertical layouts, stay in the same row
if (std::abs(stack_direction.x) > 0.0f)
{
@@ -2198,7 +2231,7 @@ void Achievements::DrawGameOverlays()
const ImVec2 progress_box_size = ImVec2(image_size.x + text_size.x + spacing + padding * 2.0f, image_size.y + padding * 2.0f);
const ImVec2 stack_direction = GetStackingDirection(EmuConfig.Achievements.OverlayPosition);
const ImVec2 box_position = AdjustPositionForAlignment(position, progress_box_size, EmuConfig.Achievements.OverlayPosition);
const ImVec2 box_min = box_position;
const ImVec2 box_max = box_position + progress_box_size;
const float box_rounding = LayoutScale(1.0f);
@@ -2232,7 +2265,7 @@ void Achievements::DrawGameOverlays()
if (!s_active_leaderboard_trackers.empty() && EmuConfig.Achievements.LBOverlays)
{
const ImVec2 stack_direction = GetStackingDirection(EmuConfig.Achievements.OverlayPosition);
for (auto it = s_active_leaderboard_trackers.begin(); it != s_active_leaderboard_trackers.end();)
{
const LeaderboardTrackerIndicator& indicator = *it;
@@ -2247,7 +2280,7 @@ void Achievements::DrawGameOverlays()
const ImVec2 tracker_box_size = ImVec2(size.x + padding * 2.0f, size.y + padding * 2.0f);
const ImVec2 box_position = AdjustPositionForAlignment(position, tracker_box_size, EmuConfig.Achievements.OverlayPosition);
const ImVec2 box_min = box_position;
const ImVec2 box_max = box_position + tracker_box_size;
const float box_rounding = LayoutScale(1.0f);
@@ -2468,7 +2501,8 @@ void Achievements::DrawAchievementsWindow()
top += GetLineHeight(g_large_font) + spacing;
ImGui::PushFont(g_large_font.first, g_large_font.second);
ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &title_bb);
ImGuiFullscreen::RenderTextClippedWithShadow(
title_bb.Min, title_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &title_bb);
ImGui::PopFont();
const ImRect summary_bb(ImVec2(left, top), ImVec2(right, top + GetLineHeight(g_medium_font)));
@@ -2599,7 +2633,8 @@ void Achievements::DrawAchievementsWindow()
const ImRect text_bb(ImVec2(text_x, bb.Min.y), ImVec2(bb.Max.x - padding, bb.Max.y));
ImGui::PushFont(g_medium_font.first, g_medium_font.second);
ImGui::RenderTextClipped(text_bb.Min, text_bb.Max, subset->title, nullptr, nullptr, ImVec2(0.0f, 0.5f), &text_bb);
ImGuiFullscreen::RenderTextClippedWithShadow(
text_bb.Min, text_bb.Max, subset->title, nullptr, nullptr, ImVec2(0.0f, 0.5f), &text_bb);
ImGui::PopFont();
if (ImGui::IsItemClicked() || ImGui::IsItemActivated())
@@ -2688,21 +2723,22 @@ void Achievements::DrawAchievementsWindow()
if (ImGuiFullscreen::IsGamepadInputSource())
{
const bool circleOK = ImGui::GetIO().ConfigNavSwapGamepadButtons;
const auto glyphs = ImGuiFullscreen::GetGamepadGlyphs();
if (s_sidebar_has_focus)
{
ImGuiFullscreen::SetFullscreenFooterText(std::array{
std::make_pair(ICON_PF_DPAD_UP_DOWN, TRANSLATE_SV("Achievements", "Navigate Subsets")),
std::make_pair(ICON_PF_DPAD_LEFT_RIGHT, TRANSLATE_SV("Achievements", "Back to List")),
std::make_pair(circleOK ? ICON_PF_BUTTON_CIRCLE : ICON_PF_BUTTON_CROSS, TRANSLATE_SV("Achievements", "Select")),
std::make_pair(circleOK ? ICON_PF_BUTTON_CROSS : ICON_PF_BUTTON_CIRCLE, TRANSLATE_SV("Achievements", "Back")),
std::make_pair(glyphs.dpad_ud, TRANSLATE_SV("Achievements", "Navigate Subsets")),
std::make_pair(glyphs.dpad_lr, TRANSLATE_SV("Achievements", "Back to List")),
std::make_pair(glyphs.confirm(circleOK), TRANSLATE_SV("Achievements", "Select")),
std::make_pair(glyphs.cancel(circleOK), TRANSLATE_SV("Achievements", "Back")),
});
}
else
{
ImGuiFullscreen::SetFullscreenFooterText(std::array{
std::make_pair(ICON_PF_DPAD_LEFT_RIGHT, TRANSLATE_SV("Achievements", "Navigate Subsets")),
std::make_pair(ICON_PF_DPAD_UP_DOWN, TRANSLATE_SV("Achievements", "Change Selection")),
std::make_pair(circleOK ? ICON_PF_BUTTON_CROSS : ICON_PF_BUTTON_CIRCLE, TRANSLATE_SV("Achievements", "Back")),
std::make_pair(glyphs.dpad_lr, TRANSLATE_SV("Achievements", "Navigate Subsets")),
std::make_pair(glyphs.dpad_ud, TRANSLATE_SV("Achievements", "Change Selection")),
std::make_pair(glyphs.cancel(circleOK), TRANSLATE_SV("Achievements", "Back")),
});
}
}
@@ -2878,8 +2914,9 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo)
ImVec2(points_template_start + ((points_template_size.x - right_icon_size.x) * 0.5f), bb.Min.y), ImVec2(bb.Max.x, midpoint));
ImGui::PushFont(g_large_font.first, g_large_font.second);
ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, cheevo->title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb);
ImGui::RenderTextClipped(lock_bb.Min, lock_bb.Max, right_icon_text, nullptr, &right_icon_size, ImVec2(0.0f, 0.0f), &lock_bb);
ImGuiFullscreen::RenderTextClippedWithShadow(title_bb.Min, title_bb.Max, cheevo->title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb);
ImGuiFullscreen::RenderTextClippedWithShadow(
lock_bb.Min, lock_bb.Max, right_icon_text, nullptr, &right_icon_size, ImVec2(0.0f, 0.0f), &lock_bb);
ImGui::PopFont();
if (badge_text && badge_icon)
@@ -2905,14 +2942,16 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo)
{
ImGui::RenderTextWrapped(summary_bb.Min, cheevo->description, cheevo->description + summary_length, summary_wrap_width);
}
ImGui::RenderTextClipped(points_bb.Min, points_bb.Max, text.c_str(), text.end_ptr(), &points_size, ImVec2(0.0f, 0.0f), &points_bb);
ImGuiFullscreen::RenderTextClippedWithShadow(
points_bb.Min, points_bb.Max, text.c_str(), text.end_ptr(), &points_size, ImVec2(0.0f, 0.0f), &points_bb);
if (is_unlocked)
{
text.format(TRANSLATE_FS("Achievements", "Unlocked: {}"), FullscreenUI::TimeToPrintableString(cheevo->unlock_time));
const ImRect unlock_bb(summary_bb.Min.x, summary_bb.Max.y + spacing, summary_bb.Max.x, bb.Max.y);
ImGui::RenderTextClipped(unlock_bb.Min, unlock_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &unlock_bb);
ImGuiFullscreen::RenderTextClippedWithShadow(
unlock_bb.Min, unlock_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &unlock_bb);
}
else if (is_measured)
{
@@ -3071,7 +3110,8 @@ void Achievements::DrawLeaderboardsWindow()
top += GetLineHeight(g_large_font) + spacing;
ImGui::PushFont(g_large_font.first, g_large_font.second);
ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &title_bb);
ImGuiFullscreen::RenderTextClippedWithShadow(
title_bb.Min, title_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &title_bb);
ImGui::PopFont();
if (is_leaderboard_open)
@@ -3082,7 +3122,7 @@ void Achievements::DrawLeaderboardsWindow()
top += GetLineHeight(g_large_font) + spacing_small;
ImGui::PushFont(g_large_font.first, g_large_font.second);
ImGui::RenderTextClipped(
ImGuiFullscreen::RenderTextClippedWithShadow(
subtitle_bb.Min, subtitle_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &subtitle_bb);
ImGui::PopFont();
@@ -3100,7 +3140,7 @@ void Achievements::DrawLeaderboardsWindow()
top += GetLineHeight(g_medium_font) + spacing_small;
ImGui::PushFont(g_medium_font.first, g_medium_font.second);
ImGui::RenderTextClipped(
ImGuiFullscreen::RenderTextClippedWithShadow(
summary_bb.Min, summary_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &summary_bb);
if (!is_leaderboard_open && !Achievements::IsHardcoreModeActive())
@@ -3108,9 +3148,9 @@ void Achievements::DrawLeaderboardsWindow()
const ImRect hardcore_warning_bb(ImVec2(left, top), ImVec2(right, top + GetLineHeight(g_medium_font)));
top += GetLineHeight(g_medium_font) + spacing_small;
ImGui::RenderTextClipped(hardcore_warning_bb.Min, hardcore_warning_bb.Max,
TRANSLATE("Achievements", "Submitting scores is disabled because hardcore mode is off. Leaderboards are read-only."),
nullptr, nullptr, ImVec2(0.0f, 0.0f), &hardcore_warning_bb);
ImGuiFullscreen::RenderTextClippedWithShadow(hardcore_warning_bb.Min, hardcore_warning_bb.Max,
TRANSLATE("Achievements", "Submitting scores is disabled because hardcore mode is off. Leaderboards are read-only."), nullptr,
nullptr, ImVec2(0.0f, 0.0f), &hardcore_warning_bb);
}
ImGui::PopFont();
@@ -3159,7 +3199,7 @@ void Achievements::DrawLeaderboardsWindow()
const auto render_centered_heading = [&](const char* text, float width) {
const ImRect rect(ImVec2(column_left, bb.Min.y), ImVec2(column_left + width, midpoint));
ImGui::RenderTextClipped(rect.Min, rect.Max, text, nullptr, nullptr, ImVec2(0.5f, 0.0f), &rect);
ImGuiFullscreen::RenderTextClippedWithShadow(rect.Min, rect.Max, text, nullptr, nullptr, ImVec2(0.5f, 0.0f), &rect);
column_left += width + column_spacing;
};
@@ -3168,7 +3208,7 @@ void Achievements::DrawLeaderboardsWindow()
const float name_column_total_width = icon_column_width + column_spacing + name_column_width;
const ImRect user_bb(ImVec2(column_left + icon_column_width + column_spacing, bb.Min.y),
ImVec2(column_left + name_column_total_width, midpoint));
ImGui::RenderTextClipped(
ImGuiFullscreen::RenderTextClippedWithShadow(
user_bb.Min, user_bb.Max, TRANSLATE("Achievements", "Name"), nullptr, nullptr, ImVec2(0.5f, 0.0f), &user_bb);
column_left += name_column_total_width + column_spacing;
@@ -3185,8 +3225,8 @@ void Achievements::DrawLeaderboardsWindow()
const float date_column_width = std::max(bb.Max.x - column_left, 0.0f);
const ImRect date_bb(ImVec2(column_left, bb.Min.y), ImVec2(column_left + date_column_width, midpoint));
ImGui::RenderTextClipped(date_bb.Min, date_bb.Max, TRANSLATE("Achievements", "Date Submitted"), nullptr, nullptr,
ImVec2(0.5f, 0.0f), &date_bb);
ImGuiFullscreen::RenderTextClippedWithShadow(
date_bb.Min, date_bb.Max, TRANSLATE("Achievements", "Date Submitted"), nullptr, nullptr, ImVec2(0.5f, 0.0f), &date_bb);
column_left += date_column_width;
ImGui::PopFont();
@@ -3303,10 +3343,11 @@ void Achievements::DrawLeaderboardsWindow()
if (ImGuiFullscreen::IsGamepadInputSource())
{
const bool circleOK = ImGui::GetIO().ConfigNavSwapGamepadButtons;
const auto glyphs = ImGuiFullscreen::GetGamepadGlyphs();
ImGuiFullscreen::SetFullscreenFooterText(std::array{
std::make_pair(ICON_PF_DPAD_LEFT_RIGHT, TRANSLATE_SV("Achievements", "Switch Rankings")),
std::make_pair(ICON_PF_DPAD_UP_DOWN, TRANSLATE_SV("Achievements", "Change Selection")),
std::make_pair(circleOK ? ICON_PF_BUTTON_CROSS : ICON_PF_BUTTON_CIRCLE, TRANSLATE_SV("Achievements", "Back")),
std::make_pair(glyphs.dpad_lr, TRANSLATE_SV("Achievements", "Switch Rankings")),
std::make_pair(glyphs.dpad_ud, TRANSLATE_SV("Achievements", "Change Selection")),
std::make_pair(glyphs.cancel(circleOK), TRANSLATE_SV("Achievements", "Back")),
});
}
else
@@ -3353,7 +3394,8 @@ void Achievements::DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& ent
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(255, 242, 0, 255));
const ImRect rank_bb(ImVec2(column_left, bb.Min.y), ImVec2(column_left + rank_column_width, midpoint));
ImGui::RenderTextClipped(rank_bb.Min, rank_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &rank_bb);
ImGuiFullscreen::RenderTextClippedWithShadow(
rank_bb.Min, rank_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &rank_bb);
column_left += rank_column_width + column_spacing;
const ImRect icon_bb(ImVec2(column_left, bb.Min.y), ImVec2(column_left + icon_column_width, bb.Min.y + icon_column_width));
@@ -3383,16 +3425,18 @@ void Achievements::DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& ent
column_left += icon_column_width + column_spacing;
const ImRect user_bb(ImVec2(column_left, bb.Min.y), ImVec2(column_left + name_column_width, midpoint));
ImGui::RenderTextClipped(user_bb.Min, user_bb.Max, entry.user, nullptr, nullptr, ImVec2(0.0f, 0.0f), &user_bb);
ImGuiFullscreen::RenderTextClippedWithShadow(user_bb.Min, user_bb.Max, entry.user, nullptr, nullptr, ImVec2(0.0f, 0.0f), &user_bb);
column_left += name_column_width + column_spacing;
const ImRect score_bb(ImVec2(column_left, bb.Min.y), ImVec2(column_left + time_column_width, midpoint));
ImGui::RenderTextClipped(score_bb.Min, score_bb.Max, entry.display, nullptr, nullptr, ImVec2(0.0f, 0.0f), &score_bb);
ImGuiFullscreen::RenderTextClippedWithShadow(
score_bb.Min, score_bb.Max, entry.display, nullptr, nullptr, ImVec2(0.0f, 0.0f), &score_bb);
column_left += time_column_width + column_spacing;
const ImRect time_bb(ImVec2(column_left, bb.Min.y), ImVec2(bb.Max.x, midpoint));
const auto submit_time = FullscreenUI::TimeToPrintableString(entry.submitted);
ImGui::RenderTextClipped(time_bb.Min, time_bb.Max, submit_time.c_str(), submit_time.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &time_bb);
ImGuiFullscreen::RenderTextClippedWithShadow(
time_bb.Min, time_bb.Max, submit_time.c_str(), submit_time.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &time_bb);
if (is_self)
ImGui::PopStyleColor();
@@ -3436,13 +3480,14 @@ void Achievements::DrawLeaderboardListEntry(const rc_client_leaderboard_t* lboar
const ImRect summary_bb(ImVec2(text_start_x, midpoint), bb.Max);
ImGui::PushFont(g_large_font.first, g_large_font.second);
ImGui::RenderTextClipped(title_bb.Min, title_bb.Max, lboard->title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb);
ImGuiFullscreen::RenderTextClippedWithShadow(title_bb.Min, title_bb.Max, lboard->title, nullptr, nullptr, ImVec2(0.0f, 0.0f), &title_bb);
ImGui::PopFont();
if (lboard->description && lboard->description[0] != '\0')
{
ImGui::PushFont(g_medium_font.first, g_medium_font.second);
ImGui::RenderTextClipped(summary_bb.Min, summary_bb.Max, lboard->description, nullptr, nullptr, ImVec2(0.0f, 0.0f), &summary_bb);
ImGuiFullscreen::RenderTextClippedWithShadow(
summary_bb.Min, summary_bb.Max, lboard->description, nullptr, nullptr, ImVec2(0.0f, 0.0f), &summary_bb);
ImGui::PopFont();
}
@@ -3693,7 +3738,13 @@ void Achievements::RAIntegration::RACallbackCausePause()
void Achievements::RAIntegration::RACallbackRebuildMenu()
{
// unused, we build the menu on demand
// Sync hardcore state in case the user changed it via the RA menu.
const bool ra_hardcore = (RA_HardcoreModeIsActive() != 0);
if (ra_hardcore != s_hardcore_mode)
{
s_hardcore_mode = ra_hardcore;
Host::OnAchievementsHardcoreModeChanged(ra_hardcore);
}
}
void Achievements::RAIntegration::RACallbackEstimateTitle(char* buf)
+3 -3
View File
@@ -792,6 +792,7 @@ struct Pcsx2Config
Mipmap : 1,
HWMipmap : 1,
HWAccurateAlphaTest: 1,
HWAA1 : 1,
ManualUserHacks : 1,
UserHacks_AlignSpriteX : 1,
UserHacks_CPUFBConversion : 1,
@@ -804,6 +805,7 @@ struct Pcsx2Config
UserHacks_ForceEvenSpritePosition : 1,
UserHacks_NativePaletteDraw : 1,
UserHacks_EstimateTextureRegion : 1,
UserHacks_DrawBuffering : 1,
FXAA : 1,
ShadeBoost : 1,
DumpGSData : 1,
@@ -1285,9 +1287,6 @@ struct Pcsx2Config
static constexpr u32 MAXIMUM_NOTIFICATION_DURATION = 30;
static constexpr u32 DEFAULT_NOTIFICATION_DURATION = 5;
static constexpr u32 DEFAULT_LEADERBOARD_DURATION = 10;
static constexpr const char* DEFAULT_INFO_SOUND_NAME = "sounds/achievements/message.wav";
static constexpr const char* DEFAULT_UNLOCK_SOUND_NAME = "sounds/achievements/unlock.wav";
static constexpr const char* DEFAULT_LBSUBMIT_SOUND_NAME = "sounds/achievements/lbsubmit.wav";
static const char* OverlayPositionNames[(size_t)AchievementOverlayPosition::MaxCount + 1];
@@ -1404,6 +1403,7 @@ struct Pcsx2Config
std::string CurrentBlockdump;
std::string CurrentIRX;
std::string CurrentGameArgs;
std::string CustomDataPath;
AspectRatioType CurrentAspectRatio = AspectRatioType::RAuto4_3_3_2;
// Fall back aspect ratio for games that have patches (when AspectRatioType::RAuto4_3_3_2) is active.
float CurrentCustomAspectRatio = 0.f;
+1 -1
View File
@@ -876,7 +876,7 @@ static __fi void rcntEndGate(bool isVblank, u64 sCycle)
isVblank ? "vblank" : "hblank", counters[i].mode.GateMode, counters[i].count);
counters[i].count = 0;
counters[i].target &= 0xffff;
counters[i].startCycle = sCycle & ~(counters[i].rate - 1);
counters[i].startCycle = sCycle & ~(static_cast<u64>(counters[i].rate - 1));
break;
}
}
+4 -3
View File
@@ -49,7 +49,8 @@ std::vector<std::unique_ptr<BiosThread>> getIOPThreads()
return {};
}
data.stackTop = iopMemRead32(item + 0x3c);
data.stacMem = iopMemRead32(item + 0x3c);
data.stackSize = iopMemRead32(item + 0x40);
data.status = iopMemRead8(item + 0xc);
data.tid = iopMemRead16(item + 0xa);
data.entrypoint = iopMemRead32(item + 0x38);
@@ -57,9 +58,9 @@ std::vector<std::unique_ptr<BiosThread>> getIOPThreads()
data.waitId = iopMemRead32(item + 0x20);
data.initPriority = iopMemRead16(item + 0xe);
data.SavedSP = iopMemRead32(item + 0x10);
data.regCtx = iopMemRead32(item + 0x10);
data.PC = iopMemRead32(data.SavedSP + 0x8c);
data.PC = iopMemRead32(data.regCtx + 0x8c);
threads.emplace_back(std::make_unique<IOPThread>(data));
+29 -16
View File
@@ -18,26 +18,37 @@ enum class ThreadStatus
THS_DORMANT = 0x10,
};
struct EEInternalCtx
{
u32 sa;
u32 fcsr;
u32 float_thing;
u32 unk;
// gpr excluding $zero
// k0/k1 contains hi, hi1, lo, lo1
u128 gpr[31];
float fpr[32];
};
struct EEInternalThread
{ // internal struct
u32 prev;
u32 next;
int status;
u32 entry;
u32 stack;
u32 resumeAddr; // address to return to when switching
u32 regCtx; // points to the saved regs on stack
u32 gpReg;
short currentPriority;
short initPriority;
short currentPriority;
int waitType;
int semaId;
int wakeupCount;
int attr;
int option;
u32 entry_init;
u32 entry;
int argc;
u32 argstring;
u32 stack_bottom; //FIXME
u32 stackMem;
int stackSize;
u32 root;
u32 heap_base;
@@ -48,8 +59,9 @@ struct IOPInternalThread
{
u32 tid;
u32 PC;
u32 stackTop;
u32 SavedSP;
u32 stacMem;
u32 stackSize;
u32 regCtx;
u32 status;
u32 entrypoint;
u32 waitstate;
@@ -71,14 +83,13 @@ enum class IOPWaitStatus
enum class EEWaitStatus
{
WAIT_NONE = 0,
WAIT_WAKEUP_REQ = 1,
WAIT_SLEEP = 1,
WAIT_SEMA = 2,
};
enum class WaitState
{
NONE,
WAKEUP_REQ,
SEMA,
SLEEP,
DELAY,
@@ -98,8 +109,10 @@ public:
[[nodiscard]] virtual WaitState Wait() const = 0;
[[nodiscard]] virtual u32 WaitId() const = 0;
[[nodiscard]] virtual u32 EntryPoint() const = 0;
[[nodiscard]] virtual u32 StackTop() const = 0;
[[nodiscard]] virtual u32 Priority() const = 0;
// Only call RegCtx on threads that aren't running
[[nodiscard]] virtual u32 RegCtx() const = 0;
};
class EEThread : public BiosThread
@@ -113,7 +126,7 @@ public:
~EEThread() override = default;
[[nodiscard]] u32 TID() const override { return tid; };
[[nodiscard]] u32 PC() const override { return data.entry; };
[[nodiscard]] u32 PC() const override { return data.resumeAddr; };
[[nodiscard]] ThreadStatus Status() const override { return static_cast<ThreadStatus>(data.status); };
[[nodiscard]] WaitState Wait() const override
{
@@ -122,17 +135,17 @@ public:
{
case EEWaitStatus::WAIT_NONE:
return WaitState::NONE;
case EEWaitStatus::WAIT_WAKEUP_REQ:
return WaitState::WAKEUP_REQ;
case EEWaitStatus::WAIT_SLEEP:
return WaitState::SLEEP;
case EEWaitStatus::WAIT_SEMA:
return WaitState::SEMA;
}
return WaitState::NONE;
};
[[nodiscard]] u32 WaitId() const override { return data.semaId; };
[[nodiscard]] u32 EntryPoint() const override { return data.entry_init; };
[[nodiscard]] u32 StackTop() const override { return data.stack; };
[[nodiscard]] u32 EntryPoint() const override { return data.entry; };
[[nodiscard]] u32 Priority() const override { return data.currentPriority; };
[[nodiscard]] u32 RegCtx() const override { return data.regCtx; };
private:
u32 tid;
@@ -175,8 +188,8 @@ public:
};
[[nodiscard]] u32 WaitId() const override { return data.waitId; };
[[nodiscard]] u32 EntryPoint() const override { return data.entrypoint; };
[[nodiscard]] u32 StackTop() const override { return data.stackTop; };
[[nodiscard]] u32 Priority() const override { return data.initPriority; };
[[nodiscard]] u32 RegCtx() const override { return data.regCtx; };
private:
IOPInternalThread data;
+33
View File
@@ -708,6 +708,23 @@ std::vector<std::unique_ptr<BiosThread>> R5900DebugInterface::GetThreadList() co
return getEEThreads();
}
std::vector<MipsStackWalk::StackFrame> R5900DebugInterface::StackTrace(const BiosThread& thread)
{
if (thread.Status() == ThreadStatus::THS_RUN)
{
return MipsStackWalk::Walk(this, getPC(), getRegister(0, 31), getRegister(0, 29),
thread.EntryPoint());
}
EEInternalCtx* ctx = static_cast<EEInternalCtx*>(PSM(thread.RegCtx()));
u32 pc = thread.PC();
// $zero is not in the array so subtract 1
u32 ra = ctx->gpr[31 - 1]._u32[0];
u32 sp = ctx->gpr[29 - 1]._u32[0];
return MipsStackWalk::Walk(this, pc, ra, sp, thread.EntryPoint());
}
std::vector<IopMod> R5900DebugInterface::GetModuleList() const
{
return {};
@@ -1049,6 +1066,22 @@ std::vector<std::unique_ptr<BiosThread>> R3000DebugInterface::GetThreadList() co
return getIOPThreads();
}
std::vector<MipsStackWalk::StackFrame> R3000DebugInterface::StackTrace(const BiosThread& thread)
{
if (thread.Status() == ThreadStatus::THS_RUN)
{
return MipsStackWalk::Walk(this, getPC(), getRegister(0, 31), getRegister(0, 29),
thread.EntryPoint());
}
u32 p = thread.RegCtx();
u32 pc = Read32(p + 0x8c);
u32 ra = Read32(p + 0x7c);
u32 sp = Read32(p + 0x74);
return MipsStackWalk::Walk(this, pc, ra, sp, thread.EntryPoint());
}
std::vector<IopMod> R3000DebugInterface::GetModuleList() const
{
return getIOPModules();
+4
View File
@@ -7,6 +7,7 @@
#include "ExpressionParser.h"
#include "SymbolGuardian.h"
#include "SymbolImporter.h"
#include "MipsStackWalk.h"
#include "common/MemoryInterface.h"
@@ -72,6 +73,7 @@ public:
virtual SymbolGuardian& GetSymbolGuardian() const = 0;
virtual SymbolImporter* GetSymbolImporter() const = 0;
virtual std::vector<std::unique_ptr<BiosThread>> GetThreadList() const = 0;
virtual std::vector<MipsStackWalk::StackFrame> StackTrace(const BiosThread& thread) = 0;
virtual std::vector<IopMod> GetModuleList() const = 0;
bool isAlive();
@@ -135,6 +137,7 @@ public:
SymbolGuardian& GetSymbolGuardian() const override;
SymbolImporter* GetSymbolImporter() const override;
std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
std::vector<MipsStackWalk::StackFrame> StackTrace(const BiosThread& thread) override;
std::vector<IopMod> GetModuleList() const override;
std::string disasm(u32 address, bool simplify) override;
@@ -180,6 +183,7 @@ public:
SymbolGuardian& GetSymbolGuardian() const override;
SymbolImporter* GetSymbolImporter() const override;
std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
std::vector<MipsStackWalk::StackFrame> StackTrace(const BiosThread& thread) override;
std::vector<IopMod> GetModuleList() const override;
std::string disasm(u32 address, bool simplify) override;
+5 -2
View File
@@ -103,7 +103,10 @@ namespace MipsStackWalk
// TODO: Check if found entry is in the same symbol? Might be wrong sometimes...
int ra_offset = -1;
const u32 start = frame.pc;
// The instruction pointed to by pc hasn't been executed yet,
// so we don't want to consider it here
const u32 start = frame.pc - 4;
u32 stop = entry;
if (entry == INVALIDTARGET)
@@ -183,7 +186,7 @@ namespace MipsStackWalk
return ScanForEntry(cpu, frame, newPossibleEntry, ra);
}
std::vector<StackFrame> Walk(DebugInterface* cpu, u32 pc, u32 ra, u32 sp, u32 threadEntry, u32 threadStackTop)
std::vector<StackFrame> Walk(DebugInterface* cpu, u32 pc, u32 ra, u32 sp, u32 threadEntry)
{
std::vector<StackFrame> frames;
StackFrame current;
+1 -1
View File
@@ -20,5 +20,5 @@ namespace MipsStackWalk {
int stackSize;
};
std::vector<StackFrame> Walk(DebugInterface* cpu, u32 pc, u32 ra, u32 sp, u32 threadEntry, u32 threadStackTop);
std::vector<StackFrame> Walk(DebugInterface* cpu, u32 pc, u32 ra, u32 sp, u32 threadEntry);
};
+1
View File
@@ -174,6 +174,7 @@ The clamp modes are also numerically based.
* cpuSpriteRenderBW [Value between `0` to `10`] {Disabled, 1 (64), 2 (128), 3 (192), 4 (256), 5 (320), 6 (384), 7 (448), 8 (512), 9 (576), 10 (640)} Default: Off (`0`)
* cpuSpriteRenderLevel [`0` or `1` or `2`] {Sprites only, Sprites/Triangles, Blended Sprites/Triangles} Default: Off unless cpuSpriteRenderBW has value other than Off then it is 'Sprites only' (`0`)
* estimateTextureRegion [`0` or `1`] {Off, On} Default: Off (`0`)
* drawBuffering [`0` or `1`] {Off, On} Default: Off (`0`)
* getSkipCount {`GSC` with suffix } {None unless specific game GSC} Default: Disabled (`0`) unless valid variable name (ex. GSC_PolyphonyDigitalGames, GSC_UrbanReign, ...)
* gpuPaletteConversion [`0` or `1`] {Off, On} Default: Off (`0`)
* gpuTargetCLUT [`0` or `1` or `2`] {Disabled, Enabled (Exact Match), Enabled (Check Inside Target)} Default: Disabled (`0`)
+5
View File
@@ -203,6 +203,11 @@
"type": "integer",
"minimum": 0,
"maximum": 1
},
"drawBuffering": {
"type": "integer",
"minimum": 0,
"maximum": 1
},
"PCRTCOffsets": {
"type": "integer",
+4 -4
View File
@@ -91,11 +91,11 @@ void GSDrawingContext::UpdateScissor()
scissor.in = rscissor + GSVector4i::cxpr(0, 0, 1, 1);
// Fixed-point scissor min/max, used for rejecting primitives which are entirely outside.
scissor.cull = rscissor.sll32<4>();
// Add half a pixel around the edges for upscaling and lines/points.
scissor.cull = rscissor.sll32<4>() + GSVector4i(-8, -8, 8, 8);
// Offset applied to vertices for culling, zw is for native resolution culling
// We want to round subpixels down, because at least one pixel gets filled per scanline.
scissor.xyof = GSVector4i::loadl(&XYOFFSET.U64).xyxy().sub32(GSVector4i::cxpr(0, 0, 15, 15));
// Offset applied to vertices for culling.
scissor.xyof = GSVector4i::loadl(&XYOFFSET.U64).xyxy();
}
GIFRegTEX0 GSDrawingContext::GetSizeFixedTEX0(const GSVector4& st, bool linear, bool mipmap) const
+14 -45
View File
@@ -512,56 +512,25 @@ GSVector4i GSLocalMemory::GetRectForPageOffset(u32 base_bp, u32 offset_bp, u32 b
bool GSLocalMemory::HasOverlap(const u32 src_bp, const u32 src_bw, const u32 src_psm, const GSVector4i src_rect,
const u32 dst_bp, const u32 dst_bw, const u32 dst_psm, const GSVector4i dst_rect)
{
const u32 src_start_bp = GSLocalMemory::GetStartBlockAddress(src_bp, src_bw, src_psm, src_rect) & ~(GS_BLOCKS_PER_PAGE - 1);
const u32 dst_start_bp = GSLocalMemory::GetStartBlockAddress(dst_bp, dst_bw, dst_psm, dst_rect) & ~(GS_BLOCKS_PER_PAGE - 1);
u32 src_start_bp = GSLocalMemory::GetStartBlockAddress(src_bp, src_bw, src_psm, src_rect) & ~(GS_BLOCKS_PER_PAGE - 1);
u32 dst_start_bp = GSLocalMemory::GetStartBlockAddress(dst_bp, dst_bw, dst_psm, dst_rect) & ~(GS_BLOCKS_PER_PAGE - 1);
u32 src_end_bp = ((GSLocalMemory::GetEndBlockAddress(src_bp, src_bw, src_psm, src_rect) + 1) + (GS_BLOCKS_PER_PAGE - 1)) & ~(GS_BLOCKS_PER_PAGE - 1);
u32 dst_end_bp = ((GSLocalMemory::GetEndBlockAddress(dst_bp, dst_bw, dst_psm, dst_rect) + 1) + (GS_BLOCKS_PER_PAGE - 1)) & ~(GS_BLOCKS_PER_PAGE - 1);
u32 src_end_bp = ((GSLocalMemory::GetUnwrappedEndBlockAddress(src_bp, src_bw, src_psm, src_rect) + 1) + (GS_BLOCKS_PER_PAGE - 1)) & ~(GS_BLOCKS_PER_PAGE - 1);
u32 dst_end_bp = ((GSLocalMemory::GetUnwrappedEndBlockAddress(dst_bp, dst_bw, dst_psm, dst_rect) + 1) + (GS_BLOCKS_PER_PAGE - 1)) & ~(GS_BLOCKS_PER_PAGE - 1);
if (src_start_bp == src_end_bp)
if (dst_end_bp > GS_MAX_BLOCKS && src_end_bp < dst_start_bp)
{
src_end_bp = (src_end_bp + GS_BLOCKS_PER_PAGE) & ~(GS_MAX_BLOCKS - 1);
src_start_bp += GS_MAX_BLOCKS;
src_end_bp += GS_MAX_BLOCKS;
}
else if (src_end_bp > GS_MAX_BLOCKS && dst_end_bp < src_start_bp)
{
dst_start_bp += GS_MAX_BLOCKS;
dst_end_bp += GS_MAX_BLOCKS;
}
if (dst_start_bp == dst_end_bp)
{
dst_end_bp = (dst_end_bp + GS_BLOCKS_PER_PAGE) & ~(GS_MAX_BLOCKS - 1);
}
// Source has wrapped, 2 separate checks.
if (src_end_bp <= src_start_bp)
{
// Destination has also wrapped, so they *have* to overlap.
if (dst_end_bp <= dst_start_bp)
{
return true;
}
else
{
if (dst_end_bp > src_start_bp)
return true;
if (dst_start_bp < src_end_bp)
return true;
}
}
else // No wrapping on source.
{
// Destination wraps.
if (dst_end_bp <= dst_start_bp)
{
if (src_end_bp > dst_start_bp)
return true;
if (src_start_bp < dst_end_bp)
return true;
}
else
{
if (dst_start_bp < src_end_bp && dst_end_bp > src_start_bp)
return true;
}
}
if (src_start_bp < dst_end_bp && dst_start_bp < src_end_bp)
return true;
return false;
}
+1134 -493
View File
File diff suppressed because it is too large Load Diff
+45 -20
View File
@@ -127,37 +127,39 @@ private:
protected:
static constexpr int INVALID_ALPHA_MINMAX = 500;
static constexpr int MAX_DRAW_BUFFERS = 3;
GSVertex m_v = {};
float m_q = 1.0f;
GSVector4i m_scissor_cull_min = {};
GSVector4i m_scissor_cull_max = {};
GSVector4i m_xyof = {};
int m_used_buffers_idx = 0;
int m_current_buffer_idx = 0;
bool m_recent_buffer_switch = false;
struct
struct GSVertexBuff
{
GSVertex* buff;
GSVertex* buff_copy; // same size buffer to copy/modify the original buffer
GSVertex* buff_copy; // same size buffer to copy/modify the original buffer
u32 head, tail, next, maxcount; // head: first vertex, tail: last vertex + 1, next: last indexed + 1
u32 xy_tail;
GSVector4i xy[4];
GSVector4i xyhead;
} m_vertex = {};
};
struct
GSVertexBuff m_vertex_buffers[MAX_DRAW_BUFFERS];
GSVertexBuff* m_vertex;
struct GSIndexBuff
{
u16* buff;
u32 tail;
} m_index = {};
};
struct
{
GSVertex* buff;
u32 head, tail, next, maxcount; // head: first vertex, tail: last vertex + 1, next: last indexed + 1
u32 xy_tail;
GSVector4i xy[4];
GSVector4i xyhead;
} m_draw_vertex = {};
GSIndexBuff m_index_buffers[MAX_DRAW_BUFFERS];
GSIndexBuff* m_index;
GSVertexBuff m_draw_vertex = {};
struct
{
@@ -165,6 +167,17 @@ protected:
u32 tail;
} m_draw_index = {};
struct GSDrawBufferEnv
{
GSDrawingEnvironment m_env;
int m_backed_up_ctx = 0;
u32 m_dirty_regs = 0;
GSVector4i draw_rect = GSVector4i::zero();
bool related_draw = false;
};
GSDrawBufferEnv m_env_buffers[MAX_DRAW_BUFFERS] = {};
void UpdateContext();
void UpdateScissor();
@@ -175,6 +188,7 @@ protected:
template<u32 prim> void HandleAutoFlush();
bool EarlyDetectShuffle(u32 prim);
void CheckCLUTValidity(u32 prim);
bool CheckOverlapVerts(u32 n);
template <u32 prim, bool auto_flush> void VertexKick(u32 skip);
@@ -208,6 +222,8 @@ protected:
bool IsMipMapDraw();
bool IsMipMapActive();
bool IsCoverageAlpha();
bool IsCoverageAlphaFixedOne();
virtual bool IsCoverageAlphaSupported();
void CalcAlphaMinMax(const int tex_min, const int tex_max);
void CorrectATEAlphaMinMax(const u32 atst, const int aref);
@@ -242,15 +258,15 @@ public:
{
EE_to_GS,
GS_to_GS,
GS_to_EE
GS_to_EE,
Clear
};
struct GSUploadQueue
{
GIFRegBITBLTBUF blit;
GSVector4i rect;
u64 draw;
bool zero_clear;
GSVector4i rect;
EEGS_TransferType transfer_type;
};
@@ -268,9 +284,10 @@ public:
GSLocalMemory m_mem;
GSDrawingEnvironment m_env = {};
GSDrawingEnvironment m_prev_env = {};
GSDrawingEnvironment m_temp_env = {};
const GSDrawingEnvironment* m_draw_env = &m_env;
GSDrawingContext* m_context = nullptr;
GSVector4i temp_draw_rect = {};
GSVector4i temp_draw_rect;
std::unique_ptr<GSDumpBase> m_dump;
bool m_scissor_invalid = false;
bool m_quad_check_valid = false;
@@ -468,7 +485,15 @@ public:
virtual void Reset(bool hardware_reset);
virtual void UpdateSettings(const Pcsx2Config::GSOptions& old_config);
void ResetDrawBuffers();
void ResetDrawBufferIdx();
void FlushBuffers(bool flush_base_only = false, bool use_flush_reason = false, GSFlushReason flush_reason = GSFlushReason::CONTEXTCHANGE);
void PushBuffer();
void SetDrawBufferEnv();
void SetDrawBuffDirty();
bool CanBufferNewDraw();
void Flush(GSFlushReason reason);
void FlushDraw(GSFlushReason reason);
u32 CalcMask(int exp, int max_exp);
void FlushPrim();
bool TestDrawChanged();
@@ -481,7 +506,7 @@ public:
virtual void Move();
GSVector4i GetTEX0Rect();
GSVector4i GetTEX0Rect(GSDrawingContext prev_ctx);
void CheckWriteOverlap(bool req_write, bool req_read);
void Write(const u8* mem, int len);
void Read(u8* mem, int len);
+24
View File
@@ -339,3 +339,27 @@ const char* GSUtil::GetPSMName(int psm)
}
return "BAD_PSM";
}
bool GSUtil::IsValidPSM(int psm)
{
switch (psm)
{
case PSMCT32:
case PSMCT24:
case PSMCT16:
case PSMCT16S:
case PSMT8:
case PSMT4:
case PSMT8H:
case PSMT4HL:
case PSMT4HH:
case PSMZ32:
case PSMZ24:
case PSMZ16:
case PSMZ16S:
case PSGPU24:
return true;
default:
return false;
}
}
+11
View File
@@ -6,6 +6,7 @@
#include "GS.h"
#include "GSRegs.h"
#include "GSPerfMon.h"
#include <climits>
class GSUtil
{
@@ -28,6 +29,8 @@ public:
static const char* GetACName(u32 ac);
static const char* GetPerfMonCounterName(GSPerfMon::counter_t counter, bool hw = true);
static bool IsValidPSM(int psm);
static const u32* HasSharedBitsPtr(u32 dpsm);
static bool HasSharedBits(u32 spsm, const u32* ptr);
static bool HasSharedBits(u32 spsm, u32 dpsm);
@@ -157,6 +160,14 @@ public:
};
}
BoundingOct ExpandOne() const
{
return {
bbox0 + GSVector4i(-1, -1, 1, 1),
bbox1 + GSVector4i(-1, -1, 1, 1),
};
}
const GSVector4i& ToBBox()
{
return bbox0;
+1 -1
View File
@@ -116,7 +116,7 @@ __forceinline_odr GSVector4i::GSVector4i(const GSVector4& v, bool truncate)
m = truncate ? _mm_cvttps_epi32(v) : _mm_cvtps_epi32(v);
#elif defined(ARCH_ARM64)
// GS thread uses default (nearest) rounding.
v4s = truncate ? vcvtq_s32_f32(v.v4s) : vcvtnq_u32_f32(v.v4s);
v4s = truncate ? vcvtq_s32_f32(v.v4s) : vreinterpretq_s32_u32(vcvtnq_u32_f32(v.v4s));
#endif
}
+6 -8
View File
@@ -703,17 +703,17 @@ public:
__forceinline GSVector4 mul64(const GSVector4& v) const
{
return GSVector4(vmulq_f64(vreinterpretq_f64_f32(v4s), vreinterpretq_f64_f32(v.v4s)));
return GSVector4(vreinterpretq_f32_f64(vmulq_f64(vreinterpretq_f64_f32(v4s), vreinterpretq_f64_f32(v.v4s))));
}
__forceinline GSVector4 add64(const GSVector4& v) const
{
return GSVector4(vaddq_f64(vreinterpretq_f64_f32(v4s), vreinterpretq_f64_f32(v.v4s)));
return GSVector4(vreinterpretq_f32_f64(vaddq_f64(vreinterpretq_f64_f32(v4s), vreinterpretq_f64_f32(v.v4s))));
}
__forceinline GSVector4 sub64(const GSVector4& v) const
{
return GSVector4(vsubq_f64(vreinterpretq_f64_f32(v4s), vreinterpretq_f64_f32(v.v4s)));
return GSVector4(vreinterpretq_f32_f64(vsubq_f64(vreinterpretq_f64_f32(v4s), vreinterpretq_f64_f32(v.v4s))));
}
__forceinline static GSVector4 f32to64(const GSVector4& v)
@@ -728,10 +728,8 @@ public:
__forceinline GSVector4i f64toi32(bool truncate = true) const
{
const float64x2_t r = truncate ? v4s : vrndiq_f64(vreinterpretq_f64_f32(v4s));
const s32 low = static_cast<s32>(vgetq_lane_f64(r, 0));
const s32 high = static_cast<s32>(vgetq_lane_f64(r, 1));
return GSVector4i(vsetq_lane_s32(high, vsetq_lane_s32(low, vdupq_n_s32(0), 0), 1));
const int64x2_t r = truncate ? vcvtq_s64_f64(vreinterpretq_f64_f32(v4s)) : vcvtnq_s64_f64(vreinterpretq_f64_f32(v4s));
return GSVector4i(vcombine_s32(vmovn_s64(r), vdup_n_s32(0)));
}
// clang-format off
@@ -782,6 +780,6 @@ public:
__forceinline static GSVector4 broadcast64(const void* f)
{
return GSVector4(vreinterpretq_f64_f32(vld1q_dup_f64((const double*)f)));
return GSVector4(vreinterpretq_f32_f64(vld1q_dup_f64((const double*)f)));
}
};
+24 -24
View File
@@ -373,7 +373,7 @@ public:
__forceinline u32 minv_u32() const
{
return vminvq_u32(v4s);
return vminvq_u32(vreinterpretq_u32_s32(v4s));
}
__forceinline u32 maxv_s32() const
@@ -383,7 +383,7 @@ public:
__forceinline u32 maxv_u32() const
{
return vmaxvq_u32(v4s);
return vmaxvq_u32(vreinterpretq_u32_s32(v4s));
}
__forceinline static int min_i16(int a, int b)
@@ -508,12 +508,12 @@ public:
__forceinline GSVector4i pkl32(const GSVector4i& a) const
{
return GSVector4i(vuzp1q_u32(v4s, a.v4s));
return GSVector4i(vuzp1q_s32(v4s, a.v4s));
}
__forceinline GSVector4i pku32(const GSVector4i& a) const
{
return GSVector4i(vuzp2q_u32(v4s, a.v4s));
return GSVector4i(vuzp2q_s32(v4s, a.v4s));
}
__forceinline GSVector4i upl8(const GSVector4i& a) const
@@ -701,7 +701,7 @@ public:
__forceinline GSVector4i srl16(s32 i) const
{
return GSVector4i(vreinterpretq_s32_u16(vshlq_u16(vreinterpretq_u16_s32(v4s), vdupq_n_u16(-i))));
return GSVector4i(vreinterpretq_s32_u16(vshlq_u16(vreinterpretq_u16_s32(v4s), vdupq_n_s16(-i))));
}
__forceinline GSVector4i srlv16(const GSVector4i& v) const
@@ -764,7 +764,7 @@ public:
__forceinline GSVector4i srav32(const GSVector4i& v) const
{
return GSVector4i(vshlq_s32(vreinterpretq_u32_s32(v4s), vnegq_s32(v.v4s)));
return GSVector4i(vshlq_s32(v4s, vnegq_s32(v.v4s)));
}
template <int i>
@@ -775,7 +775,7 @@ public:
__forceinline GSVector4i sll64(s32 i) const
{
return GSVector4i(vreinterpretq_s32_s64(vshlq_s64(vreinterpretq_s64_s32(v4s), vdupq_n_s16(i))));
return GSVector4i(vreinterpretq_s32_s64(vshlq_s64(vreinterpretq_s64_s32(v4s), vdupq_n_s64(i))));
}
__forceinline GSVector4i sllv64(const GSVector4i& v) const
@@ -791,7 +791,7 @@ public:
__forceinline GSVector4i sra64(s32 i) const
{
return GSVector4i(vreinterpretq_s32_s64(vshlq_s64(vreinterpretq_s64_s32(v4s), vdupq_n_s16(-i))));
return GSVector4i(vreinterpretq_s32_s64(vshlq_s64(vreinterpretq_s64_s32(v4s), vdupq_n_s64(-i))));
}
__forceinline GSVector4i srav64(const GSVector4i& v) const
@@ -808,7 +808,7 @@ public:
__forceinline GSVector4i srl64(s32 i) const
{
return GSVector4i(vreinterpretq_s32_u64(vshlq_u64(vreinterpretq_u64_s32(v4s), vdupq_n_u16(-i))));
return GSVector4i(vreinterpretq_s32_u64(vshlq_u64(vreinterpretq_u64_s32(v4s), vdupq_n_s64(-i))));
}
__forceinline GSVector4i srlv64(const GSVector4i& v) const
@@ -858,7 +858,7 @@ public:
//return GSVector4i(vreinterpretq_s32_s16(vpaddq_s16(vreinterpretq_s16_s32(v4s), vreinterpretq_s16_s32(v.v4s))));
const int16x8_t a = vreinterpretq_s16_s32(v4s);
const int16x8_t b = vreinterpretq_s16_s32(v.v4s);
return GSVector4i(vqaddq_s16(vuzp1q_s16(a, b), vuzp2q_s16(a, b)));
return GSVector4i(vreinterpretq_s32_s16(vqaddq_s16(vuzp1q_s16(a, b), vuzp2q_s16(a, b))));
}
__forceinline GSVector4i addus8(const GSVector4i& v) const
@@ -888,7 +888,7 @@ public:
__forceinline GSVector4i hsub32(const GSVector4i& v) const
{
return GSVector4i(vsubq_u32(vuzp1q_u32(v4s, v.v4s), vuzp2q_u32(v4s, v.v4s)));
return GSVector4i(vsubq_s32(vuzp1q_s32(v4s, v.v4s), vuzp2q_s32(v4s, v.v4s)));
}
__forceinline GSVector4i subs8(const GSVector4i& v) const
@@ -1029,62 +1029,62 @@ public:
__forceinline GSVector4i gt8(const GSVector4i& v) const
{
return GSVector4i(vreinterpretq_s32_s8(vcgtq_s8(vreinterpretq_s8_s32(v4s), vreinterpretq_s8_s32(v.v4s))));
return GSVector4i(vreinterpretq_s32_u8(vcgtq_s8(vreinterpretq_s8_s32(v4s), vreinterpretq_s8_s32(v.v4s))));
}
__forceinline GSVector4i gt16(const GSVector4i& v) const
{
return GSVector4i(vreinterpretq_s32_s16(vcgtq_s16(vreinterpretq_s16_s32(v4s), vreinterpretq_s16_s32(v.v4s))));
return GSVector4i(vreinterpretq_s32_u16(vcgtq_s16(vreinterpretq_s16_s32(v4s), vreinterpretq_s16_s32(v.v4s))));
}
__forceinline GSVector4i gt32(const GSVector4i& v) const
{
return GSVector4i(vcgtq_s32(v4s, v.v4s));
return GSVector4i(vreinterpretq_s32_u32(vcgtq_s32(v4s, v.v4s)));
}
__forceinline GSVector4i ge8(const GSVector4i& v) const
{
return GSVector4i(vreinterpretq_s32_s8(vcgeq_s8(vreinterpretq_s8_s32(v4s), vreinterpretq_s8_s32(v.v4s))));
return GSVector4i(vreinterpretq_s32_u8(vcgeq_s8(vreinterpretq_s8_s32(v4s), vreinterpretq_s8_s32(v.v4s))));
}
__forceinline GSVector4i ge16(const GSVector4i& v) const
{
return GSVector4i(vreinterpretq_s32_s16(vcgeq_s16(vreinterpretq_s16_s32(v4s), vreinterpretq_s16_s32(v.v4s))));
return GSVector4i(vreinterpretq_s32_u16(vcgeq_s16(vreinterpretq_s16_s32(v4s), vreinterpretq_s16_s32(v.v4s))));
}
__forceinline GSVector4i ge32(const GSVector4i& v) const
{
return GSVector4i(vcgeq_s32(v4s, v.v4s));
return GSVector4i(vreinterpretq_s32_u32(vcgeq_s32(v4s, v.v4s)));
}
__forceinline GSVector4i lt8(const GSVector4i& v) const
{
return GSVector4i(vreinterpretq_s32_s8(vcltq_s8(vreinterpretq_s8_s32(v4s), vreinterpretq_s8_s32(v.v4s))));
return GSVector4i(vreinterpretq_s32_u8(vcltq_s8(vreinterpretq_s8_s32(v4s), vreinterpretq_s8_s32(v.v4s))));
}
__forceinline GSVector4i lt16(const GSVector4i& v) const
{
return GSVector4i(vreinterpretq_s32_s16(vcltq_s16(vreinterpretq_s16_s32(v4s), vreinterpretq_s16_s32(v.v4s))));
return GSVector4i(vreinterpretq_s32_u16(vcltq_s16(vreinterpretq_s16_s32(v4s), vreinterpretq_s16_s32(v.v4s))));
}
__forceinline GSVector4i lt32(const GSVector4i& v) const
{
return GSVector4i(vcltq_s32(v4s, v.v4s));
return GSVector4i(vreinterpretq_s32_u32(vcltq_s32(v4s, v.v4s)));
}
__forceinline GSVector4i le8(const GSVector4i& v) const
{
return GSVector4i(vreinterpretq_s32_s8(vcleq_s8(vreinterpretq_s8_s32(v4s), vreinterpretq_s8_s32(v.v4s))));
return GSVector4i(vreinterpretq_s32_u8(vcleq_s8(vreinterpretq_s8_s32(v4s), vreinterpretq_s8_s32(v.v4s))));
}
__forceinline GSVector4i le16(const GSVector4i& v) const
{
return GSVector4i(vreinterpretq_s32_s16(vcleq_s16(vreinterpretq_s16_s32(v4s), vreinterpretq_s16_s32(v.v4s))));
return GSVector4i(vreinterpretq_s32_u16(vcleq_s16(vreinterpretq_s16_s32(v4s), vreinterpretq_s16_s32(v.v4s))));
}
__forceinline GSVector4i le32(const GSVector4i& v) const
{
return GSVector4i(vcleq_s32(v4s, v.v4s));
return GSVector4i(vreinterpretq_s32_u32(vcleq_s32(v4s, v.v4s)));
}
@@ -1112,7 +1112,7 @@ public:
__forceinline bool allfalse() const
{
// MSB should be clear in all 8-bit lanes.
return (vmaxvq_u32(vreinterpretq_u8_s32(v4s)) & 0x80) != 0x80;
return (vmaxvq_u8(vreinterpretq_u8_s32(v4s)) & 0x80) != 0x80;
}
template <int i>
+97 -8
View File
@@ -1007,6 +1007,23 @@ bool GSDevice::ResizeRenderTarget(GSTexture** t, int w, int h, bool preserve_con
return true;
}
void GSDevice::BeginDSAsRT(GSTexture* ds, const GSVector4i& drawarea)
{
// Create a temporary RT and copy the area needed for the draw.
const int w = ds->GetWidth();
const int h = ds->GetHeight();
m_ds_as_rt = g_gs_device->CreateRenderTarget(w, h, GSTexture::Format::Float32, false, true);
const GSVector4 dRect(drawarea);
const GSVector4 sRect(dRect.x / w, dRect.y / h, dRect.z / w, dRect.w / h);
StretchRect(ds, sRect, m_ds_as_rt, dRect, ShaderConvert::FLOAT32_DEPTH_TO_COLOR, false);
}
void GSDevice::EndDSAsRT()
{
Recycle(m_ds_as_rt);
m_ds_as_rt = nullptr;
}
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
@@ -1150,10 +1167,12 @@ static const char* GetVSExpandName(GSHWDrawConfig::VSExpand vsexpand)
{
switch (vsexpand)
{
case GSHWDrawConfig::VSExpand::None: return "None";
case GSHWDrawConfig::VSExpand::Point: return "Point";
case GSHWDrawConfig::VSExpand::Line: return "Line";
case GSHWDrawConfig::VSExpand::Sprite: return "Sprite";
case GSHWDrawConfig::VSExpand::None: return "None";
case GSHWDrawConfig::VSExpand::Point: return "Point";
case GSHWDrawConfig::VSExpand::Line: return "Line";
case GSHWDrawConfig::VSExpand::Sprite: return "Sprite";
case GSHWDrawConfig::VSExpand::LineAA1: return "LineAA1";
case GSHWDrawConfig::VSExpand::TriangleAA1: return "TriangleAA1";
}
return "Unknown";
}
@@ -1354,6 +1373,7 @@ static const char* GetBlendFactorFormula(GSDevice::BlendFactor blendfactor)
case GSDevice::CONST_ONE: return "1";
case GSDevice::CONST_ZERO: return "0";
}
return "Unknown";
}
static const char* GetDestinationAlphaModeName(GSHWDrawConfig::DestinationAlphaMode datm)
@@ -1394,6 +1414,18 @@ static const char* GetSetDATMName(SetDATM datm)
return "Unknown";
}
static const char* GetPSAA1Name(u32 aa1)
{
switch (static_cast<GSHWDrawConfig::PS_AA1>(aa1))
{
case GSHWDrawConfig::PS_AA1::NONE: return "NONE";
case GSHWDrawConfig::PS_AA1::LINE: return "LINE";
case GSHWDrawConfig::PS_AA1::TRIANGLE: return "TRIANGLE";
case GSHWDrawConfig::PS_AA1::TRIANGLE_SW_Z: return "TRIANGLE_SW_Z";
}
return "Unknown";
}
static void DumpPSSelector(DrawConfigWriter& out, const GSHWDrawConfig::PSSelector& ps)
{
out.WriteLn("aem_fmt: {}", ps.aem_fmt);
@@ -1450,6 +1482,9 @@ static void DumpPSSelector(DrawConfigWriter& out, const GSHWDrawConfig::PSSelect
out.WriteLn("point_sampler: {}", ps.point_sampler);
out.WriteLn("region_rect: {}", ps.region_rect);
out.WriteLn("scanmsk: {} ({})", GSUtil::GetSCANMSKName(ps.scanmsk), ps.scanmsk);
out.WriteLn("aa1: {} ({})", static_cast<u32>(ps.aa1), GetPSAA1Name(static_cast<u32>(ps.aa1)));
out.WriteLn("abe: {}", static_cast<u32>(ps.abe));
out.WriteLn("sw_aniso: {}", ps.sw_aniso);
}
static void DumpVSSelector(DrawConfigWriter& out, const GSHWDrawConfig::VSSelector& vs)
@@ -1495,7 +1530,6 @@ static void DumpSamplerSelector(DrawConfigWriter& out, const GSHWDrawConfig::Sam
out.WriteLn("tav: {}", ss.tav);
out.WriteLn("biln: {}", ss.biln);
out.WriteLn("triln: {} ({})", GetSSTrilnName(static_cast<GS_MIN_FILTER>(ss.triln)), ss.triln);
out.WriteLn("aniso: {}", ss.aniso);
out.WriteLn("lodclamp: {}", ss.lodclamp);
}
@@ -1525,8 +1559,51 @@ static void DumpBlendMultipass(DrawConfigWriter& out, const GSHWDrawConfig::Blen
DumpBlendState(out.WithIndent(), bmp.blend);
}
template<typename T, typename U = int>
static void DumpVector4(DrawConfigWriter& out, const char* name, const T& val)
{
out.WriteLn("{}: [{}, {}, {}, {}]", name, val.x, val.y, val.z, val.w);
};
template<typename T>
static void DumpVector2(DrawConfigWriter& out, const char* name, const T& val)
{
out.WriteLn("{}: [{}, {}]", name, val.x, val.y);
};
static void DumpPSConstantBuffer(DrawConfigWriter& out, const GSHWDrawConfig::PSConstantBuffer& cb)
{
DumpVector4(out, "FogColor_AREF", cb.FogColor_AREF);
DumpVector4(out, "WH", cb.WH);
DumpVector4(out, "TA_MaxDepth_Af", cb.TA_MaxDepth_Af);
DumpVector4(out, "FbMask", cb.FbMask);
DumpVector4(out, "HalfTexel", cb.HalfTexel);
DumpVector4(out, "MinMax", cb.MinMax);
DumpVector4(out, "LODParams", cb.LODParams);
DumpVector4(out, "STRange", cb.STRange);
DumpVector4(out, "ChannelShuffle", cb.ChannelShuffle);
DumpVector2(out, "ChannelShuffleOffset", cb.ChannelShuffleOffset);
DumpVector2(out, "TCOffsetHack", cb.TCOffsetHack);
DumpVector2(out, "STScale", cb.STScale);
DumpVector4(out, "DitherMatrix_0", cb.DitherMatrix[0]);
DumpVector4(out, "DitherMatrix_1", cb.DitherMatrix[1]);
DumpVector4(out, "DitherMatrix_2", cb.DitherMatrix[2]);
DumpVector4(out, "DitherMatrix_3", cb.DitherMatrix[3]);
DumpVector4(out, "ScaleFactor", cb.ScaleFactor);
}
static void DumpVSConstantBuffer(DrawConfigWriter& out, const GSHWDrawConfig::VSConstantBuffer& cb)
{
DumpVector2(out, "vertex_scale", cb.vertex_scale);
DumpVector2(out, "vertex_offset", cb.vertex_offset);
DumpVector2(out, "texture_scale", cb.texture_scale);
DumpVector2(out, "texture_offset", cb.texture_offset);
DumpVector2(out, "point_size", cb.point_size);
DumpVector2(out, "max_depth", cb.max_depth);
}
static void DumpConfig(DrawConfigWriter& out, const GSHWDrawConfig& conf,
bool ps, bool vs, bool bs, bool dss, bool ss, bool asp, bool bmp)
bool ps, bool vs, bool bs, bool dss, bool ss, bool asp, bool bmp, bool cbvs, bool cbps)
{
out.WriteLn("topology: {} ({})", GetTopologyName(conf.topology), static_cast<u32>(conf.topology));
out.WriteLn("require_one_barrier: {}", conf.require_one_barrier);
@@ -1578,15 +1655,27 @@ static void DumpConfig(DrawConfigWriter& out, const GSHWDrawConfig& conf,
out.WriteLn("blend_multi_pass:");
DumpBlendMultipass(out.WithIndent(), conf.blend_multi_pass);
}
if (cbvs)
{
out.WriteLn("cb_vs:");
DumpVSConstantBuffer(out.WithIndent(), conf.cb_vs);
}
if (cbps)
{
out.WriteLn("cb_ps:");
DumpPSConstantBuffer(out.WithIndent(), conf.cb_ps);
}
}
void GSHWDrawConfig::DumpConfig(const std::string& path, const GSHWDrawConfig& conf,
bool ps, bool vs, bool bs, bool dss, bool ss, bool asp, bool bmp)
bool ps, bool vs, bool bs, bool dss, bool ss, bool asp, bool bmp, bool cbvs, bool cbps)
{
if (FileSystem::ManagedCFilePtr file = FileSystem::OpenManagedCFile(path.c_str(), "w"))
{
DrawConfigWriter writer;
::DumpConfig(writer, conf, ps, vs, bs, dss, ss, asp, bmp);
::DumpConfig(writer, conf, ps, vs, bs, dss, ss, asp, bmp, cbvs, cbps);
fwrite(writer.buffer.data(), 1, writer.buffer.size(), file.get());
}
}
+87 -17
View File
@@ -304,6 +304,7 @@ struct alignas(16) GSHWDrawConfig
using VSExpand = GSShader::VSExpand;
using PS_ATST = GSShader::PS_ATST;
using PS_AFAIL = GSShader::PS_AFAIL;
using PS_AA1 = GSShader::PS_AA1;
#pragma pack(push, 1)
struct VSSelector
{
@@ -315,8 +316,8 @@ struct alignas(16) GSHWDrawConfig
u8 tme : 1;
u8 iip : 1;
u8 point_size : 1; ///< Set when points need to be expanded without VS expanding.
VSExpand expand : 2;
u8 _free : 2;
VSExpand expand : 3;
u8 _free : 1;
};
u8 key;
};
@@ -324,7 +325,10 @@ struct alignas(16) GSHWDrawConfig
VSSelector(u8 k): key(k) {}
/// Returns true if the fixed index buffer should be used.
__fi bool UseExpandIndexBuffer() const { return (expand == VSExpand::Point || expand == VSExpand::Sprite); }
__fi bool UseFixedExpandIndexBuffer() const { return (expand == VSExpand::Point || expand == VSExpand::Sprite); }
/// Return true if the index buffer should be bound as a vertex shader resource.
__fi bool UseVSExpandIndexBuffer() const { return (expand == VSExpand::TriangleAA1); }
};
static_assert(sizeof(VSSelector) == 1, "VSSelector is a single byte");
@@ -415,6 +419,13 @@ struct alignas(16) GSHWDrawConfig
// Scan mask
u32 scanmsk : 2;
// AA1
PS_AA1 aa1 : 2; // Pixel shader AA1 primitive. Must be used in conjunction with VS AA1 expand.
u32 abe : 1; // Alpha blend enabled. Currently only used for emulating AA1/ABE interaction.
// Anisotropic filtering
u32 sw_aniso : 5;
};
struct
@@ -441,7 +452,8 @@ struct alignas(16) GSHWDrawConfig
{
const bool afail_needs_depth = afail == PS_AFAIL::FB_ONLY || afail == PS_AFAIL::RGB_ONLY_SW_Z;
const bool ztst_needs_depth = ztst == ZTST_GEQUAL || ztst == ZTST_GREATER;
return afail_needs_depth || ztst_needs_depth;
const bool aa1_needs_depth = aa1 == PS_AA1::TRIANGLE_SW_Z;
return afail_needs_depth || ztst_needs_depth || aa1_needs_depth;
}
/// Disables color output from the pixel shader, this is done when all channels are masked.
@@ -459,6 +471,20 @@ struct alignas(16) GSHWDrawConfig
// disable both outputs.
no_color = no_color1 = 1;
}
/// Disables depth output from the pixel shader.
__fi void DisableDepthOutput()
{
if (afail == PS_AFAIL::RGB_ONLY_SW_Z)
{
afail = PS_AFAIL::RGB_ONLY;
}
if (aa1 == PS_AA1::TRIANGLE_SW_Z)
{
aa1 = PS_AA1::TRIANGLE;
}
}
};
static_assert(sizeof(PSSelector) == 12, "PSSelector is 12 bytes");
#pragma pack(pop)
@@ -482,7 +508,6 @@ struct alignas(16) GSHWDrawConfig
u8 tav : 1;
u8 biln : 1;
u8 triln : 3;
u8 aniso : 1;
u8 lodclamp : 1;
};
u8 key;
@@ -618,6 +643,46 @@ struct alignas(16) GSHWDrawConfig
return true;
}
};
struct alignas(16) VSPushConstants
{
u32 base_vertex;
u32 base_index;
u32 _pad0;
u32 _pad1;
__fi VSPushConstants()
{
memset(static_cast<void*>(this), 0, sizeof(*this));
}
__fi VSPushConstants(const VSPushConstants& other)
{
memcpy(static_cast<void*>(this), static_cast<const void*>(&other), sizeof(*this));
}
__fi VSPushConstants& operator=(const VSPushConstants& other)
{
new (this) VSPushConstants(other);
return *this;
}
__fi bool operator==(const VSPushConstants& other) const
{
return BitEqual(*this, other);
}
__fi bool operator!=(const VSPushConstants& other) const
{
return !(*this == other);
}
__fi bool Update(const VSPushConstants& other)
{
if (*this == other)
return false;
memcpy(static_cast<void*>(this), static_cast<const void*>(&other), sizeof(*this));
return true;
}
};
static_assert(sizeof(VSPushConstants) == 16, "VSPushConstants wrong size");
struct alignas(16) PSConstantBuffer
{
GSVector4 FogColor_AREF;
@@ -747,7 +812,6 @@ struct alignas(16) GSHWDrawConfig
};
GSTexture* rt; ///< Render target
GSTexture* ds_as_rt; ///< Depth as color (if supported)
GSTexture* ds; ///< Depth stencil
GSTexture* tex; ///< Source texture
GSTexture* pal; ///< Palette texture
@@ -776,8 +840,8 @@ struct alignas(16) GSHWDrawConfig
AlphaTestMode alpha_test;
DestinationAlphaMode destination_alpha;
SetDATM datm : 2;
bool line_expand : 1;
SetDATM datm;
bool line_expand;
struct AlphaPass
{
@@ -815,7 +879,8 @@ struct alignas(16) GSHWDrawConfig
// Dumping
static void DumpConfig(const std::string& path, const GSHWDrawConfig& conf,
bool ps = true, bool vs = true, bool bs = true, bool dss = true, bool ss = true, bool asp = true, bool bmp = true);
bool ps = true, bool vs = true, bool bs = true, bool dss = true, bool ss = true, bool asp = true, bool bmp = true,
bool cbvs = true, bool cbps = true);
};
static inline u32 GetExpansionFactor(GSHWDrawConfig::VSExpand expand)
@@ -824,9 +889,12 @@ static inline u32 GetExpansionFactor(GSHWDrawConfig::VSExpand expand)
{
case GSHWDrawConfig::VSExpand::Point:
case GSHWDrawConfig::VSExpand::Line:
case GSHWDrawConfig::VSExpand::LineAA1:
return 4;
case GSHWDrawConfig::VSExpand::Sprite:
return 2;
case GSHWDrawConfig::VSExpand::TriangleAA1:
return 7;
default:
return 1;
}
@@ -863,13 +931,6 @@ public:
Performance
};
enum class DepthFeedbackSupport : u8
{
None, // No support for depth feedback loops.
Depth, // Implement depth feedback loops directly on the depth buffer.
DepthAsRT, // Implement depth feedback loops by first converting depth to a color RT.
};
// clang-format off
struct FeatureSupport
{
@@ -888,11 +949,14 @@ public:
bool stencil_buffer : 1; ///< Supports stencil buffer, and can use for DATE.
bool cas_sharpening : 1; ///< Supports sufficient functionality for contrast adaptive sharpening.
bool test_and_sample_depth: 1; ///< Supports concurrently binding the depth-stencil buffer for sampling and depth testing.
DepthFeedbackSupport depth_feedback : 2; ///< Support for depth feedback loops.
bool depth_feedback : 1; ///< Depth feedback loops can be done with DS directly (otherwise need to copy to separate RT). Implies `feedback_loops`.
bool aa1 : 1; ///< Supports the GS AA1 feature.
FeatureSupport()
{
memset(this, 0, sizeof(*this));
}
/// Supports feedback loops through either texture barriers or rt copies.
bool feedback_loops() const { return texture_barrier || multidraw_fb_copy; }
};
struct MultiStretchRect
@@ -970,6 +1034,7 @@ protected:
GSTexture* m_current = nullptr;
GSTexture* m_cas = nullptr;
GSTexture* m_colclip_rt = nullptr; ///< Temp hw colclip texture
GSTexture* m_ds_as_rt = nullptr; ///< Depth as color
bool AcquireWindow(bool recreate_window);
@@ -1007,6 +1072,11 @@ public:
void SetColorClipTexture(GSTexture* tex) { m_colclip_rt = tex; }
bool IsDSInRTActive() const { return m_ds_as_rt; }
/// Create a temporary color clone of depth for depth feedback
virtual void BeginDSAsRT(GSTexture* ds, const GSVector4i& drawarea);
void EndDSAsRT();
/// Returns a string representing the specified API.
static const char* RenderAPIToString(RenderAPI api);
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0+
#include "GSDirtyRect.h"
#include <climits>
#include <vector>
GSDirtyRect::GSDirtyRect() :
+1
View File
@@ -4,6 +4,7 @@
#pragma once
#include "common/AlignedMalloc.h"
#include <climits>
template <class T>
struct Element
+4 -1
View File
@@ -718,7 +718,7 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
const GSTexture* current = g_gs_device->GetCurrent();
const GSVector2i internal_res = GetInternalResolution();
if (current->GetWidth() > internal_res.x || current->GetHeight() > internal_res.y)
if (current && (current->GetWidth() > internal_res.x || current->GetHeight() > internal_res.y))
g_gs_device->Resize(internal_res.x, internal_res.y);
}
@@ -840,6 +840,9 @@ void GSRenderer::VSync(u32 field, bool registers_written, bool idle_frame)
}
}
}
if (GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()) && GSConfig.SaveTransferImages)
DumpTransferImages();
}
void GSRenderer::QueueSnapshot(const std::string& path, const u32 gsdump_frames)
+23 -4
View File
@@ -11,10 +11,12 @@ namespace GSShader {
enum class VSExpand : uint8_t
{
None = 0,
Point = 1,
Line = 2,
Sprite = 3,
None = 0,
Point = 1,
Line = 2,
Sprite = 3,
LineAA1 = 4,
TriangleAA1 = 5,
};
enum class PS_ATST : uint32_t
@@ -37,4 +39,21 @@ enum class PS_AFAIL : uint32_t
RGB_ONLY_SW_Z = 5, ///< RGB only with software Z discard
};
// Identical to GS_ZTST
enum class ZTST : uint32_t
{
NEVER = 0,
ALWAYS = 1,
GEQUAL = 2,
GREATER = 3,
};
enum class PS_AA1 : uint32_t
{
NONE = 0, ///< No AA1
LINE = 1, ///< AA1 lines
TRIANGLE = 2, ///< AA1 triangles
TRIANGLE_SW_Z = 3, ///< AA1 triangles with software Z discard
};
} // namespace GSShader
@@ -30,6 +30,10 @@ public:
struct VertexAlpha
{
int min, max;
// Separate inference for depth if using AA1 coverage alpha, since edges don't write depth.
int depth_min, depth_max;
bool valid;
};
bool m_accurate_stq = false;
+31 -7
View File
@@ -466,28 +466,52 @@ GSRendererType D3D::GetPreferredRenderer()
}
}
wil::com_ptr_nothrow<ID3DBlob> D3D::CompileShader(D3D::ShaderType type, D3D_FEATURE_LEVEL feature_level, bool debug,
const char* D3D::ShaderModelToCacheString(D3D::ShaderModel shader_model)
{
switch (shader_model)
{
case ShaderModel::SM40:
return "sm40";
case ShaderModel::SM41:
return "sm41";
case ShaderModel::SM50:
return "sm50";
case ShaderModel::SM51:
return "sm51";
default:
return "unk";
}
}
wil::com_ptr_nothrow<ID3DBlob> D3D::CompileShader(D3D::ShaderType type, D3D::ShaderModel shader_model, bool debug,
const std::string_view code, const D3D_SHADER_MACRO* macros /* = nullptr */,
const char* entry_point /* = "main" */)
{
const char* target;
switch (feature_level)
switch (shader_model)
{
case D3D_FEATURE_LEVEL_10_0:
case ShaderModel::SM40:
{
static constexpr std::array<const char*, 4> targets = {{"vs_4_0", "ps_4_0", "cs_4_0"}};
target = targets[static_cast<int>(type)];
}
break;
case D3D_FEATURE_LEVEL_11_0:
case ShaderModel::SM41:
{
static constexpr std::array<const char*, 4> targets = {{"vs_4_1", "ps_4_1", "cs_4_1"}};
target = targets[static_cast<int>(type)];
}
break;
case ShaderModel::SM50:
{
static constexpr std::array<const char*, 4> targets = {{"vs_5_0", "ps_5_0", "cs_5_0"}};
target = targets[static_cast<int>(type)];
}
break;
case D3D_FEATURE_LEVEL_11_1:
case ShaderModel::SM51:
default:
{
static constexpr std::array<const char*, 4> targets = {{"vs_5_1", "ps_5_1", "cs_5_1"}};
@@ -496,8 +520,8 @@ wil::com_ptr_nothrow<ID3DBlob> D3D::CompileShader(D3D::ShaderType type, D3D_FEAT
break;
}
static constexpr UINT flags_non_debug = D3DCOMPILE_OPTIMIZATION_LEVEL3;
static constexpr UINT flags_debug = D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_DEBUG | D3DCOMPILE_DEBUG_NAME_FOR_SOURCE;
static constexpr UINT flags_non_debug = D3DCOMPILE_OPTIMIZATION_LEVEL3 | D3DCOMPILE_IEEE_STRICTNESS;
static constexpr UINT flags_debug = D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_DEBUG | D3DCOMPILE_DEBUG_NAME_FOR_SOURCE | D3DCOMPILE_IEEE_STRICTNESS;
wil::com_ptr_nothrow<ID3DBlob> blob;
wil::com_ptr_nothrow<ID3DBlob> error_blob;
+11 -1
View File
@@ -63,6 +63,16 @@ namespace D3D
Compute
};
wil::com_ptr_nothrow<ID3DBlob> CompileShader(ShaderType type, D3D_FEATURE_LEVEL feature_level, bool debug,
enum class ShaderModel
{
SM40 = 0x40, // DX11 FL 10_0
SM41 = 0x41, // DX11 FL 10_1
SM50 = 0x50, // DX11 FL 11_0
SM51 = 0x51, // DX12
};
const char* ShaderModelToCacheString(ShaderModel shader_model);
wil::com_ptr_nothrow<ID3DBlob> CompileShader(ShaderType type, ShaderModel shader_model, bool debug,
const std::string_view code, const D3D_SHADER_MACRO* macros = nullptr, const char* entry_point = "main");
}; // namespace D3D
+20 -14
View File
@@ -56,12 +56,27 @@ bool D3D11ShaderCache::CacheIndexKey::operator!=(const CacheIndexKey& key) const
bool D3D11ShaderCache::Open(D3D_FEATURE_LEVEL feature_level, bool debug)
{
m_feature_level = feature_level;
switch (feature_level)
{
case D3D_FEATURE_LEVEL_10_0:
m_shader_model = D3D::ShaderModel::SM40;
break;
case D3D_FEATURE_LEVEL_10_1:
m_shader_model = D3D::ShaderModel::SM41;
break;
case D3D_FEATURE_LEVEL_11_0:
m_shader_model = D3D::ShaderModel::SM50;
break;
default:
pxAssert(false);
return false;
}
m_debug = debug;
if (!GSConfig.DisableShaderCache)
{
const std::string base_filename = GetCacheBaseFileName(feature_level, debug);
const std::string base_filename = GetCacheBaseFileName(m_shader_model, debug);
const std::string index_filename = base_filename + ".idx";
const std::string blob_filename = base_filename + ".bin";
@@ -198,19 +213,10 @@ bool D3D11ShaderCache::ReadExisting(const std::string& index_filename, const std
return true;
}
std::string D3D11ShaderCache::GetCacheBaseFileName(D3D_FEATURE_LEVEL feature_level, bool debug)
std::string D3D11ShaderCache::GetCacheBaseFileName(D3D::ShaderModel shader_model, bool debug)
{
std::string base_filename = "d3d_shaders_";
switch (feature_level)
{
case D3D_FEATURE_LEVEL_11_0:
base_filename += "sm50";
break;
default:
base_filename += "unk";
break;
}
base_filename += D3D::ShaderModelToCacheString(shader_model);
if (debug)
base_filename += "_debug";
@@ -400,7 +406,7 @@ wil::com_ptr_nothrow<ID3DBlob> D3D11ShaderCache::CompileAndAddShaderBlob(const C
const std::string_view shader_code, const D3D_SHADER_MACRO* macros, const char* entry_point)
{
wil::com_ptr_nothrow<ID3DBlob> blob =
D3D::CompileShader(key.shader_type, m_feature_level, m_debug, shader_code, macros, entry_point);
D3D::CompileShader(key.shader_type, m_shader_model, m_debug, shader_code, macros, entry_point);
if (!blob)
return {};
+2 -3
View File
@@ -16,7 +16,6 @@ public:
D3D11ShaderCache();
~D3D11ShaderCache();
D3D_FEATURE_LEVEL GetFeatureLevel() const { return m_feature_level; }
bool UsingDebugShaders() const { return m_debug; }
bool Open(D3D_FEATURE_LEVEL feature_level, bool debug);
@@ -74,7 +73,7 @@ private:
using CacheIndex = std::unordered_map<CacheIndexKey, CacheIndexData, CacheIndexEntryHasher>;
static std::string GetCacheBaseFileName(D3D_FEATURE_LEVEL feature_level, bool debug);
static std::string GetCacheBaseFileName(D3D::ShaderModel shader_mode, bool debug);
static CacheIndexKey GetCacheKey(D3D::ShaderType type, const std::string_view shader_code,
const D3D_SHADER_MACRO* macros, const char* entry_point);
@@ -89,6 +88,6 @@ private:
CacheIndex m_index;
D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0;
D3D::ShaderModel m_shader_model = D3D::ShaderModel::SM50;
bool m_debug = false;
};
+281 -164
View File
@@ -57,7 +57,7 @@ GSDevice11::GSDevice11()
m_features.primitive_id = true;
m_features.texture_barrier = false;
m_features.multidraw_fb_copy = GSConfig.OverrideTextureBarriers != 0;
m_features.multidraw_fb_copy = false;
m_features.provoking_vertex_last = false;
m_features.point_expand = false;
m_features.line_expand = false;
@@ -68,15 +68,8 @@ GSDevice11::GSDevice11()
m_features.stencil_buffer = true;
m_features.cas_sharpening = true;
m_features.test_and_sample_depth = false;
if (m_features.multidraw_fb_copy && (GSConfig.DepthFeedbackMode == GSDepthFeedbackMode::Auto ||
GSConfig.DepthFeedbackMode == GSDepthFeedbackMode::Depth))
{
m_features.depth_feedback = GSDevice::DepthFeedbackSupport::Depth;
}
else
{
m_features.depth_feedback = GSDevice::DepthFeedbackSupport::None;
}
m_features.depth_feedback = false;
m_features.aa1 = false;
}
GSDevice11::~GSDevice11() = default;
@@ -414,6 +407,30 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
}
}
if (m_features.aa1)
{
bd.ByteWidth = INDEX_BUFFER_SIZE;
bd.BindFlags = D3D11_BIND_SHADER_RESOURCE;
bd.StructureByteStride = sizeof(u32);
bd.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
if (FAILED(m_dev->CreateBuffer(&bd, nullptr, m_expand_ib_vs.put())))
{
Console.Error("D3D11: Failed to create vertex shader index buffer.");
return false;
}
const CD3D11_SHADER_RESOURCE_VIEW_DESC expand_ib_vs_srv_desc(
D3D11_SRV_DIMENSION_BUFFER, DXGI_FORMAT_UNKNOWN, 0, INDEX_BUFFER_SIZE / sizeof(u32));
if (FAILED(m_dev->CreateShaderResourceView(m_expand_ib_vs.get(), &expand_ib_vs_srv_desc, m_expand_ib_vs_srv.put())))
{
Console.Error("D3D11: Failed to create vertex shader index buffer SRV.");
return false;
}
m_ctx->VSSetShaderResources(5, 1, m_expand_ib_vs_srv.addressof());
}
// rasterizer
memset(&rd, 0, sizeof(rd));
@@ -467,6 +484,18 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
memset(&bd, 0, sizeof(bd));
bd.ByteWidth = sizeof(GSHWDrawConfig::VSConstantBuffer);
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
if (FAILED(m_dev->CreateBuffer(&bd, nullptr, m_vs_pc.put())))
{
Console.Error("D3D11: Failed to create vertex shader push constant buffer.");
return false;
}
memset(&bd, 0, sizeof(bd));
bd.ByteWidth = sizeof(GSHWDrawConfig::PSConstantBuffer);
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
@@ -477,6 +506,8 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
return false;
}
VSSetPushConstants(0, 0, true); // Avoid undefined data.
// create layout
{
@@ -563,6 +594,7 @@ void GSDevice11::Destroy()
m_vs.clear();
m_vs_cb.reset();
m_vs_pc.reset();
m_gs.clear();
m_ps.clear();
m_ps_cb.reset();
@@ -605,16 +637,20 @@ void GSDevice11::Destroy()
void GSDevice11::SetFeatures(IDXGIAdapter1* adapter)
{
// Check these first as others depend on them.
m_features.multidraw_fb_copy = GSConfig.OverrideTextureBarriers != 0;
m_features.vs_expand = (!GSConfig.DisableVertexShaderExpand && m_feature_level >= D3D_FEATURE_LEVEL_11_0);
// Check all three formats, since the feature means any can be used.
m_features.dxt_textures = SupportsTextureFormat(m_dev.get(), DXGI_FORMAT_BC1_UNORM) &&
SupportsTextureFormat(m_dev.get(), DXGI_FORMAT_BC2_UNORM) &&
SupportsTextureFormat(m_dev.get(), DXGI_FORMAT_BC3_UNORM);
m_features.bptc_textures = SupportsTextureFormat(m_dev.get(), DXGI_FORMAT_BC7_UNORM);
m_features.vs_expand = (!GSConfig.DisableVertexShaderExpand && m_feature_level >= D3D_FEATURE_LEVEL_11_0);
m_features.cas_sharpening = (m_feature_level >= D3D_FEATURE_LEVEL_11_0);
m_features.test_and_sample_depth = (m_feature_level >= D3D_FEATURE_LEVEL_11_0);
m_features.depth_feedback = m_features.feedback_loops();
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand && m_features.feedback_loops();
m_max_texture_size = (m_feature_level >= D3D_FEATURE_LEVEL_11_0) ?
D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION :
@@ -1003,7 +1039,7 @@ void GSDevice11::EndPresent()
PopTimestampQuery();
// clear out the swap chain view, it might get resized..
OMSetRenderTargets(nullptr, nullptr, nullptr, nullptr);
OMSetRenderTargets(nullptr, nullptr);
const UINT sync_interval = static_cast<UINT>(m_vsync_mode == GSVSyncMode::FIFO);
const UINT flags = (m_vsync_mode == GSVSyncMode::Disabled && m_using_allow_tearing) ? DXGI_PRESENT_ALLOW_TEARING : 0;
@@ -1140,9 +1176,7 @@ void GSDevice11::DrawPrimitive()
void GSDevice11::DrawIndexedPrimitive()
{
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
PSUpdateShaderState(true, true);
m_ctx->DrawIndexed(m_index.count, m_index.start, m_vertex.start);
DrawIndexedPrimitive(0, m_index.count);
}
void GSDevice11::DrawIndexedPrimitive(int offset, int count)
@@ -1153,6 +1187,43 @@ void GSDevice11::DrawIndexedPrimitive(int offset, int count)
m_ctx->DrawIndexed(count, m_index.start + offset, m_vertex.start);
}
void GSDevice11::DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_indexing, int vs_indexing_expansion)
{
pxAssert(offset + count <= (int)m_index.count);
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
PSUpdateShaderState(true, true);
if (vs_indexing)
{
VSSetPushConstants(m_vertex.start, m_index.start + offset);
m_ctx->Draw(count * vs_indexing_expansion, 0);
}
else
{
VSSetPushConstants(m_vertex.start);
m_ctx->DrawIndexed(count, m_index.start + offset, 0);
}
}
void GSDevice11::Draw(const GSHWDrawConfig& config, int offset, int count)
{
if (config.vs.expand != GSHWDrawConfig::VSExpand::None)
{
const bool vs_indexing = config.vs.UseVSExpandIndexBuffer();
const u32 vs_indexing_expansion = GetExpansionFactor(config.vs.expand);
DrawIndexedPrimitiveVSExpand(offset, count, vs_indexing, vs_indexing_expansion);
}
else
{
DrawIndexedPrimitive(offset, count);
}
}
void GSDevice11::Draw(const GSHWDrawConfig& config)
{
Draw(config, 0, m_index.count);
}
void GSDevice11::CommitClear(GSTexture* t)
{
GSTexture11* T = static_cast<GSTexture11*>(t);
@@ -1335,13 +1406,6 @@ void GSDevice11::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTextur
GSVector2i ds;
if (dTex)
{
// preemptively bind srv if possible
if (m_state.current_rt != sTex && m_state.current_ds != sTex)
PSSetShaderResource(0, sTex);
// ps unbind conflicting srvs
PSUnbindConflictingSRVs(dTex);
ds = dTex->GetSize();
if (draw_in_depth)
OMSetRenderTargets(nullptr, dTex);
@@ -1409,13 +1473,6 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
GSVector2i ds;
if (dTex)
{
// preemptively bind srv if possible
if (m_state.current_rt != sTex && m_state.current_ds != sTex)
PSSetShaderResource(0, sTex);
// ps unbind conflicting srvs
PSUnbindConflictingSRVs(dTex);
ds = dTex->GetSize();
OMSetRenderTargets(dTex, nullptr);
}
@@ -1536,7 +1593,6 @@ void GSDevice11::DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_re
VSSetShader(m_convert.vs.get(), nullptr);
PSSetShader(m_convert.ps[static_cast<int>(shader)].get(), nullptr);
PSUnbindConflictingSRVs(dTex);
OMSetDepthStencilState(dTex->IsRenderTarget() ? m_convert.dss.get() : m_convert.dss_write.get(), 0);
OMSetRenderTargets(dTex->IsRenderTarget() ? dTex : nullptr, dTex->IsDepthStencil() ? dTex : nullptr);
@@ -1833,6 +1889,9 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
sm.AddMacro("PS_NO_COLOR", sel.no_color);
sm.AddMacro("PS_NO_COLOR1", sel.no_color1);
sm.AddMacro("PS_ZTST", sel.ztst);
sm.AddMacro("PS_AA1", static_cast<u32>(sel.aa1));
sm.AddMacro("PS_ABE", sel.abe);
sm.AddMacro("PS_ANISOTROPIC_FILTERING", sel.sw_aniso);
wil::com_ptr_nothrow<ID3D11PixelShader> ps = m_shader_cache.GetPixelShader(m_dev.get(), m_tfx_source, sm.GetPtr(), "ps_main");
i = m_ps.try_emplace(sel, std::move(ps)).first;
@@ -1860,39 +1919,33 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
}
else
{
D3D11_SAMPLER_DESC sd = {};
static constexpr std::array<D3D11_FILTER, 8> filters = {{
D3D11_FILTER_MIN_MAG_MIP_POINT, // 000 / min=point,mag=point,mip=point
D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT, // 001 / min=linear,mag=point,mip=point
D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT, // 010 / min=point,mag=linear,mip=point
D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT, // 011 / min=linear,mag=linear,mip=point
D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR, // 100 / min=point,mag=point,mip=linear
D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, // 101 / min=linear,mag=point,mip=linear
D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR, // 110 / min=point,mag=linear,mip=linear
D3D11_FILTER_MIN_MAG_MIP_LINEAR, // 111 / min=linear,mag=linear,mip=linear
}};
const int anisotropy = GSConfig.MaxAnisotropy;
if (anisotropy > 1 && ssel.aniso)
{
sd.Filter = D3D11_FILTER_ANISOTROPIC;
}
else
{
static constexpr std::array<D3D11_FILTER, 8> filters = {{
D3D11_FILTER_MIN_MAG_MIP_POINT, // 000 / min=point,mag=point,mip=point
D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT, // 001 / min=linear,mag=point,mip=point
D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT, // 010 / min=point,mag=linear,mip=point
D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT, // 011 / min=linear,mag=linear,mip=point
D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR, // 100 / min=point,mag=point,mip=linear
D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, // 101 / min=linear,mag=point,mip=linear
D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR, // 110 / min=point,mag=linear,mip=linear
D3D11_FILTER_MIN_MAG_MIP_LINEAR, // 111 / min=linear,mag=linear,mip=linear
}};
const u8 index = (static_cast<u8>(ssel.IsMipFilterLinear()) << 2) |
(static_cast<u8>(ssel.IsMagFilterLinear()) << 1) |
static_cast<u8>(ssel.IsMinFilterLinear());
const u8 index = (static_cast<u8>(ssel.IsMipFilterLinear()) << 2) |
(static_cast<u8>(ssel.IsMagFilterLinear()) << 1) |
static_cast<u8>(ssel.IsMinFilterLinear());
sd.Filter = filters[index];
}
sd.AddressU = ssel.tau ? D3D11_TEXTURE_ADDRESS_WRAP : D3D11_TEXTURE_ADDRESS_CLAMP;
sd.AddressV = ssel.tav ? D3D11_TEXTURE_ADDRESS_WRAP : D3D11_TEXTURE_ADDRESS_CLAMP;
sd.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
sd.MinLOD = 0.0f;
sd.MaxLOD = (ssel.lodclamp || !ssel.UseMipmapFiltering()) ? 0.25f : FLT_MAX;
sd.MaxAnisotropy = std::clamp(anisotropy, 1, 16);
sd.ComparisonFunc = D3D11_COMPARISON_NEVER;
const D3D11_SAMPLER_DESC sd = {
filters[index], // Filter
ssel.tau ? D3D11_TEXTURE_ADDRESS_WRAP : D3D11_TEXTURE_ADDRESS_CLAMP, // Address u
ssel.tav ? D3D11_TEXTURE_ADDRESS_WRAP : D3D11_TEXTURE_ADDRESS_CLAMP, // Address v
D3D11_TEXTURE_ADDRESS_CLAMP, // Address w
0.0f, // Lod bias
1, // Anisotropy
D3D11_COMPARISON_NEVER, // comparison function
{}, // Border colour
0.0f, // Min lod
(ssel.lodclamp || !ssel.UseMipmapFiltering()) ? 0.25f : FLT_MAX, // Max lod
};
m_dev->CreateSamplerState(&sd, &ss0);
@@ -2032,13 +2085,13 @@ bool GSDevice11::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, cons
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
static const int threadGroupWorkRegionDim = 16;
static constexpr int threadGroupWorkRegionDim = 16;
const int dispatchX = (dTex->GetWidth() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
const int dispatchY = (dTex->GetHeight() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
ID3D11ShaderResourceView* srvs[1] = {*static_cast<GSTexture11*>(sTex)};
ID3D11UnorderedAccessView* uavs[1] = {*static_cast<GSTexture11*>(dTex)};
m_ctx->OMSetRenderTargets(0, nullptr, nullptr);
OMSetRenderTargets(nullptr, nullptr);
m_ctx->UpdateSubresource(m_cas.cb.get(), 0, nullptr, constants.data(), 0, 0);
m_ctx->CSSetConstantBuffers(0, 1, m_cas.cb.addressof());
m_ctx->CSSetShader(sharpen_only ? m_cas.cs_sharpen.get() : m_cas.cs_upscale.get(), nullptr, 0);
@@ -2228,15 +2281,6 @@ void GSDevice11::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSV
m_ctx->ClearDepthStencilView(*static_cast<GSTexture11*>(ds), D3D11_CLEAR_STENCIL, 0.0f, 0);
// preemptively bind srv if possible
if (m_state.current_rt != rt && m_state.current_ds != rt)
PSSetShaderResource(0, rt);
// ps unbind conflicting srvs
PSUnbindConflictingSRVs(ds);
// om
OMSetDepthStencilState(m_date.dss.get(), 1);
@@ -2303,12 +2347,7 @@ void GSDevice11::IAUnmapVertexBuffer(u32 stride, u32 count)
{
m_ctx->Unmap(m_vb.get(), 0);
if (m_state.vb_stride != stride)
{
m_state.vb_stride = stride;
const UINT vb_offset = 0;
m_ctx->IASetVertexBuffers(0, 1, m_vb.addressof(), &stride, &vb_offset);
}
IASetVertexBuffer(m_vb.get(), stride);
m_vertex.count = count;
}
@@ -2325,6 +2364,16 @@ bool GSDevice11::IASetVertexBuffer(const void* vertex, u32 stride, u32 count)
return true;
}
void GSDevice11::IASetVertexBuffer(ID3D11Buffer* buffer, u32 stride)
{
if (m_state.vb != buffer || stride != m_state.vb_stride)
{
const UINT zero = 0;
m_ctx->IASetVertexBuffers(0, 1, &buffer, buffer ? &stride : &zero, &zero);
m_state.vb = buffer;
}
}
bool GSDevice11::IASetExpandVertexBuffer(const void* vertex, u32 stride, u32 count)
{
const u32 size = stride * count;
@@ -2444,10 +2493,73 @@ void GSDevice11::VSSetShader(ID3D11VertexShader* vs, ID3D11Buffer* vs_cb)
}
}
void GSDevice11::VSSetPushConstants(u32 base_vertex, u32 base_index, bool force_update)
{
GSHWDrawConfig::VSPushConstants vs_pc;
vs_pc.base_vertex = base_vertex;
vs_pc.base_index = base_index;
if (m_vs_pc_cache.Update(vs_pc) || force_update)
{
m_ctx->UpdateSubresource(m_vs_pc.get(), 0, NULL, &vs_pc, 0, 0);
}
if (m_state.vs_pc != m_vs_pc.get())
{
m_state.vs_pc = m_vs_pc.get();
m_ctx->VSSetConstantBuffers(1, 1, &m_state.vs_pc);
}
}
u16* GSDevice11::VSMapIndexBuffer(u32 count)
{
if (count > (INDEX_BUFFER_SIZE / sizeof(u16)))
return nullptr;
D3D11_MAP type = D3D11_MAP_WRITE_NO_OVERWRITE;
m_index.start = m_expand_ib_vs_pos;
m_expand_ib_vs_pos += count;
if (m_expand_ib_vs_pos > (INDEX_BUFFER_SIZE / sizeof(u16)))
{
m_index.start = 0;
m_expand_ib_vs_pos = count;
type = D3D11_MAP_WRITE_DISCARD;
}
D3D11_MAPPED_SUBRESOURCE m;
if (FAILED(m_ctx->Map(m_expand_ib_vs.get(), 0, type, 0, &m)))
return nullptr;
return static_cast<u16*>(m.pData) + m_index.start;
}
void GSDevice11::VSUnmapIndexBuffer(u32 count)
{
m_ctx->Unmap(m_expand_ib_vs.get(), 0);
m_index.count = count;
}
bool GSDevice11::VSSetIndexBuffer(const void* index, u32 count)
{
u16* map = VSMapIndexBuffer(count);
if (!map)
return false;
std::memcpy(map, index, count * sizeof(u16));
VSUnmapIndexBuffer(count);
return true;
}
void GSDevice11::PSSetShaderResource(int i, GSTexture* sr)
{
// Update local state only, PSUpdateShaderState updates gpu state.
m_state.ps_pending_srv[i] = *static_cast<GSTexture11*>(sr);
if (sr)
m_state.ps_pending_srv[i] = *static_cast<GSTexture11*>(sr);
else
m_state.ps_pending_srv[i] = nullptr;
}
void GSDevice11::PSSetSamplerState(ID3D11SamplerState* ss0)
@@ -2576,14 +2688,10 @@ void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector
CommitClear(rt);
rtv = *static_cast<GSTexture11*>(rt);
}
if (read_only_dsv)
{
dsv = read_only_dsv;
}
else if (ds)
if (ds)
{
CommitClear(ds);
dsv = *static_cast<GSTexture11*>(ds);
dsv = read_only_dsv ? read_only_dsv : *static_cast<GSTexture11*>(ds);
}
const bool changed = (m_state.rtv != rtv || m_state.dsv != dsv);
@@ -2607,6 +2715,9 @@ void GSDevice11::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector
m_state.dsv = dsv;
m_state.current_ds = ds;
}
PSUnbindConflictingSRVs(m_state.current_rt, read_only_dsv ? nullptr : m_state.current_ds);
if (changed)
m_ctx->OMSetRenderTargets(1, &rtv, dsv);
@@ -2728,7 +2839,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
}
// Destination Alpha Setup
const bool multidraw_fb_copy = m_features.multidraw_fb_copy && (config.require_one_barrier || config.require_full_barrier);
const bool need_barrier = config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy);
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking)
{
primid_texture = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::PrimID, false);
@@ -2742,7 +2853,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
primid_texture, GSVector4(config.drawarea), m_date.primid_init_ps[static_cast<u8>(config.datm)].get(), nullptr, false);
}
else if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::Stencil ||
(config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne && !multidraw_fb_copy))
(config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne && !need_barrier))
SetupDATE(colclip_rt ? colclip_rt : config.rt, config.ds, config.datm, config.drawarea);
if (config.vs.expand != GSHWDrawConfig::VSExpand::None)
@@ -2752,8 +2863,6 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
Console.Error("D3D11: Failed to upload structured vertices (%u)", config.nverts);
return;
}
config.cb_vs.max_depth.y = m_vertex.start;
}
else
{
@@ -2764,12 +2873,17 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
}
}
if (config.vs.UseExpandIndexBuffer())
if (config.vs.UseFixedExpandIndexBuffer())
{
IASetIndexBuffer(m_expand_ib.get());
m_index.start = 0;
m_index.count = config.nindices;
}
else if (config.vs.UseVSExpandIndexBuffer())
{
VSSetIndexBuffer(config.indices, config.nindices);
IASetVertexBuffer(nullptr, 0); // Unbind the vertex buffer to prevent unwanted fetches.
}
else
{
if (!IASetIndexBuffer(config.indices, config.nindices))
@@ -2799,29 +2913,6 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
config.tex = nullptr;
}
// Preemptively bind srv if possible.
// We update the local state, then if there are srv conflicts PSUnbindConflictingSRVs will update the gpu state.
if (config.tex)
{
CommitClear(config.tex);
if (m_state.current_rt != config.tex && m_state.current_ds != config.tex)
PSSetShaderResource(0, config.tex);
}
if (config.pal)
{
CommitClear(config.pal);
if (m_state.current_rt != config.pal && m_state.current_ds != config.pal)
PSSetShaderResource(1, config.pal);
}
// Should be called before changing current gpu state.
PSUnbindConflictingSRVs(colclip_rt ? colclip_rt : config.rt, read_only_dsv ? nullptr : config.ds);
if (config.tex)
PSSetShaderResource(0, config.tex);
if (config.pal)
PSSetShaderResource(1, config.pal);
SetupVS(config.vs, &config.cb_vs);
SetupPS(config.ps, &config.cb_ps, config.sampler);
@@ -2833,12 +2924,12 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
GSHWDrawConfig::BlendState(true, CONST_ONE, CONST_ONE, 3 /* MIN */, CONST_ONE, CONST_ZERO, false, 0));
SetupOM(dss, blend, 0);
OMSetRenderTargets(primid_texture, config.ds, &config.scissor, read_only_dsv);
DrawIndexedPrimitive();
SetRenderHWShaderResources(config, nullptr);
Draw(config);
config.ps.date = 3;
config.alpha_second_pass.ps.date = 3;
SetupPS(config.ps, nullptr, config.sampler);
PSSetShaderResource(3, primid_texture);
}
// Avoid changing framebuffer just to switch from rt+depth to rt and vice versa.
@@ -2846,7 +2937,6 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
GSTexture* draw_ds = config.ds;
// Make sure no tex is bound as both rtv and srv at the same time.
// All conflicts should've been taken care of by PSUnbindConflictingSRVs.
// It is fine to do the optimiation when on slot 0 tex is fb, tex is ds, and slot 2 sw blend as they are copies bound to srv.
if (!draw_rt && draw_ds && m_state.rtv && m_state.current_rt && m_state.rtv == *static_cast<GSTexture11*>(m_state.current_rt) &&
m_state.current_ds == draw_ds && config.tex != m_state.current_rt && m_state.current_rt->GetSize() == draw_ds->GetSize())
{
@@ -2858,31 +2948,43 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
draw_ds = m_state.current_ds;
}
if (draw_rt && (config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy) || (config.tex && config.tex == config.rt)))
const bool tex_is_fb = config.tex && config.tex == draw_rt;
const bool rt_feedbackloop_pass1 = config.ps.IsFeedbackLoopRT() || tex_is_fb;
const bool rt_feedbackloop_pass2 = config.alpha_second_pass.ps.IsFeedbackLoopRT() || tex_is_fb;
if (draw_rt && (((config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
(rt_feedbackloop_pass1 || rt_feedbackloop_pass2))))
{
config.require_one_barrier |= (tex_is_fb && !config.require_full_barrier);
config.alpha_second_pass.require_one_barrier |= (tex_is_fb && !config.require_full_barrier);
// Requires a copy of the RT.
draw_rt_clone = CreateTexture(rtsize.x, rtsize.y, 1, draw_rt->GetFormat(), true);
if (!draw_rt_clone)
Console.Warning("D3D11: Failed to allocate temp texture for RT copy.");
}
if (draw_ds && (config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
m_features.depth_feedback == GSDevice::DepthFeedbackSupport::Depth && config.ps.IsFeedbackLoopDepth())
const bool ds_feedbackloop_pass1 = config.ps.IsFeedbackLoopDepth();
const bool ds_feedbackloop_pass2 = config.alpha_second_pass.ps.IsFeedbackLoopDepth();
if (draw_ds && m_features.depth_feedback && (config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
(ds_feedbackloop_pass1 || ds_feedbackloop_pass2))
{
// Requires a copy of the DS.
draw_ds_clone = CreateTexture(rtsize.x, rtsize.y, 1, draw_ds->GetFormat(), true);
if (!draw_ds_clone)
Console.Warning("D3D11: Failed to allocate temp texture for DS copy.");
}
OMSetRenderTargets(draw_rt, draw_ds, &config.scissor, read_only_dsv);
SetRenderHWShaderResources(config, primid_texture);
SetupOM(config.depth, OMBlendSelector(config.colormask, config.blend), config.blend.constant);
// Clear stencil as close as possible to the RT bind, to avoid framebuffer swaps.
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne && multidraw_fb_copy)
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne && need_barrier)
m_ctx->ClearDepthStencilView(*static_cast<GSTexture11*>(draw_ds), D3D11_CLEAR_STENCIL, 0.0f, 1);
SendHWDraw(config, draw_rt_clone, draw_rt, draw_ds_clone, draw_ds,
SendHWDraw(config, rt_feedbackloop_pass1 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass1 ? draw_ds_clone : nullptr, draw_ds,
config.require_one_barrier, config.require_full_barrier);
if (config.blend_multi_pass.enable)
@@ -2892,7 +2994,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
config.ps.dither = config.blend_multi_pass.dither;
SetupPS(config.ps, &config.cb_ps, config.sampler);
SetupOM(config.depth, OMBlendSelector(config.colormask, config.blend_multi_pass.blend), config.blend_multi_pass.blend.constant);
DrawIndexedPrimitive();
Draw(config);
}
if (config.alpha_second_pass.enable)
@@ -2908,9 +3010,10 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
SetupPS(config.alpha_second_pass.ps, nullptr, config.sampler);
}
const bool one_barrier = config.alpha_second_pass.require_one_barrier && m_features.multidraw_fb_copy;
SetupOM(config.alpha_second_pass.depth, OMBlendSelector(config.alpha_second_pass.colormask, config.blend), config.blend.constant);
SendHWDraw(config, draw_rt_clone, draw_rt, draw_ds_clone, draw_ds,
false, config.alpha_second_pass.require_full_barrier);
SendHWDraw(config, rt_feedbackloop_pass2 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass2 ? draw_ds_clone : nullptr, draw_ds,
one_barrier, config.alpha_second_pass.require_full_barrier);
}
if (colclip_rt)
@@ -2934,56 +3037,70 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config,
GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds,
const bool one_barrier, const bool full_barrier)
{
if (draw_rt_clone || draw_ds_clone)
{
#ifdef PCSX2_DEVBUILD
if ((one_barrier || full_barrier) && !(config.ps.IsFeedbackLoopRT() || config.ps.IsFeedbackLoopDepth())) [[unlikely]]
Console.Warning("D3D11: Possible unnecessary copy detected.");
if ((one_barrier || full_barrier) && !(config.ps.IsFeedbackLoopRT() || config.ps.IsFeedbackLoopDepth())) [[unlikely]]
Console.Warning("D3D11: Possible unnecessary copy detected.");
#endif
auto CopyAndBind = [&](GSVector4i drawarea) {
if (draw_rt_clone)
{
CopyRect(draw_rt, draw_rt_clone, drawarea, drawarea.left, drawarea.top);
if ((one_barrier || full_barrier))
PSSetShaderResource(2, draw_rt_clone);
if (config.tex && config.tex == draw_rt)
PSSetShaderResource(0, draw_rt_clone);
}
if (draw_ds_clone)
{
CopyRect(draw_ds, draw_ds_clone, drawarea, drawarea.left, drawarea.top);
PSSetShaderResource(4, draw_ds_clone);
}
};
const GSVector4i rtsize(0, 0, (draw_rt ? draw_rt : draw_ds)->GetWidth(), (draw_rt ? draw_rt : draw_ds)->GetHeight());
if (full_barrier)
auto CopyAndBind = [&](GSVector4i drawarea) {
if (draw_rt_clone)
{
const u32 draw_list_size = static_cast<u32>(config.drawlist->size());
const u32 indices_per_prim = config.indices_per_prim;
CopyRect(draw_rt, draw_rt_clone, drawarea, drawarea.left, drawarea.top);
if ((one_barrier || full_barrier))
PSSetShaderResource(2, draw_rt_clone);
if (config.tex && config.tex == draw_rt)
PSSetShaderResource(0, draw_rt_clone);
}
if (draw_ds_clone)
{
CopyRect(draw_ds, draw_ds_clone, drawarea, drawarea.left, drawarea.top);
PSSetShaderResource(4, draw_ds_clone);
}
};
pxAssert(config.drawlist && !config.drawlist->empty());
pxAssert(config.drawlist_bbox && static_cast<u32>(config.drawlist_bbox->size()) == draw_list_size);
const GSVector4i rtsize(0, 0, (draw_rt ? draw_rt : draw_ds)->GetWidth(), (draw_rt ? draw_rt : draw_ds)->GetHeight());
for (u32 n = 0, p = 0; n < draw_list_size; n++)
{
const u32 count = (*config.drawlist)[n] * indices_per_prim;
if (full_barrier)
{
const u32 draw_list_size = static_cast<u32>(config.drawlist->size());
const u32 indices_per_prim = config.indices_per_prim;
const GSVector4i original_bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea);
CopyAndBind(ProcessCopyArea(rtsize, original_bbox));
pxAssert(config.drawlist && !config.drawlist->empty());
pxAssert(config.drawlist_bbox && static_cast<u32>(config.drawlist_bbox->size()) == draw_list_size);
DrawIndexedPrimitive(p, count);
p += count;
}
for (u32 n = 0, p = 0; n < draw_list_size; n++)
{
const u32 count = (*config.drawlist)[n] * indices_per_prim;
return;
const GSVector4i original_bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea);
CopyAndBind(ProcessCopyArea(rtsize, original_bbox));
Draw(config, p, count);
p += count;
}
// Optimization: For alpha second pass we can reuse the copy snapshot from the first pass.
CopyAndBind(ProcessCopyArea(rtsize, config.drawarea));
return;
}
DrawIndexedPrimitive();
if (one_barrier)
CopyAndBind(ProcessCopyArea(rtsize, config.drawarea));
Draw(config);
}
void GSDevice11::SetRenderHWShaderResources(const GSHWDrawConfig& config, GSTexture* primid_texture)
{
if (config.tex && config.tex != config.rt)
{
CommitClear(config.tex);
PSSetShaderResource(0, config.tex);
}
if (config.pal)
{
CommitClear(config.pal);
PSSetShaderResource(1, config.pal);
}
if (primid_texture)
PSSetShaderResource(3, primid_texture);
}
+20 -1
View File
@@ -125,12 +125,15 @@ private:
wil::com_ptr_nothrow<ID3D11Buffer> m_ib;
wil::com_ptr_nothrow<ID3D11Buffer> m_expand_vb;
wil::com_ptr_nothrow<ID3D11Buffer> m_expand_ib;
wil::com_ptr_nothrow<ID3D11Buffer> m_expand_ib_vs;
wil::com_ptr_nothrow<ID3D11ShaderResourceView> m_expand_vb_srv;
wil::com_ptr_nothrow<ID3D11ShaderResourceView> m_expand_ib_vs_srv;
D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_10_0;
u32 m_vb_pos = 0; // bytes
u32 m_ib_pos = 0; // indices/sizeof(u32)
u32 m_ib_pos = 0; // indices/sizeof(u16)
u32 m_structured_vb_pos = 0; // bytes
u32 m_expand_ib_vs_pos = 0; // indices/sizeof(u16)
bool m_allow_tearing_supported = false;
bool m_using_flip_model_swap_chain = true;
@@ -146,6 +149,7 @@ private:
ID3D11Buffer* index_buffer;
ID3D11VertexShader* vs;
ID3D11Buffer* vs_cb;
ID3D11Buffer* vs_pc;
std::array<ID3D11ShaderResourceView*, MAX_TEXTURES> ps_pending_srv;
std::array<ID3D11ShaderResourceView*, MAX_TEXTURES> ps_current_srv;
ID3D11PixelShader* ps;
@@ -154,6 +158,7 @@ private:
std::array<ID3D11SamplerState*, MAX_SAMPLERS> ps_current_ss;
GSVector2i viewport;
GSVector4i scissor;
ID3D11Buffer* vb;
u32 vb_stride;
ID3D11DepthStencilState* dss;
u8 sref;
@@ -246,6 +251,7 @@ private:
std::unordered_map<u32, GSVertexShader11> m_vs;
wil::com_ptr_nothrow<ID3D11Buffer> m_vs_cb;
wil::com_ptr_nothrow<ID3D11Buffer> m_vs_pc;
std::unordered_map<u32, wil::com_ptr_nothrow<ID3D11GeometryShader>> m_gs;
std::unordered_map<PSSelector, wil::com_ptr_nothrow<ID3D11PixelShader>, GSHWDrawConfig::PSSelectorHash> m_ps;
wil::com_ptr_nothrow<ID3D11Buffer> m_ps_cb;
@@ -256,6 +262,7 @@ private:
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
GSHWDrawConfig::VSPushConstants m_vs_pc_cache;
D3D11ShaderCache m_shader_cache;
std::string m_tfx_source;
@@ -294,9 +301,15 @@ public:
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
// Helpers and utility draws.
void DrawPrimitive();
void DrawIndexedPrimitive();
void DrawIndexedPrimitive(int offset, int count);
void DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_indexing, int vs_indexing_expansion);
// Main GS primitive draws.
void Draw(const GSHWDrawConfig& config);
void Draw(const GSHWDrawConfig& config, int offset, int count);
void PushDebugGroup(const char* fmt, ...) override;
void PopDebugGroup() override;
@@ -323,6 +336,7 @@ public:
void* IAMapVertexBuffer(u32 stride, u32 count);
void IAUnmapVertexBuffer(u32 stride, u32 count);
bool IASetVertexBuffer(const void* vertex, u32 stride, u32 count);
void IASetVertexBuffer(ID3D11Buffer* buffer, u32 stride);
bool IASetExpandVertexBuffer(const void* vertex, u32 stride, u32 count);
u16* IAMapIndexBuffer(u32 count);
@@ -334,6 +348,10 @@ public:
void IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY topology);
void VSSetShader(ID3D11VertexShader* vs, ID3D11Buffer* vs_cb);
void VSSetPushConstants(u32 base_vertex, u32 base_index = 0, bool force_update = false);
u16* VSMapIndexBuffer(u32 count);
void VSUnmapIndexBuffer(u32 count);
bool VSSetIndexBuffer(const void* indices, u32 count);
void PSSetShaderResource(int i, GSTexture* sr);
void PSSetShader(ID3D11PixelShader* ps, ID3D11Buffer* ps_cb);
@@ -357,6 +375,7 @@ public:
void SendHWDraw(const GSHWDrawConfig& config,
GSTexture* draw_rt_clone, GSTexture* draw_rt, GSTexture* draw_ds_clone, GSTexture* draw_ds,
const bool one_barrier, const bool full_barrier);
void SetRenderHWShaderResources(const GSHWDrawConfig& config, GSTexture* primid_texture);
void ClearSamplerCache() override;
+13 -21
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: GPL-3.0+
#include "GS/Renderers/DX12/D3D12ShaderCache.h"
#include "GS/Renderers/DX11/D3D.h"
#include "GS/GS.h"
#include "Config.h"
@@ -54,16 +53,18 @@ bool D3D12ShaderCache::CacheIndexKey::operator!=(const CacheIndexKey& key) const
source_length != key.source_length);
}
bool D3D12ShaderCache::Open(D3D_FEATURE_LEVEL feature_level, bool debug)
bool D3D12ShaderCache::Open(D3D::ShaderModel shader_model, bool debug)
{
m_feature_level = feature_level;
// Only support SM5.1 for now, which is the minimum for D3D12.
pxAssert(shader_model >= D3D::ShaderModel::SM51);
m_shader_model = shader_model;
m_debug = debug;
bool result = true;
if (!GSConfig.DisableShaderCache)
{
const std::string base_shader_filename = GetCacheBaseFileName("shaders", feature_level, debug);
const std::string base_shader_filename = GetCacheBaseFileName("shaders", m_shader_model, debug);
const std::string shader_index_filename = base_shader_filename + ".idx";
const std::string shader_blob_filename = base_shader_filename + ".bin";
@@ -75,7 +76,7 @@ bool D3D12ShaderCache::Open(D3D_FEATURE_LEVEL feature_level, bool debug)
if (result)
{
const std::string base_pipelines_filename = GetCacheBaseFileName("pipelines", feature_level, debug);
const std::string base_pipelines_filename = GetCacheBaseFileName("pipelines", m_shader_model, debug);
const std::string pipelines_index_filename = base_pipelines_filename + ".idx";
const std::string pipelines_blob_filename = base_pipelines_filename + ".bin";
@@ -133,7 +134,7 @@ void D3D12ShaderCache::InvalidatePipelineCache()
if (GSConfig.DisableShaderCache)
return;
const std::string base_pipelines_filename = GetCacheBaseFileName("pipelines", m_feature_level, m_debug);
const std::string base_pipelines_filename = GetCacheBaseFileName("pipelines", m_shader_model, m_debug);
const std::string pipelines_index_filename = base_pipelines_filename + ".idx";
const std::string pipelines_blob_filename = base_pipelines_filename + ".bin";
CreateNew(pipelines_index_filename, pipelines_blob_filename, m_pipeline_index_file, m_pipeline_blob_file);
@@ -253,21 +254,12 @@ bool D3D12ShaderCache::ReadExisting(const std::string& index_filename, const std
return true;
}
std::string D3D12ShaderCache::GetCacheBaseFileName(const std::string_view type, D3D_FEATURE_LEVEL feature_level, bool debug)
std::string D3D12ShaderCache::GetCacheBaseFileName(const std::string_view type, D3D::ShaderModel shader_model, bool debug)
{
std::string base_filename = "d3d12_";
base_filename += type;
base_filename += "_";
switch (feature_level)
{
case D3D_FEATURE_LEVEL_11_0:
base_filename += "sm50";
break;
default:
base_filename += "unk";
break;
}
base_filename += D3D::ShaderModelToCacheString(shader_model);
if (debug)
base_filename += "_debug";
@@ -497,15 +489,15 @@ D3D12ShaderCache::ComPtr<ID3DBlob> D3D12ShaderCache::CompileAndAddShaderBlob(
{
case EntryType::VertexShader:
blob =
D3D::CompileShader(D3D::ShaderType::Vertex, m_feature_level, m_debug, shader_code, macros, entry_point);
D3D::CompileShader(D3D::ShaderType::Vertex, m_shader_model, m_debug, shader_code, macros, entry_point);
break;
case EntryType::PixelShader:
blob =
D3D::CompileShader(D3D::ShaderType::Pixel, m_feature_level, m_debug, shader_code, macros, entry_point);
D3D::CompileShader(D3D::ShaderType::Pixel, m_shader_model, m_debug, shader_code, macros, entry_point);
break;
case EntryType::ComputeShader:
blob = D3D::CompileShader(
D3D::ShaderType::Compute, m_feature_level, m_debug, shader_code, macros, entry_point);
blob =
D3D::CompileShader(D3D::ShaderType::Compute, m_shader_model, m_debug, shader_code, macros, entry_point);
break;
default:
break;
+4 -4
View File
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0+
#pragma once
#include "GS/Renderers/DX11/D3D.h"
#include "common/Pcsx2Defs.h"
#include "common/HashCombine.h"
@@ -32,10 +33,9 @@ public:
D3D12ShaderCache();
~D3D12ShaderCache();
__fi D3D_FEATURE_LEVEL GetFeatureLevel() const { return m_feature_level; }
__fi bool UsingDebugShaders() const { return m_debug; }
bool Open(D3D_FEATURE_LEVEL feature_level, bool debug);
bool Open(D3D::ShaderModel shader_model, bool debug);
void Close();
__fi ComPtr<ID3DBlob> GetVertexShader(
@@ -95,7 +95,7 @@ private:
using CacheIndex = std::unordered_map<CacheIndexKey, CacheIndexData, CacheIndexEntryHasher>;
static std::string GetCacheBaseFileName(const std::string_view type, D3D_FEATURE_LEVEL feature_level, bool debug);
static std::string GetCacheBaseFileName(const std::string_view type, D3D::ShaderModel shader_model, bool debug);
static CacheIndexKey GetShaderCacheKey(
EntryType type, const std::string_view shader_code, const D3D_SHADER_MACRO* macros, const char* entry_point);
static CacheIndexKey GetPipelineCacheKey(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& gpdesc);
@@ -123,6 +123,6 @@ private:
std::FILE* m_pipeline_blob_file = nullptr;
CacheIndex m_pipeline_index;
D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0;
D3D::ShaderModel m_shader_model = D3D::ShaderModel::SM51;
bool m_debug = false;
};
+163 -88
View File
@@ -282,12 +282,19 @@ bool GSDevice12::CreateDevice(u32& vendor_id)
}
}
// Intel Haswell doesn't actually support DX12 even tho the device is created which results in a crash,
// to get around this check if device can be created using feature level 12 (skylake+).
const bool isIntel = (vendor_id == 0x163C || vendor_id == 0x8086 || vendor_id == 0x8087);
// Create the actual device.
// Intel Haswell DX12 support is specific:
// Newerest drivers have dx12 support disabled so the last driver to support dx12 is 15.40.42.5063.
// Shader cache must be also disabled, and make sure Debug Device option is disabled as well.
// Let's enable it on dev/debug for testing purposes so we don't have to change this all the time.
// TODO: Find out the status of Broadwell.
#ifdef PCSX2_DEVBUILD
hr = D3D12CreateDevice(m_adapter.get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
#else
const bool isIntel = (vendor_id == 0x163C || vendor_id == 0x8086 || vendor_id == 0x8087);
hr = D3D12CreateDevice(m_adapter.get(), isIntel ? D3D_FEATURE_LEVEL_12_0 : D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device));
#endif
if (FAILED(hr))
{
Console.Error("D3D12: Failed to create device: %08X", hr);
@@ -549,6 +556,7 @@ bool GSDevice12::ExecuteCommandList(WaitType wait_for_completion)
// Flush stream buffers to GPU memory
m_vertex_stream_buffer.FlushMemory();
m_index_stream_buffer.FlushMemory();
m_expand_index_stream_buffer.FlushMemory();
m_vertex_constant_buffer.FlushMemory();
m_pixel_constant_buffer.FlushMemory();
@@ -603,6 +611,9 @@ bool GSDevice12::ExecuteCommandList(WaitType wait_for_completion)
if (wait_for_completion != WaitType::None)
WaitForFence(res.ready_fence_value, wait_for_completion == WaitType::Spin);
// Push constants need to be refreshed each command list.
m_dirty_flags |= DIRTY_FLAG_VS_PUSH_CONSTANTS;
return true;
}
@@ -881,7 +892,7 @@ bool GSDevice12::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
m_tfx_source = std::move(*shader);
}
if (!m_shader_cache.Open(m_feature_level, GSConfig.UseDebugDevice))
if (!m_shader_cache.Open(D3D::ShaderModel::SM51, GSConfig.UseDebugDevice))
Console.Warning("D3D12: Shader cache failed to open.");
if (!CreateRootSignatures())
@@ -1401,15 +1412,8 @@ bool GSDevice12::CheckFeatures(const u32& vendor_id)
m_features.cas_sharpening = true;
m_features.test_and_sample_depth = true;
m_features.vs_expand = !GSConfig.DisableVertexShaderExpand;
if (m_features.texture_barrier && (GSConfig.DepthFeedbackMode == GSDepthFeedbackMode::Auto ||
GSConfig.DepthFeedbackMode == GSDepthFeedbackMode::DepthAsRT))
{
m_features.depth_feedback = GSDevice::DepthFeedbackSupport::DepthAsRT;
}
else
{
m_features.depth_feedback = GSDevice::DepthFeedbackSupport::None;
}
m_features.depth_feedback = false;
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand && m_features.feedback_loops();
m_features.dxt_textures = SupportsTextureFormat(DXGI_FORMAT_BC1_UNORM) &&
SupportsTextureFormat(DXGI_FORMAT_BC2_UNORM) &&
@@ -1460,8 +1464,7 @@ void GSDevice12::DrawPrimitive()
void GSDevice12::DrawIndexedPrimitive()
{
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
GetCommandList().list4->DrawIndexedInstanced(m_index.count, 1, m_index.start, m_vertex.start, 0);
DrawIndexedPrimitive(0, m_index.count);
}
void GSDevice12::DrawIndexedPrimitive(int offset, int count)
@@ -1471,6 +1474,40 @@ void GSDevice12::DrawIndexedPrimitive(int offset, int count)
GetCommandList().list4->DrawIndexedInstanced(count, 1, m_index.start + offset, m_vertex.start, 0);
}
void GSDevice12::DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_indexing, int vs_indexing_expansion)
{
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
if (vs_indexing)
{
SetVSPushConstants(m_vertex.start, m_index.start + offset);
GetCommandList().list4->DrawInstanced(count * vs_indexing_expansion, 1, 0, 0);
}
else
{
SetVSPushConstants(m_vertex.start);
GetCommandList().list4->DrawIndexedInstanced(count, 1, m_index.start + offset, 0, 0);
}
}
void GSDevice12::Draw(const GSHWDrawConfig& config, int offset, int count)
{
if (config.vs.expand != GSHWDrawConfig::VSExpand::None)
{
const bool vs_indexing = config.vs.UseVSExpandIndexBuffer();
const u32 vs_indexing_expansion = GetExpansionFactor(config.vs.expand);
DrawIndexedPrimitiveVSExpand(offset, count, vs_indexing, vs_indexing_expansion);
}
else
{
DrawIndexedPrimitive(offset, count);
}
}
void GSDevice12::Draw(const GSHWDrawConfig& config)
{
Draw(config, 0, m_index.count);
}
void GSDevice12::LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format,
DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format) const
{
@@ -2383,22 +2420,32 @@ void GSDevice12::IASetVertexBuffer(const void* vertex, size_t stride, size_t cou
m_vertex_stream_buffer.CommitMemory(size);
}
void GSDevice12::IASetIndexBuffer(const void* index, size_t count)
void GSDevice12::UploadIndices(D3D12StreamBuffer& buffer, const void* index, size_t count)
{
const u32 size = sizeof(u16) * static_cast<u32>(count);
if (!m_index_stream_buffer.ReserveMemory(size, sizeof(u16)))
if (!buffer.ReserveMemory(size, sizeof(u16)))
{
ExecuteCommandListAndRestartRenderPass(false, "Uploading bytes to index buffer");
if (!m_index_stream_buffer.ReserveMemory(size, sizeof(u16)))
pxFailRel("Failed to reserve space for vertices");
if (!buffer.ReserveMemory(size, sizeof(u16)))
pxFailRel("Failed to reserve space for indices");
}
m_index.start = m_index_stream_buffer.GetCurrentOffset() / sizeof(u16);
m_index.start = buffer.GetCurrentOffset() / sizeof(u16);
m_index.count = count;
SetIndexBuffer(m_index_stream_buffer.GetGPUPointer(), m_index_stream_buffer.GetSize(), DXGI_FORMAT_R16_UINT);
std::memcpy(m_index_stream_buffer.GetCurrentHostPointer(), index, size);
m_index_stream_buffer.CommitMemory(size);
std::memcpy(buffer.GetCurrentHostPointer(), index, size);
buffer.CommitMemory(size);
}
void GSDevice12::IASetIndexBuffer(const void* index, size_t count)
{
UploadIndices(m_index_stream_buffer, index, count);
SetIndexBuffer(m_index_stream_buffer.GetGPUPointer(), m_index_stream_buffer.GetSize(), DXGI_FORMAT_R16_UINT);
}
void GSDevice12::VSSetIndexBuffer(const void* index, size_t count)
{
UploadIndices(m_expand_index_stream_buffer, index, count);
}
void GSDevice12::OMSetRenderTargets(GSTexture* rt, GSTexture* ds_as_rt, GSTexture* ds, const GSVector4i& scissor,
@@ -2463,37 +2510,33 @@ bool GSDevice12::GetSampler(D3D12DescriptorHandle* cpu_handle, GSHWDrawConfig::S
return true;
}
D3D12_SAMPLER_DESC sd = {};
const int anisotropy = GSConfig.MaxAnisotropy;
if (anisotropy > 1 && ss.aniso)
{
sd.Filter = D3D12_FILTER_ANISOTROPIC;
}
else
{
static constexpr std::array<D3D12_FILTER, 8> filters = {{
D3D12_FILTER_MIN_MAG_MIP_POINT, // 000 / min=point,mag=point,mip=point
D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT, // 001 / min=linear,mag=point,mip=point
D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT, // 010 / min=point,mag=linear,mip=point
D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT, // 011 / min=linear,mag=linear,mip=point
D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR, // 100 / min=point,mag=point,mip=linear
D3D12_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, // 101 / min=linear,mag=point,mip=linear
D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR, // 110 / min=point,mag=linear,mip=linear
D3D12_FILTER_MIN_MAG_MIP_LINEAR, // 111 / min=linear,mag=linear,mip=linear
}};
static constexpr std::array<D3D12_FILTER, 8> filters = {{
D3D12_FILTER_MIN_MAG_MIP_POINT, // 000 / min=point,mag=point,mip=point
D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT, // 001 / min=linear,mag=point,mip=point
D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT, // 010 / min=point,mag=linear,mip=point
D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT, // 011 / min=linear,mag=linear,mip=point
D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR, // 100 / min=point,mag=point,mip=linear
D3D12_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR, // 101 / min=linear,mag=point,mip=linear
D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR, // 110 / min=point,mag=linear,mip=linear
D3D12_FILTER_MIN_MAG_MIP_LINEAR, // 111 / min=linear,mag=linear,mip=linear
}};
const u8 index = (static_cast<u8>(ss.IsMipFilterLinear()) << 2) |
(static_cast<u8>(ss.IsMagFilterLinear()) << 1) | static_cast<u8>(ss.IsMinFilterLinear());
sd.Filter = filters[index];
}
const u8 index = (static_cast<u8>(ss.IsMipFilterLinear()) << 2) |
(static_cast<u8>(ss.IsMagFilterLinear()) << 1) |
static_cast<u8>(ss.IsMinFilterLinear());
sd.AddressU = ss.tau ? D3D12_TEXTURE_ADDRESS_MODE_WRAP : D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
sd.AddressV = ss.tav ? D3D12_TEXTURE_ADDRESS_MODE_WRAP : D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
sd.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
sd.MinLOD = 0.0f;
sd.MaxLOD = (ss.lodclamp || !ss.UseMipmapFiltering()) ? 0.25f : FLT_MAX;
sd.MaxAnisotropy = std::clamp<u8>(GSConfig.MaxAnisotropy, 1, 16);
sd.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
const D3D12_SAMPLER_DESC sd = {
filters[index], // Filter
ss.tau ? D3D12_TEXTURE_ADDRESS_MODE_WRAP : D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // Address u
ss.tav ? D3D12_TEXTURE_ADDRESS_MODE_WRAP : D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // Address v
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // Address w
0.0f, // Lod bias
1, // Anisotropy
D3D12_COMPARISON_FUNC_NEVER, // comparison function
{}, // Border colour
0.0f, // Min lod
(ss.lodclamp || !ss.UseMipmapFiltering()) ? 0.25f : FLT_MAX, // Max lod
};
if (!GetSamplerHeapManager().Allocate(cpu_handle))
return false;
@@ -2591,6 +2634,12 @@ bool GSDevice12::CreateBuffers()
return false;
}
if (!m_expand_index_stream_buffer.Create(m_features.aa1 ? INDEX_BUFFER_SIZE : 4, false))
{
Host::ReportErrorAsync("GS", "Failed to allocate expansion index buffer (VS resource)");
return false;
}
if (!m_vertex_constant_buffer.Create(VERTEX_UNIFORM_BUFFER_SIZE, !m_uma))
{
Host::ReportErrorAsync("GS", "Failed to allocate vertex uniform buffer");
@@ -2641,9 +2690,11 @@ bool GSDevice12::CreateRootSignatures()
rsb.AddCBVParameter(0, D3D12_SHADER_VISIBILITY_ALL);
rsb.AddCBVParameter(1, D3D12_SHADER_VISIBILITY_PIXEL);
rsb.AddSRVParameter(0, D3D12_SHADER_VISIBILITY_VERTEX);
rsb.AddSRVParameter(5, D3D12_SHADER_VISIBILITY_VERTEX);
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 2, D3D12_SHADER_VISIBILITY_PIXEL); // Source / Palette
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 0, NUM_TFX_SAMPLERS, D3D12_SHADER_VISIBILITY_PIXEL);
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 2, 3, D3D12_SHADER_VISIBILITY_PIXEL); // RT / PrimID / Depth
rsb.Add32BitConstants(2, sizeof(m_vs_pc_cache) / sizeof(u32), D3D12_SHADER_VISIBILITY_VERTEX);
if (!(m_tfx_root_signature = rsb.Create()))
return false;
D3D12::SetObjectName(m_tfx_root_signature.get(), "TFX root signature");
@@ -3037,6 +3088,7 @@ void GSDevice12::DestroyResources()
m_vertex_constant_buffer.Destroy(false);
m_index_stream_buffer.Destroy(false);
m_vertex_stream_buffer.Destroy(false);
m_expand_index_stream_buffer.Destroy(false);
m_utility_root_signature.reset();
m_tfx_root_signature.reset();
@@ -3157,6 +3209,9 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector&
sm.AddMacro("PS_NO_COLOR", sel.no_color);
sm.AddMacro("PS_NO_COLOR1", sel.no_color1);
sm.AddMacro("PS_ZTST", sel.ztst);
sm.AddMacro("PS_AA1", static_cast<u32>(sel.aa1));
sm.AddMacro("PS_ABE", sel.abe);
sm.AddMacro("PS_ANISOTROPIC_FILTERING", sel.sw_aniso);
ComPtr<ID3DBlob> ps(m_shader_cache.GetPixelShader(m_tfx_source, sm.GetPtr(), "ps_main"));
it = m_tfx_pixel_shaders.emplace(sel, std::move(ps)).first;
@@ -3363,7 +3418,7 @@ void GSDevice12::ExecuteCommandListAndRestartRenderPass(bool wait_for_completion
{
Console.Warning("D3D12: Executing command buffer due to '%s'", reason);
const bool was_in_render_pass = m_in_render_pass;
const bool was_in_render_pass = InRenderPass();
EndRenderPass();
ExecuteCommandList(GetWaitType(wait_for_completion, GSConfig.HWSpinCPUForReadbacks));
InvalidateCachedState();
@@ -3393,7 +3448,7 @@ void GSDevice12::ExecuteCommandListForReadback()
void GSDevice12::InvalidateCachedState()
{
m_dirty_flags |= DIRTY_BASE_STATE | DIRTY_TFX_STATE | DIRTY_UTILITY_STATE | DIRTY_CONSTANT_BUFFER_STATE;
m_dirty_flags |= DIRTY_BASE_STATE | DIRTY_ROOT_PARAMS | DIRTY_TFX_STATE | DIRTY_UTILITY_STATE | DIRTY_CONSTANT_BUFFER_STATE;
m_current_root_signature = RootSignature::Undefined;
m_utility_texture_cpu.Clear();
m_utility_texture_gpu.Clear();
@@ -3786,7 +3841,7 @@ __ri void GSDevice12::ApplyBaseState(u32 flags, ID3D12GraphicsCommandList* cmdli
if (flags & DIRTY_FLAG_PRIMITIVE_TOPOLOGY)
cmdlist->IASetPrimitiveTopology(m_primitive_topology);
if (flags & DIRTY_FLAG_PIPELINE)
if ((flags & DIRTY_FLAG_PIPELINE) && m_current_pipeline)
cmdlist->SetPipelineState(const_cast<ID3D12PipelineState*>(m_current_pipeline));
if (flags & DIRTY_FLAG_VIEWPORT)
@@ -3918,9 +3973,7 @@ bool GSDevice12::ApplyTFXState(bool already_execed)
if (m_current_root_signature != RootSignature::TFX)
{
m_current_root_signature = RootSignature::TFX;
flags |= DIRTY_FLAG_VS_CONSTANT_BUFFER_BINDING | DIRTY_FLAG_PS_CONSTANT_BUFFER_BINDING |
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE | DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE |
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE_2 | DIRTY_FLAG_PIPELINE;
flags |= DIRTY_ROOT_PARAMS | DIRTY_FLAG_PIPELINE;
cmdlist->SetGraphicsRootSignature(m_tfx_root_signature.get());
}
@@ -3928,10 +3981,17 @@ bool GSDevice12::ApplyTFXState(bool already_execed)
cmdlist->SetGraphicsRootConstantBufferView(TFX_ROOT_SIGNATURE_PARAM_VS_CBV, m_tfx_constant_buffers[0]);
if (flags & DIRTY_FLAG_PS_CONSTANT_BUFFER_BINDING)
cmdlist->SetGraphicsRootConstantBufferView(TFX_ROOT_SIGNATURE_PARAM_PS_CBV, m_tfx_constant_buffers[1]);
if (m_features.vs_expand && (flags & DIRTY_FLAG_VS_PUSH_CONSTANTS))
SetVSPushConstants(m_vs_pc_cache.base_vertex, m_vs_pc_cache.base_index, true);
if (flags & DIRTY_FLAG_VS_VERTEX_BUFFER_BINDING)
{
cmdlist->SetGraphicsRootShaderResourceView(TFX_ROOT_SIGNATURE_PARAM_VS_SRV,
m_vertex_stream_buffer.GetGPUPointer() + m_vertex.start * sizeof(GSVertex));
cmdlist->SetGraphicsRootShaderResourceView(TFX_ROOT_SIGNATURE_PARAM_VS_VB_SRV,
m_vertex_stream_buffer.GetGPUPointer());
}
if (flags & DIRTY_FLAG_VS_INDEX_BUFFER_BINDING)
{
cmdlist->SetGraphicsRootShaderResourceView(TFX_ROOT_SIGNATURE_PARAM_VS_IB_SRV,
m_expand_index_stream_buffer.GetGPUPointer());
}
if (flags & DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE)
cmdlist->SetGraphicsRootDescriptorTable(TFX_ROOT_SIGNATURE_PARAM_PS_TEXTURES, m_tfx_textures_handle_gpu);
@@ -3982,6 +4042,19 @@ void GSDevice12::SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb)
m_dirty_flags |= DIRTY_FLAG_PS_CONSTANT_BUFFER;
}
void GSDevice12::SetVSPushConstants(u32 base_vertex, u32 base_index, bool force_update)
{
GSHWDrawConfig::VSPushConstants pc;
pc.base_vertex = base_vertex;
pc.base_index = base_index;
if (m_vs_pc_cache.Update(pc) || force_update)
{
GetCommandList().list4->SetGraphicsRoot32BitConstants(
TFX_ROOT_SIGNATURE_PARAM_VS_PUSH_CONSTANTS, sizeof(m_vs_pc_cache) / sizeof(u32),
&m_vs_pc_cache, 0);
}
}
void GSDevice12::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSVector4i& bbox)
{
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
@@ -4078,7 +4151,7 @@ GSTexture12* GSDevice12::SetupPrimitiveTrackingDATE(GSHWDrawConfig& config, Pipe
init_pipe.ps.no_color = false;
init_pipe.ps.no_color1 = true;
if (BindDrawPipeline(init_pipe))
DrawIndexedPrimitive();
Draw(config);
// image is initialized/prepass is done, so finish up and get ready to do the "real" draw
EndRenderPass();
@@ -4177,7 +4250,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
}
// Destination Alpha Setup
const bool need_barrier = m_features.texture_barrier && (config.require_one_barrier || config.require_full_barrier);
const bool need_barrier = config.require_one_barrier || (config.require_full_barrier && m_features.texture_barrier);
switch (config.destination_alpha)
{
case GSHWDrawConfig::DestinationAlphaMode::Off: // No setup
@@ -4278,7 +4351,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
(draw_ds && static_cast<GSTexture12*>(draw_ds)->GetSRVDescriptor() == m_tfx_textures[0])))
PSSetShaderResource(0, nullptr, false);
if (m_in_render_pass && (m_current_render_target == draw_rt || m_current_depth_target == draw_ds))
if (InRenderPass() && (m_current_render_target == draw_rt || m_current_depth_target == draw_ds))
{
// avoid restarting the render pass just to switch from rt+depth to rt and vice versa
// keep the depth even if doing colclip hw draws, because the next draw will probably re-enable depth
@@ -4288,17 +4361,18 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
draw_rt = m_current_render_target;
m_pipeline_selector.rt = true;
}
}
else if (!draw_ds && m_current_depth_target && config.tex != m_current_depth_target &&
m_current_depth_target->GetSize() == draw_rt->GetSize())
{
draw_ds = m_current_depth_target;
m_pipeline_selector.ds = true;
else if (!draw_ds && m_current_depth_target && config.tex != m_current_depth_target &&
m_current_depth_target->GetSize() == draw_rt->GetSize())
{
draw_ds = m_current_depth_target;
m_pipeline_selector.ds = true;
}
}
GSTexture12* draw_ds_as_rt = static_cast<GSTexture12*>(config.ds_as_rt);
GSTexture12* draw_ds_as_rt = static_cast<GSTexture12*>(m_ds_as_rt);
const bool feedback_rt = draw_rt && (config.require_one_barrier || (config.require_full_barrier && m_features.texture_barrier) || (config.tex && config.tex == config.rt));
const bool feedback_rt = draw_rt && (((config.require_one_barrier || (config.require_full_barrier && m_features.texture_barrier)) && (config.ps.IsFeedbackLoopRT() ||
config.alpha_second_pass.ps.IsFeedbackLoopRT())) || (config.tex && config.tex == config.rt));
const bool feedback_depth = draw_ds_as_rt != nullptr;
if (feedback_rt && !m_features.texture_barrier)
@@ -4338,7 +4412,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
}
// Begin render pass if new target or out of the area.
if (!m_in_render_pass)
if (!InRenderPass())
{
GSVector4 clear_color = draw_rt ? draw_rt->GetUNormClearColor() : GSVector4::zero();
if (pipe.ps.colclip_hw)
@@ -4398,7 +4472,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
pipe.ps.blend_hw = config.blend_multi_pass.blend_hw;
pipe.ps.dither = config.blend_multi_pass.dither;
if (BindDrawPipeline(pipe))
DrawIndexedPrimitive();
Draw(config);
}
// and the alpha pass
@@ -4462,14 +4536,15 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
GSTexture12* draw_ds, const bool feedback_rt, const bool feedback_depth, const bool one_barrier,
const bool full_barrier)
{
if (BindDrawPipeline(pipe) && !m_features.texture_barrier) [[unlikely]]
const int n_barriers = static_cast<int>(feedback_rt) + static_cast<int>(feedback_depth);
if (!m_features.texture_barrier) [[unlikely]]
{
DrawIndexedPrimitive();
if (BindDrawPipeline(pipe))
Draw(config);
return;
}
const int n_barriers = static_cast<int>(feedback_rt) + static_cast<int>(feedback_depth);
if (feedback_rt || feedback_depth)
{
#ifdef PCSX2_DEVBUILD
@@ -4502,7 +4577,7 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
FeedbackBarrier(draw_ds);
if (BindDrawPipeline(pipe))
DrawIndexedPrimitive(p, count);
Draw(config, p, count);
p += count;
}
@@ -4514,14 +4589,14 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
g_perfmon.Put(GSPerfMon::Barriers, n_barriers);
if (feedback_rt)
FeedbackBarrier(draw_rt);
FeedbackBarrier(draw_rt);
if (feedback_depth)
FeedbackBarrier(draw_ds);
}
}
if (BindDrawPipeline(pipe))
DrawIndexedPrimitive();
Draw(config);
}
void GSDevice12::UpdateHWPipelineSelector(GSHWDrawConfig& config)
@@ -4536,23 +4611,23 @@ void GSDevice12::UpdateHWPipelineSelector(GSHWDrawConfig& config)
m_pipeline_selector.topology = static_cast<u32>(config.topology);
m_pipeline_selector.rt = config.rt != nullptr;
m_pipeline_selector.ds = config.ds != nullptr;
m_pipeline_selector.ds_as_rt = config.ds_as_rt != nullptr;
m_pipeline_selector.ds_as_rt = m_ds_as_rt != nullptr;
}
void GSDevice12::UploadHWDrawVerticesAndIndices(GSHWDrawConfig& config)
{
IASetVertexBuffer(config.verts, sizeof(GSVertex), config.nverts);
// Update SRV in root signature directly, rather than using a uniform for base vertex.
if (config.vs.expand != GSHWDrawConfig::VSExpand::None)
m_dirty_flags |= DIRTY_FLAG_VS_VERTEX_BUFFER_BINDING;
if (config.vs.UseExpandIndexBuffer())
if (config.vs.UseFixedExpandIndexBuffer())
{
m_index.start = 0;
m_index.count = config.nindices;
SetIndexBuffer(m_expand_index_buffer->GetGPUVirtualAddress(), EXPAND_BUFFER_SIZE, DXGI_FORMAT_R16_UINT);
}
else if (config.vs.UseVSExpandIndexBuffer())
{
VSSetIndexBuffer(config.indices, config.nindices);
}
else
{
IASetIndexBuffer(config.indices, config.nindices);
+41 -23
View File
@@ -148,6 +148,7 @@ public:
// Allocates a temporary CPU staging buffer, fires the callback with it to populate, then copies to a GPU buffer.
bool AllocatePreinitializedGPUBuffer(u32 size, ID3D12Resource** gpu_buffer, D3D12MA::Allocation** gpu_allocation,
const std::function<void(void*)>& fill_callback);
void UploadIndices(D3D12StreamBuffer& buffer, const void* index, size_t count);
private:
struct CommandListResources
@@ -298,10 +299,12 @@ public:
TFX_ROOT_SIGNATURE_PARAM_VS_CBV = 0,
TFX_ROOT_SIGNATURE_PARAM_PS_CBV = 1,
TFX_ROOT_SIGNATURE_PARAM_VS_SRV = 2,
TFX_ROOT_SIGNATURE_PARAM_PS_TEXTURES = 3,
TFX_ROOT_SIGNATURE_PARAM_PS_SAMPLERS = 4,
TFX_ROOT_SIGNATURE_PARAM_PS_RT_TEXTURES = 5,
TFX_ROOT_SIGNATURE_PARAM_VS_VB_SRV = 2,
TFX_ROOT_SIGNATURE_PARAM_VS_IB_SRV = 3,
TFX_ROOT_SIGNATURE_PARAM_PS_TEXTURES = 4,
TFX_ROOT_SIGNATURE_PARAM_PS_SAMPLERS = 5,
TFX_ROOT_SIGNATURE_PARAM_PS_RT_TEXTURES = 6,
TFX_ROOT_SIGNATURE_PARAM_VS_PUSH_CONSTANTS = 7,
UTILITY_ROOT_SIGNATURE_PARAM_PUSH_CONSTANTS = 0,
UTILITY_ROOT_SIGNATURE_PARAM_PS_TEXTURES = 1,
@@ -331,6 +334,7 @@ private:
D3D12StreamBuffer m_vertex_stream_buffer;
D3D12StreamBuffer m_index_stream_buffer;
D3D12StreamBuffer m_expand_index_stream_buffer;
D3D12StreamBuffer m_vertex_constant_buffer;
D3D12StreamBuffer m_pixel_constant_buffer;
D3D12StreamBuffer m_texture_stream_buffer;
@@ -365,6 +369,7 @@ private:
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
GSHWDrawConfig::VSPushConstants m_vs_pc_cache;
D3D12ShaderCache m_shader_cache;
ComPtr<ID3DBlob> m_convert_vs;
@@ -458,9 +463,15 @@ public:
void PopDebugGroup() override;
void InsertDebugMessage(DebugMessageCategory category, const char* fmt, ...) override;
// Helpers and utility draws.
void DrawPrimitive();
void DrawIndexedPrimitive();
void DrawIndexedPrimitive(int offset, int count);
void DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_indexing, int vs_indexing_expansion);
// Main GS primitive draws.
void Draw(const GSHWDrawConfig& config);
void Draw(const GSHWDrawConfig& config, int offset, int count);
std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) override;
@@ -489,6 +500,7 @@ public:
void IASetVertexBuffer(const void* vertex, size_t stride, size_t count);
void IASetIndexBuffer(const void* index, size_t count);
void VSSetIndexBuffer(const void* index, size_t count);
void PSSetShaderResource(int i, GSTexture* sr, bool check_state, bool feedback = false);
void PSSetSampler(GSHWDrawConfig::SamplerSelector sel);
@@ -498,6 +510,7 @@ public:
void SetVSConstantBuffer(const GSHWDrawConfig::VSConstantBuffer& cb);
void SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb);
void SetVSPushConstants(u32 base_vertex, u32 base_index = 0, bool force_update = false);
bool BindDrawPipeline(const PipelineSelector& p);
void RenderHW(GSHWDrawConfig& config) override;
@@ -568,29 +581,34 @@ private:
DIRTY_FLAG_VS_CONSTANT_BUFFER_BINDING = (1 << 5),
DIRTY_FLAG_PS_CONSTANT_BUFFER_BINDING = (1 << 6),
DIRTY_FLAG_VS_VERTEX_BUFFER_BINDING = (1 << 7),
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE = (1 << 8),
DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE = (1 << 9),
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE_2 = (1 << 10),
DIRTY_FLAG_VS_INDEX_BUFFER_BINDING = (1 << 8),
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE = (1 << 9),
DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE = (1 << 10),
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE_2 = (1 << 11),
DIRTY_FLAG_VS_PUSH_CONSTANTS = (1 << 12),
DIRTY_FLAG_VERTEX_BUFFER = (1 << 11),
DIRTY_FLAG_INDEX_BUFFER = (1 << 12),
DIRTY_FLAG_PRIMITIVE_TOPOLOGY = (1 << 13),
DIRTY_FLAG_VIEWPORT = (1 << 14),
DIRTY_FLAG_SCISSOR = (1 << 15),
DIRTY_FLAG_RENDER_TARGET = (1 << 16),
DIRTY_FLAG_PIPELINE = (1 << 17),
DIRTY_FLAG_BLEND_CONSTANTS = (1 << 18),
DIRTY_FLAG_STENCIL_REF = (1 << 19),
DIRTY_FLAG_VERTEX_BUFFER = (1 << 13),
DIRTY_FLAG_INDEX_BUFFER = (1 << 14),
DIRTY_FLAG_PRIMITIVE_TOPOLOGY = (1 << 15),
DIRTY_FLAG_VIEWPORT = (1 << 16),
DIRTY_FLAG_SCISSOR = (1 << 17),
DIRTY_FLAG_RENDER_TARGET = (1 << 18),
DIRTY_FLAG_PIPELINE = (1 << 19),
DIRTY_FLAG_BLEND_CONSTANTS = (1 << 20),
DIRTY_FLAG_STENCIL_REF = (1 << 21),
DIRTY_BASE_STATE = DIRTY_FLAG_VS_CONSTANT_BUFFER_BINDING | DIRTY_FLAG_PS_CONSTANT_BUFFER_BINDING |
DIRTY_FLAG_VS_VERTEX_BUFFER_BINDING | DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE |
DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE | DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE_2 |
DIRTY_FLAG_VERTEX_BUFFER | DIRTY_FLAG_INDEX_BUFFER |
DIRTY_FLAG_PRIMITIVE_TOPOLOGY | DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR | DIRTY_FLAG_RENDER_TARGET |
DIRTY_FLAG_PIPELINE | DIRTY_FLAG_BLEND_CONSTANTS | DIRTY_FLAG_STENCIL_REF,
DIRTY_ROOT_PARAMS = DIRTY_FLAG_VS_CONSTANT_BUFFER_BINDING | DIRTY_FLAG_PS_CONSTANT_BUFFER_BINDING |
DIRTY_FLAG_VS_VERTEX_BUFFER_BINDING | DIRTY_FLAG_VS_INDEX_BUFFER_BINDING |
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE | DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE |
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE_2 | DIRTY_FLAG_VS_PUSH_CONSTANTS,
DIRTY_TFX_STATE = DIRTY_BASE_STATE | DIRTY_FLAG_TFX_TEXTURES | DIRTY_FLAG_TFX_SAMPLERS |
DIRTY_BASE_STATE = DIRTY_FLAG_VERTEX_BUFFER | DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_PRIMITIVE_TOPOLOGY |
DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR | DIRTY_FLAG_RENDER_TARGET | DIRTY_FLAG_PIPELINE |
DIRTY_FLAG_BLEND_CONSTANTS | DIRTY_FLAG_STENCIL_REF,
DIRTY_TFX_STATE = DIRTY_BASE_STATE | DIRTY_ROOT_PARAMS | DIRTY_FLAG_TFX_TEXTURES | DIRTY_FLAG_TFX_SAMPLERS |
DIRTY_FLAG_TFX_RT_TEXTURES,
DIRTY_UTILITY_STATE = DIRTY_BASE_STATE,
DIRTY_CONSTANT_BUFFER_STATE = DIRTY_FLAG_VS_CONSTANT_BUFFER | DIRTY_FLAG_PS_CONSTANT_BUFFER,
};
+49 -49
View File
@@ -50,8 +50,8 @@ bool GSHwHack::GSC_IRem(GSRendererHW& r, int& skip)
GIFRegTEX0 RTLookup = GIFRegTEX0::Create(RTBP0, RFBW, RFPSM);
GSTextureCache::Source* src = g_texture_cache->LookupSource(true, RTLookup, r.m_cached_ctx.TEXA, r.m_cached_ctx.CLAMP, GSVector4i(0, 0, 1, 1), nullptr, true, false, r.m_cached_ctx.FRAME, true, true);
GSTextureCache::Target* rt = g_texture_cache->LookupTarget(GIFRegTEX0::Create(RTBP0, RFBW, RFPSM),
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true, 0, false, false, true, true, GSVector4i(0, 0, 1, 1), true, false, true, src);
GSTextureCache::Target* rt = g_texture_cache->LookupDrawTarget(GIFRegTEX0::Create(RTBP0, RFBW, RFPSM),
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true, 0, false, true, true, GSVector4i(0, 0, 1, 1), true, false, true, src);
if (!rt)
return false;
@@ -115,7 +115,7 @@ bool GSHwHack::GSC_IRem(GSRendererHW& r, int& skip)
// Detect the deswizzling shuffle from depth, copying the RG and BA separately on each half of the page (ignore the split).
if (RTME && RFBP != RTBP0 && RFPSM == PSMCT16S && RTPSM == PSMCT16S)
{
if (r.m_vt.m_max.p.x == 64 && r.m_vt.m_max.p.y == 64 && r.m_index.tail == 128)
if (r.m_vt.m_max.p.x == 64 && r.m_vt.m_max.p.y == 64 && r.m_index->tail == 128)
{
const GSVector4i draw_size(r.m_vt.m_min.p.x, r.m_vt.m_min.p.y/2, r.m_vt.m_max.p.x, r.m_vt.m_max.p.y/2);
const GSVector4i read_size(r.m_vt.m_min.t.x, r.m_vt.m_min.t.y/2, r.m_vt.m_max.t.x, r.m_vt.m_max.t.y/2);
@@ -126,7 +126,7 @@ bool GSHwHack::GSC_IRem(GSRendererHW& r, int& skip)
}
// Following the previous draw, it tries to copy everything read from depth and offset it by 2, for the alternate line channel shuffle (skipped above).
if (RTBP0 == (RFBP - 0x20) && r.m_vt.m_max.p.x == 64 && r.m_vt.m_max.p.y == 34 && r.m_index.tail == 2)
if (RTBP0 == (RFBP - 0x20) && r.m_vt.m_max.p.x == 64 && r.m_vt.m_max.p.y == 34 && r.m_index->tail == 2)
{
GSVector4i draw_size(r.m_vt.m_min.p.x, r.m_vt.m_min.p.y - 2.0f, r.m_vt.m_max.p.x, r.m_vt.m_max.p.y - 2.0f);
GSVector4i read_size(r.m_vt.m_min.t.x, r.m_vt.m_min.t.y, r.m_vt.m_max.t.x, r.m_vt.m_max.t.y);
@@ -138,8 +138,8 @@ bool GSHwHack::GSC_IRem(GSRendererHW& r, int& skip)
GIFRegTEX0 RTLookup = GIFRegTEX0::Create(RTBP0, RFBW, RFPSM);
GSTextureCache::Source* src = g_texture_cache->LookupSource(true, RTLookup, r.m_cached_ctx.TEXA, r.m_cached_ctx.CLAMP, GSVector4i(0,0,1,1), nullptr, true, false, r.m_cached_ctx.FRAME, true, true);
GSTextureCache::Target* rt = g_texture_cache->LookupTarget(GIFRegTEX0::Create(RTBP0, RFBW, RFPSM),
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true, 0, false, false, true, true, GSVector4i(0,0,1,1), true, false, true, src);
GSTextureCache::Target* rt = g_texture_cache->LookupDrawTarget(GIFRegTEX0::Create(RTBP0, RFBW, RFPSM),
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true, 0, false, true, true, GSVector4i(0,0,1,1), true, false, true, src);
if (!rt)
return false;
@@ -264,10 +264,10 @@ bool GSHwHack::GSC_SFEX3(GSRendererHW& r, int& skip)
// Skipping is no good as the copy is used again later, and it causes a weird shimmer/echo effect every other frame.
// Add on the height from the second part of the draw to the first, to make it one big rect.
r.m_vertex.buff[1].XYZ.Y += r.m_vertex.buff[r.m_vertex.tail - 1].XYZ.Y - r.m_context->XYOFFSET.OFY;
r.m_vertex.buff[1].V = r.m_vertex.buff[r.m_vertex.tail - 1].V;
r.m_vertex.tail = 2;
r.m_index.tail = 2;
r.m_vertex->buff[1].XYZ.Y += r.m_vertex->buff[r.m_vertex->tail - 1].XYZ.Y - r.m_context->XYOFFSET.OFY;
r.m_vertex->buff[1].V = r.m_vertex->buff[r.m_vertex->tail - 1].V;
r.m_vertex->tail = 2;
r.m_index->tail = 2;
}
}
@@ -283,7 +283,7 @@ bool GSHwHack::GSC_DTGames(GSRendererHW& r, int& skip)
// The further problem to this is the limitation of alpha we can save on an RT as they copy in 255, so I can cheese it here pretending it's RTA'd
if (RTME && RFPSM == PSMCT32 && RTBP0 == RFBP && RTPSM == PSMCT16 && RTEST.ATE && RTEST.ATST == ATST_NEVER && RTEST.AFAIL == AFAIL_FB_ONLY && RFBMSK == 0xFFFFFF)
{
GSTextureCache::Target* rt = g_texture_cache->LookupTarget(GIFRegTEX0::Create(RTBP0, RFBW, RFPSM),
GSTextureCache::Target* rt = g_texture_cache->LookupDrawTarget(GIFRegTEX0::Create(RTBP0, RFBW, RFPSM),
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget);
if (!rt)
@@ -332,9 +332,9 @@ bool GSHwHack::GSC_NamcoGames(GSRendererHW& r, int& skip)
{
if (skip == 0)
{
if (!s_nativeres && r.PRIM->PRIM == GS_SPRITE && RTME && RTEX0.TFX == 1 && RFPSM == RTPSM && RTPSM == PSMCT32 && RFBMSK == 0xFF000000 && r.m_index.tail > 2)
if (!s_nativeres && r.PRIM->PRIM == GS_SPRITE && RTME && RTEX0.TFX == 1 && RFPSM == RTPSM && RTPSM == PSMCT32 && RFBMSK == 0xFF000000 && r.m_index->tail > 2)
{
GSVertex* v = &r.m_vertex.buff[0];
GSVertex* v = &r.m_vertex->buff[0];
// Don't enable hack on native res.
// Fixes ghosting/blur effect and white lines appearing in stages: Moonfit Wilderness, Acid Rain - caused by upscaling.
// Game copies the framebuffer as individual page rects with slight offsets (like 1/16 of a pixel etc) which doesn't wokr well with upscaling.
@@ -348,7 +348,7 @@ bool GSHwHack::GSC_NamcoGames(GSRendererHW& r, int& skip)
else
{
// Fixes the alignment of the two halves for the heat haze on the temple stage.
for (u32 i = 0; i < r.m_index.tail; i+=2)
for (u32 i = 0; i < r.m_index->tail; i+=2)
{
v[i].XYZ.Y -= 0x8;
}
@@ -373,13 +373,13 @@ bool GSHwHack::GSC_SandGrainGames(GSRendererHW& r, int& skip)
if (r.PRIM->PRIM == GS_SPRITE && RTME && RFPSM == PSMCT16S && RTPSM == PSMZ16S && next_ctx.TEX0.TBP0 == RFBP && next_ctx.TEX0.PSM == PSMT8H)
{
GSTextureCache::Target* texsrc = g_texture_cache->LookupTarget(GIFRegTEX0::Create(RTBP0, RTBW, RTPSM),
GSTextureCache::Target* texsrc = g_texture_cache->LookupDrawTarget(GIFRegTEX0::Create(RTBP0, RTBW, RTPSM),
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil);
if (!texsrc)
return false;
GSTextureCache::Target* rt = g_texture_cache->LookupTarget(GIFRegTEX0::Create(next_ctx.FRAME.Block(), next_ctx.FRAME.FBW, next_ctx.FRAME.PSM),
GSTextureCache::Target* rt = g_texture_cache->LookupDrawTarget(GIFRegTEX0::Create(next_ctx.FRAME.Block(), next_ctx.FRAME.FBW, next_ctx.FRAME.PSM),
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget);
if (!rt)
@@ -468,7 +468,7 @@ bool GSHwHack::GSC_BurnoutGames(GSRendererHW& r, int& skip)
break;
// Next draw should contain our source.
GSTextureCache::Target* tgt = g_texture_cache->LookupTarget(r.m_env.CTXT[r.m_backed_up_ctx].TEX0,
GSTextureCache::Target* tgt = g_texture_cache->LookupDrawTarget(r.m_env.CTXT[r.m_backed_up_ctx].TEX0,
GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget);
if (!tgt)
break;
@@ -718,8 +718,8 @@ bool GSHwHack::GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip)
return false;
}
GSTextureCache::Target* src = g_texture_cache->LookupTarget(RTEX0, GSVector2i(1, 1), r.GetTextureScaleFactor(),
GSTextureCache::RenderTarget, true, 0, false, false, true, true, GSVector4i::zero(), true);
GSTextureCache::Target* src = g_texture_cache->LookupDrawTarget(RTEX0, GSVector2i(1, 1), r.GetTextureScaleFactor(),
GSTextureCache::RenderTarget, true, 0, false, true, true, GSVector4i::zero(), true);
if (!src)
return false;
@@ -777,7 +777,7 @@ bool GSHwHack::GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip)
for (u32 channel = 0; channel < 3; channel++)
{
const GIFRegTEX0 TEX0 = GIFRegTEX0::Create(base + channel * page_offset, 10, PSMCT32);
GSTextureCache::Target* dst = g_texture_cache->LookupTarget(TEX0, src->GetUnscaledSize(), src->GetScale(), GSTextureCache::RenderTarget, true, fbmsk);
GSTextureCache::Target* dst = g_texture_cache->LookupDrawTarget(TEX0, src->GetUnscaledSize(), src->GetScale(), GSTextureCache::RenderTarget, true, fbmsk);
if (!dst)
{
dst = g_texture_cache->CreateTarget(TEX0, size, size, src->GetScale(), GSTextureCache::RenderTarget, true, fbmsk);
@@ -822,7 +822,7 @@ bool GSHwHack::GSC_Battlefield2(GSRendererHW& r, int& skip)
GIFRegTEX0 TEX0 = {};
TEX0.TBP0 = RFBP;
TEX0.TBW = 8;
GSTextureCache::Target* dst = g_texture_cache->LookupTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil);
GSTextureCache::Target* dst = g_texture_cache->LookupDrawTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil);
if (!dst)
dst = g_texture_cache->CreateTarget(TEX0, r.GetTargetSize(), r.GetValidSize(nullptr), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil,
@@ -830,7 +830,7 @@ bool GSHwHack::GSC_Battlefield2(GSRendererHW& r, int& skip)
if (dst)
{
float dc = r.m_vertex.buff[1].XYZ.Z;
float dc = r.m_vertex->buff[1].XYZ.Z;
g_gs_device->ClearDepth(dst->m_texture, dc * std::exp2(-32.0f));
}
}
@@ -848,9 +848,9 @@ bool GSHwHack::GSC_BlueTongueGames(GSRendererHW& r, int& skip)
if (RPRIM->TME && RTEX0.TW == 3 && RTEX0.TH == 3 && RTEX0.PSM == 0 && RFRAME.FBMSK == 0x00FFFFFF && RFRAME.FBW == 8 && r.PCRTCDisplays.GetResolution().x > 512)
{
// Check we are drawing stripes
for (u32 i = 1; i < r.m_vertex.tail; i+=2)
for (u32 i = 1; i < r.m_vertex->tail; i+=2)
{
int value = (((r.m_vertex.buff[i].XYZ.X - r.m_vertex.buff[i - 1].XYZ.X) + 8) >> 4);
int value = (((r.m_vertex->buff[i].XYZ.X - r.m_vertex->buff[i - 1].XYZ.X) + 8) >> 4);
if (value != 32)
return false;
}
@@ -862,18 +862,18 @@ bool GSHwHack::GSC_BlueTongueGames(GSRendererHW& r, int& skip)
for (int vert = 32; vert < 40; vert+=2)
{
r.m_vertex.buff[vert].XYZ.X = context->XYOFFSET.OFX + (((vert * 16) << 4) - 8);
r.m_vertex.buff[vert].XYZ.Y = context->XYOFFSET.OFY;
r.m_vertex.buff[vert].U = (vert * 16) << 4;
r.m_vertex.buff[vert].V = 0;
r.m_vertex.buff[vert+1].XYZ.X = context->XYOFFSET.OFX + ((((vert * 16) + 32) << 4) - 8);
r.m_vertex.buff[vert+1].XYZ.Y = context->XYOFFSET.OFY + (r.PCRTCDisplays.GetResolution().y << 4) + 8;
r.m_vertex.buff[vert+1].U = ((vert * 16) + 32) << 4;
r.m_vertex.buff[vert+1].V = r.PCRTCDisplays.GetResolution().y << 4;
r.m_vertex->buff[vert].XYZ.X = context->XYOFFSET.OFX + (((vert * 16) << 4) - 8);
r.m_vertex->buff[vert].XYZ.Y = context->XYOFFSET.OFY;
r.m_vertex->buff[vert].U = (vert * 16) << 4;
r.m_vertex->buff[vert].V = 0;
r.m_vertex->buff[vert+1].XYZ.X = context->XYOFFSET.OFX + ((((vert * 16) + 32) << 4) - 8);
r.m_vertex->buff[vert+1].XYZ.Y = context->XYOFFSET.OFY + (r.PCRTCDisplays.GetResolution().y << 4) + 8;
r.m_vertex->buff[vert+1].U = ((vert * 16) + 32) << 4;
r.m_vertex->buff[vert+1].V = r.PCRTCDisplays.GetResolution().y << 4;
}
/*r.m_vertex.head = r.m_vertex.tail = r.m_vertex.next = 2;
r.m_index.tail = 2;*/
/*r.m_vertex->head = r.m_vertex->tail = r.m_vertex->next = 2;
r.m_index->tail = 2;*/
r.m_vt.m_max.p.x = r.m_r.z;
r.m_vt.m_max.p.y = r.m_r.w;
@@ -907,7 +907,7 @@ bool GSHwHack::GSC_BlueTongueGames(GSRendererHW& r, int& skip)
// This is the giant dither-like depth buffer. We need this on the CPU *and* the GPU for textures which are
// rendered on both.
if (context->FRAME.FBW == 8 && r.m_index.tail == 32 && r.PRIM->TME && context->TEX0.TBW == 1)
if (context->FRAME.FBW == 8 && r.m_index->tail == 32 && r.PRIM->TME && context->TEX0.TBW == 1)
{
r.SwPrimRender(r, false, false);
return false;
@@ -947,8 +947,8 @@ bool GSHwHack::GSC_MetalGearSolid3(GSRendererHW& r, int& skip)
GL_INS("OI_MetalGearSolid3(): %x -> %x, %dx%d, subtract %d", RFBP, RFBP + (RFBW / 2), r.m_r.width(), r.m_r.height(),
w_sub);
for (u32 i = 0; i < r.m_vertex.next; i++)
r.m_vertex.buff[i].XYZ.X -= w_sub_fp;
for (u32 i = 0; i < r.m_vertex->next; i++)
r.m_vertex->buff[i].XYZ.X -= w_sub_fp;
// No point adjusting the scissor, it just ends up expanding out anyway.. but we do have to fix up the draw rect.
r.m_r -= GSVector4i(w_sub);
@@ -961,7 +961,7 @@ bool GSHwHack::GSC_Turok(GSRendererHW& r, int& skip)
// Since we can't look in to the future to check this, the options are either rearrange all the pages in a target when the width changes
// (very slow, could break a ton of stuff which stores different things in the alpha channel), or this. I choose this.
if (r.m_index.tail == 6 && RPRIM->PRIM == 4 && !RTME && RFBMSK == 0x00FFFFFF && floor(r.m_vt.m_max.p.x) == 512 && r.m_env.CTXT[r.m_backed_up_ctx].FRAME.FBW == 10 && RFRAME.FBW == 8 && RFPSM == PSMCT32 && RTEST.ATE && RTEST.ATST == ATST_GEQUAL)
if (r.m_index->tail == 6 && RPRIM->PRIM == 4 && !RTME && RFBMSK == 0x00FFFFFF && floor(r.m_vt.m_max.p.x) == 512 && r.m_env.CTXT[r.m_backed_up_ctx].FRAME.FBW == 10 && RFRAME.FBW == 8 && RFPSM == PSMCT32 && RTEST.ATE && RTEST.ATST == ATST_GEQUAL)
{
int num_pages = r.m_cached_ctx.FRAME.FBW * ((floor(r.m_vt.m_max.p.y) + 31) / 32);
r.m_cached_ctx.FRAME.FBW = 10;
@@ -978,7 +978,7 @@ bool GSHwHack::GSC_Turok(GSRendererHW& r, int& skip)
bool GSHwHack::OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t)
{
const u32 n_vertices = r.m_vertex.next;
const u32 n_vertices = r.m_vertex->next;
const int w = r.m_r.width();
const int h = r.m_r.height();
const bool is_copy = !r.PRIM->ABE || (
@@ -1011,7 +1011,7 @@ bool GSHwHack::OI_PointListPalette(GSRendererHW& r, GSTexture* rt, GSTexture* ds
const u32 FBP = r.m_cached_ctx.FRAME.Block();
const u32 FBW = r.m_cached_ctx.FRAME.FBW;
GL_INS("PointListPalette - m_r = <%d, %d => %d, %d>, n_vertices = %u, FBP = 0x%x, FBW = %u", r.m_r.x, r.m_r.y, r.m_r.z, r.m_r.w, n_vertices, FBP, FBW);
const GSVertex* RESTRICT v = r.m_vertex.buff;
const GSVertex* RESTRICT v = r.m_vertex->buff;
const int ox(r.m_context->XYOFFSET.OFX);
const int oy(r.m_context->XYOFFSET.OFY);
for (size_t i = 0; i < n_vertices; ++i)
@@ -1067,7 +1067,7 @@ bool GSHwHack::OI_RozenMaidenGebetGarden(GSRendererHW& r, GSTexture* rt, GSTextu
TEX0.TBW = RFRAME.FBW;
TEX0.PSM = RFRAME.PSM;
if (GSTextureCache::Target* tmp_rt = g_texture_cache->LookupTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget))
if (GSTextureCache::Target* tmp_rt = g_texture_cache->LookupDrawTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget))
{
GL_INS("OI_RozenMaidenGebetGarden FB clear");
g_gs_device->ClearRenderTarget(tmp_rt->m_texture, 0);
@@ -1089,7 +1089,7 @@ bool GSHwHack::OI_RozenMaidenGebetGarden(GSRendererHW& r, GSTexture* rt, GSTextu
TEX0.TBW = RFRAME.FBW;
TEX0.PSM = RZBUF.PSM;
if (GSTextureCache::Target* tmp_ds = g_texture_cache->LookupTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil))
if (GSTextureCache::Target* tmp_ds = g_texture_cache->LookupDrawTarget(TEX0, r.GetTargetSize(), r.GetTextureScaleFactor(), GSTextureCache::DepthStencil))
{
GL_INS("OI_RozenMaidenGebetGarden ZB clear");
g_gs_device->ClearDepth(tmp_ds->m_texture, 0.0f);
@@ -1122,14 +1122,14 @@ bool GSHwHack::OI_SonicUnleashed(GSRendererHW& r, GSTexture* rt, GSTexture* ds,
GL_INS("OI_SonicUnleashed replace draw by a copy draw %lld", r.s_n);
GSTextureCache::Target* src = g_texture_cache->LookupTarget(Texture, GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true, 0, false, false, true, true, GSVector4i::zero(), true);
GSTextureCache::Target* src = g_texture_cache->LookupDrawTarget(Texture, GSVector2i(1, 1), r.GetTextureScaleFactor(), GSTextureCache::RenderTarget, true, 0, false, true, true, GSVector4i::zero(), true);
if (!src)
return true;
const GSVector2i src_size(src->m_texture->GetSize());
GSTextureCache::Target* rt_again = g_texture_cache->LookupTarget(Frame, src_size, src->m_scale, GSTextureCache::RenderTarget);
GSTextureCache::Target* rt_again = g_texture_cache->LookupDrawTarget(Frame, src_size, src->m_scale, GSTextureCache::RenderTarget);
if ((rt_again->m_TEX0.PSM & 0x3) == PSMCT16)
{
GSVector4 dRect;
@@ -1209,9 +1209,9 @@ bool GSHwHack::OI_ArTonelico2(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GST
buffer to adapt the page width properly.
*/
const GSVertex* v = &r.m_vertex.buff[0];
const GSVertex* v = &r.m_vertex->buff[0];
if (ds && r.m_vertex.next == 2 && !RPRIM->TME && RFRAME.FBW == 10 && v->XYZ.Z == 0 && RTEST.ZTST == ZTST_ALWAYS)
if (ds && r.m_vertex->next == 2 && !RPRIM->TME && RFRAME.FBW == 10 && v->XYZ.Z == 0 && RTEST.ZTST == ZTST_ALWAYS)
{
GL_INS("OI_ArTonelico2");
g_gs_device->ClearDepth(ds, 0.0f);
@@ -1284,15 +1284,15 @@ static bool GetMoveTargetPair(GSRendererHW& r, GSTextureCache::Target** src, GIF
const int src_type =
GSLocalMemory::m_psm[src_desc.PSM].depth ? GSTextureCache::DepthStencil : GSTextureCache::RenderTarget;
GSTextureCache::Target* tsrc =
g_texture_cache->LookupTarget(src_desc, GSVector2i(1, 1), r.GetTextureScaleFactor(), src_type);
g_texture_cache->LookupDrawTarget(src_desc, GSVector2i(1, 1), r.GetTextureScaleFactor(), src_type);
if (!tsrc)
return false;
// The target might not.
const int dst_type =
GSLocalMemory::m_psm[dst_desc.PSM].depth ? GSTextureCache::DepthStencil : GSTextureCache::RenderTarget;
GSTextureCache::Target* tdst = g_texture_cache->LookupTarget(dst_desc, tsrc->GetUnscaledSize(), tsrc->GetScale(),
dst_type, true, 0, false, false, preserve_target, preserve_target, tsrc->GetUnscaledRect());
GSTextureCache::Target* tdst = g_texture_cache->LookupDrawTarget(dst_desc, tsrc->GetUnscaledSize(), tsrc->GetScale(),
dst_type, true, 0, false, preserve_target, preserve_target, tsrc->GetUnscaledRect());
if (!tdst)
{
if (req_target)
File diff suppressed because it is too large Load Diff
+5 -4
View File
@@ -210,7 +210,7 @@ private:
void ResetStates();
void HandleProvokingVertexFirst();
void SetupIA(float target_scale, float sx, float sy, bool req_vert_backup);
void SetupIA(float target_scale, float sx, float sy, bool req_vert_backup, const bool no_rt);
void EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GSTextureCache::Source* tex);
u32 EmulateChannelShuffle(GSTextureCache::Target* src, bool test_only, GSTextureCache::Target* rt = nullptr);
void EmulateBlending(int rt_alpha_min, int rt_alpha_max, DATEOptions& date_options, GSTextureCache::Target* rt,
@@ -226,9 +226,11 @@ private:
const TextureMinMaxResult& tmm);
void EmulateZbuffer(const GSTextureCache::Target* ds);
void EmulateAA1();
static void GetAlphaTestConfigPS(const u32 atst, const u8 aref, const bool invert_test, PS_ATST& ps_atst_out, float& aref_out);
void EmulateAlphaTest(DATEOptions& date_options);
void EmulateAlphaTestSecondPass();
void ConfigureDepthFeedback();
void CalculateAlphaRange(GSTextureCache::Target* rt, GSTextureCache::Target* ds, DATEOptions& date_options,
int& blend_alpha_min, int& blend_alpha_max, int& rt_new_alpha_min, int& rt_new_alpha_max);
@@ -399,7 +401,6 @@ public:
/// Compute the drawlist (if not already present) and bounding boxes for the current draw.
std::size_t ComputeDrawlistGetSize(float scale);
/// Create a temporary color clone of depth for depth feedback (DX12 and GL only right now)
void StartDepthAsRTFeedback();
void CleanupDepthAsRTFeedback();
/// Does the current draw allow using AA1 coverage (if AA1 is enabled).
bool IsCoverageAlphaSupported() override;
};
@@ -39,21 +39,21 @@ bool GSRendererHWFunctions::SwPrimRender(GSRendererHW& hw, bool invalidate_tc, b
GSRasterizerData data;
GSScanlineGlobalData& gd = data.global;
hw.m_sw_vertex_buffer.resize(((hw.m_vertex.next + 1) & ~1));
hw.m_sw_vertex_buffer.resize(((hw.m_vertex->next + 1) & ~1));
data.primclass = vt.m_primclass;
data.buff = nullptr;
data.vertex = hw.m_sw_vertex_buffer.data();
data.vertex_count = hw.m_vertex.next;
data.index = hw.m_index.buff;
data.index_count = hw.m_index.tail;
data.vertex_count = hw.m_vertex->next;
data.index = hw.m_index->buff;
data.index_count = hw.m_index->tail;
data.scanmsk_value = env.SCANMSK.MSK;
// Skip per pixel division if q is constant.
// Optimize the division by 1 with a nop. It also means that GS_SPRITE_CLASS must be processed when !vt.m_eq.q.
// If you have both GS_SPRITE_CLASS && vt.m_eq.q, it will depends on the first part of the 'OR'.
const u32 q_div = !hw.IsMipMapActive() && ((vt.m_eq.q && vt.m_min.t.z != 1.0f) || (!vt.m_eq.q && vt.m_primclass == GS_SPRITE_CLASS));
GSVertexSW::s_cvb[vt.m_primclass][PRIM->TME][PRIM->FST][q_div](context, data.vertex, hw.m_vertex.buff, hw.m_vertex.next);
GSVertexSW::s_cvb[vt.m_primclass][PRIM->TME][PRIM->FST][q_div](context, data.vertex, hw.m_vertex->buff, hw.m_vertex->next);
GSVector4i scissor = context->scissor.in;
GSVector4i bbox = GSVector4i(vt.m_min.p.floor().xyxy(vt.m_max.p.ceil())).rintersect(scissor);
@@ -524,12 +524,12 @@ bool GSRendererHWFunctions::SwPrimRender(GSRendererHW& hw, bool invalidate_tc, b
const u32 ofx = context->XYOFFSET.OFX;
for (int i = 0, j = hw.m_vertex.tail; i < j; i++)
for (int i = 0, j = hw.m_vertex->tail; i < j; i++)
{
#if _M_SSE >= 0x501
if ((((hw.m_vertex.buff[i].XYZ.X - ofx) + 15) >> 4) & 7) // aligned to 8
if ((((hw.m_vertex->buff[i].XYZ.X - ofx) + 15) >> 4) & 7) // aligned to 8
#else
if ((((hw.m_vertex.buff[i].XYZ.X - ofx) + 15) >> 4) & 3) // aligned to 4
if ((((hw.m_vertex->buff[i].XYZ.X - ofx) + 15) >> 4) & 3) // aligned to 4
#endif
{
gd.sel.notest = 0;
@@ -551,13 +551,13 @@ bool GSRendererHWFunctions::SwPrimRender(GSRendererHW& hw, bool invalidate_tc, b
if (add_ee_transfer)
{
GSRendererHW::GSUploadQueue uq;
uq.transfer_type = GSRendererHW::GetInstance()->EEGS_TransferType::EE_to_GS;
uq.blit.U64 = 0;
uq.blit.DBP = hw.m_cached_ctx.FRAME.Block();
uq.blit.DBW = hw.m_cached_ctx.FRAME.FBW;
uq.blit.DPSM = hw.m_cached_ctx.FRAME.PSM;
uq.draw = GSState::s_n;
uq.rect = bbox;
uq.zero_clear = false;
hw.m_draw_transfers.push_back(uq);
}
File diff suppressed because it is too large Load Diff
+32 -6
View File
@@ -30,6 +30,8 @@ public:
constexpr static u32 MAX_BP = 0x3fff;
constexpr static GSVector4 FullSrcRect = GSVector4::cxpr(0.0f, 0.0f, 1.0f, 1.0f);
constexpr static bool CheckOverlap(const u32 a_bp, const u32 a_bp_end, const u32 b_bp, const u32 b_bp_end) noexcept
{
u32 b_bp_start_synced = b_bp;
@@ -261,7 +263,7 @@ public:
bool OverlapsValid(u32 bp, u32 bw, u32 psm, const GSVector4i& rect) const;
__fi bool HasValidAlpha() const { return (m_valid_alpha_low | m_valid_alpha_high); }
__fi bool HasValidAlpha() const { return (m_valid_alpha_low || m_valid_alpha_high); }
bool HasValidBitsForFormat(u32 psm, bool req_color, bool req_alpha, bool width_match);
void ResizeDrawn(const GSVector4i& rect);
@@ -485,6 +487,20 @@ protected:
Source* CreateMergedSource(GIFRegTEX0 TEX0, GIFRegTEXA TEXA, SourceRegion region, float scale);
// Used in target lookup functions to determine new sizes of targets.
struct RescaleHelper
{
RescaleHelper(const GSVector2i& size, float scale);
void CalcRescale(const Target* tgt);
void SetNewSize(const GSVector2i& new_size, float scale);
const GSVector2i m_size;
float m_scale;
GSVector2i m_new_size{};
GSVector2i m_new_scaled_size{};
GSVector4 m_dRect{};
bool m_clear = true;
};
public:
GSTextureCache();
~GSTextureCache();
@@ -516,14 +532,24 @@ public:
Target* FindTargetOverlap(Target* target, int type, int psm);
void CombineAlignedInsideTargets(Target* target, GSTextureCache::Source* src = nullptr);
Target* LookupTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type, bool used = true, u32 fbmask = 0,
bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_rgb = true, bool preserve_alpha = true,
const GSVector4i draw_rc = GSVector4i::zero(), bool is_shuffle = false, bool possible_clear = false, bool preserve_scale = false, GSTextureCache::Source* src = nullptr, GSTextureCache::Target* ds = nullptr, int offset = -1);
Target* CreateTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, float scale, int type, bool used = true, u32 fbmask = 0,
bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_target = true,
private:
Target* ProcessTargetAfterLookup(RescaleHelper& rescaler, Target* dst, GIFRegTEX0 TEX0, const GSVector2i& size, int type,
bool used, u32 fbmask, bool is_frame, bool preload, bool preserve_rgb = true, bool preserve_alpha = true,
const GSVector4i draw_rc = GSVector4i::zero(), bool is_shuffle = false, bool possible_clear = false,
bool preserve_scale = false, GSTextureCache::Source* src = nullptr, GSTextureCache::Target* ds = nullptr, int offset = -1);
public:
Target* CreateTarget(GIFRegTEX0 TEX0, const GSVector2i& size, const GSVector2i& valid_size, float scale, int type, bool used = true,
u32 fbmask = 0, bool is_frame = false, bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_target = true,
const GSVector4i draw_rc = GSVector4i::zero(), GSTextureCache::Source* src = nullptr);
Target* LookupDisplayTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, bool is_feedback);
Target* LookupDrawTarget(GIFRegTEX0 TEX0, const GSVector2i& size, float scale, int type, bool used = true, u32 fbmask = 0,
bool preload = GSConfig.PreloadFrameWithGSData, bool preserve_rgb = true, bool preserve_alpha = true,
const GSVector4i draw_rc = GSVector4i::zero(), bool is_shuffle = false, bool possible_clear = false, bool preserve_scale = false, GSTextureCache::Source* src = nullptr, GSTextureCache::Target* ds = nullptr, int offset = -1);
/// Looks up a target in the cache, and only returns it if the BP/BW match exactly.
Target* GetExactTarget(u32 BP, u32 BW, int type, u32 end_bp);
Target* GetTargetWithSharedBits(u32 BP, u32 PSM) const;
+19 -9
View File
@@ -40,14 +40,15 @@ struct PipelineSelectorExtrasMTL
GSDevice::BlendFactor dst_factor_alpha : 4;
GSDevice::BlendOp blend_op : 2;
bool blend_enable : 1;
bool has_depth : 1;
bool has_stencil : 1;
bool has_depth : 1;
bool has_stencil : 1;
bool has_rt1 : 1;
};
u32 fullkey;
};
PipelineSelectorExtrasMTL(): fullkey(0) {}
PipelineSelectorExtrasMTL(GSHWDrawConfig::BlendState blend, GSTexture* rt, GSHWDrawConfig::ColorMaskSelector cms, bool has_depth, bool has_stencil)
PipelineSelectorExtrasMTL(GSHWDrawConfig::BlendState blend, GSTexture* rt, GSHWDrawConfig::ColorMaskSelector cms, bool has_depth, bool has_stencil, bool has_rt1)
: fullkey(0)
{
this->rt = rt ? rt->GetFormat() : GSTexture::Format::Invalid;
@@ -65,6 +66,7 @@ struct PipelineSelectorExtrasMTL
this->blend_enable = blend.enable;
this->has_depth = has_depth;
this->has_stencil = has_stencil;
this->has_rt1 = has_rt1;
}
};
struct PipelineSelectorMTL
@@ -202,7 +204,7 @@ public:
bool iip : 1;
bool fst : 1;
bool point_size : 1;
GSShader::VSExpand expand : 2;
GSShader::VSExpand expand : 3;
};
u8 key;
};
@@ -263,11 +265,11 @@ public:
MRCOwned<id<MTLRenderPipelineState>> m_shadeboost_pipeline;
MRCOwned<id<MTLRenderPipelineState>> m_imgui_pipeline;
MRCOwned<id<MTLFunction>> m_hw_vs[1 << 5];
MRCOwned<id<MTLFunction>> m_hw_vs[6 << 3];
std::unordered_map<PSSelector, MRCOwned<id<MTLFunction>>> m_hw_ps;
std::unordered_map<PipelineSelectorMTL, MRCOwned<id<MTLRenderPipelineState>>> m_hw_pipeline;
MRCOwned<MTLRenderPassDescriptor*> m_render_pass_desc[8];
MRCOwned<MTLRenderPassDescriptor*> m_render_pass_desc[16];
MRCOwned<id<MTLSamplerState>> m_sampler_hw[1 << 8];
@@ -287,8 +289,9 @@ public:
GSTexture* color_target = nullptr;
GSTexture* depth_target = nullptr;
GSTexture* stencil_target = nullptr;
GSTexture* tex[8] = {};
void* vertex_buffer = nullptr;
GSTexture* tex[GSMTLTextureIndexCount] = {};
id<MTLBuffer> vertex_buffer = nullptr;
id<MTLBuffer> vs_index_buffer = nullptr;
void* name = nullptr;
struct Has
{
@@ -298,6 +301,7 @@ public:
bool blend_color : 1;
bool pipeline_sel : 1;
bool sampler : 1;
bool rt1_depth : 1;
} has = {};
DepthStencilSelector depth_sel = DepthStencilSelector::NoDepth();
// Clear line (Things below here are tracked by `has` and don't need to be cleared to reset)
@@ -315,6 +319,8 @@ public:
MRCOwned<id<MTLBlitCommandEncoder>> m_late_texture_upload_encoder;
MRCOwned<id<MTLCommandBuffer>> m_vertex_upload_cmdbuf;
MRCOwned<id<MTLBlitCommandEncoder>> m_vertex_upload_encoder;
id<MTLTexture> m_ds_as_rt_texture = nil;
GSTexture* m_ds_as_rt_gstexture = nullptr;
struct DebugEntry
{
@@ -347,6 +353,8 @@ public:
id<MTLCommandBuffer> GetRenderCmdBufWithoutCreate();
/// Get the spin fence if spinning is enabled.
id<MTLFence> GetSpinFence();
/// Get the texture to use as RT1 depth for the given depth texture
id<MTLTexture> GetRT1DepthTexture(GSTextureMTL* depth);
/// Called by command buffers when they finish
void DrawCommandBufferFinished(u64 draw, id<MTLCommandBuffer> buffer);
/// Flush pending operations from all encoders to the GPU
@@ -356,7 +364,7 @@ public:
/// End current render pass without flushing
void EndRenderPass();
/// Begin a new render pass (may reuse existing)
void BeginRenderPass(NSString* name, GSTexture* color, MTLLoadAction color_load, GSTexture* depth, MTLLoadAction depth_load, GSTexture* stencil = nullptr, MTLLoadAction stencil_load = MTLLoadActionDontCare);
void BeginRenderPass(NSString* name, GSTexture* color, MTLLoadAction color_load, GSTexture* depth, MTLLoadAction depth_load, GSTexture* stencil = nullptr, MTLLoadAction stencil_load = MTLLoadActionDontCare, bool rt1 = false);
/// Call at the end of each frame
void FrameCompleted();
@@ -412,6 +420,7 @@ public:
void UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) override;
void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) override;
void FilteredDownsampleTexture(GSTexture* sTex, GSTexture* dTex, u32 downsample_factor, const GSVector2i& clamp_min, const GSVector4& dRect) override;
void BeginDSAsRT(GSTexture* ds, const GSVector4i& drawarea) override;
void FlushClears(GSTexture* tex);
@@ -422,6 +431,7 @@ public:
void MRESetSampler(SamplerSelector sel);
void MRESetTexture(GSTexture* tex, int pos);
void MRESetVertices(id<MTLBuffer> buffer, size_t offset);
void MRESetVSIndices(id<MTLBuffer> buffer, size_t offset);
void MRESetScissor(const GSVector4i& scissor);
void MREClearScissor();
void MRESetCB(const GSHWDrawConfig::VSConstantBuffer& cb_vs);
+192 -59
View File
@@ -35,14 +35,7 @@ std::vector<GSAdapterInfo> GetMetalAdapterList()
{
GSAdapterInfo ai;
ai.name = [[dev name] UTF8String];
ai.max_texture_size = 8192;
if ([dev supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v1])
ai.max_texture_size = 16384;
if (@available(macOS 10.15, iOS 13.0, *))
if ([dev supportsFamily:MTLGPUFamilyApple3])
ai.max_texture_size = 16384;
ai.max_texture_size = GSMTLDevice::GetMaxTextureSize(dev);
ai.max_upscale_multiplier = GSGetMaxUpscaleMultiplier(ai.max_texture_size);
list.push_back(std::move(ai));
}
@@ -102,6 +95,11 @@ GSDeviceMTL::GSDeviceMTL()
GSDeviceMTL::~GSDeviceMTL()
{
// m_ds_as_rt_texture is owned if the device has memoryless textures
if (m_dev.features.memoryless_textures)
[m_ds_as_rt_texture release];
else if (m_ds_as_rt_gstexture)
delete m_ds_as_rt_gstexture;
}
GSDeviceMTL::Map GSDeviceMTL::Allocate(UploadBuffer& buffer, size_t amt)
@@ -252,6 +250,14 @@ id<MTLFence> GSDeviceMTL::GetSpinFence()
return m_spin_timer ? m_spin_fence : nil;
}
id<MTLTexture> GSDeviceMTL::GetRT1DepthTexture(GSTextureMTL* depth)
{
if (m_dev.features.framebuffer_fetch)
return m_ds_as_rt_texture;
else
return static_cast<GSTextureMTL*>(m_ds_as_rt)->GetTexture();
}
void GSDeviceMTL::DrawCommandBufferFinished(u64 draw, id<MTLCommandBuffer> buffer)
{
// We can do the update non-atomically because we only ever update under the lock
@@ -395,14 +401,15 @@ void GSDeviceMTL::EndRenderPass()
}
}
void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadAction color_load, GSTexture* depth, MTLLoadAction depth_load, GSTexture* stencil, MTLLoadAction stencil_load)
void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadAction color_load, GSTexture* depth, MTLLoadAction depth_load, GSTexture* stencil, MTLLoadAction stencil_load, bool rt1)
{
GSTextureMTL* mc = static_cast<GSTextureMTL*>(color);
GSTextureMTL* md = static_cast<GSTextureMTL*>(depth);
GSTextureMTL* ms = static_cast<GSTextureMTL*>(stencil);
bool needs_new = color != m_current_render.color_target
|| depth != m_current_render.depth_target
|| stencil != m_current_render.stencil_target;
|| stencil != m_current_render.stencil_target
|| rt1 != m_current_render.has.rt1_depth;
GSVector4 color_clear;
float depth_clear;
// Depth and stencil might be the same, so do all invalidation checks before resetting invalidation
@@ -456,6 +463,7 @@ void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadActio
if (mc) idx |= 1;
if (md) idx |= 2;
if (ms) idx |= 4;
if (rt1) idx |= 8;
MTLRenderPassDescriptor* desc = m_render_pass_desc[idx];
if (mc)
@@ -481,6 +489,14 @@ void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadActio
pxAssert(stencil_load != MTLLoadActionClear);
desc.stencilAttachment.loadAction = stencil_load;
}
if (rt1)
{
pxAssert(md);
MTLRenderPassColorAttachmentDescriptor* color1 = desc.colorAttachments[1];
color1.texture = GetRT1DepthTexture(md);
if (m_features.framebuffer_fetch)
color1.clearColor = MTLClearColorMake(depth_load == MTLLoadActionClear ? depth_clear : -1, 0, 0, 0);
}
EndRenderPass();
m_current_render.encoder = MRCRetain([GetRenderCmdBuf() renderCommandEncoderWithDescriptor:desc]);
@@ -492,6 +508,7 @@ void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadActio
m_current_render.color_target = color;
m_current_render.depth_target = depth;
m_current_render.stencil_target = stencil;
m_current_render.has.rt1_depth |= rt1;
pxAssertRel(m_current_render.encoder, "Failed to create render encoder!");
}
@@ -845,7 +862,7 @@ static MRCOwned<id<MTLSamplerState>> CreateSampler(id<MTLDevice> dev, GSHWDrawCo
[sdesc setTAddressMode:sel.tav ? MTLSamplerAddressModeRepeat : MTLSamplerAddressModeClampToEdge];
[sdesc setRAddressMode:MTLSamplerAddressModeClampToEdge];
[sdesc setMaxAnisotropy:GSConfig.MaxAnisotropy && sel.aniso ? GSConfig.MaxAnisotropy : 1];
[sdesc setMaxAnisotropy:1];
bool clampLOD = sel.lodclamp || !sel.UseMipmapFiltering();
const char* clampdesc = clampLOD ? " LODClamp" : "";
[sdesc setLodMaxClamp:clampLOD ? 0.25f : FLT_MAX];
@@ -856,6 +873,20 @@ static MRCOwned<id<MTLSamplerState>> CreateSampler(id<MTLDevice> dev, GSHWDrawCo
return ret;
}
static bool getDepthFeedback(const GSMTLDevice& dev, bool fbfetch)
{
switch (GSConfig.DepthFeedbackMode)
{
case GSDepthFeedbackMode::Auto:
return dev.features.depth_feedback;
case GSDepthFeedbackMode::Depth:
// Depth feedback + FBFetch not supported
return !fbfetch;
default:
return false;
}
}
bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
{ @autoreleasepool {
if (!GSDevice::Create(vsync_mode, allow_present_throttle))
@@ -942,12 +973,14 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
m_features.stencil_buffer = true;
m_features.cas_sharpening = true;
m_features.test_and_sample_depth = true;
m_features.depth_feedback = GSDevice::DepthFeedbackSupport::None;
m_features.depth_feedback = getDepthFeedback(m_dev, m_features.framebuffer_fetch);
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand;
m_max_texture_size = m_dev.features.max_texsize;
// Init metal stuff
m_fn_constants = MRCTransfer([MTLFunctionConstantValues new]);
setFnConstantB(m_fn_constants, m_dev.features.framebuffer_fetch, GSMTLConstantIndex_FRAMEBUFFER_FETCH);
setFnConstantB(m_fn_constants, m_features.framebuffer_fetch, GSMTLConstantIndex_FRAMEBUFFER_FETCH);
setFnConstantB(m_fn_constants, m_features.depth_feedback, GSMTLConstantIndex_DEPTH_FEEDBACK);
m_draw_sync_fence = MRCTransfer([m_dev.dev newFence]);
[m_draw_sync_fence setLabel:@"Draw Sync Fence"];
@@ -983,11 +1016,27 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
applyAttribute(m_hw_vertex, GSMTLAttributeIndexUV, MTLVertexFormatUShort2, offsetof(GSVertex, UV), GSMTLBufferIndexHWVertices);
applyAttribute(m_hw_vertex, GSMTLAttributeIndexF, MTLVertexFormatUChar4Normalized, offsetof(GSVertex, FOG), GSMTLBufferIndexHWVertices);
for (auto& desc : m_render_pass_desc)
for (size_t i = 0; i < std::size(m_render_pass_desc); i++)
{
desc = MRCTransfer([MTLRenderPassDescriptor new]);
const bool depth = i & 2;
const bool stencil = i & 4;
const bool rt1 = i & 8;
if (rt1 && m_features.depth_feedback)
continue;
if (rt1 && !depth)
continue;
if (stencil && m_features.framebuffer_fetch)
continue;
auto desc = MRCTransfer([MTLRenderPassDescriptor new]);
[[desc depthAttachment] setStoreAction:MTLStoreActionStore];
[[desc stencilAttachment] setStoreAction:MTLStoreActionStore];
if (rt1)
{
MTLRenderPassColorAttachmentDescriptor* color1 = [[desc colorAttachments] objectAtIndexedSubscript:1];
[color1 setStoreAction:MTLStoreActionDontCare];
[color1 setLoadAction:m_features.framebuffer_fetch ? MTLLoadActionClear : MTLLoadActionLoad];
}
m_render_pass_desc[i] = desc;
}
// Init samplers
@@ -1062,13 +1111,8 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
setFnConstantB(m_fn_constants, sel.fst, GSMTLConstantIndex_FST);
setFnConstantB(m_fn_constants, sel.iip, GSMTLConstantIndex_IIP);
setFnConstantB(m_fn_constants, sel.point_size, GSMTLConstantIndex_VS_POINT_SIZE);
NSString* shader = @"vs_main";
if (sel.expand != GSShader::VSExpand::None)
{
setFnConstantI(m_fn_constants, sel.expand, GSMTLConstantIndex_VS_EXPAND_TYPE);
shader = @"vs_main_expand";
}
m_hw_vs[i] = LoadShader(shader);
setFnConstantI(m_fn_constants, sel.expand, GSMTLConstantIndex_VS_EXPAND_TYPE);
m_hw_vs[i] = LoadShader(sel.expand == GSShader::VSExpand::None ? @"vs_main" : @"vs_main_expand");
}
// Init pipelines
@@ -1273,12 +1317,14 @@ bool GSDeviceMTL::SupportsExclusiveFullscreen() const { return false; }
std::string GSDeviceMTL::GetDriverInfo() const
{ @autoreleasepool {
std::string desc([[m_dev.dev description] UTF8String]);
desc += "\n Texture Swizzle: " + std::string(m_dev.features.texture_swizzle ? "Supported" : "Unsupported");
desc += "\n Unified Memory: " + std::string(m_dev.features.unified_memory ? "Supported" : "Unsupported");
desc += "\n Framebuffer Fetch: " + std::string(m_dev.features.framebuffer_fetch ? "Supported" : "Unsupported");
desc += "\n Primitive ID: " + std::string(m_dev.features.primid ? "Supported" : "Unsupported");
desc += "\n Shader Version: " + std::string(to_string(m_dev.features.shader_version));
desc += "\n Max Texture Size: " + std::to_string(m_dev.features.max_texsize);
desc += "\n Texture Swizzle: " + std::string(m_dev.features.texture_swizzle ? "Supported" : "Unsupported");
desc += "\n Unified Memory: " + std::string(m_dev.features.unified_memory ? "Supported" : "Unsupported");
desc += "\n Framebuffer Fetch: " + std::string(m_dev.features.framebuffer_fetch ? "Supported" : "Unsupported");
desc += "\n Memoryless Textures: " + std::string(m_dev.features.memoryless_textures ? "Supported" : "Unsupported");
desc += "\n Primitive ID: " + std::string(m_dev.features.primid ? "Supported" : "Unsupported");
desc += "\n Depth Feedback: " + std::string(m_dev.features.depth_feedback ? "Supported" : "Unsupported");
desc += "\n Shader Version: " + std::string(to_string(m_dev.features.shader_version));
desc += "\n Max Texture Size: " + std::to_string(m_dev.features.max_texsize);
return desc;
}}
@@ -1764,6 +1810,50 @@ void GSDeviceMTL::FilteredDownsampleTexture(GSTexture* sTex, GSTexture* dTex, u3
DoStretchRect(sTex, GSVector4::zero(), dTex, dRect, pipeline, false, LoadAction::DontCareIfFull, &uniform, sizeof(uniform));
}}
static id<MTLTexture> CreateDSAsRTTexture(id<MTLDevice> dev, NSUInteger width, NSUInteger height, MTLStorageMode storage, NSString* name)
{
MTLTextureDescriptor *desc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatR32Float width:width height:height mipmapped:false];
[desc setUsage:MTLTextureUsageRenderTarget];
[desc setStorageMode:storage];
id<MTLTexture> result = [dev newTextureWithDescriptor:desc];
[result setLabel:name];
return result;
}
void GSDeviceMTL::BeginDSAsRT(GSTexture* ds, const GSVector4i& drawarea)
{
if (!m_features.framebuffer_fetch)
return GSDevice::BeginDSAsRT(ds, drawarea);
u32 needed_width = ds->GetWidth();
u32 needed_height = ds->GetHeight();
u32 current_width = static_cast<u32>([m_ds_as_rt_texture width]);
u32 current_height = static_cast<u32>([m_ds_as_rt_texture height]);
if (m_dev.features.memoryless_textures)
{
if (needed_width > current_width || needed_height > current_height) [[unlikely]] @autoreleasepool
{
u32 width = std::max(needed_width, current_width);
u32 height = std::max(needed_height, current_height);
[m_ds_as_rt_texture release];
m_ds_as_rt_texture = CreateDSAsRTTexture(m_dev.dev, width, height, MTLStorageModeMemoryless, @"DS as RT");
}
}
else
{
if (needed_width == current_width && needed_height == current_height)
return;
if (m_ds_as_rt_gstexture)
Recycle(m_ds_as_rt_gstexture);
m_ds_as_rt_gstexture = CreateRenderTarget(needed_width, needed_height, GSTexture::Format::Float32, false, true);
m_ds_as_rt_texture = static_cast<GSTextureMTL*>(m_ds_as_rt_gstexture)->GetTexture();
@autoreleasepool
{
NSString* name = [NSString stringWithFormat:@"DS as RT %dx%d", needed_width, needed_height];
[m_ds_as_rt_texture setLabel:name];
}
}
}
void GSDeviceMTL::FlushClears(GSTexture* tex)
{
if (tex)
@@ -1807,7 +1897,7 @@ static MTLBlendOperation ConvertBlendOp(GSDevice::BlendOp generic)
void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDrawConfig::PSSelector pssel, GSHWDrawConfig::BlendState blend, GSHWDrawConfig::ColorMaskSelector cms)
{
PipelineSelectorExtrasMTL extras(blend, m_current_render.color_target, cms, m_current_render.depth_target, m_current_render.stencil_target);
PipelineSelectorExtrasMTL extras(blend, m_current_render.color_target, cms, m_current_render.depth_target, m_current_render.stencil_target, m_current_render.has.rt1_depth);
PipelineSelectorMTL fullsel(vssel, pssel, extras);
if (m_current_render.has.pipeline_sel && fullsel == m_current_render.pipeline_sel)
return;
@@ -1849,6 +1939,7 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
setFnConstantI(m_fn_constants, pssel.date, GSMTLConstantIndex_PS_DATE);
setFnConstantI(m_fn_constants, pssel.atst, GSMTLConstantIndex_PS_ATST);
setFnConstantI(m_fn_constants, pssel.afail, GSMTLConstantIndex_PS_AFAIL);
setFnConstantI(m_fn_constants, pssel.ztst, GSMTLConstantIndex_PS_ZTST);
setFnConstantI(m_fn_constants, pssel.tfx, GSMTLConstantIndex_PS_TFX);
setFnConstantB(m_fn_constants, pssel.tcc, GSMTLConstantIndex_PS_TCC);
setFnConstantI(m_fn_constants, pssel.wms, GSMTLConstantIndex_PS_WMS);
@@ -1893,6 +1984,9 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
setFnConstantB(m_fn_constants, pssel.manual_lod, GSMTLConstantIndex_PS_MANUAL_LOD);
setFnConstantB(m_fn_constants, pssel.region_rect, GSMTLConstantIndex_PS_REGION_RECT);
setFnConstantI(m_fn_constants, pssel.scanmsk, GSMTLConstantIndex_PS_SCANMSK);
setFnConstantI(m_fn_constants, pssel.aa1, GSMTLConstantIndex_PS_AA1);
setFnConstantB(m_fn_constants, pssel.abe, GSMTLConstantIndex_PS_ABE);
setFnConstantI(m_fn_constants, pssel.sw_aniso, GSMTLConstantIndex_PS_SW_ANISO);
auto newps = LoadShader(@"ps_main");
ps = newps;
m_hw_ps.insert(std::make_pair(pssel, std::move(newps)));
@@ -1927,6 +2021,11 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
color.sourceAlphaBlendFactor = ConvertBlendFactor(extras.src_factor_alpha);
color.destinationAlphaBlendFactor = ConvertBlendFactor(extras.dst_factor_alpha);
}
if (extras.has_rt1)
{
MTLRenderPipelineColorAttachmentDescriptor* color1 = [[pdesc colorAttachments] objectAtIndexedSubscript:1];
[color1 setPixelFormat:MTLPixelFormatR32Float];
}
NSString* pname = [NSString stringWithFormat:@"HW Render %x.%x.%llx.%x", vssel_mtl.key, pssel.key_hi, pssel.key_lo, extras.fullkey];
auto pipeline = MakePipeline(pdesc, vs, ps, pname);
@@ -1980,9 +2079,9 @@ void GSDeviceMTL::MRESetTexture(GSTexture* tex, int pos)
void GSDeviceMTL::MRESetVertices(id<MTLBuffer> buffer, size_t offset)
{
if (m_current_render.vertex_buffer != (__bridge void*)buffer)
if (m_current_render.vertex_buffer != buffer)
{
m_current_render.vertex_buffer = (__bridge void*)buffer;
m_current_render.vertex_buffer = buffer;
[m_current_render.encoder setVertexBuffer:buffer offset:offset atIndex:GSMTLBufferIndexHWVertices];
}
else
@@ -1991,6 +2090,19 @@ void GSDeviceMTL::MRESetVertices(id<MTLBuffer> buffer, size_t offset)
}
}
void GSDeviceMTL::MRESetVSIndices(id<MTLBuffer> buffer, size_t offset)
{
if (m_current_render.vs_index_buffer != buffer)
{
m_current_render.vs_index_buffer = buffer;
[m_current_render.encoder setVertexBuffer:buffer offset:offset atIndex:GSMTLBufferIndexHWIndices];
}
else
{
[m_current_render.encoder setVertexBufferOffset:offset atIndex:GSMTLBufferIndexHWIndices];
}
}
void GSDeviceMTL::MRESetScissor(const GSVector4i& scissor)
{
if (m_current_render.has.scissor && (m_current_render.scissor == scissor).alltrue())
@@ -2130,6 +2242,8 @@ void GSDeviceMTL::MREInitHWDraw(GSHWDrawConfig& config, const Map& verts)
MRESetCB(config.cb_vs);
MRESetCB(config.cb_ps);
MRESetVertices(verts.gpu_buffer, verts.gpu_offset);
if (config.vs.UseVSExpandIndexBuffer())
MRESetVSIndices(verts.gpu_buffer, verts.gpu_offset + config.nverts * sizeof(*config.verts));
}
void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
@@ -2138,22 +2252,31 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
EndRenderPass(); // Barrier
size_t vertsize = config.nverts * sizeof(*config.verts);
size_t idxsize = config.vs.UseExpandIndexBuffer() ? 0 : (config.nindices * sizeof(*config.indices));
size_t idxsize = config.vs.UseFixedExpandIndexBuffer() ? 0 : (config.nindices * sizeof(*config.indices));
Map allocation = Allocate(m_vertex_upload_buf, vertsize + idxsize);
memcpy(allocation.cpu_buffer, config.verts, vertsize);
id<MTLBuffer> index_buffer;
size_t index_buffer_offset;
if (!config.vs.UseExpandIndexBuffer())
id<MTLBuffer> index_buffer = nil;
size_t index_buffer_offset = 0;
if (!config.vs.UseFixedExpandIndexBuffer())
{
memcpy(static_cast<u8*>(allocation.cpu_buffer) + vertsize, config.indices, idxsize);
index_buffer = allocation.gpu_buffer;
index_buffer_offset = allocation.gpu_offset + vertsize;
if (config.vs.UseVSExpandIndexBuffer())
{
// VS expand index buffer is bound to the VS instead of the input assembler
u32 expand = GetExpansionFactor(config.vs.expand);
config.nindices *= expand;
config.indices_per_prim *= expand;
}
else
{
index_buffer = allocation.gpu_buffer;
index_buffer_offset = allocation.gpu_offset + vertsize;
}
}
else
{
index_buffer = m_expand_index_buffer;
index_buffer_offset = 0;
}
FlushClears(config.tex);
@@ -2273,7 +2396,9 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
return;
}
BeginRenderPass(@"RenderHW", rt, MTLLoadActionLoad, config.ds, MTLLoadActionLoad, stencil, MTLLoadActionLoad);
const bool feedback_depth = config.ps.IsFeedbackLoopDepth();
const bool rt1 = feedback_depth && !m_features.depth_feedback;
BeginRenderPass(@"RenderHW", rt, MTLLoadActionLoad, config.ds, MTLLoadActionLoad, stencil, MTLLoadActionLoad, rt1);
id<MTLRenderCommandEncoder> mtlenc = m_current_render.encoder;
FlushDebugEntries(mtlenc);
if (usesStencil(config.destination_alpha))
@@ -2281,6 +2406,11 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
MREInitHWDraw(config, allocation);
if (config.require_one_barrier || config.require_full_barrier)
MRESetTexture(rt, GSMTLTextureIndexRenderTarget);
if (feedback_depth)
{
GSTexture* tex = !m_features.depth_feedback && !m_features.framebuffer_fetch ? m_ds_as_rt : config.ds;
MRESetTexture(tex, GSMTLTextureIndexDepthTarget);
}
if (primid_tex)
MRESetTexture(primid_tex, GSMTLTextureIndexPrimIDs);
if (config.blend.constant_enable)
@@ -2313,7 +2443,7 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
Recycle(colclip_rt);
g_gs_device->SetColorClipTexture(nullptr);
SetColorClipTexture(nullptr);
}
}
@@ -2321,6 +2451,24 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
Recycle(primid_tex);
}}
static void EncodeDraw(id<MTLRenderCommandEncoder> enc, MTLPrimitiveType topology, size_t count, id<MTLBuffer> indices, size_t off, size_t base_vertex)
{
if (indices)
{
[enc drawIndexedPrimitives:topology
indexCount:count
indexType:MTLIndexTypeUInt16
indexBuffer:indices
indexBufferOffset:off + base_vertex * sizeof(uint16_t)];
}
else
{
[enc drawPrimitives:topology
vertexStart:base_vertex
vertexCount:count];
}
}
void GSDeviceMTL::SendHWDraw(GSHWDrawConfig& config, id<MTLRenderCommandEncoder> enc, id<MTLBuffer> buffer, size_t off, bool one_barrier, bool full_barrier)
{
MTLPrimitiveType topology;
@@ -2333,14 +2481,8 @@ void GSDeviceMTL::SendHWDraw(GSHWDrawConfig& config, id<MTLRenderCommandEncoder>
if (!m_features.texture_barrier) [[unlikely]]
{
[enc drawIndexedPrimitives:topology
indexCount:config.nindices
indexType:MTLIndexTypeUInt16
indexBuffer:buffer
indexBufferOffset:off];
EncodeDraw(enc, topology, config.nindices, buffer, off, 0);
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
return;
}
@@ -2373,13 +2515,9 @@ void GSDeviceMTL::SendHWDraw(GSHWDrawConfig& config, id<MTLRenderCommandEncoder>
for (u32 n = 0, p = 0; n < draw_list_size; n++)
{
const u32 count = (*config.drawlist)[n] * indices_per_prim;
const size_t count = (*config.drawlist)[n] * indices_per_prim;
textureBarrier(enc);
[enc drawIndexedPrimitives:topology
indexCount:count
indexType:MTLIndexTypeUInt16
indexBuffer:buffer
indexBufferOffset:off + p * sizeof(*config.indices)];
EncodeDraw(enc, topology, count, buffer, off, p);
p += count;
}
@@ -2393,12 +2531,7 @@ void GSDeviceMTL::SendHWDraw(GSHWDrawConfig& config, id<MTLRenderCommandEncoder>
g_perfmon.Put(GSPerfMon::Barriers, 1);
}
[enc drawIndexedPrimitives:topology
indexCount:config.nindices
indexType:MTLIndexTypeUInt16
indexBuffer:buffer
indexBufferOffset:off];
EncodeDraw(enc, topology, config.nindices, buffer, off, 0);
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
}
+11 -6
View File
@@ -11,6 +11,7 @@
#include "common/MRCHelpers.h"
#include "common/Pcsx2Types.h"
#include "GS/Renderers/Common/GSShaderEnums.h"
#include <Metal/Metal.h>
struct GSMTLDevice
@@ -25,12 +26,14 @@ struct GSMTLDevice
struct Features
{
bool unified_memory;
bool texture_swizzle;
bool framebuffer_fetch;
bool primid;
bool slow_color_compression; ///< Color compression seems to slow down rt read on AMD
bool has_fast_half;
bool unified_memory : 1;
bool texture_swizzle : 1;
bool framebuffer_fetch : 1;
bool primid : 1;
bool slow_color_compression : 1; ///< Color compression seems to slow down rt read on AMD
bool has_fast_half : 1;
bool memoryless_textures : 1;
bool depth_feedback : 1;
MetalVersion shader_version;
int max_texsize;
};
@@ -42,6 +45,8 @@ struct GSMTLDevice
GSMTLDevice() = default;
explicit GSMTLDevice(MRCOwned<id<MTLDevice>> dev);
static u32 GetMaxTextureSize(id<MTLDevice> dev);
bool IsOk() const { return dev && shaders; }
void Reset()
{
+56 -18
View File
@@ -147,7 +147,7 @@ GSMTLDevice::GSMTLDevice(MRCOwned<id<MTLDevice>> dev)
if (@available(macOS 11.0, iOS 13.0, *))
if ([dev supportsFamily:MTLGPUFamilyApple1])
features.framebuffer_fetch = true;
features.framebuffer_fetch = features.memoryless_textures = true;
if (@available(macOS 10.15, iOS 13.0, *))
if ([dev supportsFamily:MTLGPUFamilyMac2] || [dev supportsFamily:MTLGPUFamilyApple1])
@@ -164,21 +164,49 @@ GSMTLDevice::GSMTLDevice(MRCOwned<id<MTLDevice>> dev)
if (features.primid && !detectPrimIDSupport(dev, shaders))
features.primid = false;
if (!features.framebuffer_fetch && features.shader_version >= MetalVersion::Metal23)
features.depth_feedback = false;
NSString* name = [dev name];
if ([name containsString:@"Intel"])
{
switch (detectIntelGPU(dev, shaders))
if (!features.framebuffer_fetch && features.shader_version >= MetalVersion::Metal23)
{
case DetectionResult::HaswellOrNotIntel:
break;
case DetectionResult::Broadwell:
features.primid = false; // Broken
break;
case DetectionResult::Skylake:
features.primid = false; // Broken
features.framebuffer_fetch = true;
break;
switch (detectIntelGPU(dev, shaders))
{
case DetectionResult::HaswellOrNotIntel:
// Older Intel GPUs seem to be fine with depth feedback
features.depth_feedback = true;
break;
case DetectionResult::Broadwell:
features.primid = false; // Broken
break;
case DetectionResult::Skylake:
features.primid = false; // Broken
features.framebuffer_fetch = true;
break;
}
}
}
else if ([name containsString:@"AMD"])
{
// RDNA+ seems to work fine with depth feedback
if (@available(macOS 13, iOS 16, *))
if ([dev supportsFamily:MTLGPUFamilyMetal3])
features.depth_feedback = true;
}
else if ([name containsString:@"NVIDIA"])
{
// macOS only supports Kepler, which seems to work fine with depth feedback
features.depth_feedback = true;
}
else if ([name containsString:@"Apple"])
{
// No special settings
}
else
{
Console.Warning("Unrecognized GPU vendor %s", [name UTF8String]);
}
if (features.framebuffer_fetch && GSConfig.DisableFramebufferFetch)
{
@@ -191,16 +219,26 @@ GSMTLDevice::GSMTLDevice(MRCOwned<id<MTLDevice>> dev)
else
features.slow_color_compression = [[dev name] containsString:@"AMD"] || [[dev name] isEqualToString:@"Intel HD Graphics 4000"];
features.max_texsize = 8192;
if ([dev supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v1])
features.max_texsize = 16384;
if (@available(macOS 10.15, iOS 13.0, *))
if ([dev supportsFamily:MTLGPUFamilyApple3])
features.max_texsize = 16384;
features.max_texsize = GetMaxTextureSize(dev);
this->dev = std::move(dev);
}
u32 GSMTLDevice::GetMaxTextureSize(id<MTLDevice> dev)
{
if (@available(macOS 10.15, iOS 13.0, *))
{
MTLGPUFamily apple10 = static_cast<MTLGPUFamily>(1010); // Avoid relying on latest SDK, define ourselves
if ([dev supportsFamily:apple10])
return 32768;
if ([dev supportsFamily:MTLGPUFamilyApple3])
return 16384;
}
if ([dev supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v1])
return 16384;
return 8192;
}
const char* to_string(GSMTLDevice::MetalVersion ver)
{
switch (ver)
@@ -11,6 +11,7 @@ enum GSMTLBufferIndices
GSMTLBufferIndexUniforms,
GSMTLBufferIndexHWVertices,
GSMTLBufferIndexHWUniforms,
GSMTLBufferIndexHWIndices,
};
enum GSMTLTextureIndex
@@ -20,6 +21,8 @@ enum GSMTLTextureIndex
GSMTLTextureIndexPalette,
GSMTLTextureIndexRenderTarget,
GSMTLTextureIndexPrimIDs,
GSMTLTextureIndexDepthTarget,
GSMTLTextureIndexCount,
};
struct GSMTLConvertPSUniform
@@ -95,6 +98,7 @@ struct GSMTLMainVSUniform
vector_float2 texture_offset;
vector_float2 point_size;
uint max_depth;
uint _pad0;
};
struct GSMTLMainPSUniform
@@ -153,6 +157,7 @@ enum GSMTLFnConstants
{
GSMTLConstantIndex_CAS_SHARPEN_ONLY,
GSMTLConstantIndex_FRAMEBUFFER_FETCH,
GSMTLConstantIndex_DEPTH_FEEDBACK,
GSMTLConstantIndex_FST,
GSMTLConstantIndex_IIP,
GSMTLConstantIndex_VS_POINT_SIZE,
@@ -167,6 +172,7 @@ enum GSMTLFnConstants
GSMTLConstantIndex_PS_DATE,
GSMTLConstantIndex_PS_ATST,
GSMTLConstantIndex_PS_AFAIL,
GSMTLConstantIndex_PS_ZTST,
GSMTLConstantIndex_PS_TFX,
GSMTLConstantIndex_PS_TCC,
GSMTLConstantIndex_PS_WMS,
@@ -211,4 +217,7 @@ enum GSMTLFnConstants
GSMTLConstantIndex_PS_MANUAL_LOD,
GSMTLConstantIndex_PS_REGION_RECT,
GSMTLConstantIndex_PS_SCANMSK,
GSMTLConstantIndex_PS_AA1,
GSMTLConstantIndex_PS_ABE,
GSMTLConstantIndex_PS_SW_ANISO,
};
+341 -47
View File
@@ -12,6 +12,7 @@ constant uint SHUFFLE_READ = 1;
constant uint SHUFFLE_READWRITE = 3;
constant bool HAS_FBFETCH [[function_constant(GSMTLConstantIndex_FRAMEBUFFER_FETCH)]];
constant bool DEPTH_FEEDBACK [[function_constant(GSMTLConstantIndex_DEPTH_FEEDBACK)]];
constant bool FST [[function_constant(GSMTLConstantIndex_FST)]];
constant bool IIP [[function_constant(GSMTLConstantIndex_IIP)]];
constant bool VS_POINT_SIZE [[function_constant(GSMTLConstantIndex_VS_POINT_SIZE)]];
@@ -26,6 +27,7 @@ constant bool PS_FOG [[function_constant(GSMTLConstantIndex_PS_FO
constant uint PS_DATE [[function_constant(GSMTLConstantIndex_PS_DATE)]];
constant uint PS_ATST_RAW [[function_constant(GSMTLConstantIndex_PS_ATST)]];
constant uint PS_AFAIL_RAW [[function_constant(GSMTLConstantIndex_PS_AFAIL)]];
constant uint PS_ZTST_RAW [[function_constant(GSMTLConstantIndex_PS_ZTST)]];
constant uint PS_TFX [[function_constant(GSMTLConstantIndex_PS_TFX)]];
constant bool PS_TCC [[function_constant(GSMTLConstantIndex_PS_TCC)]];
constant uint PS_WMS [[function_constant(GSMTLConstantIndex_PS_WMS)]];
@@ -70,13 +72,20 @@ constant bool PS_AUTOMATIC_LOD [[function_constant(GSMTLConstantIndex_PS_AU
constant bool PS_MANUAL_LOD [[function_constant(GSMTLConstantIndex_PS_MANUAL_LOD)]];
constant bool PS_REGION_RECT [[function_constant(GSMTLConstantIndex_PS_REGION_RECT)]];
constant uint PS_SCANMSK [[function_constant(GSMTLConstantIndex_PS_SCANMSK)]];
constant uint PS_AA1_RAW [[function_constant(GSMTLConstantIndex_PS_AA1)]];
constant bool PS_ABE [[function_constant(GSMTLConstantIndex_PS_ABE)]];
constant uint PS_SW_ANISO [[function_constant(GSMTLConstantIndex_PS_SW_ANISO)]];
using GSShader::VSExpand;
using AFAIL = GSShader::PS_AFAIL;
using ATST = GSShader::PS_ATST;
using GSShader::ZTST;
using AA1 = GSShader::PS_AA1;
constant VSExpand VS_EXPAND_TYPE = static_cast<VSExpand>(VS_EXPAND_TYPE_RAW);
constant AFAIL PS_AFAIL = static_cast<AFAIL>(PS_AFAIL_RAW);
constant ATST PS_ATST = static_cast<ATST>(PS_ATST_RAW);
constant ZTST PS_ZTST = static_cast<ZTST>(PS_ZTST_RAW);
constant AA1 PS_AA1 = static_cast<AA1>(PS_AA1_RAW);
#if defined(__METAL_MACOS__) && __METAL_VERSION__ >= 220
#define PRIMID_SUPPORT 1
@@ -103,12 +112,24 @@ constant bool SW_BLEND = (PS_BLEND_A != PS_BLEND_B) || PS_BLEND_D;
constant bool SW_AD_TO_HW = (PS_BLEND_C == 1 && PS_A_MASKED);
constant bool NEEDS_RT_FOR_BLEND = (((PS_BLEND_A != PS_BLEND_B) && (PS_BLEND_A == 1 || PS_BLEND_B == 1 || PS_BLEND_C == 1)) || PS_BLEND_D == 1 || SW_AD_TO_HW);
constant bool NEEDS_RT_EARLY = PS_TEX_IS_FB || PS_DATE >= 5;
constant bool NEEDS_RT_FOR_AFAIL = PS_AFAIL == AFAIL::ZB_ONLY || PS_AFAIL == AFAIL::RGB_ONLY;
constant bool NEEDS_RT_FOR_AFAIL = PS_AFAIL == AFAIL::ZB_ONLY || PS_AFAIL == AFAIL::RGB_ONLY || PS_AFAIL == AFAIL::RGB_ONLY_SW_Z;
constant bool NEEDS_RT = NEEDS_RT_FOR_AFAIL || NEEDS_RT_EARLY || (!PS_PRIM_CHECKING_INIT && (PS_FBMASK || NEEDS_RT_FOR_BLEND));
constant bool NEEDS_DEPTH_FOR_AFAIL = PS_AFAIL == AFAIL::FB_ONLY || PS_AFAIL == AFAIL::RGB_ONLY_SW_Z;
constant bool NEEDS_DEPTH_FOR_ZTST = PS_ZTST == ZTST::GEQUAL || PS_ZTST == ZTST::GREATER;
constant bool NEEDS_DEPTH_FOR_AA1 = PS_AA1 == AA1::TRIANGLE_SW_Z;
constant bool SW_DEPTH = NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST || NEEDS_DEPTH_FOR_AA1;
constant bool PS_COLOR0 = !PS_NO_COLOR;
constant bool PS_COLOR1 = !PS_NO_COLOR1;
constant bool PS_ZOUTPUT = PS_ZCLAMP || PS_ZFLOOR;
constant bool PS_ZOUTPUT = PS_ZCLAMP || PS_ZFLOOR || SW_DEPTH;
constant bool PS_ZOUTPUT_LESS = PS_ZOUTPUT && !SW_DEPTH;
constant bool PS_ZOUTPUT_ANY = PS_ZOUTPUT && SW_DEPTH;
constant bool PS_ZOUTPUT_COLOR = PS_ZOUTPUT_ANY && !DEPTH_FEEDBACK;
constant bool VS_NEEDS_INDEX_BUFFER = VS_EXPAND_TYPE == VSExpand::TriangleAA1;
constant bool VS_COVERAGE = VS_EXPAND_TYPE == VSExpand::LineAA1 || VS_EXPAND_TYPE == VSExpand::TriangleAA1;
constant bool VS_INTERIOR = VS_EXPAND_TYPE == VSExpand::TriangleAA1;
constant bool PS_COVERAGE = PS_AA1 != AA1::NONE;
constant bool PS_INTERIOR = PS_AA1 == AA1::TRIANGLE_SW_Z;
struct MainVSIn
{
@@ -128,6 +149,8 @@ struct MainVSOut
float4 ti;
float4 c [[function_constant(IIP)]];
float4 fc [[flat, function_constant(NOT_IIP)]];
float inv_cov [[function_constant(VS_COVERAGE)]];
uint interior [[function_constant(VS_INTERIOR)]];
float point_size [[point_size, function_constant(VS_POINT_SIZE)]];
};
@@ -138,13 +161,26 @@ struct MainPSIn
float4 ti;
float4 c [[function_constant(IIP)]];
float4 fc [[flat, function_constant(NOT_IIP)]];
float inv_cov [[function_constant(PS_COVERAGE)]];
uint interior [[function_constant(PS_INTERIOR)]];
};
struct MainPSOut
{
float4 c0 [[color(0), index(0), function_constant(PS_COLOR0)]];
float4 c1 [[color(0), index(1), function_constant(PS_COLOR1)]];
float depth [[depth(less), function_constant(PS_ZOUTPUT)]];
float depthColor [[color(1), function_constant(PS_ZOUTPUT_COLOR)]];
float depthLess [[depth(less), function_constant(PS_ZOUTPUT_LESS)]];
float depthAny [[depth(any), function_constant(PS_ZOUTPUT_ANY)]];
void setDepth(float depth)
{
if (PS_ZOUTPUT_LESS)
depthLess = depth;
if (PS_ZOUTPUT_ANY)
depthAny = depth;
if (PS_ZOUTPUT_COLOR)
depthColor = depth;
}
};
// MARK: - Vertex functions
@@ -220,7 +256,8 @@ static MainVSIn load_vertex(GSMTLMainVertex base)
vertex MainVSOut vs_main_expand(
uint vid [[vertex_id]],
device const GSMTLMainVertex* vertices [[buffer(GSMTLBufferIndexHWVertices)]],
constant GSMTLMainVSUniform& cb [[buffer(GSMTLBufferIndexHWUniforms)]])
constant GSMTLMainVSUniform& cb [[buffer(GSMTLBufferIndexHWUniforms)]],
device const ushort* indices [[buffer(GSMTLBufferIndexHWIndices), function_constant(VS_NEEDS_INDEX_BUFFER)]])
{
switch (VS_EXPAND_TYPE)
{
@@ -236,6 +273,7 @@ vertex MainVSOut vs_main_expand(
return point;
}
case VSExpand::Line:
case VSExpand::LineAA1:
{
uint vid_base = vid >> 2;
bool is_bottom = vid & 2;
@@ -244,13 +282,27 @@ vertex MainVSOut vs_main_expand(
MainVSOut point = vs_main_run(load_vertex(vertices[vid_base]), cb);
MainVSOut other = vs_main_run(load_vertex(vertices[vid_other]), cb);
float2 line_vector = normalize(point.p.xy - other.p.xy);
float2 line_normal = float2(line_vector.y, -line_vector.x);
float2 line_width = (line_normal * cb.point_size) / 2;
// line_normal is inverted for bottom point
float2 offset = (is_bottom ^ is_right) ? line_width : -line_width;
// Use bottom minus top for delta regardless of which vertex we are expanding.
float2 line_delta = is_bottom ? point.p.xy - other.p.xy : other.p.xy - point.p.xy;
float2 line_expand;
if (VS_EXPAND_TYPE == VSExpand::Line)
{
float2 line_vector = normalize(line_delta);
line_expand = float2(line_vector.y, -line_vector.x);
}
else
{
// Expand in y direction for shallow lines and x direction for steep lines.
line_delta /= cb.vertex_scale;
line_expand = abs(line_delta.x) >= abs(line_delta.y) ? float2(0, 2) : float2(2, 0);
}
float2 line_width = (line_expand * cb.point_size) / 2;
float2 offset = is_right ? line_width : -line_width;
point.p.xy += offset;
if (VS_EXPAND_TYPE == VSExpand::LineAA1)
point.inv_cov = is_right ? 1.f : -1.f;
// Lines will be run as (0 1 2) (1 2 3)
// This means that both triangles will have a point based off the top line point as their first point
// So we don't have to do anything for !IIP
@@ -284,6 +336,68 @@ vertex MainVSOut vs_main_expand(
out.ti.yw = lt.ti.yw;
}
return out;
}
case VSExpand::TriangleAA1:
{
// Triangles with AA1 are expanded as follows:
// - Vertices 0-2: Interior of triangle (1 triangle).
// - Vertices 3-8: First edge expanded (2 triangles).
// - Vertices 9-14: Second edge expanded (2 triangles).
// - Vertices 15-20: Third edge expanded (2 triangles).
uint prim_id = vid / 21;
uint prim_offset = vid - 21 * prim_id; // range: 0-20
bool interior = prim_offset < 3;
uint i0 = interior ? prim_offset : (prim_offset - 3) / 6;
uint i1 = (i0 >= 2) ? i0 - 2 : i0 + 1;
uint i2 = (i0 >= 1) ? i0 - 1 : i0 + 2;
MainVSOut out = vs_main_run(load_vertex(vertices[indices[3 * prim_id + i0]]), cb);
MainVSOut other = vs_main_run(load_vertex(vertices[indices[3 * prim_id + i1]]), cb);
MainVSOut opposite = vs_main_run(load_vertex(vertices[indices[3 * prim_id + i2]]), cb);
if (interior)
{
out.inv_cov = 0.f;
out.interior = 1;
}
else
{
// Vertex indices for this edge. We need all 3 for determining exterior/interior.
uint prim_offset_edges = prim_offset - 3; // range: 0-17
uint edge_offset = prim_offset_edges - 6 * i0; // range: 0-5
// Note: order of top/bottom, inside/outside order is arbitrary,
// as long as it assembles into two triangles forming a quad.
bool is_bottom = (2 <= edge_offset) && (edge_offset <= 4);
bool is_outside = (edge_offset & 1) != 0;
// Similar expansion to line AA1 except instead of expanding on both sides of
// the line we expand on on the side towards the outside of the triangle.
float2 line_delta = out.p.xy - other.p.xy;
float2 line_normal = normalize(float2(line_delta.y, -line_delta.x));
float2 line_expand = abs(line_delta.x) >= abs(line_delta.y) ? float2(0, 2) : float2(2, 0);
if ((dot(line_expand, line_normal) >= 0.0f) == (dot(opposite.p.xy - out.p.xy, line_normal) >= 0.0f))
{
// Expand direction point towards the interior so flip it.
line_expand = -line_expand;
}
float2 line_width = (line_expand * cb.point_size) / 2;
if (is_bottom)
out = other;
if (is_outside)
{
out.p.xy += line_width;
out.inv_cov = 1.0f; // No coverage
}
else
{
out.inv_cov = 0.0f; // Full coverage
}
out.interior = 0;
}
return out;
}
}
@@ -299,6 +413,7 @@ struct PSMain
texture2d<float> prim_id_tex;
sampler tex_sampler;
float4 current_color;
float current_depth;
uint prim_id;
const thread MainPSIn& in;
constant GSMTLMainPSUniform& cb;
@@ -322,6 +437,154 @@ struct PSMain
return tex.read(pos, lod);
}
uint2 get_tex_dims()
{
if (PS_TEX_IS_DEPTH)
return uint2(tex_depth.get_width(), tex_depth.get_height());
else
return uint2(tex.get_width(), tex.get_height());
}
float manual_lod(float uv_w)
{
// FIXME add LOD: K - ( LOG2(Q) * (1 << L))
float K = cb.lod_params.x;
float L = cb.lod_params.y;
float bias = cb.lod_params.z;
float max_lod = cb.lod_params.w;
float gs_lod = K - log2(abs(uv_w)) * L;
// FIXME max useful ?
//return max(min(gs_lod, max_lod) - bias, 0.0f);
return min(gs_lod, max_lod) - bias;
}
float4 sample_c_af(float2 uv, float uv_w)
{
// HW sampler will reject bad UVs, match that here.
uv = any(isnan(uv) | isinf(uv)) ? float2(0, 0) : uv;
// Large floating point values risk NaN/Inf values.
// Above this value floats lose decimal precision, so seems a resonable limit for UVs.
uv = clamp(uv, -8388608.0f, 8388608.0f);
// Below taken from https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#7.18.11%20LOD%20Calculations
// With guidance from https://pema.dev/2025/05/09/mipmaps-too-much-detail/
float2 sz = float2(get_tex_dims());
float2 dX = dfdx(uv) * sz;
float2 dY = dfdy(uv) * sz;
// Calculate Ellipse Transform
bool d_zero = length(dX) == 0 || length(dY) == 0;
bool d_par = (dX.x * dY.y - dY.x * dX.y) == 0;
bool d_per = dot(dX, dY) == 0;
bool d_inf_nan = any(isinf(dX) | isinf(dY) | isnan(dX) | isnan(dY));
if (!(d_zero || d_par || d_per || d_inf_nan))
{
float A = dX.y * dX.y + dY.y * dY.y;
float B = -2 * (dX.x * dX.y + dY.x * dY.y);
float C = dX.x * dX.x + dY.x * dY.x;
float f = (dX.x * dY.y - dY.x * dX.y);
float F = f * f;
float p = A - C;
float q = A + C;
float t = sqrt(p * p + B * B);
float2 new_dX = float2(
sqrt(F * (t + p) / (t * (q + t))),
sqrt(F * (t - p) / (t * (q + t))) * sign(B)
);
float2 new_dY = float2(
sqrt(F * (t - p) / (t * (q - t))) * -sign(B),
sqrt(F * (t + p) / (t * (q - t)))
);
d_inf_nan = any(isinf(new_dX) | isinf(new_dY) | isnan(new_dX) | isnan(new_dY));
if (!d_inf_nan)
{
dX = new_dX;
dY = new_dY;
}
}
// Compute AF values
float squared_length_x = dX.x * dX.x + dX.y * dX.y;
float squared_length_y = dY.x * dY.x + dY.y * dY.y;
float determinant = abs(dX.x * dY.y - dX.y * dY.x);
bool is_major_x = squared_length_x > squared_length_y;
float squared_length_major = is_major_x ? squared_length_x : squared_length_y;
float length_major = sqrt(squared_length_major);
float aniso_ratio;
float length_lod;
float2 aniso_line;
if (length_major <= 1.0f)
{
// A zero length_major would result in NaN Lod and break sampling.
// A small length_major would result in aniso_ratio getting clamped to 1.
// Perform isotropic filtering instead.
aniso_ratio = 1.0f;
length_lod = length_major;
aniso_line = float2(0, 0);
}
else
{
float norm_major = 1.0f / length_major;
float2 aniso_line_dir = float2(
(is_major_x ? dX.x : dY.x) * norm_major,
(is_major_x ? dX.y : dY.y) * norm_major
);
aniso_ratio = squared_length_major / determinant;
// Calculate the minor length of the ellipse for Lod, while also clamping the ratio of anisotropy.
if (aniso_ratio > PS_SW_ANISO)
{
// ratio is clamped - Lod is based on ratio (preserves area)
aniso_ratio = PS_SW_ANISO;
length_lod = length_major / PS_SW_ANISO;
}
else
{
// ratio not clamped - Lod is based on area
length_lod = determinant / length_major;
}
// clamp to top Lod
if (length_lod < 1.0f)
aniso_ratio = max(1.0f, aniso_ratio * length_lod);
aniso_ratio = round(aniso_ratio);
aniso_line = aniso_line_dir * 0.5f * length_major * (1.0f / sz);
}
float lod = PS_AUTOMATIC_LOD ? log2(length_lod) : PS_MANUAL_LOD ? manual_lod(uv_w) : 0;
float4 colour;
if (aniso_ratio == 1.0f)
{
colour = sample_tex(tex_sampler, uv, level(lod));
}
else
{
float4 num = float4(0, 0, 0, 0);
for (int i = 0; i < aniso_ratio; i++)
{
float2 d = -aniso_line + (0.5f + i) * (2.0f * aniso_line) / aniso_ratio;
float2 uv_sample = uv + d;
float4 sample_colour = sample_tex(tex_sampler, uv_sample, level(lod));
num += sample_colour;
}
colour = num / aniso_ratio;
}
return colour;
}
float4 sample_c(float2 uv)
{
if (PS_TEX_IS_FB)
@@ -345,28 +608,14 @@ struct PSMain
uv.y = uv.y * cb.st_scale.y;
}
if (PS_AUTOMATIC_LOD)
{
if (PS_SW_ANISO > 1)
return sample_c_af(uv, in.t.w);
else if (PS_AUTOMATIC_LOD)
return sample_tex(tex_sampler, uv);
}
else if (PS_MANUAL_LOD)
{
float K = cb.lod_params.x;
float L = cb.lod_params.y;
float bias = cb.lod_params.z;
float max_lod = cb.lod_params.w;
float gs_lod = K - log2(abs(in.t.w)) * L;
// FIXME max useful ?
//float lod = max(min(gs_lod, max_lod) - bias, 0.f);
float lod = min(gs_lod, max_lod) - bias;
return sample_tex(tex_sampler, uv, level(lod));
}
return sample_tex(tex_sampler, uv, level(manual_lod(in.t.w)));
else
{
return sample_tex(tex_sampler, uv, level(0));
}
}
float4 sample_p(uint idx)
@@ -1061,6 +1310,17 @@ struct PSMain
MainPSOut ps_main()
{
MainPSOut out = {};
float input_z = in.p.z;
if (PS_ZFLOOR)
input_z = floor(input_z * 0x1p32) * 0x1p-32;
if (PS_ZTST == ZTST::GEQUAL || PS_ZTST == ZTST::GREATER)
{
if (PS_ZTST == ZTST::GEQUAL && input_z < current_depth)
discard_fragment();
if (PS_ZTST == ZTST::GREATER && input_z <= current_depth)
discard_fragment();
}
if (PS_SCANMSK & 2)
{
@@ -1088,18 +1348,25 @@ struct PSMain
}
float4 C = ps_color();
bool atst_pass = atst(C);
if (PS_AFAIL == GSShader::PS_AFAIL::KEEP && !atst_pass)
discard_fragment();
// Must be done before alpha correction
// AA (Fixed one) will output a coverage of 1.0 as alpha
if (PS_FIXED_ONE_A)
if (PS_AA1 != AA1::NONE)
{
float cov = saturate(1.f - abs(in.inv_cov));
if (!PS_ABE || floor(C.a) == 128.f) // The coverage is only used if the fragment alpha is 128.
C.a = 128.f * cov;
}
else if (PS_FIXED_ONE_A)
{
// AA (Fixed one) will output a coverage of 1.0 as alpha
C.a = 128.0f;
}
bool atst_pass = atst(C);
if (PS_AFAIL == AFAIL::KEEP && !atst_pass)
discard_fragment();
float4 alpha_blend = float4(0.f);
if (SW_AD_TO_HW)
{
@@ -1171,10 +1438,7 @@ struct PSMain
uint2 denorm_TA = uint2(cb.ta * 255.5f);
C.rb = (denorm_c.r >> 3) | (((denorm_c.g >> 3) & 0x7) << 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);
C.ga = (denorm_c.g >> 6) | ((denorm_c.b >> 3) << 2) | (denorm_TA.x & 0x80);
}
else if (PS_SHUFFLE_ACROSS)
{
@@ -1211,18 +1475,28 @@ struct PSMain
{
out.c0.a = PS_RTA_CORRECTION ? C.a / 128.f : C.a / 255.f;
out.c0.rgb = PS_COLCLIP_HW ? float3(C.rgb / 65535.f) : C.rgb / 255.f;
if (PS_AFAIL == AFAIL::RGB_ONLY && !atst_pass)
out.c0.a = current_color.a;
}
if (PS_COLOR1)
out.c1 = alpha_blend;
float depth_value = PS_ZFLOOR ? (floor(in.p.z * exp2(32.0f)) * exp2(-32.0f)) : in.p.z;
if (PS_ZCLAMP)
out.depth = min(depth_value, cb.max_depth);
else if (PS_ZFLOOR)
out.depth = depth_value;
input_z = min(input_z, cb.max_depth);
if (PS_AA1 == AA1::TRIANGLE_SW_Z && !in.interior)
input_z = current_depth; // No depth update for triangle edges.
if (!atst_pass)
{
if (PS_AFAIL == AFAIL::RGB_ONLY_SW_Z || PS_AFAIL == AFAIL::RGB_ONLY)
out.c0.a = current_color.a;
else if (PS_AFAIL == AFAIL::ZB_ONLY)
out.c0 = current_color;
if (PS_AFAIL == AFAIL::RGB_ONLY_SW_Z || PS_AFAIL == AFAIL::FB_ONLY)
input_z = current_depth;
}
out.setDepth(input_z);
return out;
}
@@ -1236,9 +1510,14 @@ fragment float4 fbfetch_test(float4 in [[color(0), raster_order_group(0)]])
constant bool NEEDS_RT_TEX = NEEDS_RT && !HAS_FBFETCH;
constant bool NEEDS_RT_FBF = NEEDS_RT && HAS_FBFETCH;
constant bool NEEDS_DS_FBF = SW_DEPTH && HAS_FBFETCH && !DEPTH_FEEDBACK;
#else
constant bool NEEDS_RT_TEX = NEEDS_RT;
constant bool NEEDS_DS_FBF = false;
constant float ds_fbf = 0;
#endif
constant bool NEEDS_DS_TEX = SW_DEPTH && !DEPTH_FEEDBACK && !NEEDS_DS_FBF;
constant bool NEEDS_DS_DEPTH = SW_DEPTH && DEPTH_FEEDBACK || NEEDS_DS_FBF;
fragment MainPSOut ps_main(
MainPSIn in [[stage_in]],
@@ -1249,12 +1528,15 @@ fragment MainPSOut ps_main(
#endif
#if FBFETCH_SUPPORT
float4 rt_fbf [[color(0), raster_order_group(0), function_constant(NEEDS_RT_FBF)]],
float ds_fbf [[color(1), raster_order_group(1), function_constant(NEEDS_DS_FBF)]],
#endif
texture2d<float> tex [[texture(GSMTLTextureIndexTex), function_constant(PS_TEX_IS_COLOR)]],
depth2d<float> depth [[texture(GSMTLTextureIndexTex), function_constant(PS_TEX_IS_DEPTH)]],
texture2d<float> palette [[texture(GSMTLTextureIndexPalette), function_constant(PS_HAS_PALETTE)]],
texture2d<float> rt [[texture(GSMTLTextureIndexRenderTarget), function_constant(NEEDS_RT_TEX)]],
texture2d<float> primidtex [[texture(GSMTLTextureIndexPrimIDs), function_constant(PS_PRIM_CHECKING_READ)]])
texture2d<float> primidtex [[texture(GSMTLTextureIndexPrimIDs), function_constant(PS_PRIM_CHECKING_READ)]],
texture2d<float> ds_tex [[texture(GSMTLTextureIndexDepthTarget), function_constant(NEEDS_DS_TEX)]],
depth2d<float> ds_depth [[texture(GSMTLTextureIndexDepthTarget), function_constant(NEEDS_DS_DEPTH)]])
{
PSMain main(in, cb);
main.tex_sampler = s;
@@ -1271,12 +1553,24 @@ fragment MainPSOut ps_main(
main.prim_id = primid;
#endif
uint2 coord = uint2(in.p.xy);
if (SW_DEPTH)
{
if (DEPTH_FEEDBACK)
main.current_depth = ds_depth.read(coord);
else if (NEEDS_DS_FBF)
main.current_depth = ds_fbf < 0 ? ds_depth.read(coord) : ds_fbf;
else
main.current_depth = ds_tex.read(coord).x;
}
if (NEEDS_RT)
{
#if FBFETCH_SUPPORT
main.current_color = HAS_FBFETCH ? rt_fbf : rt.read(uint2(in.p.xy));
main.current_color = HAS_FBFETCH ? rt_fbf : rt.read(coord);
#else
main.current_color = rt.read(uint2(in.p.xy));
main.current_color = rt.read(coord);
#endif
}
else
+173 -114
View File
@@ -22,6 +22,9 @@
#include <fstream>
#include <sstream>
static constexpr u32 g_vs_pc_index = 4;
static constexpr u32 g_vs_ib_index = 3;
static constexpr u32 g_vs_vb_index = 2;
static constexpr u32 g_vs_cb_index = 1;
static constexpr u32 g_ps_cb_index = 0;
@@ -29,6 +32,7 @@ static constexpr u32 VERTEX_BUFFER_SIZE = 32 * 1024 * 1024;
static constexpr u32 INDEX_BUFFER_SIZE = 16 * 1024 * 1024;
static constexpr u32 VERTEX_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024;
static constexpr u32 FRAGMENT_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024;
static constexpr u32 VERTEX_PUSH_CONSTANT_BUFFER_SIZE = 1024 * 1024;
static constexpr u32 TEXTURE_UPLOAD_BUFFER_SIZE = 128 * 1024 * 1024;
namespace ReplaceGL
@@ -326,10 +330,13 @@ bool GSDeviceOGL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
m_vertex_stream_buffer = GLStreamBuffer::Create(GL_ARRAY_BUFFER, VERTEX_BUFFER_SIZE);
m_index_stream_buffer = GLStreamBuffer::Create(GL_ELEMENT_ARRAY_BUFFER, INDEX_BUFFER_SIZE);
m_expand_index_stream_buffer = GLStreamBuffer::Create(GL_ARRAY_BUFFER, INDEX_BUFFER_SIZE);
m_vertex_uniform_stream_buffer = GLStreamBuffer::Create(GL_UNIFORM_BUFFER, VERTEX_UNIFORM_BUFFER_SIZE);
m_fragment_uniform_stream_buffer = GLStreamBuffer::Create(GL_UNIFORM_BUFFER, FRAGMENT_UNIFORM_BUFFER_SIZE);
m_vertex_push_constants_stream_buffer = GLStreamBuffer::Create(GL_UNIFORM_BUFFER, VERTEX_PUSH_CONSTANT_BUFFER_SIZE);
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &m_uniform_buffer_alignment);
if (!m_vertex_stream_buffer || !m_index_stream_buffer || !m_vertex_uniform_stream_buffer || !m_fragment_uniform_stream_buffer)
if (!m_vertex_stream_buffer || !m_index_stream_buffer || !m_expand_index_stream_buffer ||
!m_vertex_uniform_stream_buffer || !m_fragment_uniform_stream_buffer || !m_vertex_push_constants_stream_buffer)
{
Host::ReportErrorAsync("GS", "Failed to create vertex/index/uniform streaming buffers");
return false;
@@ -369,8 +376,16 @@ bool GSDeviceOGL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
glGenBuffers(1, &m_expand_ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_expand_ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, EXPAND_BUFFER_SIZE, expand_data.get(), GL_STATIC_DRAW);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 2, m_vertex_stream_buffer->GetGLBufferId(), 0, VERTEX_BUFFER_SIZE);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, g_vs_vb_index, m_vertex_stream_buffer->GetGLBufferId(), 0, VERTEX_BUFFER_SIZE);
}
if (m_features.aa1)
{
glGenVertexArrays(1, &m_dummy_vao);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, g_vs_ib_index, m_expand_index_stream_buffer->GetGLBufferId(), 0, INDEX_BUFFER_SIZE);
}
VSSetPushConstants(0, 0, true); // Avoid undefined data.
}
// ****************************************************************
@@ -553,6 +568,9 @@ bool GSDeviceOGL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
glDisable(GL_MULTISAMPLE);
glDisable(GL_DITHER); // Honestly I don't know!
// Initialise stencil ref/mask.
glStencilFunc(GLState::stencil_func, 1, 1);
}
// ****************************************************************
@@ -827,38 +845,12 @@ bool GSDeviceOGL::CheckFeatures()
m_features.prefer_new_textures = false;
m_features.stencil_buffer = true;
m_features.test_and_sample_depth = m_features.texture_barrier;
if (m_features.texture_barrier)
// Auto select chooses depth-as-rt as it appears to be more compatible across hardware.
m_features.depth_feedback = GSConfig.DepthFeedbackMode == GSDepthFeedbackMode::Depth;
if (!m_features.texture_barrier && m_features.multidraw_fb_copy)
{
// Auto select chooses depth-as-rt as it appears to be more compatible across hardware.
if (GSConfig.DepthFeedbackMode == GSDepthFeedbackMode::DepthAsRT ||
GSConfig.DepthFeedbackMode == GSDepthFeedbackMode::Auto)
{
m_features.depth_feedback = GSDevice::DepthFeedbackSupport::DepthAsRT;
}
else if (GSConfig.DepthFeedbackMode == GSDepthFeedbackMode::Depth)
{
m_features.depth_feedback = GSDevice::DepthFeedbackSupport::Depth;
}
else
{
m_features.depth_feedback = GSDevice::DepthFeedbackSupport::None;
}
}
else if (m_features.multidraw_fb_copy)
{
if (GSConfig.DepthFeedbackMode == GSDepthFeedbackMode::Depth ||
GSConfig.DepthFeedbackMode == GSDepthFeedbackMode::Auto)
{
m_features.depth_feedback = GSDevice::DepthFeedbackSupport::Depth;
}
else
{
m_features.depth_feedback = GSDevice::DepthFeedbackSupport::None;
}
}
else
{
m_features.depth_feedback = GSDevice::DepthFeedbackSupport::None;
// Multidraw fb copy can do depth feedback just fine
m_features.depth_feedback |= GSConfig.DepthFeedbackMode == GSDepthFeedbackMode::Auto;
}
if (GLAD_GL_ARB_shader_storage_buffer_object)
@@ -890,6 +882,8 @@ bool GSDeviceOGL::CheckFeatures()
{
Console.Warning("GLAD_GL_ARB_conservative_depth is not supported. This will reduce performance.");
}
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand && m_features.feedback_loops();
return true;
}
@@ -961,14 +955,18 @@ void GSDeviceOGL::DestroyResources()
m_fragment_uniform_stream_buffer.reset();
m_vertex_uniform_stream_buffer.reset();
m_vertex_push_constants_stream_buffer.reset();
glBindVertexArray(0);
if (m_expand_ibo != 0)
glDeleteVertexArrays(1, &m_expand_ibo);
if (m_vao != 0)
glDeleteVertexArrays(1, &m_vao);
if (m_dummy_vao != 0)
glDeleteVertexArrays(1, &m_dummy_vao);
m_index_stream_buffer.reset();
m_expand_index_stream_buffer.reset();
m_vertex_stream_buffer.reset();
m_texture_upload_buffer.reset();
if (m_expand_ibo)
@@ -1176,21 +1174,52 @@ void GSDeviceOGL::DrawPrimitive()
void GSDeviceOGL::DrawIndexedPrimitive()
{
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
glDrawElementsBaseVertex(m_draw_topology, static_cast<u32>(m_index.count), GL_UNSIGNED_SHORT,
reinterpret_cast<void*>(static_cast<u32>(m_index.start) * sizeof(u16)), static_cast<GLint>(m_vertex.start));
DrawIndexedPrimitive(0, m_index.count);
}
void GSDeviceOGL::DrawIndexedPrimitive(int offset, int count)
{
//ASSERT(offset + count <= (int)m_index.count);
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
glDrawElementsBaseVertex(m_draw_topology, count, GL_UNSIGNED_SHORT,
reinterpret_cast<void*>((static_cast<u32>(m_index.start) + static_cast<u32>(offset)) * sizeof(u16)),
static_cast<GLint>(m_vertex.start));
}
void GSDeviceOGL::DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_indexing, int vs_indexing_expansion)
{
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
if (vs_indexing)
{
VSSetPushConstants(m_vertex.start, m_index.start + offset);
glDrawArrays(m_draw_topology, 0, count * vs_indexing_expansion);
}
else
{
VSSetPushConstants(m_vertex.start);
glDrawElementsBaseVertex(m_draw_topology, count, GL_UNSIGNED_SHORT,
reinterpret_cast<void*>((static_cast<u32>(m_index.start) + static_cast<u32>(offset)) * sizeof(u16)), 0);
}
}
void GSDeviceOGL::Draw(const GSHWDrawConfig& config, int offset, int count)
{
if (config.vs.expand != GSHWDrawConfig::VSExpand::None)
{
const bool vs_indexing = config.vs.UseVSExpandIndexBuffer();
const u32 vs_indexing_expansion = GetExpansionFactor(config.vs.expand);
DrawIndexedPrimitiveVSExpand(offset, count, vs_indexing, vs_indexing_expansion);
}
else
{
DrawIndexedPrimitive(offset, count);
}
}
void GSDeviceOGL::Draw(const GSHWDrawConfig& config)
{
Draw(config, 0, m_index.count);
}
void GSDeviceOGL::CommitClear(GSTexture* t, bool use_write_fbo)
{
GSTextureOGL* T = static_cast<GSTextureOGL*>(t);
@@ -1332,15 +1361,6 @@ GLuint GSDeviceOGL::CreateSampler(PSSamplerSelector sel)
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
const int anisotropy = GSConfig.MaxAnisotropy;
if (anisotropy > 1 && sel.aniso)
{
if (GLAD_GL_ARB_texture_filter_anisotropic)
glSamplerParameterf(sampler, GL_TEXTURE_MAX_ANISOTROPY, static_cast<float>(anisotropy));
else if (GLAD_GL_EXT_texture_filter_anisotropic)
glSamplerParameterf(sampler, GL_TEXTURE_MAX_ANISOTROPY_EXT, static_cast<float>(anisotropy));
}
return sampler;
}
@@ -1435,22 +1455,17 @@ std::string GSDeviceOGL::GenGlslHeader(const std::string_view entry, GLenum type
header += "#define PS_HAS_CONSERVATIVE_DEPTH 0\n";
}
switch (m_features.depth_feedback)
if (!m_features.texture_barrier && !m_features.multidraw_fb_copy)
{
case GSDevice::DepthFeedbackSupport::None:
header += "#define DEPTH_FEEDBACK_SUPPORT 0\n"; // None
static_assert(static_cast<int>(GSDevice::DepthFeedbackSupport::None) == 0);
break;
case GSDevice::DepthFeedbackSupport::Depth:
header += "#define DEPTH_FEEDBACK_SUPPORT 1\n"; // Depth
static_assert(static_cast<int>(GSDevice::DepthFeedbackSupport::Depth) == 1);
break;
case GSDevice::DepthFeedbackSupport::DepthAsRT:
header += "#define DEPTH_FEEDBACK_SUPPORT 2\n"; // Depth as RT
static_assert(static_cast<int>(GSDevice::DepthFeedbackSupport::DepthAsRT) == 2);
break;
default:
pxFail("Incorrect depth feedback support."); // Impossible
header += "#define DEPTH_FEEDBACK_SUPPORT 0\n"; // None
}
else if (m_features.depth_feedback)
{
header += "#define DEPTH_FEEDBACK_SUPPORT 1\n"; // Depth
}
else
{
header += "#define DEPTH_FEEDBACK_SUPPORT 2\n"; // Depth as RT
}
// Allow to puts several shader in 1 files
@@ -1558,6 +1573,9 @@ std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
+ fmt::format("#define PS_NO_COLOR {}\n", sel.no_color)
+ fmt::format("#define PS_NO_COLOR1 {}\n", sel.no_color1)
+ fmt::format("#define PS_ZTST {}\n", sel.ztst)
+ fmt::format("#define PS_AA1 {}\n", static_cast<u32>(sel.aa1))
+ fmt::format("#define PS_ABE {}\n", sel.abe)
+ fmt::format("#define PS_ANISOTROPIC_FILTERING {}", sel.sw_aniso)
;
std::string src = GenGlslHeader("ps_main", GL_FRAGMENT_SHADER, macro);
@@ -2115,6 +2133,40 @@ void GSDeviceOGL::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GS
DrawPrimitive();
}
__fi static void WriteToStreamBuffer(GLStreamBuffer* sb, u32 index, u32 align, const void* data, u32 size)
{
const auto res = sb->Map(align, size);
std::memcpy(res.pointer, data, size);
sb->Unmap(size);
glBindBufferRange(GL_UNIFORM_BUFFER, index, sb->GetGLBufferId(), res.buffer_offset, size);
}
void GSDeviceOGL::VSSetUniformBuffer(GSHWDrawConfig::VSConstantBuffer& cb)
{
WriteToStreamBuffer(m_vertex_uniform_stream_buffer.get(), g_vs_cb_index,
m_uniform_buffer_alignment, &cb, sizeof(cb));
}
void GSDeviceOGL::PSSetUniformBuffer(GSHWDrawConfig::PSConstantBuffer& cb)
{
WriteToStreamBuffer(m_fragment_uniform_stream_buffer.get(), g_ps_cb_index,
m_uniform_buffer_alignment, &cb, sizeof(cb));
}
void GSDeviceOGL::VSSetPushConstants(u32 base_vertex, u32 base_index, bool force_update)
{
GSHWDrawConfig::VSPushConstants vs_pc;
vs_pc.base_vertex = base_vertex;
vs_pc.base_index = base_index;
if (m_vs_pc_cache.Update(vs_pc) || force_update)
{
WriteToStreamBuffer(m_vertex_push_constants_stream_buffer.get(), g_vs_pc_index,
m_uniform_buffer_alignment, &vs_pc, sizeof(vs_pc));
}
}
void GSDeviceOGL::IASetVAO(GLuint vao)
{
if (GLState::vao == vao)
@@ -2134,14 +2186,24 @@ void GSDeviceOGL::IASetVertexBuffer(const void* vertices, size_t count, size_t a
m_vertex_stream_buffer->Unmap(size);
}
void GSDeviceOGL::IASetIndexBuffer(const void* index, size_t count)
void GSDeviceOGL::SetIndexBuffer(std::unique_ptr<GLStreamBuffer>& buffer, const void* index, size_t count)
{
const u32 size = static_cast<u32>(count) * sizeof(u16);
auto res = m_index_stream_buffer->Map(sizeof(u16), size);
auto res = buffer->Map(sizeof(u16), size);
m_index.start = res.index_aligned;
m_index.count = count;
std::memcpy(res.pointer, index, size);
m_index_stream_buffer->Unmap(size);
buffer->Unmap(size);
}
void GSDeviceOGL::IASetIndexBuffer(const void* index, size_t count)
{
SetIndexBuffer(m_index_stream_buffer, index, count);
}
void GSDeviceOGL::VSSetIndexBuffer(const void* index, size_t count)
{
SetIndexBuffer(m_expand_index_stream_buffer, index, count);
}
void GSDeviceOGL::IASetPrimitiveTopology(GLenum topology)
@@ -2153,7 +2215,7 @@ void GSDeviceOGL::PSSetShaderResource(int i, GSTexture* sr)
{
pxAssert(i < static_cast<int>(std::size(GLState::tex_unit)));
const GLuint id = static_cast<GSTextureOGL*>(sr)->GetID();
const GLuint id = sr ? static_cast<GSTextureOGL*>(sr)->GetID() : 0;
if (GLState::tex_unit[i] != id)
{
GLState::tex_unit[i] = id;
@@ -2562,15 +2624,6 @@ void GSDeviceOGL::SetScissor(const GSVector4i& scissor)
}
}
__fi static void WriteToStreamBuffer(GLStreamBuffer* sb, u32 index, u32 align, const void* data, u32 size)
{
const auto res = sb->Map(align, size);
std::memcpy(res.pointer, data, size);
sb->Unmap(size);
glBindBufferRange(GL_UNIFORM_BUFFER, index, sb->GetGLBufferId(), res.buffer_offset, size);
}
void GSDeviceOGL::SetupPipeline(const ProgramSelector& psel)
{
auto it = m_programs.find(psel);
@@ -2624,7 +2677,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
GLState::scissor = config.scissor;
}
if (config.tex)
if (config.tex && (m_features.texture_barrier || (config.tex != config.rt)))
CommitClear(config.tex, true);
if (config.pal)
CommitClear(config.pal, true);
@@ -2690,7 +2743,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
}
// Destination Alpha Setup
const bool multidraw_fb_copy = m_features.multidraw_fb_copy && (config.require_one_barrier || config.require_full_barrier);
const bool need_barrier = config.require_one_barrier || (config.require_full_barrier && m_features.feedback_loops());
switch (config.destination_alpha)
{
case GSHWDrawConfig::DestinationAlphaMode::Off:
@@ -2705,7 +2758,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
}
break;
case GSHWDrawConfig::DestinationAlphaMode::StencilOne:
if (m_features.texture_barrier || multidraw_fb_copy)
if (need_barrier)
{
// Cleared after RT bind.
break;
@@ -2717,14 +2770,18 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
}
IASetVertexBuffer(config.verts, config.nverts, GetVertexAlignment(config.vs.expand));
m_vertex.start *= GetExpansionFactor(config.vs.expand);
if (config.vs.UseExpandIndexBuffer())
if (config.vs.UseFixedExpandIndexBuffer())
{
IASetVAO(m_expand_vao);
m_index.start = 0;
m_index.count = config.nindices;
}
else if (config.vs.UseVSExpandIndexBuffer())
{
IASetVAO(m_dummy_vao); // Unbind vertex buffer from IA to prevent unwanted fetches.
VSSetIndexBuffer(config.indices, config.nindices);
}
else
{
IASetVAO(m_vao);
@@ -2740,28 +2797,21 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
}
IASetPrimitiveTopology(topology);
if (config.tex)
if (config.tex && (m_features.texture_barrier || (config.tex != config.rt)))
PSSetShaderResource(0, config.tex);
if (config.pal)
PSSetShaderResource(1, config.pal);
if (m_features.texture_barrier && (config.require_one_barrier || config.require_full_barrier))
PSSetShaderResource(2, colclip_rt ? colclip_rt : config.rt);
const bool depth_feedback = m_features.depth_feedback == GSDevice::DepthFeedbackSupport::Depth;
if (m_features.texture_barrier && (config.require_one_barrier || config.require_full_barrier) && config.ps.IsFeedbackLoopDepth() &&
(depth_feedback || m_features.depth_feedback == GSDevice::DepthFeedbackSupport::DepthAsRT))
PSSetShaderResource(4, depth_feedback ? config.ds : config.ds_as_rt);
if (m_features.texture_barrier && (config.require_one_barrier || config.require_full_barrier) && config.ps.IsFeedbackLoopDepth())
PSSetShaderResource(4, m_features.depth_feedback ? config.ds : m_ds_as_rt);
SetupSampler(config.sampler);
if (m_vs_cb_cache.Update(config.cb_vs))
{
WriteToStreamBuffer(m_vertex_uniform_stream_buffer.get(), g_vs_cb_index,
m_uniform_buffer_alignment, &config.cb_vs, sizeof(config.cb_vs));
}
VSSetUniformBuffer(m_vs_cb_cache);
if (m_ps_cb_cache.Update(config.cb_ps))
{
WriteToStreamBuffer(m_fragment_uniform_stream_buffer.get(), g_ps_cb_index,
m_uniform_buffer_alignment, &config.cb_ps, sizeof(config.cb_ps));
}
PSSetUniformBuffer(m_ps_cb_cache);
ProgramSelector psel;
psel.vs = config.vs;
@@ -2771,7 +2821,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
SetupPipeline(psel);
bool rt_hazard_barrier = config.tex && (config.tex == config.ds || config.tex == config.rt || config.tex == config.ds_as_rt);
bool rt_hazard_barrier = config.tex && (config.tex == config.ds || config.tex == config.rt);
// In Time Crisis:
// 1. Fullscreen sprite reads depth and writes alpha (rt_hazard_barrier true from config.ds == config.tex)
// 2. Fullscreen sprite writes gray, rta hw blend blends based on dst alpha.
@@ -2821,7 +2871,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
SetupOM(dss);
// Compute primitiveID max that pass the date test (Draw without barrier)
DrawIndexedPrimitive();
Draw(config);
psel.ps.date = 3;
config.alpha_second_pass.ps.date = 3;
@@ -2829,7 +2879,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
PSSetShaderResource(3, primid_texture);
}
if (config.ds_as_rt)
if (m_ds_as_rt)
{
// We must clear the blend equation of any dual source blending factors or
// it may interact badly with MRTs, even if blending is disabled.
@@ -2849,16 +2899,13 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
}
GSTexture* draw_rt = colclip_rt ? colclip_rt : config.rt;
GSTexture* draw_ds_as_rt = config.ds_as_rt;
GSTexture* draw_ds_as_rt = m_ds_as_rt;
GSTexture* draw_ds = config.ds;
// Clear texture binding when it's bound to RT or DS.
if (!config.tex && ((draw_rt && static_cast<GSTextureOGL*>(draw_rt)->GetID() == GLState::tex_unit[0]) ||
(draw_ds && static_cast<GSTextureOGL*>(draw_ds)->GetID() == GLState::tex_unit[0])))
{
GLState::tex_unit[0] = 0;
glBindTextureUnit(0, 0);
}
PSSetShaderResource(0, nullptr);
// Avoid changing framebuffer just to switch from rt+depth to rt and vice versa.
bool fb_optimization_needs_barrier = false;
@@ -2884,20 +2931,30 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
glTextureBarrier();
}
if (draw_rt && (config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy) || (config.tex && config.tex == config.rt)) &&
!m_features.texture_barrier)
const bool tex_is_fb = config.tex && config.tex == draw_rt;
const bool rt_feedbackloop_pass1 = config.ps.IsFeedbackLoopRT() || tex_is_fb;
const bool rt_feedbackloop_pass2 = config.alpha_second_pass.ps.IsFeedbackLoopRT() || tex_is_fb;
if (draw_rt && !m_features.texture_barrier && (((config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
(rt_feedbackloop_pass1 || rt_feedbackloop_pass2))))
{
config.require_one_barrier |= (tex_is_fb && !config.require_full_barrier);
config.alpha_second_pass.require_one_barrier |= (tex_is_fb && !config.require_full_barrier);
// Requires a copy of the RT.
draw_rt_clone = CreateTexture(rtsize.x, rtsize.y, 1, draw_rt->GetFormat(), true);
if (!draw_rt_clone)
Console.Warning("GL: Failed to allocate temp texture for RT copy.");
}
if (draw_ds && (config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) &&
!m_features.texture_barrier && depth_feedback && config.ps.IsFeedbackLoopDepth())
const bool ds_feedbackloop_pass1 = config.ps.IsFeedbackLoopDepth();
const bool ds_feedbackloop_pass2 = config.alpha_second_pass.ps.IsFeedbackLoopDepth();
if (draw_ds && !m_features.texture_barrier && m_features.depth_feedback &&
(config.require_one_barrier || (config.require_full_barrier && m_features.multidraw_fb_copy)) && (ds_feedbackloop_pass1 || ds_feedbackloop_pass2))
{
// Requires a copy of the DS.
draw_ds_clone = CreateTexture(rtsize.x, rtsize.y, 1, draw_ds->GetFormat(), true);
if (!draw_ds_clone)
Console.Warning("GL: Failed to allocate temp texture for DS copy.");
}
@@ -2907,13 +2964,13 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
SetupOM(config.depth);
// Clear stencil as close as possible to the RT bind, to avoid framebuffer swaps.
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne && (m_features.texture_barrier || multidraw_fb_copy))
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne && need_barrier)
{
constexpr GLint clear_color = 1;
glClearBufferiv(GL_STENCIL, 0, &clear_color);
}
SendHWDraw(config, draw_rt_clone, draw_rt, draw_ds_clone, draw_ds,
SendHWDraw(config, rt_feedbackloop_pass1 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass1 ? draw_ds_clone : nullptr, draw_ds,
config.require_one_barrier, config.require_full_barrier);
if (config.blend_multi_pass.enable)
@@ -2933,7 +2990,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
psel.ps.blend_hw = config.blend_multi_pass.blend_hw;
psel.ps.dither = config.blend_multi_pass.dither;
SetupPipeline(psel);
DrawIndexedPrimitive();
Draw(config);
}
if (config.alpha_second_pass.enable)
@@ -2942,8 +2999,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
if (config.cb_ps.FogColor_AREF.a != config.alpha_second_pass.ps_aref)
{
config.cb_ps.FogColor_AREF.a = config.alpha_second_pass.ps_aref;
WriteToStreamBuffer(m_fragment_uniform_stream_buffer.get(), g_ps_cb_index,
m_uniform_buffer_alignment, &config.cb_ps, sizeof(config.cb_ps));
PSSetUniformBuffer(config.cb_ps);
}
psel.ps = config.alpha_second_pass.ps;
@@ -2960,9 +3016,10 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
{
OMSetBlendState();
}
const bool one_barrier = config.alpha_second_pass.require_one_barrier && m_features.feedback_loops();
SetupOM(config.alpha_second_pass.depth);
SendHWDraw(config, draw_rt_clone, draw_rt, draw_ds_clone, draw_ds,
m_features.texture_barrier ? config.alpha_second_pass.require_one_barrier : false, config.alpha_second_pass.require_full_barrier);
SendHWDraw(config, rt_feedbackloop_pass2 ? draw_rt_clone : nullptr, draw_rt, ds_feedbackloop_pass2 ? draw_ds_clone : nullptr, draw_ds,
one_barrier, config.alpha_second_pass.require_full_barrier);
}
if (colclip_rt)
@@ -3026,14 +3083,16 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config,
const u32 count = (*config.drawlist)[n] * indices_per_prim;
if (m_features.texture_barrier)
{
glTextureBarrier();
}
else
{
const GSVector4i original_bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea);
CopyAndBind(ProcessCopyArea(rtsize, original_bbox));
}
DrawIndexedPrimitive(p, count);
Draw(config, p, count);
p += count;
}
@@ -3054,7 +3113,7 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config,
}
}
DrawIndexedPrimitive();
Draw(config);
}
// Note: used as a callback of DebugMessageCallback. Don't change the signature
+17
View File
@@ -157,13 +157,16 @@ private:
std::unique_ptr<GLStreamBuffer> m_vertex_stream_buffer;
std::unique_ptr<GLStreamBuffer> m_index_stream_buffer;
std::unique_ptr<GLStreamBuffer> m_expand_index_stream_buffer;
GLuint m_expand_ibo = 0;
GLuint m_vao = 0;
GLuint m_expand_vao = 0;
GLuint m_dummy_vao = 0;
GLenum m_draw_topology = 0;
std::unique_ptr<GLStreamBuffer> m_vertex_uniform_stream_buffer;
std::unique_ptr<GLStreamBuffer> m_fragment_uniform_stream_buffer;
std::unique_ptr<GLStreamBuffer> m_vertex_push_constants_stream_buffer;
GLint m_uniform_buffer_alignment = 0;
struct
@@ -233,6 +236,7 @@ private:
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
GSHWDrawConfig::VSPushConstants m_vs_pc_cache;
std::string m_shader_tfx_vgs;
std::string m_shader_tfx_fs;
@@ -272,6 +276,8 @@ private:
void DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds);
void SetIndexBuffer(std::unique_ptr<GLStreamBuffer>& buffer, const void* index, size_t count);
protected:
virtual void DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
GSHWDrawConfig::ColorMaskSelector cms, ShaderConvert shader, bool linear) override;
@@ -312,9 +318,15 @@ public:
bool SetGPUTimingEnabled(bool enabled) override;
float GetAndResetAccumulatedGPUTime() override;
// Helpers and utility draws.
void DrawPrimitive();
void DrawIndexedPrimitive();
void DrawIndexedPrimitive(int offset, int count);
void DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_indexing, int vs_indexing_expansion);
// Main GS primitive draws.
void Draw(const GSHWDrawConfig& config);
void Draw(const GSHWDrawConfig& config, int offset, int count);
std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) override;
@@ -345,10 +357,15 @@ public:
const bool one_barrier, const bool full_barrier);
void SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSVector4i& bbox);
void VSSetUniformBuffer(GSHWDrawConfig::VSConstantBuffer& cb);
void PSSetUniformBuffer(GSHWDrawConfig::PSConstantBuffer& cb);
void VSSetPushConstants(u32 base_vertex, u32 base_index = 0, bool force_update = false);
void IASetVAO(GLuint vao);
void IASetPrimitiveTopology(GLenum topology);
void IASetVertexBuffer(const void* vertices, size_t count, size_t align_multiplier = 1);
void IASetIndexBuffer(const void* index, size_t count);
void VSSetIndexBuffer(const void* index, size_t count);
void PSSetShaderResource(int i, GSTexture* sr);
void PSSetSamplerState(GLuint ss);
@@ -77,8 +77,10 @@ static const auto& _d4_f = v9;
static const auto& _test = v8;
static const auto& _fd = v2;
#define _local(field) MemOperand(_locals, offsetof(GSScanlineLocalData, field))
#define _global(field) MemOperand(_globals, offsetof(GSScanlineGlobalData, field))
// Yay, you can't offsetof with non-constant array indices in GCC
#define OFFSETOF(base, field) (reinterpret_cast<uptr>(&reinterpret_cast<base*>(0)->field))
#define _local(field) MemOperand(_locals, OFFSETOF(GSScanlineLocalData, field))
#define _global(field) MemOperand(_globals, OFFSETOF(GSScanlineGlobalData, field))
#define armAsm (&m_emitter)
GSDrawScanlineCodeGenerator::GSDrawScanlineCodeGenerator(u64 key, void* code, size_t maxsize)
+5 -1
View File
@@ -527,6 +527,10 @@ void GSRasterizer::DrawEdgeLine(const GSVertexSW& v0, const GSVertexSW& v1, cons
const int rxi1 = static_cast<int>(rx1);
const int ryi1 = static_cast<int>(ry1);
// Early exit for horizontal lines.
if (delta_y == 0.0f && !IsOneOfMyScanlines(ryi0) && !aa)
return;
const GSVertexSW dedge = dv / GSVector4(std::abs(step_x ? delta_x : delta_y));
GSVertexSW edge(v0);
@@ -1546,7 +1550,7 @@ void GSRasterizerList::Queue(const GSRingHeap::SharedPtr<GSRasterizerData>& data
m_ds.SetupDraw(*data.get());
}
pxAssert(r.top >= 0 && r.top < 2048 && r.bottom >= 0 && r.bottom < 2048);
pxAssert(r.top >= 0 && r.top <= 2048 && r.bottom >= 0 && r.bottom <= 2048);
int top = r.top >> m_thread_height;
int bottom = std::min<int>((r.bottom + (1 << m_thread_height) - 1) >> m_thread_height, top + (int)m_workers.size());
+47 -24
View File
@@ -320,14 +320,14 @@ void GSRendererSW::RewriteVerticesIfSTOverflow()
constexpr int n = GSUtil::GetClassVertexCount(primclass);
// Make sure the copy buffer is large enough.
while (m_vertex.maxcount < m_index.tail)
while (m_vertex->maxcount < m_index->tail)
GrowVertexBuffer();
GSVertex* RESTRICT vertex = m_vertex.buff;
GSVertex* RESTRICT vertex_copy = m_vertex.buff_copy;
u16* RESTRICT index = m_index.buff;
GSVertex* RESTRICT vertex = m_vertex->buff;
GSVertex* RESTRICT vertex_copy = m_vertex->buff_copy;
u16* RESTRICT index = m_index->buff;
for (int i = 0; i < static_cast<int>(m_index.tail); i += n)
for (int i = 0; i < static_cast<int>(m_index->tail); i += n)
{
GSVector4 stcq[n];
@@ -381,18 +381,18 @@ void GSRendererSW::RewriteVerticesIfSTOverflow()
}
// Swap the buffers and fix the counts.
std::swap(m_vertex.buff, m_vertex.buff_copy);
m_vertex.head = m_vertex.next = m_vertex.tail = m_index.tail;
std::swap(m_vertex->buff, m_vertex->buff_copy);
m_vertex->head = m_vertex->next = m_vertex->tail = m_index->tail;
// Recalculate ST min/max/eq in the vertex trace.
GSVector4 tmin = GSVector4::cxpr(FLT_MAX);
GSVector4 tmax = GSVector4::cxpr(-FLT_MAX);
for (int i = 0; i < static_cast<int>(m_index.tail); i += n)
for (int i = 0; i < static_cast<int>(m_index->tail); i += n)
{
for (int j = 0; j < n; j++)
{
GSVector4 stcq = GSVector4::cast(GSVector4i(m_vertex.buff[i + j].m[0]));
const float Q = (primclass == GS_SPRITE_CLASS) ? stcq.w : m_vertex.buff[i + 1].RGBAQ.Q;
GSVector4 stcq = GSVector4::cast(GSVector4i(m_vertex->buff[i + j].m[0]));
const float Q = (primclass == GS_SPRITE_CLASS) ? stcq.w : m_vertex->buff[i + 1].RGBAQ.Q;
stcq = (stcq / Q).xyzw(stcq);
tmin = tmin.min(stcq);
@@ -451,11 +451,11 @@ void GSRendererSW::Draw()
SharedData* sd = static_cast<SharedData*>(data.get());
sd->primclass = m_vt.m_primclass;
sd->buff = (u8*)m_vertex_heap.alloc(sizeof(GSVertexSW) * ((m_vertex.next + 1) & ~1) + sizeof(u32) * m_index.tail, 64);
sd->buff = (u8*)m_vertex_heap.alloc(sizeof(GSVertexSW) * ((m_vertex->next + 1) & ~1) + sizeof(u32) * m_index->tail, 64);
sd->vertex = (GSVertexSW*)sd->buff;
sd->vertex_count = m_vertex.next;
sd->index = (u16*)(sd->buff + sizeof(GSVertexSW) * ((m_vertex.next + 1) & ~1));
sd->index_count = m_index.tail;
sd->vertex_count = m_vertex->next;
sd->index = (u16*)(sd->buff + sizeof(GSVertexSW) * ((m_vertex->next + 1) & ~1));
sd->index_count = m_index->tail;
sd->scanmsk_value = m_draw_env->SCANMSK.MSK;
// skip per pixel division if q is constant.
@@ -463,12 +463,30 @@ void GSRendererSW::Draw()
// If you have both GS_SPRITE_CLASS && m_vt.m_eq.q, it will depends on the first part of the 'OR'
u32 q_div = !IsMipMapActive() && ((m_vt.m_eq.q && m_vt.m_min.t.z != 1.0f) || (!m_vt.m_eq.q && m_vt.m_primclass == GS_SPRITE_CLASS));
GSVertexSW::s_cvb[m_vt.m_primclass][PRIM->TME][PRIM->FST][q_div](m_context, sd->vertex, m_vertex.buff, m_vertex.next);
GSVertexSW::s_cvb[m_vt.m_primclass][PRIM->TME][PRIM->FST][q_div](m_context, sd->vertex, m_vertex->buff, m_vertex->next);
std::memcpy(sd->index, m_index.buff, sizeof(u16) * m_index.tail);
std::memcpy(sd->index, m_index->buff, sizeof(u16) * m_index->tail);
GSVector4i scissor = context->scissor.in;
GSVector4i bbox = GSVector4i(m_vt.m_min.p.floor().upld(m_vt.m_max.p.floor())) + GSVector4i(0, 0, 1, 1); // right/bottom should be exclusive so +1
GSVector4i bbox;
if (m_vt.m_primclass == GS_LINE_CLASS || m_vt.m_primclass == GS_POINT_CLASS)
{
// min: round, max: round
bbox = GSVector4i((m_vt.m_min.p + GSVector4(0.5f)).floor().upld((m_vt.m_max.p + GSVector4(0.5f)).floor()));
}
else
{
// min: ceil, max: floor
bbox = GSVector4i(m_vt.m_min.p.ceil().upld(m_vt.m_max.p.floor()));
}
if (PRIM->AA1 && (m_vt.m_primclass == GS_LINE_CLASS || m_vt.m_primclass == GS_TRIANGLE_CLASS))
{
bbox += GSVector4i(-1, -1, 1, 1); // Expand bbox by 1 on all sides when using antialiasing.
}
bbox += GSVector4i(0, 0, 1, 1); // right/bottom should be exclusive so +1
GSVector4i r = bbox.rintersect(scissor);
@@ -485,12 +503,12 @@ void GSRendererSW::Draw()
{
int n = GSUtil::GetVertexCount(PRIM->PRIM);
for (u32 i = 0, j = 0; i < m_index.tail; i += n, j++)
for (u32 i = 0, j = 0; i < m_index->tail; i += n, j++)
{
for (int k = 0; k < n; k++)
{
GSVertex* v = &m_vertex.buff[m_index.buff[i + k]];
GSVertex* vn = &m_vertex.buff[m_index.buff[i + n - 1]];
GSVertex* v = &m_vertex->buff[m_index->buff[i + k]];
GSVertex* vn = &m_vertex->buff[m_index->buff[i + n - 1]];
fprintf(s_fp, "%d:%d %f %f %f %f\n",
j, k,
@@ -1393,7 +1411,7 @@ bool GSRendererSW::GetScanlineGlobalData(SharedData* data)
gd.sel.pabe = 1;
}
if (PRIM->AA1 && (primclass == GS_LINE_CLASS || primclass == GS_TRIANGLE_CLASS))
if (IsCoverageAlpha())
{
gd.sel.aa1 = 1;
}
@@ -1505,12 +1523,12 @@ bool GSRendererSW::GetScanlineGlobalData(SharedData* data)
u32 ofx = context->XYOFFSET.OFX;
for (int i = 0, j = m_vertex.tail; i < j; i++)
for (int i = 0, j = m_vertex->tail; i < j; i++)
{
#if _M_SSE >= 0x501
if ((((m_vertex.buff[i].XYZ.X - ofx) + 15) >> 4) & 7) // aligned to 8
if ((((m_vertex->buff[i].XYZ.X - ofx) + 15) >> 4) & 7) // aligned to 8
#else
if ((((m_vertex.buff[i].XYZ.X - ofx) + 15) >> 4) & 3) // aligned to 4
if ((((m_vertex->buff[i].XYZ.X - ofx) + 15) >> 4) & 3) // aligned to 4
#endif
{
gd.sel.notest = 0;
@@ -1523,6 +1541,11 @@ bool GSRendererSW::GetScanlineGlobalData(SharedData* data)
return true;
}
bool GSRendererSW::IsCoverageAlphaSupported()
{
return IsCoverageAlpha();
}
GSRendererSW::SharedData::SharedData()
: m_fpsm(0)
, m_zpsm(0)

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