Compare commits

...
77 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
85 changed files with 5784 additions and 4177 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
@@ -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
@@ -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
@@ -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
+47 -22
View File
@@ -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"
@@ -31954,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"
@@ -32128,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"
@@ -44023,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 アディダス"
@@ -54490,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]"
@@ -69655,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:
@@ -69665,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"
@@ -70098,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"
@@ -75575,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"
+9 -2
View File
@@ -654,7 +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,x:b3,y:b4,back:b8,guide:b10,start:b9,leftshoulder:b6,rightshoulder:b5,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,misc1:b11,lefttrigger:b7,righttrigger:b2,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,
@@ -683,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,
@@ -774,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,
@@ -1336,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,
@@ -1442,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,
@@ -1535,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,
@@ -1792,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,
+154 -17
View File
@@ -244,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
@@ -276,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
@@ -1242,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)
{
+153 -17
View File
@@ -193,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
@@ -216,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
@@ -1191,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;
+152 -13
View File
@@ -533,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
@@ -556,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
+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")},
+40 -2
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)
@@ -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"));
}
@@ -2583,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();
+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.");
@@ -120,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);
+113 -103
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>
@@ -423,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="textInteractionFlags">
<set>Qt::TextInteractionFlag::TextBrowserInteraction</set>
</property>
<property name="buddy">
<cstring>viewProfile</cstring>
</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::TextInteractionFlag::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">
@@ -528,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>
@@ -556,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);
+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);
File diff suppressed because it is too large Load Diff
+24 -1
View File
@@ -1540,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
@@ -1553,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.
@@ -3721,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)
+1
View File
@@ -1403,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);
};
+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
+190 -163
View File
@@ -1279,12 +1279,12 @@ void GSState::DumpTransferList(const std::string& filename)
(*file) << std::endl;
// clear, EE->GS, or GS->GS
(*file) << LIST_ITEM << "type: " << (transfer.zero_clear ? "clear" : ((transfer.transfer_type == EEGS_TransferType::EE_to_GS) ? "EE_to_GS" : "GS_to_GS")) << std::endl;
(*file) << LIST_ITEM << "type: " << ((transfer.transfer_type == EEGS_TransferType::Clear) ? "clear" : ((transfer.transfer_type == EEGS_TransferType::EE_to_GS) ? "EE_to_GS" : "GS_to_GS")) << std::endl;
// Dump BITBLTBUF
(*file) << INDENT << "BITBLTBUF: " << OPEN_MAP;
const bool gs_to_gs = (transfer.transfer_type == EEGS_TransferType::GS_to_GS) && !transfer.zero_clear;
const bool gs_to_gs = (transfer.transfer_type == EEGS_TransferType::GS_to_GS) && transfer.transfer_type != EEGS_TransferType::Clear;
if (gs_to_gs)
{
@@ -1330,11 +1330,11 @@ void GSState::DumpTransferImages()
const GSUploadQueue& transfer = m_draw_transfers[i];
std::string filename;
if ((transfer.transfer_type == EEGS_TransferType::EE_to_GS) || transfer.zero_clear)
if ((transfer.transfer_type == EEGS_TransferType::EE_to_GS) || transfer.transfer_type == EEGS_TransferType::Clear)
{
// clear or EE->GS: only the destination info is relevant.
filename = GetDrawDumpPath("%05lld_transfer%02d_%s_%04x_%d_%s_%d_%d_%d_%d.png",
s_n, transfer_n++, (transfer.zero_clear ? "clear" : "EE_to_GS"), transfer.blit.DBP, transfer.blit.DBW,
s_n, transfer_n++, ((transfer.transfer_type == EEGS_TransferType::Clear) ? "clear" : "EE_to_GS"), transfer.blit.DBP, transfer.blit.DBW,
GSUtil::GetPSMName(transfer.blit.DPSM), transfer.rect.x, transfer.rect.y, transfer.rect.z, transfer.rect.w);
}
else
@@ -2403,7 +2403,7 @@ void GSState::FlushWrite()
if (m_draw_transfers.size() > 0 && m_tr.m_blit.DBP == m_draw_transfers.back().blit.DBP)
{
m_draw_transfers.back().rect = r;
m_draw_transfers.back().rect = m_draw_transfers.back().rect.runion(r);
}
}
}
@@ -2666,7 +2666,6 @@ void GSState::FlushPrim()
GSVector4i* RESTRICT vert_ptr = (GSVector4i*)&m_vertex->buff[i];
GSVector4i v = vert_ptr[1];
v = v.xxxx().u16to32().sub32(m_xyof);
v = v.blend32<12>(v.sra32<4>());
m_vertex->xy[i & 3] = v;
m_vertex->xy_tail = unused;
}
@@ -2912,12 +2911,11 @@ void GSState::Write(const u8* mem, int len)
m_draw_transfers.pop_back();
transfer.rect = transfer.rect.runion(r);
transfer.draw = s_n;
transfer.zero_clear = false;
m_draw_transfers.push_back(transfer);
}
else
{
const GSUploadQueue new_transfer = {blit, r, s_n, false, EEGS_TransferType::EE_to_GS};
const GSUploadQueue new_transfer = {blit, s_n, r, EEGS_TransferType::EE_to_GS};
m_draw_transfers.push_back(new_transfer);
}
@@ -3112,12 +3110,11 @@ void GSState::Move()
m_draw_transfers.pop_back();
transfer.rect = transfer.rect.runion(r);
transfer.draw = s_n;
transfer.zero_clear = false;
m_draw_transfers.push_back(transfer);
}
else
{
const GSUploadQueue new_transfer = {m_env.BITBLTBUF, r, s_n, false, EEGS_TransferType::GS_to_GS};
const GSUploadQueue new_transfer = {m_env.BITBLTBUF, s_n, r, EEGS_TransferType::GS_to_GS};
m_draw_transfers.push_back(new_transfer);
}
@@ -3790,10 +3787,8 @@ void GSState::UpdateContext()
void GSState::UpdateScissor()
{
m_scissor_cull_min = m_context->scissor.cull.xyxy();
m_scissor_cull_max = m_context->scissor.cull.zwzw();
m_xyof = m_context->scissor.xyof;
m_scissor_invalid = !m_context->scissor.in.gt32(m_context->scissor.in.zwzw()).allfalse();
m_scissor_invalid = m_context->scissor.in.rempty();
}
void GSState::UpdateVertexKick()
@@ -5841,6 +5836,7 @@ template <u32 prim, bool auto_flush>
__forceinline void GSState::VertexKick(u32 skip)
{
constexpr u32 n = NumIndicesForPrim(prim);
constexpr int primclass = GSUtil::GetPrimClass(prim);
static_assert(n > 0);
pxAssert(m_vertex->tail < m_vertex->maxcount + 3);
@@ -5884,11 +5880,7 @@ __forceinline void GSState::VertexKick(u32 skip)
// We maintain the X/Y coordinates for the last 4 vertices, as well as the head for triangle fans, so we can compute
// the min/max, and cull degenerate triangles, which saves draws in some cases. Why 4? Mod 4 is cheaper than Mod 3.
// These vertices are a full vector containing <X_Fixed_Point, Y_Fixed_Point, X_Integer, Y_Integer>. We use the
// integer coordinates for culling at native resolution, and the fixed point for all others. The XY offset has to be
// applied, then we split it into the fixed/integer portions.
const GSVector4i xy_ofs = new_v1.xxxx().u16to32().sub32(m_xyof);
const GSVector4i xy = xy_ofs.blend32<12>(xy_ofs.sra32<4>());
const GSVector4i xy = new_v1.xxxx().u16to32().sub32(m_xyof);
m_vertex->xy[xy_tail & 3] = xy;
// Backup head for triangle fans so we can read it later, otherwise it'll get lost after the 4th vertex.
@@ -5918,73 +5910,66 @@ __forceinline void GSState::VertexKick(u32 skip)
// Skip draws when scissor is out of range (i.e. bottom-right is less than top-left), since everything will get clipped.
skip |= static_cast<u32>(m_scissor_invalid);
GSVector4i pmin, pmax;
GSVector4i bbox;
if (skip == 0)
{
const GSVector4i v0 = m_vertex->xy[(xy_tail - 1) & 3];
const GSVector4i v1 = m_vertex->xy[(xy_tail - 2) & 3];
const GSVector4i v2 = (prim == GS_TRIANGLEFAN) ? m_vertex->xyhead : m_vertex->xy[(xy_tail - 3) & 3];
switch (prim)
if constexpr (n == 1)
{
case GS_POINTLIST:
pmin = v0;
pmax = v0;
break;
case GS_LINELIST:
case GS_LINESTRIP:
case GS_SPRITE:
pmin = v0.min_i32(v1);
pmax = v0.max_i32(v1);
break;
case GS_TRIANGLELIST:
case GS_TRIANGLESTRIP:
case GS_TRIANGLEFAN:
pmin = v0.min_i32(v1.min_i32(v2));
pmax = v0.max_i32(v1.max_i32(v2));
break;
default:
break;
bbox = v0;
}
else if constexpr (n == 2)
{
bbox = v0.runion(v1);
}
else if constexpr (n == 3)
{
bbox = v0.runion(v1).runion(v2);
}
GSVector4i test = pmax.lt32(m_scissor_cull_min) | pmin.gt32(m_scissor_cull_max);
switch (prim)
if constexpr (primclass == GS_TRIANGLE_CLASS || primclass == GS_SPRITE_CLASS)
{
case GS_TRIANGLELIST:
case GS_TRIANGLESTRIP:
case GS_TRIANGLEFAN:
case GS_SPRITE:
if (m_nativeres)
{
// Discard degenerate triangles which don't cover at least one pixel. Since the vertices are in native
// resolution space, we can use the integer locations. When upscaling, we can't, because a primitive which
// does not span a single pixel at 1x may span multiple pixels at higher resolutions.
const GSVector4i degen_test = pmin.eq32(pmax);
test |= m_nativeres ? degen_test.zwzw() : degen_test;
// For triangles and sprites at native res take the interior pixel centers.
const GSVector4i interior = (bbox + GSVector4i(0xF, 0xF, -1, -1)) & GSVector4i(~0xF);
bbox = interior + GSVector4i(0, 0, 1, 1); // +1 to bottom/right so empty test works correctly.
}
else
{
// For upscaling, remove bottom/right subtexels.
bbox -= ((bbox & GSVector4i(0xF)) == GSVector4i(0)) & GSVector4i(0, 0, 1, 1);
}
// For AA1 triangles and lines, expand the bounds by 1 pixel on all sides.
// Note: redundant check for the AA1 flag to avoid calling a function if not needed.
if (PRIM->AA1 && IsCoverageAlphaSupported())
{
bbox += GSVector4i(-0x10, -0x10, 0x10, 0x10);
}
break;
default:
break;
}
switch (prim)
// Do scissor test.
const GSVector4i bbox_ex = bbox + GSVector4i(0, 0, 1, 1); // Exclusive coords for the scissor test.
const GSVector4i& scissor = m_context->scissor.cull;
u32 test = static_cast<u32>(!bbox_ex.rintersects(scissor));
// Test for empty bbox.
if constexpr (primclass == GS_TRIANGLE_CLASS || primclass == GS_SPRITE_CLASS)
{
case GS_TRIANGLELIST:
case GS_TRIANGLESTRIP:
case GS_TRIANGLEFAN:
test = (test | v0.eq64(v1)) | (v1.eq64(v2) | v0.eq64(v2));
break;
default:
break;
test |= static_cast<u32>(bbox.rempty());
}
#ifndef ARCH_ARM64
// We only care about the xy passing the skip test. zw is the offset coordinates for native culling.
skip |= test.mask() & 0xff;
#else
// mask() is slow on ARM, so just pull the bits out instead, thankfully we only care about the first 4 bytes.
skip |= (static_cast<u64>(test.extract64<0>()) & UINT64_C(0x8080808080808080)) != 0;
#endif
// Test for degenerate triangle.
if constexpr (primclass == GS_TRIANGLE_CLASS)
{
test |= static_cast<u32>(v0.eq(v1)) | static_cast<u32>(v1.eq(v2)) | static_cast<u32>(v0.eq(v2));
}
skip |= test;
}
if (skip != 0)
@@ -6094,13 +6079,12 @@ __forceinline void GSState::VertexKick(u32 skip)
ASSUME(0);
}
// Update rectangle for the current draw. We can use the re-integer coordinates from min/max here.
const GSVector4i draw_min = pmin.zwzw();
const GSVector4i draw_max = pmax;
if (m_index->tail != n)
temp_draw_rect = temp_draw_rect.min_i32(draw_min).blend32<12>(temp_draw_rect.max_i32(draw_max));
// Update rectangle for the current draw. Needs exclusive endpoints.
const GSVector4i draw_rect = bbox.sra32<4>() + GSVector4i(0, 0, 1, 1);
if (m_vertex->tail != n)
temp_draw_rect = temp_draw_rect.runion(draw_rect);
else
temp_draw_rect = draw_min.blend32<12>(draw_max);
temp_draw_rect = draw_rect;
temp_draw_rect = temp_draw_rect.rintersect(m_context->scissor.in);
constexpr u32 max_vertices = MaxVerticesForPrim(prim);
@@ -6438,11 +6422,6 @@ void GSState::CalcAlphaMinMax(const int tex_alpha_min, const int tex_alpha_max)
min = 128;
max = 128;
}
else if (IsCoverageAlphaSupported())
{
min = 0;
max = 128;
}
else
{
const GSDrawingContext* context = m_context;
@@ -6520,7 +6499,34 @@ void GSState::CalcAlphaMinMax(const int tex_alpha_min, const int tex_alpha_max)
m_vt.m_alpha.min = min;
m_vt.m_alpha.max = max;
m_vt.m_alpha.depth_min = min;
m_vt.m_alpha.depth_max = max;
m_vt.m_alpha.valid = true;
if (IsCoverageAlphaSupported())
{
// Expand the alpha range depending on what the AA1 can do to the alpha.
if (PRIM->ABE)
{
// ABE==1: Coverage is used for alpha only used when incoming alpha is exactly 128.
// If 128 is in the incoming range, expand it down to 0, since edges could be 0-127.
// Don't change depth min, since depth isn't written on the edges.
if (min <= 128 && 128 <= max)
{
m_vt.m_alpha.min = std::min(0, min);
}
}
else
{
// ABE==0: Coverage is always used for alpha, so assume exactly 0-128.
// Assume exactly 128 for depth, since depth isn't written on the edges.
m_vt.m_alpha.min = 0;
m_vt.m_alpha.max = 128;
m_vt.m_alpha.depth_min = 128;
m_vt.m_alpha.depth_max = 128;
}
}
}
void GSState::CorrectATEAlphaMinMax(const u32 atst, const int aref)
@@ -6586,107 +6592,127 @@ bool GSState::TryAlphaTest(u32& fm, u32& zm)
return true;
break;
default:
ASSUME(0);
pxFailRel("Impossible.");
break;
}
bool pass = true;
enum AlphaTestResult
{
UNKNOWN,
ALL_PASS,
ALL_FAIL,
};
AlphaTestResult result, depth_result;
if (m_context->TEST.ATST == ATST_NEVER)
{
pass = false; // Shortcut to avoid GetAlphaMinMax below
// Shortcut for NEVER to avoid GetAlphaMinMax below.
result = ALL_FAIL;
depth_result = ALL_FAIL;
}
else
{
const GSVertexTrace::VertexAlpha& aminmax = GetAlphaMinMax();
const int amin = aminmax.min;
const int amax = aminmax.max;
const int aref = m_context->TEST.AREF;
switch (m_context->TEST.ATST)
{
case ATST_NEVER:
pass = false;
break;
case ATST_ALWAYS:
pass = true;
break;
case ATST_LESS:
if (amax < aref)
pass = true;
else if (amin >= aref)
pass = false;
else
return false;
break;
case ATST_LEQUAL:
if (amax <= aref)
pass = true;
else if (amin > aref)
pass = false;
else
return false;
break;
case ATST_EQUAL:
if (amin == aref && amax == aref)
pass = true;
else if (amin > aref || amax < aref)
pass = false;
else
return false;
break;
case ATST_GEQUAL:
if (amin >= aref)
pass = true;
else if (amax < aref)
pass = false;
else
return false;
break;
case ATST_GREATER:
if (amin > aref)
pass = true;
else if (amax <= aref)
pass = false;
else
return false;
break;
case ATST_NOTEQUAL:
if (amin == aref && amax == aref)
pass = false;
else if (amin > aref || amax < aref)
pass = true;
else
return false;
break;
default:
ASSUME(0);
}
const auto GetResult = [&](int amin, int amax) {
switch (m_context->TEST.ATST)
{
case ATST_NEVER:
return ALL_FAIL;
case ATST_ALWAYS:
return ALL_PASS;
case ATST_LESS:
{
if (amax < aref)
return ALL_PASS;
else if (amin >= aref)
return ALL_FAIL;
else
return UNKNOWN;
}
case ATST_LEQUAL:
{
if (amax <= aref)
return ALL_PASS;
else if (amin > aref)
return ALL_FAIL;
else
return UNKNOWN;
}
case ATST_EQUAL:
{
if (amin == aref && amax == aref)
return ALL_PASS;
else if (amin > aref || amax < aref)
return ALL_FAIL;
else
return UNKNOWN;
}
case ATST_GEQUAL:
{
if (amin >= aref)
return ALL_PASS;
else if (amax < aref)
return ALL_FAIL;
else
return UNKNOWN;
}
case ATST_GREATER:
{
if (amin > aref)
return ALL_PASS;
else if (amax <= aref)
return ALL_FAIL;
else
return UNKNOWN;
}
case ATST_NOTEQUAL:
{
if (amin == aref && amax == aref)
return ALL_FAIL;
else if (amin > aref || amax < aref)
return ALL_PASS;
else
return UNKNOWN;
}
default:
pxFailRel("Impossible");
return UNKNOWN;
}
};
result = GetResult(aminmax.min, aminmax.max);
depth_result = (result != UNKNOWN) ? result : GetResult(aminmax.depth_min, aminmax.depth_max);
}
if (!pass)
const u32 fail_fm = (result == ALL_FAIL) ? 0xffffffff : 0x0;
const u32 fail_zm = (depth_result == ALL_FAIL) ? 0xffffffff : 0x0;
switch (fail_type)
{
switch (fail_type)
{
case AFAIL_KEEP:
fm = zm = 0xffffffff;
break;
case AFAIL_FB_ONLY:
zm = 0xffffffff;
break;
case AFAIL_ZB_ONLY:
fm = 0xffffffff;
break;
case AFAIL_RGB_ONLY:
fm |= 0xff000000;
zm = 0xffffffff;
break;
default:
ASSUME(0);
}
case AFAIL_KEEP:
fm |= fail_fm;
zm |= fail_zm;
break;
case AFAIL_FB_ONLY:
zm |= fail_zm;
break;
case AFAIL_ZB_ONLY:
fm |= fail_fm;
break;
case AFAIL_RGB_ONLY:
fm |= (fail_fm & 0xff000000);
zm |= fail_zm;
break;
default:
pxFailRel("Impossible.");
break;
}
return true;
return result != UNKNOWN;
}
bool GSState::IsFlatShaded()
@@ -6756,6 +6782,7 @@ bool GSState::IsCoverageAlphaFixedOne()
bool GSState::IsCoverageAlphaSupported()
{
pxFailRel("Not implemented");
return false;
}
+3 -5
View File
@@ -131,8 +131,6 @@ protected:
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;
@@ -260,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;
};
+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;
}
}
+3
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);
+76 -4
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"
@@ -1467,6 +1484,7 @@ static void DumpPSSelector(DrawConfigWriter& out, const GSHWDrawConfig::PSSelect
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)
@@ -1512,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);
}
@@ -1542,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);
@@ -1595,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());
}
}
+14 -11
View File
@@ -423,6 +423,9 @@ struct alignas(16) GSHWDrawConfig
// 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
@@ -505,7 +508,6 @@ struct alignas(16) GSHWDrawConfig
u8 tav : 1;
u8 biln : 1;
u8 triln : 3;
u8 aniso : 1;
u8 lodclamp : 1;
};
u8 key;
@@ -810,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
@@ -878,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)
@@ -929,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
{
@@ -954,12 +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
@@ -1037,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);
@@ -1074,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
+1 -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);
}
@@ -39,6 +39,15 @@ 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
@@ -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;
};
+110 -108
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;
@@ -644,17 +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.aa1 = GSConfig.HWAA1 && m_features.vs_expand && (m_features.depth_feedback != GSDevice::DepthFeedbackSupport::None);
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 :
@@ -1180,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)
@@ -1211,6 +1205,25 @@ void GSDevice11::DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_ind
}
}
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);
@@ -1878,6 +1891,7 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
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;
@@ -1905,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);
@@ -2831,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);
@@ -2845,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)
@@ -2917,7 +2925,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
SetupOM(dss, blend, 0);
OMSetRenderTargets(primid_texture, config.ds, &config.scissor, read_only_dsv);
SetRenderHWShaderResources(config, nullptr);
DrawIndexedPrimitive();
Draw(config);
config.ps.date = 3;
config.alpha_second_pass.ps.date = 3;
@@ -2940,19 +2948,30 @@ 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.");
}
@@ -2962,10 +2981,10 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
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)
@@ -2975,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)
@@ -2991,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)
@@ -3017,74 +3037,56 @@ 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)
{
const bool vs_expand = config.vs.expand != GSHWDrawConfig::VSExpand::None;
const bool vs_indexing = config.vs.UseVSExpandIndexBuffer();
const u32 vs_indexing_expansion = GetExpansionFactor(config.vs.expand);
#ifdef PCSX2_DEVBUILD
if ((one_barrier || full_barrier) && !(config.ps.IsFeedbackLoopRT() || config.ps.IsFeedbackLoopDepth())) [[unlikely]]
Console.Warning("D3D11: Possible unnecessary copy detected.");
#endif
auto Draw = [&](int offset, int count) {
if (vs_expand)
auto CopyAndBind = [&](GSVector4i drawarea) {
if (draw_rt_clone)
{
DrawIndexedPrimitiveVSExpand(offset, count, vs_indexing, vs_indexing_expansion);
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);
}
else
if (draw_ds_clone)
{
DrawIndexedPrimitive(offset, count);
CopyRect(draw_ds, draw_ds_clone, drawarea, drawarea.left, drawarea.top);
PSSetShaderResource(4, draw_ds_clone);
}
};
if (draw_rt_clone || 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)
{
#ifdef PCSX2_DEVBUILD
if ((one_barrier || full_barrier) && !(config.ps.IsFeedbackLoopRT() || config.ps.IsFeedbackLoopDepth())) [[unlikely]]
Console.Warning("D3D11: Possible unnecessary copy detected.");
#endif
const u32 draw_list_size = static_cast<u32>(config.drawlist->size());
const u32 indices_per_prim = config.indices_per_prim;
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);
}
};
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());
if (full_barrier)
for (u32 n = 0, p = 0; n < draw_list_size; n++)
{
const u32 draw_list_size = static_cast<u32>(config.drawlist->size());
const u32 indices_per_prim = config.indices_per_prim;
const u32 count = (*config.drawlist)[n] * indices_per_prim;
pxAssert(config.drawlist && !config.drawlist->empty());
pxAssert(config.drawlist_bbox && static_cast<u32>(config.drawlist_bbox->size()) == draw_list_size);
const GSVector4i original_bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea);
CopyAndBind(ProcessCopyArea(rtsize, original_bbox));
for (u32 n = 0, p = 0; n < draw_list_size; n++)
{
const u32 count = (*config.drawlist)[n] * indices_per_prim;
Draw(config, p, count);
const GSVector4i original_bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea);
CopyAndBind(ProcessCopyArea(rtsize, original_bbox));
Draw(p, count);
p += count;
}
return;
p += count;
}
// Optimization: For alpha second pass we can reuse the copy snapshot from the first pass.
CopyAndBind(ProcessCopyArea(rtsize, config.drawarea));
return;
}
Draw(0, m_index.count);
if (one_barrier)
CopyAndBind(ProcessCopyArea(rtsize, config.drawarea));
Draw(config);
}
void GSDevice11::SetRenderHWShaderResources(const GSHWDrawConfig& config, GSTexture* primid_texture)
+6 -1
View File
@@ -301,10 +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 = false, int vs_indexing_expansion = 1);
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;
+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;
};
+79 -79
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);
@@ -885,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())
@@ -1405,17 +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.aa1 = GSConfig.HWAA1 && m_features.vs_expand && (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) &&
@@ -1464,6 +1462,18 @@ void GSDevice12::DrawPrimitive()
GetCommandList().list4->DrawInstanced(m_vertex.count, 1, m_vertex.start, 0);
}
void GSDevice12::DrawIndexedPrimitive()
{
DrawIndexedPrimitive(0, m_index.count);
}
void GSDevice12::DrawIndexedPrimitive(int offset, int count)
{
pxAssert(offset + count <= (int)m_index.count);
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
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);
@@ -1479,17 +1489,23 @@ void GSDevice12::DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_ind
}
}
void GSDevice12::DrawIndexedPrimitive()
void GSDevice12::Draw(const GSHWDrawConfig& config, int offset, int count)
{
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
GetCommandList().list4->DrawIndexedInstanced(m_index.count, 1, m_index.start, m_vertex.start, 0);
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::DrawIndexedPrimitive(int offset, int count)
void GSDevice12::Draw(const GSHWDrawConfig& config)
{
pxAssert(offset + count <= (int)m_index.count);
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
GetCommandList().list4->DrawIndexedInstanced(count, 1, m_index.start + offset, m_vertex.start, 0);
Draw(config, 0, m_index.count);
}
void GSDevice12::LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format,
@@ -2494,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;
@@ -3199,6 +3211,7 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector&
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;
@@ -4138,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();
@@ -4237,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
@@ -4356,9 +4369,10 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
}
}
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)
@@ -4458,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
@@ -4522,29 +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);
const bool vs_expand = config.vs.expand != GSHWDrawConfig::VSExpand::None;
const bool vs_indexing = config.vs.UseVSExpandIndexBuffer();
const u32 vs_indexing_expansion = GetExpansionFactor(config.vs.expand);
auto Draw = [&](int offset, int count) {
if (vs_expand)
{
DrawIndexedPrimitiveVSExpand(offset, count, vs_indexing, vs_indexing_expansion);
}
else
{
DrawIndexedPrimitive(offset, count);
}
};
if (feedback_rt || feedback_depth)
{
#ifdef PCSX2_DEVBUILD
@@ -4577,7 +4577,7 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
FeedbackBarrier(draw_ds);
if (BindDrawPipeline(pipe))
Draw(p, count);
Draw(config, p, count);
p += count;
}
@@ -4596,7 +4596,7 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
}
if (BindDrawPipeline(pipe))
Draw(0, m_index.count);
Draw(config);
}
void GSDevice12::UpdateHWPipelineSelector(GSHWDrawConfig& config)
@@ -4611,7 +4611,7 @@ 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)
+6 -1
View File
@@ -463,10 +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 = false, int vs_indexing_expansion = 1);
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;
+152 -147
View File
@@ -790,11 +790,14 @@ void GSRendererHW::DetectTextureShuffle()
void GSRendererHW::DetectTextureShuffleSecondPass(GSTextureCache::Target* rt, GSTextureCache::Source* tex)
{
const auto HasLowerOnes = [&](u32 x) { return x != 0 && (x & (x + 1)) == 0; };
if (!m_process_texture || !rt)
{
GL_INS("HW: Texture shuffle detection (2): Not detected.");
m_texture_shuffle.Disable();
return;
}
const GSLocalMemory::psm_t& tex_psm = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM];
const GSLocalMemory::psm_t& frame_psm = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM];
if (frame_psm.bpp != 16)
{
GL_INS("HW: Texture shuffle detection (2): Failed (not 16 bit RT).");
@@ -804,7 +807,7 @@ void GSRendererHW::DetectTextureShuffleSecondPass(GSTextureCache::Target* rt, GS
if (m_texture_shuffle)
{
if (tex->m_32_bits_fmt)
if (tex && tex->m_32_bits_fmt)
{
GL_INS("HW: Texture shuffle detection (2): Passed.");
}
@@ -812,9 +815,9 @@ void GSRendererHW::DetectTextureShuffleSecondPass(GSTextureCache::Target* rt, GS
{
// Detects when the source texture is really a 16 bit texture instead of 32 bit being reinterpreted as 16 bit.
// Make sure it's opaque and not bilinear to reduce false positives.
const auto HasLowerOnes = [&](u32 x) { return x != 0 && (x & (x + 1)) == 0; };
if (m_cached_ctx.TEX0.TBP0 != m_cached_ctx.FRAME.Block() &&
rt->m_32_bits_fmt == true && IsOpaque() && !m_vt.IsRealLinear() &&
rt && rt->m_32_bits_fmt == true && IsOpaque() && !m_vt.IsRealLinear() &&
HasLowerOnes(m_cached_ctx.FRAME.FBMSK))
{
GL_INS("HW: Texture shuffle detection (2): Passed (real 16 bit source).");
@@ -832,10 +835,10 @@ void GSRendererHW::DetectTextureShuffleSecondPass(GSTextureCache::Target* rt, GS
// Last ditch check for reinterpreting a 32 bit source and RT as 16 bits.
// These "shuffles" appear to not do anything useful for games, but using the texture shuffle
// path helps to maintain correct sizes in the texture cache. Occurs in NFS Most Wanted.
const GSLocalMemory::psm_t& tex_psm = GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM];
if (PRIM->TME &&
(m_vt.m_primclass == GS_SPRITE_CLASS || m_vt.m_primclass == GS_TRIANGLE_CLASS && TrianglesAreQuads(true)) &&
(tex_psm.bpp == 16) && (frame_psm.bpp == 16) && rt->m_32_bits_fmt && tex->m_32_bits_fmt)
((m_vt.m_primclass == GS_SPRITE_CLASS || m_vt.m_primclass == GS_TRIANGLE_CLASS) && TrianglesAreQuads(true)) &&
(tex_psm.bpp == 16) && (frame_psm.bpp == 16) && rt && rt->m_32_bits_fmt && tex && tex->m_32_bits_fmt)
{
GL_INS("HW: Texture shuffle detection (2): Passed (HACK: reinterpreting both source/RT as 16 bit).");
m_texture_shuffle.type = TextureShuffleType::HackShuffle;
@@ -2918,7 +2921,7 @@ void GSRendererHW::Draw()
}
// Test if we can optimize Alpha Test as a NOP
m_cached_ctx.TEST.ATE = m_cached_ctx.TEST.ATE && !GSRenderer::TryAlphaTest(fm, zm);
m_cached_ctx.TEST.ATE = !!m_cached_ctx.TEST.ATE && !GSRenderer::TryAlphaTest(fm, zm);
// Need to fix the alpha test, since the alpha will be fixed to 1.0 if ABE is disabled and AA1 is enabled
// So if it doesn't meet the condition, always fail, if it does, always pass (turn off the test).
@@ -2992,7 +2995,7 @@ void GSRendererHW::Draw()
// I hate that I have to do this, but some games (like Pac-Man World Rally) troll us by causing a flush with degenerate triangles, so we don't have all available information about the next draw.
// So we have to check when the next draw happens if our frame has changed or if it's become recursive.
const bool has_colclip_texture = g_gs_device->GetColorClipTexture() != nullptr;
if (!no_rt && has_colclip_texture && (m_conf.colclip_frame.FBP != m_cached_ctx.FRAME.FBP || m_conf.colclip_frame.Block() == m_cached_ctx.TEX0.TBP0))
if (!no_rt && has_colclip_texture && (m_conf.colclip_frame.FBP != m_cached_ctx.FRAME.FBP || (PRIM->TME && m_conf.colclip_frame.Block() == m_cached_ctx.TEX0.TBP0)))
{
GIFRegTEX0 FRAME;
FRAME.TBP0 = m_conf.colclip_frame.Block();
@@ -4388,15 +4391,15 @@ void GSRendererHW::Draw()
}
}
// Second pass texture shuffle detection using RT and source.
DetectTextureShuffleSecondPass(rt, src);
if (m_process_texture)
{
GIFRegCLAMP MIP_CLAMP = m_cached_ctx.CLAMP;
if (rt)
{
// Second pass texture shuffle detection using RT and source.
DetectTextureShuffleSecondPass(rt, src);
if (m_texture_shuffle)
{
if (IsSplitTextureShuffle(rt->m_TEX0, rt->m_valid))
@@ -5362,7 +5365,7 @@ void GSRendererHW::HandleProvokingVertexFirst()
}
}
void GSRendererHW::SetupIA(float target_scale, float sx, float sy, bool req_vert_backup)
void GSRendererHW::SetupIA(float target_scale, float sx, float sy, bool req_vert_backup, const bool no_rt)
{
GL_PUSH("HW: IA");
@@ -5376,6 +5379,7 @@ void GSRendererHW::SetupIA(float target_scale, float sx, float sy, bool req_vert
const bool unscale_pt_ln = !GSConfig.UserHacks_DisableSafeFeatures && (target_scale != 1.0f);
const GSDevice::FeatureSupport features = g_gs_device->Features();
const bool draw_aa1 = !no_rt && PRIM->AA1 && features.aa1;
pxAssert(VerifyIndices());
@@ -5419,7 +5423,7 @@ void GSRendererHW::SetupIA(float target_scale, float sx, float sy, bool req_vert
{
m_conf.topology = GSHWDrawConfig::Topology::Line;
m_conf.indices_per_prim = 2;
if (PRIM->AA1 && features.aa1)
if (draw_aa1)
{
// AA1 expansion uses a similar path as upscale expansion but it is used
// for both upscaling and native resolution drawing.
@@ -5428,8 +5432,6 @@ void GSRendererHW::SetupIA(float target_scale, float sx, float sy, bool req_vert
m_conf.cb_vs.point_size = GSVector2(16.0f * sx, 16.0f * sy);
m_conf.topology = GSHWDrawConfig::Topology::Triangle;
m_conf.indices_per_prim = 6;
m_conf.ps.aa1 = GSHWDrawConfig::PS_AA1::LINE;
m_conf.ps.abe = PRIM->ABE != 0;
ExpandLineIndices();
}
else if (unscale_pt_ln)
@@ -5489,16 +5491,13 @@ void GSRendererHW::SetupIA(float target_scale, float sx, float sy, bool req_vert
case GS_TRIANGLE_CLASS:
{
if (PRIM->AA1 && features.aa1)
if (draw_aa1)
{
GL_INS("HW: AA1 triangle expand.");
m_conf.vs.expand = GSHWDrawConfig::VSExpand::TriangleAA1;
m_conf.cb_vs.point_size = GSVector2(16.0f * sx, 16.0f * sy);
m_conf.topology = GSHWDrawConfig::Topology::Triangle;
m_conf.indices_per_prim = 3;
m_conf.ps.aa1 = m_cached_ctx.DepthWrite() ? GSHWDrawConfig::PS_AA1::TRIANGLE_SW_Z :
GSHWDrawConfig::PS_AA1::TRIANGLE;
m_conf.ps.abe = PRIM->ABE != 0;
}
else
{
@@ -5792,37 +5791,42 @@ void GSRendererHW::DetermineAlphaScaling(GSTextureCache::Target* rt, GSTextureCa
}
}
void GSRendererHW::EmulateZbufferAA1()
void GSRendererHW::EmulateAA1()
{
const GSDevice::FeatureSupport& features = g_gs_device->Features();
pxAssert(!features.aa1 || features.depth_feedback != GSDevice::DepthFeedbackSupport::None);
pxAssert(!features.aa1 || features.feedback_loops());
if (IsCoverageAlphaSupported())
{
m_conf.ps.abe = PRIM->ABE; // ABE flag determines how coverage is used for alpha.
if (m_vt.m_primclass == GS_LINE_CLASS)
{
GL_INS("HW: AA1 lines. No depth write.");
// AA1: Z is not written on lines since coverage is always less than 0x80.
m_conf.depth.zwe = false;
m_cached_ctx.ZBUF.ZMSK = 1;
m_conf.ps.aa1 = GSHWDrawConfig::PS_AA1::LINE;
}
else if (m_vt.m_primclass == GS_TRIANGLE_CLASS)
{
// Force SW depth so that Z writes can be prevented for edge pixels.
if (m_cached_ctx.DepthWrite())
{
// Z test must be also done in the shader.
if (m_conf.depth.ztst == ZTST_GEQUAL || m_conf.depth.ztst == ZTST_GREATER)
{
m_conf.ps.ztst = m_conf.depth.ztst; // Enable shader Z test.
m_conf.depth.ztst = ZTST_ALWAYS; // Disable HW Z test.
GL_INS("HW: AA1 triangles with depth feedback.");
// We need barriers for the feedback
if (features.texture_barrier || features.multidraw_fb_copy)
{
m_conf.require_one_barrier |= (m_prim_overlap == PRIM_OVERLAP_NO);
m_conf.require_full_barrier |= (m_prim_overlap != PRIM_OVERLAP_NO);
}
}
m_conf.ps.aa1 = GSHWDrawConfig::PS_AA1::TRIANGLE_SW_Z; // Allows discarding depth on edge pixels.
ConfigureDepthFeedback(); // Enable barriers/SW depth test.
}
else
{
GL_INS("HW: AA1 triangles with no depth write.");
m_conf.ps.aa1 = GSHWDrawConfig::PS_AA1::TRIANGLE; // No special depth handling.
}
}
else
@@ -5887,24 +5891,31 @@ void GSRendererHW::EmulateDATESelectMethod(DATEOptions& date_options, GSTextureC
if (features.framebuffer_fetch)
{
// Full DATE is "free" with framebuffer fetch. The barrier gets cleared below.
GL_PERF("DATE: Accurate with framebuffer fetch");
date_options.barrier = true;
m_conf.require_full_barrier = true;
}
else if (IsCoverageAlphaSupported())
else if (features.feedback_loops() && IsCoverageAlphaSupported())
{
// We're using AA1 for this draw so use only full barrier DATE, to avoid the complications
// with stencil/primid setup with AA1 vertex shaders. AA1 triangles usually require full barriers anyway.
GL_PERF("DATE: Accurate with IsCoverageAlphaSupported");
date_options.barrier = true;
m_conf.require_full_barrier = true;
}
else if ((features.texture_barrier && m_prim_overlap == PRIM_OVERLAP_NO) || m_texture_shuffle)
else if ((features.texture_barrier && m_prim_overlap == PRIM_OVERLAP_NO))
{
GL_PERF("DATE: Accurate with %s", (features.texture_barrier && m_prim_overlap == PRIM_OVERLAP_NO) ? "no overlap" : "texture shuffle");
if (features.texture_barrier)
{
m_conf.require_full_barrier = true;
date_options.barrier = true;
}
// We only enable this for texture barriers as it's fast, multidraw fb copy might be slower.
GL_PERF("DATE: Accurate with no overlap");
m_conf.require_full_barrier = true;
date_options.barrier = true;
}
else if (features.feedback_loops() && m_texture_shuffle)
{
// Ensure texture shuffles run with full barrier DATE.
GL_PERF("DATE: Accurate with texture shuffle");
m_conf.require_full_barrier = true;
date_options.barrier = true;
}
// When Blending is disabled and Edge Anti Aliasing is enabled,
// the output alpha is Coverage (which we force to 128) so DATE will fail/pass guaranteed on second pass.
@@ -5938,7 +5949,7 @@ void GSRendererHW::EmulateDATESelectMethod(DATEOptions& date_options, GSTextureC
m_conf.require_full_barrier = true;
date_options.barrier = true;
}
else if ((features.texture_barrier || features.multidraw_fb_copy) && m_conf.require_full_barrier)
else if (features.feedback_loops() && m_conf.require_full_barrier)
{
// Full barrier is enabled (likely sw fbmask), we need to use date barrier.
GL_PERF("DATE: Accurate with alpha %d-%d", GetAlphaMinMax().min, GetAlphaMinMax().max);
@@ -5950,7 +5961,7 @@ void GSRendererHW::EmulateDATESelectMethod(DATEOptions& date_options, GSTextureC
GL_PERF("DATE: Accurate with alpha %d-%d", GetAlphaMinMax().min, GetAlphaMinMax().max);
date_options.primid = true;
}
else if (features.texture_barrier || features.multidraw_fb_copy)
else if (features.feedback_loops())
{
GL_PERF("DATE: Accurate with alpha %d-%d", GetAlphaMinMax().min, GetAlphaMinMax().max);
m_conf.require_full_barrier = true;
@@ -5988,7 +5999,7 @@ void GSRendererHW::EmulateDATEGetConfig(DATEOptions& date_options, bool scale_rt
// Always swap DATE with DATE_BARRIER if we have barriers on when alpha write is masked.
// This is always enabled on VK/GL/DX12 but not on DX11 as copies are slow so we can selectively enable it like now.
if (!m_conf.colormask.wa && (m_conf.require_one_barrier || m_conf.require_full_barrier))
if (!m_conf.colormask.wa && (m_conf.require_one_barrier || (m_conf.require_full_barrier && features.feedback_loops())))
date_options.barrier = true;
if (m_conf.ps.scanmsk & 2)
@@ -6008,17 +6019,25 @@ void GSRendererHW::EmulateDATEGetConfig(DATEOptions& date_options, bool scale_rt
else
m_conf.datm = static_cast<SetDATM>(m_cached_ctx.TEST.DATM);
if (m_conf.destination_alpha >= GSHWDrawConfig::DestinationAlphaMode::Stencil &&
m_conf.destination_alpha <= GSHWDrawConfig::DestinationAlphaMode::StencilOne && !m_conf.ds)
// DATE Stencil always needs a depth stencil texture.
const bool date_stencil_needs_ds = !m_conf.ds &&
(m_conf.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::Stencil || m_conf.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne);
if (date_stencil_needs_ds)
{
const bool is_one_barrier = (features.texture_barrier && m_conf.require_full_barrier &&
(m_prim_overlap == PRIM_OVERLAP_NO || m_conf.ps.shuffle || m_channel_shuffle));
const bool need_barrier = m_conf.require_one_barrier || (m_conf.require_full_barrier && features.feedback_loops());
if ((temp_ds.reset(g_gs_device->CreateDepthStencil(m_conf.rt->GetWidth(), m_conf.rt->GetHeight(),
GSTexture::Format::DepthStencil, false)), temp_ds))
{
m_conf.ds = temp_ds.get();
}
else if (features.primitive_id && !(m_conf.ps.scanmsk & 2) && (!m_conf.require_full_barrier || is_one_barrier))
else if (need_barrier)
{
date_options.stencil_one = false;
date_options.barrier = true;
m_conf.destination_alpha = GSHWDrawConfig::DestinationAlphaMode::Full;
DevCon.Warning("HW: Depth buffer creation failed for Stencil Date. Fallback to Full.");
}
else if (features.primitive_id && !(m_conf.ps.scanmsk & 2))
{
date_options.stencil_one = false;
date_options.primid = true;
@@ -6040,7 +6059,7 @@ void GSRendererHW::EmulateDATEGetConfig(DATEOptions& date_options, bool scale_rt
}
else if (date_options.stencil_one)
{
const bool multidraw_fb_copy = features.multidraw_fb_copy && (m_conf.require_one_barrier || m_conf.require_full_barrier);
const bool multidraw_fb_copy = m_conf.require_one_barrier || (m_conf.require_full_barrier && features.multidraw_fb_copy);
if (features.texture_barrier || multidraw_fb_copy)
{
m_conf.require_one_barrier = true;
@@ -6149,8 +6168,7 @@ void GSRendererHW::DetermineBarriers(GSTextureCache::Target* rt)
// If we use depth feedback directly, we must use barriers for the depth texture.
// If we use depth-as-color feedback, then FB fetch can be used for depth also.
bool need_barriers_for_depth = m_conf.ps.IsFeedbackLoopDepth() &&
(features.depth_feedback == GSDevice::DepthFeedbackSupport::Depth);
const bool need_barriers_for_depth = m_conf.ps.IsFeedbackLoopDepth() && features.depth_feedback;
if (!need_barriers_for_depth)
{
@@ -6163,19 +6181,18 @@ void GSRendererHW::DetermineBarriers(GSTextureCache::Target* rt)
pxAssert(!m_conf.require_full_barrier || !m_conf.ps.colclip_hw);
// Swap full barrier for one barrier when there's no overlap, or a shuffle.
if ((features.texture_barrier || features.multidraw_fb_copy) && m_conf.require_full_barrier && (m_prim_overlap == PRIM_OVERLAP_NO || m_conf.ps.shuffle || m_channel_shuffle))
if (features.feedback_loops() && m_conf.require_full_barrier && (m_prim_overlap == PRIM_OVERLAP_NO || m_conf.ps.shuffle || m_channel_shuffle))
{
m_conf.require_full_barrier = false;
m_conf.require_one_barrier = true;
}
else if (!(features.texture_barrier || features.multidraw_fb_copy))
else if (!features.feedback_loops())
{
// These shouldn't be enabled if texture barriers aren't supported, make sure they are off.
m_conf.ps.write_rg = 0;
m_conf.require_full_barrier = false;
}
if (m_conf.require_full_barrier && (g_gs_device->Features().texture_barrier || g_gs_device->Features().multidraw_fb_copy))
if (m_conf.require_full_barrier && features.feedback_loops())
{
ComputeDrawlistGetSize(rt->m_scale);
m_conf.drawlist = &m_drawlist;
@@ -6247,9 +6264,8 @@ void GSRendererHW::EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GS
// If date is enabled you need to test the green channel instead of the alpha channel.
// Only enable this code in DATE mode to reduce the number of shaders.
m_conf.ps.write_rg = (process_rg & SHUFFLE_WRITE) && (features.texture_barrier || features.multidraw_fb_copy) &&
m_cached_ctx.TEST.DATE;
m_conf.ps.write_rg = !!(process_rg & SHUFFLE_WRITE) && !!m_cached_ctx.TEST.DATE;
m_conf.ps.real16src = m_texture_shuffle.real_16_bit_source;
// Shuffle that copies B to A, which are in the same column.
@@ -6363,7 +6379,7 @@ void GSRendererHW::EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GS
have been invalidated before subsequent Draws are executed.
*/
// No blending so hit unsafe path.
if (!PRIM->ABE || !(~ff_fbmask & ~zero_fbmask & 0x7) || !(features.texture_barrier || features.multidraw_fb_copy))
if (!PRIM->ABE || !(~ff_fbmask & ~zero_fbmask & 0x7) || !features.feedback_loops())
{
GL_INS("HW: FBMASK Unsafe SW emulated fb_mask:%x on %d bits format", m_cached_ctx.FRAME.FBMSK,
(m_conf.ps.dst_fmt == GSLocalMemory::PSM_FMT_16) ? 16 : 32);
@@ -6873,7 +6889,7 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, DATEOptio
// Condition 2: One barrier is already enabled, prims don't overlap or is a channel shuffle so let's use sw blend instead.
// Condition 3: A texture shuffle is unlikely to overlap, so we can prefer full sw blend.
// Condition 4: If it's tex in fb draw and there's no overlap prefer sw blend, fb is already being read.
const bool prefer_sw_blend = ((features.texture_barrier || features.multidraw_fb_copy) && m_conf.require_full_barrier) || (m_conf.require_one_barrier && (no_prim_overlap || m_channel_shuffle)) || m_conf.ps.shuffle || (no_prim_overlap && (m_conf.tex == m_conf.rt));
const bool prefer_sw_blend = (features.feedback_loops() && m_conf.require_full_barrier) || (m_conf.require_one_barrier && (no_prim_overlap || m_channel_shuffle)) || m_conf.ps.shuffle || (no_prim_overlap && (m_conf.tex == m_conf.rt));
const bool free_blend = blend_non_recursive // Free sw blending, doesn't require barriers or reading fb
|| accumulation_blend; // Mix of hw/sw blending
@@ -6884,7 +6900,7 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, DATEOptio
const bool blend_multipass_group = blend_multi_pass_support && !features.texture_barrier &&
(bmix1_multi_pass1 || bmix1_multi_pass2 || bmix3_multi_pass || (blend_flag & (BLEND_HW3 | BLEND_HW4 | BLEND_HW5 | BLEND_HW6 | BLEND_HW7 | BLEND_HW8 | BLEND_HW9)));
const bool barriers_supported = features.texture_barrier || features.multidraw_fb_copy;
const bool barriers_supported = features.feedback_loops();
const bool blend_requires_barrier =
// We don't want the cases to be enabled if barriers aren't supported so limit it to no overlap.
(no_prim_overlap || barriers_supported)
@@ -6902,7 +6918,7 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, DATEOptio
{
case AccBlendLevel::Maximum:
sw_blending |= true;
accumulation_blend &= (GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 32);
accumulation_blend &= !barriers_supported || (GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 32);
[[fallthrough]];
case AccBlendLevel::Full:
sw_blending |= m_conf.ps.blend_a != m_conf.ps.blend_b && alpha_c0_high_max_one;
@@ -6921,7 +6937,7 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, DATEOptio
case AccBlendLevel::Basic:
default:
// Prefer sw blend if possible.
color_dest_blend &= !(m_channel_shuffle || m_conf.ps.dither);
color_dest_blend &= !m_conf.ps.dither;
color_dest_blend2 &= !(prefer_sw_blend || m_conf.ps.dither);
blend_zero_to_one_range &= !(prefer_sw_blend || m_conf.ps.dither);
accumulation_blend &= !prefer_sw_blend;
@@ -6952,10 +6968,9 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, DATEOptio
}
}
if (m_conf.alpha_test == GSHWDrawConfig::AlphaTestMode::FEEDBACK &&
features.depth_feedback == GSDevice::DepthFeedbackSupport::DepthAsRT)
if (m_conf.ps.IsFeedbackLoopDepth() && !features.depth_feedback)
{
// If we are doing feedback alpha test with a second RT we must use SW blending to avoid
// If we are doing depth feedback with a second RT we must use SW blending to avoid
// mixing dual source blending with multiple render targets.
sw_blending = true;
color_dest_blend = false;
@@ -7051,7 +7066,7 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, DATEOptio
m_conf.ps.pabe = 1;
}
else if (features.texture_barrier || features.multidraw_fb_copy)
else if (features.feedback_loops())
{
// PABE sw blend:
// Disable hw/sw mix and do pure sw blend with reading the framebuffer.
@@ -7224,7 +7239,7 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, DATEOptio
const bool blend_non_recursive_one_barrier = blend_non_recursive && blend_ad_alpha_masked;
if (blend_non_recursive_one_barrier)
m_conf.require_one_barrier |= true;
else if (features.texture_barrier || features.multidraw_fb_copy)
else if (features.feedback_loops())
m_conf.require_full_barrier |= !blend_non_recursive;
else
m_conf.require_one_barrier |= !blend_non_recursive;
@@ -7834,7 +7849,7 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
if (shader_emulated_sampler)
{
m_conf.sampler.biln = 0;
m_conf.sampler.aniso = 0;
m_conf.ps.sw_aniso = 0;
// Remove linear from trilinear, since we're doing the bilinear in the shader, and we only want this for mip selection.
m_conf.sampler.triln = (trilinear >= static_cast<u8>(GS_MIN_FILTER::Linear_Mipmap_Nearest)) ?
@@ -7847,7 +7862,7 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
// Aniso filtering doesn't work with textureLod so use texture (automatic_lod) instead.
// Enable aniso only for triangles. Sprites are flat so aniso is likely useless (it would save perf for others primitives).
const bool anisotropic = m_vt.m_primclass == GS_TRIANGLE_CLASS && !trilinear_manual;
m_conf.sampler.aniso = anisotropic;
m_conf.ps.sw_aniso = anisotropic ? GSConfig.MaxAnisotropy : 0;
m_conf.sampler.triln = trilinear;
if (anisotropic && !trilinear_manual)
m_conf.ps.automatic_lod = 1;
@@ -7877,7 +7892,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
{
m_conf.tex = nullptr;
m_conf.ps.tex_is_fb = true;
if (m_prim_overlap == PRIM_OVERLAP_NO || !(g_gs_device->Features().texture_barrier || g_gs_device->Features().multidraw_fb_copy))
if (m_prim_overlap == PRIM_OVERLAP_NO || !g_gs_device->Features().feedback_loops())
m_conf.require_one_barrier = true;
else
m_conf.require_full_barrier = true;
@@ -7957,7 +7972,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
const int horizontal_offset = ((page_offset % src_target->m_TEX0.TBW) * GSLocalMemory::m_psm[src_target->m_TEX0.PSM].pgs.x);
const int vertical_offset = ((page_offset / src_target->m_TEX0.TBW) * GSLocalMemory::m_psm[src_target->m_TEX0.PSM].pgs.y);
if (g_gs_device->Features().texture_barrier || g_gs_device->Features().multidraw_fb_copy)
if (g_gs_device->Features().feedback_loops())
{
const u32 max_skip = ((m_channel_shuffle_finish || !m_channel_shuffle_width) ? 1 : m_channel_shuffle_width) << 5;
const bool new_shuffle = !(m_last_channel_shuffle_fbmsk == m_context->FRAME.FBMSK &&
@@ -8181,7 +8196,7 @@ bool GSRendererHW::CanUseTexIsFB(const GSTextureCache::Target* rt, const GSTextu
}
// No barriers -> we can't use tex-is-fb when there's overlap.
if (!(g_gs_device->Features().texture_barrier || g_gs_device->Features().multidraw_fb_copy) && m_prim_overlap != PRIM_OVERLAP_NO)
if (!g_gs_device->Features().feedback_loops() && m_prim_overlap != PRIM_OVERLAP_NO)
{
GL_CACHE("HW: Disabling tex-is-fb due to no barriers.");
return false;
@@ -8423,19 +8438,18 @@ void GSRendererHW::EmulateAlphaTest(DATEOptions& date_options)
// Determine whether the feedback methods require a single pass.
const bool feedback_one_pass = simple_fb_only || simple_rgb_only || simple_zb_only;
// If we already have the required barriers for the accurate feedback path and
// do not require depth feedback.
// If we already have the required barriers for the accurate feedback path.
const bool free_barrier_feedback =
((m_conf.require_one_barrier && feedback_one_pass) || m_conf.require_full_barrier) &&
(features.texture_barrier || features.multidraw_fb_copy) &&
!afail_needs_depth;
features.feedback_loops() &&
(!afail_needs_depth || m_conf.ps.IsFeedbackLoopDepth());
// Determine if we can use FB-fetch for color only feedback.
const bool free_fbfetch_feedback = features.framebuffer_fetch && !afail_needs_depth;
// Determine if we have the correct features for depth feedback.
const bool depth_feedback_supported = features.depth_feedback != GSDevice::DepthFeedbackSupport::None;
const bool depth_feedback_supported = features.feedback_loops();
// We need depth feedback but do not have the correct features.
const bool avoid_feedback = afail_needs_depth && !depth_feedback_supported;
@@ -8458,26 +8472,19 @@ void GSRendererHW::EmulateAlphaTest(DATEOptions& date_options)
m_conf.cb_ps.FogColor_AREF.a = ps_aref;
m_conf.ps.afail = static_cast<PS_AFAIL>(afail);
if ((afail_needs_rt || afail_needs_depth) && (features.texture_barrier || features.multidraw_fb_copy))
if ((afail_needs_rt || afail_needs_depth) && features.feedback_loops())
{
m_conf.require_one_barrier |= feedback_one_pass;
m_conf.require_full_barrier |= !feedback_one_pass;
}
// Handle SW depth writing and/or testing.
if (afail_needs_depth)
{
pxAssert(zwe);
GL_INS("Enable SW depth write for depth feedback");
// Let the pipeline know we need depth feedback with RGB_ONLY.
if (afail == AFAIL_RGB_ONLY)
m_conf.ps.afail = PS_AFAIL::RGB_ONLY_SW_Z;
if (m_cached_ctx.DepthRead())
{
GL_INS("Enable SW depth testing for depth feedback");
m_conf.ps.ztst = m_cached_ctx.TEST.ZTST; // Enable SW Z test.
m_conf.depth.ztst = ZTST_ALWAYS; // Disable HW Z test.
}
}
else
{
@@ -8485,6 +8492,8 @@ void GSRendererHW::EmulateAlphaTest(DATEOptions& date_options)
pxAssert(afail != AFAIL_FB_ONLY);
}
ConfigureDepthFeedback(); // Enable barriers/SW depth test if needed.
m_conf.alpha_test = GSHWDrawConfig::AlphaTestMode::FEEDBACK;
}
else if (simple_fb_only)
@@ -8676,6 +8685,30 @@ void GSRendererHW::EmulateAlphaTestSecondPass()
m_conf.alpha_test = GSHWDrawConfig::AlphaTestMode::ABORT_DRAW;
}
// Setup barriers and/or SW depth testing for depth feedback.
void GSRendererHW::ConfigureDepthFeedback()
{
if (m_conf.ps.IsFeedbackLoopDepth())
{
const GSDevice::FeatureSupport& features = g_gs_device->Features();
// We need barriers for the feedback.
if (features.feedback_loops())
{
m_conf.require_one_barrier |= (m_prim_overlap == PRIM_OVERLAP_NO);
m_conf.require_full_barrier |= (m_prim_overlap != PRIM_OVERLAP_NO);
}
// We need to do SW depth testing if it's used.
if (m_cached_ctx.DepthRead())
{
GL_INS("HW: Enable SW depth test and disable HW.");
m_conf.ps.ztst = m_cached_ctx.TEST.ZTST; // Enable SW Z test.
m_conf.depth.ztst = ZTST_ALWAYS; // Disable HW Z test.
}
}
}
void GSRendererHW::CleanupDraw(bool invalidate_temp_src)
{
// Remove any RT source.
@@ -8742,7 +8775,8 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
m_prim_overlap = PrimitiveOverlap(false);
EmulateZbufferAA1();
// Do AA1 setup early so we can mask depth if possible.
EmulateAA1();
if (rt)
{
@@ -8773,7 +8807,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
EmulateDATESelectMethod(date_options, rt, blend_alpha_min, blend_alpha_max);
// Before emulateblending, dither will be used
m_conf.ps.dither = GSConfig.Dithering > 0 && m_conf.ps.dst_fmt == GSLocalMemory::PSM_FMT_16 && env.DTHE.DTHE;
m_conf.ps.dither = GSConfig.Dithering > 0 && m_conf.ps.dst_fmt == GSLocalMemory::PSM_FMT_16 && !!env.DTHE.DTHE;
if (m_conf.ps.dst_fmt == GSLocalMemory::PSM_FMT_24)
{
@@ -8834,7 +8868,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
}
// Similar to IsRTWritten(), check if the rt will change.
const bool no_rt = !rt || !(m_conf.colormask.wrgba || m_channel_shuffle);
const bool no_rt = !rt || m_conf.colormask.wrgba == 0;
const bool no_ds = !ds ||
// Depth will be written through the RT.
(!no_rt && m_cached_ctx.FRAME.FBP == m_cached_ctx.ZBUF.ZBP && !PRIM->TME && m_cached_ctx.ZBUF.ZMSK == 0 &&
@@ -8927,9 +8961,24 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
HandleProvokingVertexFirst();
SetupIA(rtscale, vs_scale_x, vs_scale_y, m_channel_shuffle_width != 0);
SetupIA(rtscale, vs_scale_x, vs_scale_y, m_channel_shuffle_width != 0, no_rt);
StartDepthAsRTFeedback(); // Depends on the drawarea and alpha test having been determined.
if (m_conf.ds && m_conf.ps.IsFeedbackLoopDepth() && !g_gs_device->Features().depth_feedback)
{
GL_PUSH("HW: Creating temporary R32 RT for depth feedback");
// Should not be hw blending with multiple render targets.
pxAssert(!m_conf.blend.enable && !m_conf.blend_multi_pass.blend.enable);
// We should have depth output or feedback doesn't make sense.
// We will output to both the depth buffer and color clone simultaneously in the shader.
pxAssert(m_conf.depth.zwe);
// HW depth test should be disabled in place of SW depth test
pxAssert(m_conf.depth.ztst == ZTST_ALWAYS);
// Second pass alpha shouldn't be enabled
pxAssert(!m_conf.alpha_second_pass.enable);
g_gs_device->BeginDSAsRT(m_conf.ds, m_conf.drawarea);
}
if (GSConfig.SaveHWConfig && GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
{
@@ -8941,7 +8990,8 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
else
m_last_rt = rt;
CleanupDepthAsRTFeedback();
if (g_gs_device->IsDSInRTActive())
g_gs_device->EndDSAsRT();
}
// If the EE uploaded a new CLUT since the last draw, use that.
@@ -9714,12 +9764,12 @@ bool GSRendererHW::TryGSMemClear(bool no_rt, bool preserve_rt, bool invalidate_r
rt_end_bp, m_cached_ctx.FRAME.PSM, m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.FBMSK);
GSUploadQueue clear_queue;
clear_queue.transfer_type = EEGS_TransferType::Clear;
clear_queue.draw = s_n;
clear_queue.rect = m_r;
clear_queue.blit.DBP = m_cached_ctx.FRAME.Block();
clear_queue.blit.DBW = m_cached_ctx.FRAME.FBW;
clear_queue.blit.DPSM = m_cached_ctx.FRAME.PSM;
clear_queue.zero_clear = true;
m_draw_transfers.push_back(clear_queue);
}
else
@@ -9744,12 +9794,12 @@ bool GSRendererHW::TryGSMemClear(bool no_rt, bool preserve_rt, bool invalidate_r
ds_end_bp, m_cached_ctx.ZBUF.PSM, m_cached_ctx.FRAME.FBW);
GSUploadQueue clear_queue;
clear_queue.transfer_type = EEGS_TransferType::Clear;
clear_queue.draw = s_n;
clear_queue.rect = m_r;
clear_queue.blit.DBP = m_cached_ctx.ZBUF.Block();
clear_queue.blit.DBW = m_cached_ctx.FRAME.FBW;
clear_queue.blit.DPSM = m_cached_ctx.ZBUF.PSM;
clear_queue.zero_clear = true;
m_draw_transfers.push_back(clear_queue);
}
}
@@ -10459,52 +10509,7 @@ std::size_t GSRendererHW::ComputeDrawlistGetSize(float scale)
return m_drawlist.size();
}
void GSRendererHW::StartDepthAsRTFeedback()
{
// Create a temporary depth color target
if (m_conf.ds && m_conf.ps.IsFeedbackLoopDepth() &&
g_gs_device->Features().depth_feedback == GSDevice::DepthFeedbackSupport::DepthAsRT)
{
GL_PUSH("HW: Creating temporary R32 RT for depth feedback");
// Should not be hw blending with multiple render targets.
pxAssert(!m_conf.blend.enable && !m_conf.blend_multi_pass.blend.enable);
// We should have depth output or feedback doesn't make sense.
// We will output to both the depth buffer and color clone simultaneously in the shader.
pxAssert(m_conf.depth.zwe || (m_conf.alpha_second_pass.enable && m_conf.alpha_second_pass.depth.zwe));
// Disable HW depth test since we will be using SW depth test (if needed).
m_conf.depth.ztst = ZTST_ALWAYS;
if (m_conf.alpha_second_pass.enable && m_conf.alpha_second_pass.depth.zwe)
{
// Do the same with the alpha second pass.
m_conf.alpha_second_pass.depth.ztst = ZTST_ALWAYS;
}
// Create a temporary RT and copy the area needed for the draw.
const int w = m_conf.ds->GetWidth();
const int h = m_conf.ds->GetHeight();
m_conf.ds_as_rt = g_gs_device->CreateRenderTarget(w, h, GSTexture::Format::Float32, false, true);
const GSVector4 dRect(m_conf.drawarea);
const GSVector4 sRect(dRect.x / w, dRect.y / h, dRect.z / w, dRect.w / h);
g_gs_device->StretchRect(m_conf.ds, sRect, m_conf.ds_as_rt, dRect, ShaderConvert::FLOAT32_DEPTH_TO_COLOR, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1.0);
}
}
void GSRendererHW::CleanupDepthAsRTFeedback()
{
if (m_conf.ds_as_rt)
{
g_gs_device->Recycle(m_conf.ds_as_rt);
m_conf.ds_as_rt = nullptr;
}
}
bool GSRendererHW::IsCoverageAlphaSupported()
{
return IsCoverageAlpha() &&
((m_vt.m_primclass == GS_LINE_CLASS || m_vt.m_primclass == GS_TRIANGLE_CLASS) &&
g_gs_device->Features().aa1);
return IsCoverageAlpha() && IsRTWritten() && g_gs_device->Features().aa1;
}
+3 -6
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,10 +226,11 @@ private:
const TextureMinMaxResult& tmm);
void EmulateZbuffer(const GSTextureCache::Target* ds);
void EmulateZbufferAA1();
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);
@@ -402,8 +403,4 @@ public:
/// Does the current draw allow using AA1 coverage (if AA1 is enabled).
bool IsCoverageAlphaSupported() override;
/// Create a temporary color clone of depth for depth feedback (DX12 and GL only right now)
void StartDepthAsRTFeedback();
void CleanupDepthAsRTFeedback();
};
@@ -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);
}
+40 -18
View File
@@ -1340,6 +1340,14 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
return nullptr;
}
// Invalid values show up in dumps but it's unclear if it's a core issue or a bug in the games.
// Return a temporary source to prevent the lookup from corrupting the TC.
if (TEX0.TBW > 32 && !GSUtil::IsValidPSM(static_cast<int>(TEX0.PSM)))
{
GL_CACHE("TC: Invalid TEX0 (TBW=%d, PSM=%x), using temporary source.", TEX0.TBW, TEX0.PSM);
return CreateSource(TEX0, TEXA, CLAMP, nullptr, 0, 0, nullptr, nullptr, nullptr, SourceRegion(), true);
}
const GSVector2i compare_lod(lod ? *lod : GSVector2i(0, 0));
Source* src = nullptr;
@@ -2253,20 +2261,34 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const bool is_color, const
}
}
if (src->m_from_target && src->m_target_direct && src->m_region.HasEither())
if (src->m_from_target && src->m_target_direct)
{
if (src->m_from_target->m_TEX0.TBP0 == src->m_TEX0.TBP0)
GSVector4i req_rect = r;
// The read area might be offset but the start of the texture is at the beginning of the space.
req_rect.x = region.HasX() ? region.GetMinX() : 0;
req_rect.y = region.HasY() ? region.GetMinY() : 0;
const bool dirty_overlap = !src->m_from_target->m_dirty.GetTotalRect(src->m_from_target->m_TEX0, src->m_from_target->m_unscaled_size).rintersect(req_rect).rempty();
if (dirty_overlap)
src->m_from_target->Update();
if (src->m_region.HasEither())
{
src->m_region.bits = 0;
src->m_region.SetX(0, region.HasX() ? region.GetMaxX() : (1 << TEX0.TW));
src->m_region.SetY(0, region.HasY() ? region.GetMaxY() : (1 << TEX0.TH));
}
else if (src->m_TEX0.TBP0 > src->m_from_target->m_TEX0.TBP0)
{
GSVector4i dst_offset = TranslateAlignedRectByPage(src->m_from_target, src->m_TEX0.TBP0, src->m_TEX0.PSM, src->m_TEX0.TBW, GSVector4i(0, 0, 1, 1), false);
src->m_region.bits = 0;
src->m_region.SetX(dst_offset.x, dst_offset.x + (region.HasX() ? std::min(region.GetMaxX(), (1 << TEX0.TW)) : (1 << TEX0.TW)));
src->m_region.SetY(dst_offset.y, dst_offset.y + (region.HasY() ? std::min(region.GetMaxY(), (1 << TEX0.TH)) : (1 << TEX0.TH)));
if (src->m_from_target->m_TEX0.TBP0 == src->m_TEX0.TBP0)
{
src->m_region.bits = 0;
src->m_region.SetX(0, region.HasX() ? region.GetMaxX() : (1 << TEX0.TW));
src->m_region.SetY(0, region.HasY() ? region.GetMaxY() : (1 << TEX0.TH));
}
else if (src->m_TEX0.TBP0 > src->m_from_target->m_TEX0.TBP0)
{
GSVector4i dst_offset = TranslateAlignedRectByPage(src->m_from_target, src->m_TEX0.TBP0, src->m_TEX0.PSM, src->m_TEX0.TBW, GSVector4i(0, 0, 1, 1), false);
src->m_region.bits = 0;
src->m_region.SetX(dst_offset.x, dst_offset.x + (region.HasX() ? std::min(region.GetMaxX(), (1 << TEX0.TW)) : (1 << TEX0.TW)));
src->m_region.SetY(dst_offset.y, dst_offset.y + (region.HasY() ? std::min(region.GetMaxY(), (1 << TEX0.TH)) : (1 << TEX0.TH)));
}
}
}
@@ -2341,7 +2363,7 @@ void GSTextureCache::CombineAlignedInsideTargets(Target* target, GSTextureCache:
const GSVector4 target_drect = GSVector4(target_drect_unscaled) * target->m_scale;
const bool valid_color = t->m_valid_rgb;
const bool valid_alpha = (t->m_valid_alpha_high | t->m_valid_alpha_low) && (GSUtil::GetChannelMask(t->m_TEX0.PSM) & 0x8);
const bool valid_alpha = (t->m_valid_alpha_high || t->m_valid_alpha_low) && (GSUtil::GetChannelMask(t->m_TEX0.PSM) & 0x8);
target->m_valid_alpha_high |= t->m_valid_alpha_high;
target->m_valid_alpha_low |= t->m_valid_alpha_low;
@@ -3474,14 +3496,15 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
else
eerect = eerect.runion(targetr);
const bool transfer_is_clear = iter->transfer_type == GSRendererHW::GetInstance()->EEGS_TransferType::Clear;
// Later writes might be partial over a previously cleared area. We want to upload in these cases.
hw_clear = hw_clear.has_value() ? (hw_clear.value() && iter->zero_clear) : iter->zero_clear;
hw_clear = hw_clear.has_value() ? (hw_clear.value() && transfer_is_clear) : transfer_is_clear;
// When the write covers the entire target, don't bother checking any earlier writes.
if (iter->blit.DBP <= TEX0.TBP0 && transfer_end >= rect_end)
{
// If it was a clear draw then we can use that as our target size.
if (iter->zero_clear && iter->blit.DBP == TEX0.TBP0 && iter->blit.DPSM == TEX0.PSM)
if (iter->transfer_type == GSRendererHW::GetInstance()->EEGS_TransferType::Clear && iter->blit.DBP == TEX0.TBP0 && iter->blit.DPSM == TEX0.PSM)
dst->UpdateValidity(iter->rect);
// Some games clear RT and Z at the same time, only erase if it's specifically this target.
@@ -3520,7 +3543,7 @@ bool GSTextureCache::PreloadTarget(GIFRegTEX0 TEX0, const GSVector2i& size, cons
if (iter->draw < (GSState::s_n - 1))
break;
if (iter->zero_clear && iter->blit.DBP == TEX0.TBP0 && iter->blit.DBW == TEX0.TBW)
if (iter->transfer_type == GSRendererHW::GetInstance()->EEGS_TransferType::Clear && iter->blit.DBP == TEX0.TBP0 && iter->blit.DBW == TEX0.TBW)
{
hw_clear = true;
transfers.erase(std::next(iter).base());
@@ -3855,7 +3878,6 @@ GSTextureCache::Target* GSTextureCache::LookupDisplayTarget(GIFRegTEX0 TEX0, con
const u32 bp = TEX0.TBP0;
Target* dst = nullptr;
Target* dst_match = nullptr;
auto* list = &m_dst[RenderTarget];
// Let's try to find a perfect frame that contains valid data
@@ -4011,7 +4033,7 @@ GSTextureCache::Target* GSTextureCache::LookupDisplayTarget(GIFRegTEX0 TEX0, con
else
eerect = eerect.runion(targetr);
if (iter->zero_clear && iter->draw == last_draw)
if (iter->transfer_type == GSRendererHW::GetInstance()->EEGS_TransferType::Clear && iter->draw == last_draw)
{
can_create = false;
break;
+1 -1
View File
@@ -263,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);
+18 -8
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
@@ -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);
+189 -49
View File
@@ -95,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)
@@ -245,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
@@ -388,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
@@ -449,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)
@@ -474,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]);
@@ -485,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!");
}
@@ -838,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];
@@ -849,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))
@@ -935,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"];
@@ -976,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
@@ -1055,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
@@ -1266,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;
}}
@@ -1757,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)
@@ -1800,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;
@@ -1842,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);
@@ -1886,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)));
@@ -1920,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);
@@ -1973,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
@@ -1984,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())
@@ -2123,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)
@@ -2135,18 +2256,27 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
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;
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);
@@ -2266,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))
@@ -2274,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)
@@ -2306,7 +2443,7 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
Recycle(colclip_rt);
g_gs_device->SetColorClipTexture(nullptr);
SetColorClipTexture(nullptr);
}
}
@@ -2314,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;
@@ -2326,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;
}
@@ -2366,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;
}
@@ -2386,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);
}
+9 -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;
};
+40 -12
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)
{
@@ -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
@@ -154,6 +157,7 @@ enum GSMTLFnConstants
{
GSMTLConstantIndex_CAS_SHARPEN_ONLY,
GSMTLConstantIndex_FRAMEBUFFER_FETCH,
GSMTLConstantIndex_DEPTH_FEEDBACK,
GSMTLConstantIndex_FST,
GSMTLConstantIndex_IIP,
GSMTLConstantIndex_VS_POINT_SIZE,
@@ -168,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,
@@ -212,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
+68 -105
View File
@@ -845,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)
@@ -909,7 +883,7 @@ 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.depth_feedback != GSDevice::DepthFeedbackSupport::None);
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand && m_features.feedback_loops();
return true;
}
@@ -1200,15 +1174,11 @@ 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)),
@@ -1217,8 +1187,6 @@ void GSDeviceOGL::DrawIndexedPrimitive(int offset, int count)
void GSDeviceOGL::DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_indexing, int vs_indexing_expansion)
{
//ASSERT(offset + count <= (int)m_index.count);
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
if (vs_indexing)
{
@@ -1233,6 +1201,25 @@ void GSDeviceOGL::DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_in
}
}
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);
@@ -1374,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;
}
@@ -1477,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
@@ -1602,6 +1575,7 @@ std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
+ 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);
@@ -2769,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:
@@ -2784,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;
@@ -2829,10 +2803,9 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
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))
@@ -2848,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.
@@ -2898,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;
@@ -2906,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.
@@ -2926,7 +2899,7 @@ 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.
@@ -2958,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.");
}
@@ -2981,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)
@@ -3007,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)
@@ -3033,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)
@@ -3059,27 +3043,6 @@ void GSDeviceOGL::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)
{
const bool vs_expand = config.vs.expand != GSHWDrawConfig::VSExpand::None;
const bool vs_indexing = config.vs.UseVSExpandIndexBuffer();
const u32 vs_indexing_expansion = GetExpansionFactor(config.vs.expand);
auto Draw = [&](int offset, int count) {
if (vs_expand)
{
DrawIndexedPrimitiveVSExpand(offset, count, vs_indexing, vs_indexing_expansion);
}
else
{
DrawIndexedPrimitive(offset, count);
}
};
if (!m_features.texture_barrier) [[unlikely]]
{
Draw(0, m_index.count);
return;
}
#ifdef PCSX2_DEVBUILD
if ((one_barrier || full_barrier) && !(config.ps.IsFeedbackLoopRT() || config.ps.IsFeedbackLoopDepth())) [[unlikely]]
Console.Warning("OpenGL: Possible unnecessary barrier detected.");
@@ -3129,7 +3092,7 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config,
CopyAndBind(ProcessCopyArea(rtsize, original_bbox));
}
Draw(p, count);
Draw(config, p, count);
p += count;
}
@@ -3150,7 +3113,7 @@ void GSDeviceOGL::SendHWDraw(const GSHWDrawConfig& config,
}
}
Draw(0, m_index.count);
Draw(config);
}
// Note: used as a callback of DebugMessageCallback. Don't change the signature
+6 -1
View File
@@ -318,10 +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 = false, int vs_indexing_expansion = 1);
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;
+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());
+25 -2
View File
@@ -468,7 +468,25 @@ void GSRendererSW::Draw()
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);
@@ -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;
}
@@ -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)
+1 -1
View File
@@ -82,7 +82,7 @@ protected:
template <u32 primclass>
void RewriteVerticesIfSTOverflow();
bool IsCoverageAlphaSupported() override { return true; }
bool IsCoverageAlphaSupported() override;
public:
GSRendererSW(int threads);
~GSRendererSW() override;
+34 -41
View File
@@ -461,7 +461,6 @@ bool GSDeviceVK::SelectDeviceFeatures()
m_device_features.wideLines = available_features.wideLines;
m_device_features.fragmentStoresAndAtomics = available_features.fragmentStoresAndAtomics;
m_device_features.textureCompressionBC = available_features.textureCompressionBC;
m_device_features.samplerAnisotropy = available_features.samplerAnisotropy;
m_device_features.geometryShader = available_features.geometryShader;
return true;
@@ -2702,18 +2701,8 @@ bool GSDeviceVK::CheckFeatures()
m_features.line_expand =
(m_device_features.wideLines && limits.lineWidthRange[0] <= f_upscale && limits.lineWidthRange[1] >= f_upscale);
if (m_features.texture_barrier && (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.aa1 = GSConfig.HWAA1 && m_features.vs_expand && (m_features.depth_feedback != GSDevice::DepthFeedbackSupport::None);
m_features.depth_feedback = m_features.feedback_loops();
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand && m_features.feedback_loops();
DevCon.WriteLn("Optional features:%s%s%s%s%s", m_features.primitive_id ? " primitive_id" : "",
m_features.texture_barrier ? " texture_barrier" : "", m_features.framebuffer_fetch ? " framebuffer_fetch" : "",
@@ -2767,8 +2756,7 @@ void GSDeviceVK::DrawPrimitive()
void GSDeviceVK::DrawIndexedPrimitive()
{
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
vkCmdDrawIndexed(GetCurrentCommandBuffer(), m_index.count, 1, m_index.start, m_vertex.start, 0);
DrawIndexedPrimitive(0, m_index.count);
}
void GSDeviceVK::DrawIndexedPrimitive(int offset, int count)
@@ -2795,6 +2783,25 @@ void GSDeviceVK::DrawIndexedPrimitiveVSExpand(int offset, int count, bool vs_ind
}
}
void GSDeviceVK::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 GSDeviceVK::Draw(const GSHWDrawConfig& config)
{
Draw(config, 0, m_index.count);
}
VkFormat GSDeviceVK::LookupNativeFormat(GSTexture::Format format) const
{
static constexpr std::array<VkFormat, static_cast<int>(GSTexture::Format::Last) + 1> s_format_mapping = {{
@@ -3656,8 +3663,6 @@ VkSampler GSDeviceVK::GetSampler(GSHWDrawConfig::SamplerSelector ss)
if (it != m_samplers.end())
return it->second;
const bool aniso = (ss.aniso && GSConfig.MaxAnisotropy > 1 && m_device_features.samplerAnisotropy);
// See https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkSamplerCreateInfo.html#_description
// for the reasoning behind 0.25f here.
const VkSamplerCreateInfo ci = {
@@ -3671,8 +3676,8 @@ VkSampler GSDeviceVK::GetSampler(GSHWDrawConfig::SamplerSelector ss)
ss.tav ? VK_SAMPLER_ADDRESS_MODE_REPEAT : VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE), // v
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // w
0.0f, // lod bias
static_cast<VkBool32>(aniso), // anisotropy enable
aniso ? static_cast<float>(GSConfig.MaxAnisotropy) : 1.0f, // anisotropy
VK_FALSE, // anisotropy enable
1.0f, // anisotropy
VK_FALSE, // compare enable
VK_COMPARE_OP_ALWAYS, // compare op
0.0f, // min lod
@@ -4902,6 +4907,7 @@ VkShaderModule GSDeviceVK::GetTFXFragmentShader(const GSHWDrawConfig::PSSelector
AddMacro(ss, "PS_ZTST", sel.ztst);
AddMacro(ss, "PS_AA1", static_cast<u32>(sel.aa1));
AddMacro(ss, "PS_ABE", sel.abe);
AddMacro(ss, "PS_ANISOTROPIC_FILTERING", sel.sw_aniso);
ss << m_tfx_source;
@@ -5756,7 +5762,7 @@ GSTextureVK* GSDeviceVK::SetupPrimitiveTrackingDATE(GSHWDrawConfig& config)
pipe.ps.no_color = false;
pipe.ps.no_color1 = true;
if (BindDrawPipeline(pipe))
DrawIndexedPrimitive();
Draw(config);
// image is initialized/prepass is done, so finish up and get ready to do the "real" draw
EndRenderPass();
@@ -5886,6 +5892,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
}
// Destination Alpha Setup
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
@@ -5895,7 +5902,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
case GSHWDrawConfig::DestinationAlphaMode::StencilOne: // setup is done below
{
// we only need to do the setup here if we don't have barriers, in which case do full DATE.
if (!m_features.texture_barrier)
if (!need_barrier)
{
SetupDATE(draw_rt, config.ds, config.datm, config.drawarea);
config.destination_alpha = GSHWDrawConfig::DestinationAlphaMode::Stencil;
@@ -5985,7 +5992,8 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
pipe.feedback_loop_flags |= m_current_framebuffer_feedback_loop;
}
if (draw_rt && (config.require_one_barrier || (config.tex && config.tex == config.rt)) && !m_features.texture_barrier)
if (draw_rt && ((config.require_one_barrier && (config.ps.IsFeedbackLoopRT() || config.alpha_second_pass.ps.IsFeedbackLoopRT())) ||
(config.tex && config.tex == config.rt)) && !m_features.texture_barrier)
{
// Requires a copy of the RT.
draw_rt_clone = static_cast<GSTextureVK*>(CreateTexture(rtsize.x, rtsize.y, 1, draw_rt->GetFormat(), true));
@@ -6105,7 +6113,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
if (BindDrawPipeline(pipe))
{
// TODO: This probably should have barriers, in case we want to use it conditionally.
DrawIndexedPrimitive();
Draw(config);
}
}
@@ -6273,24 +6281,9 @@ VkDependencyFlags GSDeviceVK::GetFeedbackBarrierDependencyFlags() const
void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt, GSTextureVK* draw_ds,
bool one_barrier, bool full_barrier)
{
const bool vs_expand = config.vs.expand != GSHWDrawConfig::VSExpand::None;
const bool vs_indexing = config.vs.UseVSExpandIndexBuffer();
const u32 vs_indexing_expansion = GetExpansionFactor(config.vs.expand);
auto Draw = [&](int offset, int count) {
if (vs_expand)
{
DrawIndexedPrimitiveVSExpand(offset, count, vs_indexing, vs_indexing_expansion);
}
else
{
DrawIndexedPrimitive(offset, count);
}
};
if (!m_features.texture_barrier) [[unlikely]]
{
Draw(0, m_index.count);
Draw(config);
return;
}
@@ -6346,7 +6339,7 @@ void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt,
IssueBarriers();
const u32 count = (*config.drawlist)[n] * indices_per_prim;
Draw(p, count);
Draw(config, p, count);
p += count;
}
@@ -6359,5 +6352,5 @@ void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt,
IssueBarriers();
}
Draw(0, m_index.count);
Draw(config);
}
+6 -1
View File
@@ -533,10 +533,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 = false, int vs_indexing_expansion = 1);
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;
+85 -31
View File
@@ -649,7 +649,7 @@ void FullscreenUI::Render()
if (s_about_window_open)
DrawAboutWindow();
if (s_achievements_login_open)
if (s_achievements_login_open || ImGui::IsPopupOpen("RetroAchievements"))
DrawAchievementsLoginWindow();
if (s_input_binding_type != InputBindingInfo::Type::Unknown)
@@ -3499,14 +3499,18 @@ bool FullscreenUI::OpenAchievementsWindow()
void FullscreenUI::DrawAchievementsLoginWindow()
{
ImGui::SetNextWindowSize(LayoutScale(400.0f, 330.0f));
if (s_achievements_login_open && !ImGui::IsPopupOpen("RetroAchievements"))
ImGui::OpenPopup("RetroAchievements");
const float dialog_width = std::clamp(LayoutScale(420.0f), 300.0f, ImGui::GetIO().DisplaySize.x);
ImGui::SetNextWindowSizeConstraints(ImVec2(dialog_width, 0.0f), ImVec2(dialog_width, ImGui::GetIO().DisplaySize.y));
ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(ImGuiFullscreen::LAYOUT_WINDOW_ROUNDING));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(24.0f, 24.0f));
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.13f, 0.13f, 0.13f, 0.95f));
if (ImGui::BeginPopupModal("RetroAchievements", &s_achievements_login_open, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar))
if (ImGui::BeginPopupModal("RetroAchievements", &s_achievements_login_open, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize))
{
const float content_width = ImGui::GetContentRegionAvail().x;
@@ -3553,7 +3557,7 @@ void FullscreenUI::DrawAchievementsLoginWindow()
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.4f, 0.4f, 0.4f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
if (s_achievements_login_logging_in)
if (s_achievements_login_logging_in || s_achievements_login_show_dismiss)
ImGui::BeginDisabled();
ImGui::SetNextItemWidth(content_width);
@@ -3567,7 +3571,7 @@ void FullscreenUI::DrawAchievementsLoginWindow()
ImGui::PopStyleColor(5);
ImGui::PopStyleVar(3);
if (s_achievements_login_logging_in)
if (s_achievements_login_logging_in || s_achievements_login_show_dismiss)
ImGui::EndDisabled();
ImGui::Spacing();
@@ -3582,20 +3586,67 @@ void FullscreenUI::DrawAchievementsLoginWindow()
ImGui::PopStyleColor();
ImGui::Spacing();
}
else if (s_achievements_login_show_dismiss)
{
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.35f, 0.9f, 0.35f, 1.0f));
const TinyString username = Host::GetBaseTinyStringSettingValue("Achievements", "Username", "");
const SmallString success_text = SmallString::from_format(
FSUI_FSTR("Successfully logged in as {}."), username.empty() ? "Unknown" : username.view());
const float status_width = ImGui::CalcTextSize(success_text.c_str()).x;
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (content_width - status_width) * 0.5f);
ImGui::TextUnformatted(success_text.c_str());
ImGui::PopStyleColor();
ImGui::Spacing();
}
const float button_height = LayoutScale(36.0f);
const float button_width = LayoutScale(100.0f);
const float button_height = std::max(LayoutScale(36.0f), 28.0f);
const float button_spacing = LayoutScale(12.0f);
const float total_width = (button_width * 2) + button_spacing;
const float start_x = (content_width - total_width) * 0.5f;
const float button_width = (content_width - button_spacing) * 0.5f;
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + start_x);
auto CloseLoginPopup = []() {
ImGui::CloseCurrentPopup();
s_achievements_login_open = false;
s_achievements_login_logging_in = false;
s_achievements_login_show_dismiss = false;
s_achievements_login_username[0] = '\0';
s_achievements_login_password[0] = '\0';
};
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, LayoutScale(ImGuiFullscreen::LAYOUT_FRAME_ROUNDING));
const bool can_login = !s_achievements_login_logging_in &&
strlen(s_achievements_login_username) > 0 &&
strlen(s_achievements_login_password) > 0;
const bool can_login = !s_achievements_login_show_dismiss && !s_achievements_login_logging_in &&
strlen(s_achievements_login_username) > 0 &&
strlen(s_achievements_login_password) > 0;
if (s_achievements_login_show_dismiss)
{
// keep dialog open and let user explicitly dismiss.
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.5f, 0.9f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.6f, 1.0f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.1f, 0.4f, 0.8f, 1.0f));
if (ImGui::Button(FSUI_CSTR("Dismiss"), ImVec2(button_width, button_height)) && !s_achievements_login_logging_in)
CloseLoginPopup();
ImGui::PopStyleColor(3);
ImGui::SameLine(0, button_spacing);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
ImGui::BeginDisabled();
ImGui::Button(FSUI_CSTR("Login"), ImVec2(button_width, button_height));
ImGui::EndDisabled();
ImGui::PopStyleColor(3);
ImGui::PopStyleVar();
ImGui::EndPopup();
ImGui::PopStyleColor();
ImGui::PopStyleVar(2);
return;
}
if (can_login)
{
@@ -3613,6 +3664,7 @@ void FullscreenUI::DrawAchievementsLoginWindow()
if (ImGui::Button(FSUI_CSTR("Login"), ImVec2(button_width, button_height)) && can_login)
{
s_achievements_login_logging_in = true;
s_achievements_login_show_dismiss = false;
Host::RunOnCPUThread([username = std::string(s_achievements_login_username),
password = std::string(s_achievements_login_password)]() {
@@ -3624,14 +3676,10 @@ void FullscreenUI::DrawAchievementsLoginWindow()
if (!result)
{
ShowToast(std::string(), fmt::format(FSUI_FSTR("Login failed.\nError: {}\n\nPlease check your username and password, and try again."),
error.GetDescription()));
error.GetDescription()));
return;
}
ImGui::CloseCurrentPopup();
s_achievements_login_open = false;
s_achievements_login_username[0] = '\0';
s_achievements_login_password[0] = '\0';
if (s_achievements_login_reason == Achievements::LoginRequestReason::UserInitiated)
@@ -3683,11 +3731,12 @@ void FullscreenUI::DrawAchievementsLoginWindow()
});
}
}
s_achievements_login_show_dismiss = true;
});
}
ImGui::PopStyleColor(3);
ImGui::SameLine(0, button_spacing);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.4f, 0.4f, 1.0f));
@@ -3702,12 +3751,7 @@ void FullscreenUI::DrawAchievementsLoginWindow()
Achievements::DisableHardcoreMode();
}
ImGui::CloseCurrentPopup();
s_achievements_login_open = false;
s_achievements_login_logging_in = false;
s_achievements_login_username[0] = '\0';
s_achievements_login_password[0] = '\0';
CloseLoginPopup();
}
ImGui::PopStyleColor(3);
@@ -3718,9 +3762,6 @@ void FullscreenUI::DrawAchievementsLoginWindow()
ImGui::PopStyleColor();
ImGui::PopStyleVar(2);
if (!ImGui::IsPopupOpen("RetroAchievements"))
ImGui::OpenPopup("RetroAchievements");
}
bool FullscreenUI::IsAchievementsWindowOpen()
@@ -3960,11 +4001,14 @@ void FullscreenUI::DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& se
"sounds/achievements/lbsubmit.wav", FSUI_ICONSTR(ICON_FA_FOLDER_OPEN, "Select Leaderboard Submit Sound"));
MenuHeading(FSUI_CSTR("Account"));
if (bsi->ContainsValue("Achievements", "Token"))
SettingsInterface* secrets_si = Host::Internal::GetSecretsSettingsLayer();
const TinyString username = bsi->GetTinyStringValue("Achievements", "Username", "");
const bool has_token = (secrets_si && secrets_si->ContainsValue("Achievements", "Token"));
if (has_token)
{
ImGui::PushStyleColor(ImGuiCol_TextDisabled, ImGui::GetStyle().Colors[ImGuiCol_Text]);
ActiveButton(SmallString::from_format(
fmt::runtime(FSUI_ICONSTR(ICON_FA_USER, "Username: {}")), bsi->GetTinyStringValue("Achievements", "Username")),
fmt::runtime(FSUI_ICONSTR(ICON_FA_USER, "Username: {}")), username.empty() ? "Unknown" : username.view()),
false, false, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY);
ActiveButton(SmallString::from_format(fmt::runtime(FSUI_ICONSTR(ICON_FA_CLOCK, "Login token generated on {}")),
TimeToPrintableString(static_cast<time_t>(
@@ -3984,6 +4028,7 @@ void FullscreenUI::DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& se
if (MenuButton(FSUI_ICONSTR(ICON_FA_KEY, "Login"), FSUI_CSTR("Logs in to RetroAchievements.")))
{
s_achievements_login_reason = Achievements::LoginRequestReason::UserInitiated;
s_achievements_login_show_dismiss = false;
s_achievements_login_open = true;
}
}
@@ -4117,6 +4162,9 @@ TRANSLATE_NOOP("FullscreenUI", "Reset System");
TRANSLATE_NOOP("FullscreenUI", "Hardcore mode will not be enabled until the system is reset. Do you want to reset the system now?");
TRANSLATE_NOOP("FullscreenUI", "This game has no achievements.");
TRANSLATE_NOOP("FullscreenUI", "This game has no leaderboards.");
TRANSLATE_NOOP("FullscreenUI", "No file selected.");
TRANSLATE_NOOP("FullscreenUI", "Sound reset to default.");
TRANSLATE_NOOP("FullscreenUI", "Sound is already using default.");
TRANSLATE_NOOP("FullscreenUI", "Failed to Load State");
TRANSLATE_NOOP("FullscreenUI", "Failed to Save State");
TRANSLATE_NOOP("FullscreenUI", "Game List");
@@ -4164,12 +4212,12 @@ TRANSLATE_NOOP("FullscreenUI", "Forces a full rescan of all games previously ide
TRANSLATE_NOOP("FullscreenUI", "About PCSX2");
TRANSLATE_NOOP("FullscreenUI", "PCSX2 is a free and open-source PlayStation 2 (PS2) emulator. Its purpose is to emulate the PS2's hardware, using a combination of MIPS CPU Interpreters, Recompilers and a Virtual Machine which manages hardware states and PS2 system memory. This allows you to play PS2 games on your PC, with many additional features and benefits.");
TRANSLATE_NOOP("FullscreenUI", "PlayStation 2 and PS2 are registered trademarks of Sony Interactive Entertainment. This application is not affiliated in any way with Sony Interactive Entertainment.");
TRANSLATE_NOOP("FullscreenUI", "Version: %s");
TRANSLATE_NOOP("FullscreenUI", "RetroAchievements");
TRANSLATE_NOOP("FullscreenUI", "Please enter your user name and password for retroachievements.org below.\n\nYour password will not be saved in PCSX2, an access token will be generated and used instead.");
TRANSLATE_NOOP("FullscreenUI", "Username");
TRANSLATE_NOOP("FullscreenUI", "Password");
TRANSLATE_NOOP("FullscreenUI", "Logging in...");
TRANSLATE_NOOP("FullscreenUI", "Dismiss");
TRANSLATE_NOOP("FullscreenUI", "Login");
TRANSLATE_NOOP("FullscreenUI", "Cancel");
TRANSLATE_NOOP("FullscreenUI", "When enabled and logged in, PCSX2 will scan for achievements on startup.");
@@ -4207,7 +4255,10 @@ TRANSLATE_NOOP("FullscreenUI", "Time Played: {}");
TRANSLATE_NOOP("FullscreenUI", "Last Played: {}");
TRANSLATE_NOOP("FullscreenUI", "Size: {:.2f} MB");
TRANSLATE_NOOP("FullscreenUI", "Are you sure you want to reset the play time for '{}' ({})?\n\nYour current play time is {}.\n\nThis action cannot be undone.");
TRANSLATE_NOOP("FullscreenUI", "Version: {}");
TRANSLATE_NOOP("FullscreenUI", "Successfully logged in as {}.");
TRANSLATE_NOOP("FullscreenUI", "Login failed.\nError: {}\n\nPlease check your username and password, and try again.");
TRANSLATE_NOOP("FullscreenUI", "Failed to preview sound:\n{}");
TRANSLATE_NOOP("FullscreenUI", "Failed to Load State From Backup Slot {}");
TRANSLATE_NOOP("FullscreenUI", "Failed to Load State From Slot {}");
TRANSLATE_NOOP("FullscreenUI", "Failed to Save State To Slot {}");
@@ -4289,6 +4340,9 @@ TRANSLATE_NOOP("FullscreenUI", "Notification Position");
TRANSLATE_NOOP("FullscreenUI", "Encore Mode");
TRANSLATE_NOOP("FullscreenUI", "Spectator Mode");
TRANSLATE_NOOP("FullscreenUI", "Test Unofficial Achievements");
TRANSLATE_NOOP("FullscreenUI", "Select File");
TRANSLATE_NOOP("FullscreenUI", "Preview");
TRANSLATE_NOOP("FullscreenUI", "Reset to Default");
TRANSLATE_NOOP("FullscreenUI", "Notification Sound");
TRANSLATE_NOOP("FullscreenUI", "Select Notification Sound");
TRANSLATE_NOOP("FullscreenUI", "Unlock Sound");
+1
View File
@@ -234,6 +234,7 @@ namespace FullscreenUI
// achievements login dialog state
inline bool s_achievements_login_open = false;
inline bool s_achievements_login_logging_in = false;
inline bool s_achievements_login_show_dismiss = false;
inline char s_achievements_login_username[256] = {};
inline char s_achievements_login_password[256] = {};
inline Achievements::LoginRequestReason s_achievements_login_reason = Achievements::LoginRequestReason::UserInitiated;
+69 -64
View File
@@ -2308,12 +2308,12 @@ void FullscreenUI::DrawInterfaceSettingsPage()
}
if (MenuButtonWithValue(FSUI_ICONSTR(ICON_FA_GAMEPAD, "Controller Glyph Style"),
FSUI_CSTR("Changes which gamepad button glyph set is used in Big Picture UI and input binding displays."),
(glyph_index < std::size(glyph_values)) ? FSUI_CSTR(glyph_names[glyph_index]) : FSUI_CSTR("Unknown")))
(glyph_index < std::size(glyph_values)) ? Host::TranslateToCString(TR_CONTEXT, glyph_names[glyph_index]) : FSUI_CSTR("Unknown")))
{
ImGuiFullscreen::ChoiceDialogOptions cd_options;
cd_options.reserve(std::size(glyph_values));
for (size_t i = 0; i < std::size(glyph_values); i++)
cd_options.emplace_back(FSUI_STR(glyph_names[i]), i == static_cast<size_t>(glyph_index));
cd_options.emplace_back(Host::TranslateToString(TR_CONTEXT, glyph_names[i]), i == static_cast<size_t>(glyph_index));
OpenChoiceDialog(FSUI_ICONSTR(ICON_FA_GAMEPAD, "Controller Glyph Style"), false, std::move(cd_options), [](s32 index, const std::string& title, bool checked) {
if (index >= 0 && static_cast<size_t>(index) < std::size(glyph_values))
@@ -2356,7 +2356,7 @@ void FullscreenUI::DrawInterfaceSettingsPage()
swap_summery.format(FSUI_FSTR("Uses {} as confirm when using a controller."),
glyphs.confirm(ImGui::GetIO().ConfigNavSwapGamepadButtons));
if (MenuButtonWithValue(FSUI_ICONSTR(ICON_FA_GAMEPAD, "Swap OK/Cancel in Big Picture Mode"), swap_summery.c_str(),
(swap_index < std::size(swap_values)) ? FSUI_CSTR(swap_names[swap_index]) : FSUI_CSTR("Unknown")))
(swap_index < std::size(swap_values)) ? Host::TranslateToCString(TR_CONTEXT, swap_names[swap_index]) : FSUI_CSTR("Unknown")))
{
ImGuiFullscreen::ChoiceDialogOptions cd_options;
cd_options.reserve(std::size(swap_values));
@@ -2390,7 +2390,7 @@ void FullscreenUI::DrawInterfaceSettingsPage()
swap_summery.format(FSUI_FSTR("Swaps both {}/{} (When Swap OK/Cancel is set to automatic) and {}/{} buttons"),
glyphs.south, glyphs.east, glyphs.west, glyphs.north);
if (MenuButtonWithValue(FSUI_ICONSTR(ICON_FA_GAMEPAD, "Use Legacy Nintendo Layout in Big Picture Mode"), swap_summery.c_str(),
(nintendo_index < std::size(swap_values)) ? FSUI_CSTR(swap_names[nintendo_index]) : FSUI_CSTR("Unknown")))
(nintendo_index < std::size(swap_values)) ? Host::TranslateToCString(TR_CONTEXT, swap_names[nintendo_index]) : FSUI_CSTR("Unknown")))
{
ImGuiFullscreen::ChoiceDialogOptions cd_options;
cd_options.reserve(std::size(swap_values));
@@ -5206,6 +5206,7 @@ TRANSLATE_NOOP("FullscreenUI", "Display a modal dialog when a save state load/sa
TRANSLATE_NOOP("FullscreenUI", "Determines whether a prompt will be displayed to confirm shutting down the emulator/game when the hotkey is pressed.");
TRANSLATE_NOOP("FullscreenUI", "Automatically saves the emulator state when powering down or exiting. You can then resume directly from where you left off next time.");
TRANSLATE_NOOP("FullscreenUI", "Creates a backup copy of a save state if it already exists when the save is created. The backup copy has a .backup suffix");
TRANSLATE_NOOP("FullscreenUI", "Changes which gamepad button glyph set is used in Big Picture UI and input binding displays.");
TRANSLATE_NOOP("FullscreenUI", "Integration");
TRANSLATE_NOOP("FullscreenUI", "Shows the game you are currently playing as part of your profile on Discord.");
TRANSLATE_NOOP("FullscreenUI", "Game Display");
@@ -5272,57 +5273,30 @@ TRANSLATE_NOOP("FullscreenUI", "Force a primitive flush when a framebuffer is al
TRANSLATE_NOOP("FullscreenUI", "Enables emulation of the GS's edge anti-aliasing (AA1).");
TRANSLATE_NOOP("FullscreenUI", "Hardware Fixes");
TRANSLATE_NOOP("FullscreenUI", "Disables automatic hardware fixes, allowing you to set fixes manually.");
TRANSLATE_NOOP("FullscreenUI", "CPU Sprite Render Size");
TRANSLATE_NOOP("FullscreenUI", "Uses software renderer to draw texture decompression-like sprites.");
TRANSLATE_NOOP("FullscreenUI", "CPU Sprite Render Level");
TRANSLATE_NOOP("FullscreenUI", "Determines filter level for CPU sprite render.");
TRANSLATE_NOOP("FullscreenUI", "Software CLUT Render");
TRANSLATE_NOOP("FullscreenUI", "Uses software renderer to draw texture CLUT points/sprites.");
TRANSLATE_NOOP("FullscreenUI", "GPU Target CLUT");
TRANSLATE_NOOP("FullscreenUI", "Try to detect when a game is drawing its own color palette and then renders it on the GPU with special handling.");
TRANSLATE_NOOP("FullscreenUI", "Skip Draw Start");
TRANSLATE_NOOP("FullscreenUI", "Object range to skip drawing.");
TRANSLATE_NOOP("FullscreenUI", "Skip Draw End");
TRANSLATE_NOOP("FullscreenUI", "Auto Flush (Hardware)");
TRANSLATE_NOOP("FullscreenUI", "CPU Framebuffer Conversion");
TRANSLATE_NOOP("FullscreenUI", "Convert 4-bit and 8-bit framebuffer on the CPU instead of the GPU.");
TRANSLATE_NOOP("FullscreenUI", "Disable Depth Conversion");
TRANSLATE_NOOP("FullscreenUI", "Disable the support of depth buffers in the texture cache.");
TRANSLATE_NOOP("FullscreenUI", "Disable Safe Features");
TRANSLATE_NOOP("FullscreenUI", "This option disables multiple safe features.");
TRANSLATE_NOOP("FullscreenUI", "Disable Render Fixes");
TRANSLATE_NOOP("FullscreenUI", "This option disables game-specific render fixes.");
TRANSLATE_NOOP("FullscreenUI", "Preload Frame Data");
TRANSLATE_NOOP("FullscreenUI", "Uploads GS data when rendering a new frame to reproduce some effects accurately.");
TRANSLATE_NOOP("FullscreenUI", "Disable Partial Invalidation");
TRANSLATE_NOOP("FullscreenUI", "Removes texture cache entries when there is any intersection, rather than only the intersected areas.");
TRANSLATE_NOOP("FullscreenUI", "Texture Inside RT");
TRANSLATE_NOOP("FullscreenUI", "Allows the texture cache to reuse as an input texture the inner portion of a previous framebuffer.");
TRANSLATE_NOOP("FullscreenUI", "Read Targets When Closing");
TRANSLATE_NOOP("FullscreenUI", "Flushes all targets in the texture cache back to local memory when shutting down.");
TRANSLATE_NOOP("FullscreenUI", "Estimate Texture Region");
TRANSLATE_NOOP("FullscreenUI", "Attempts to reduce the texture size when games do not set it themselves (e.g. Snowblind games).");
TRANSLATE_NOOP("FullscreenUI", "GPU Palette Conversion");
TRANSLATE_NOOP("FullscreenUI", "When enabled GPU converts colormap-textures, otherwise the CPU will. It is a trade-off between GPU and CPU.");
TRANSLATE_NOOP("FullscreenUI", "Upscaling Fixes");
TRANSLATE_NOOP("FullscreenUI", "Half Pixel Offset");
TRANSLATE_NOOP("FullscreenUI", "Adjusts vertices relative to upscaling.");
TRANSLATE_NOOP("FullscreenUI", "Native Scaling");
TRANSLATE_NOOP("FullscreenUI", "Attempt to do rescaling at native resolution.");
TRANSLATE_NOOP("FullscreenUI", "Round Sprite");
TRANSLATE_NOOP("FullscreenUI", "Adjusts sprite coordinates.");
TRANSLATE_NOOP("FullscreenUI", "Bilinear Dirty Upscale");
TRANSLATE_NOOP("FullscreenUI", "Can smooth out textures due to be bilinear filtered when upscaling. E.g. Brave sun glare.");
TRANSLATE_NOOP("FullscreenUI", "Texture Offset X");
TRANSLATE_NOOP("FullscreenUI", "Adjusts target texture offsets.");
TRANSLATE_NOOP("FullscreenUI", "Texture Offset Y");
TRANSLATE_NOOP("FullscreenUI", "Align Sprite");
TRANSLATE_NOOP("FullscreenUI", "Fixes issues with upscaling (vertical lines) in some games.");
TRANSLATE_NOOP("FullscreenUI", "Merge Sprite");
TRANSLATE_NOOP("FullscreenUI", "Replaces multiple post-processing sprites with a larger single sprite.");
TRANSLATE_NOOP("FullscreenUI", "Force Even Sprite Position");
TRANSLATE_NOOP("FullscreenUI", "Lowers the GS precision to avoid gaps between pixels when upscaling. Fixes the text on Wild Arms games.");
TRANSLATE_NOOP("FullscreenUI", "Unscaled Palette Texture Draws");
TRANSLATE_NOOP("FullscreenUI", "Can fix some broken effects which rely on pixel perfect precision.");
TRANSLATE_NOOP("FullscreenUI", "Texture Replacement");
TRANSLATE_NOOP("FullscreenUI", "Loads replacement textures where available and user-provided.");
@@ -5341,35 +5315,22 @@ TRANSLATE_NOOP("FullscreenUI", "Filters");
TRANSLATE_NOOP("FullscreenUI", "Enables brightness/contrast/gamma/saturation adjustment.");
TRANSLATE_NOOP("FullscreenUI", "Adjusts brightness. 50 is normal.");
TRANSLATE_NOOP("FullscreenUI", "Adjusts contrast. 50 is normal.");
TRANSLATE_NOOP("FullscreenUI", "Shade Boost Gamma");
TRANSLATE_NOOP("FullscreenUI", "Adjusts gamma. 50 is normal.");
TRANSLATE_NOOP("FullscreenUI", "Adjusts saturation. 50 is normal.");
TRANSLATE_NOOP("FullscreenUI", "Applies a shader which replicates the visual effects of different styles of television set.");
TRANSLATE_NOOP("FullscreenUI", "Advanced");
TRANSLATE_NOOP("FullscreenUI", "Skip Presenting Duplicate Frames");
TRANSLATE_NOOP("FullscreenUI", "Skips displaying frames that don't change in 25/30fps games. Can improve speed, but increase input lag/make frame pacing worse.");
TRANSLATE_NOOP("FullscreenUI", "Disable Mailbox Presentation");
TRANSLATE_NOOP("FullscreenUI", "Forces the use of FIFO over Mailbox presentation, i.e. double buffering instead of triple buffering. Usually results in worse frame pacing.");
TRANSLATE_NOOP("FullscreenUI", "Displays additional, very high upscaling multipliers dependent on GPU and driver capability.");
TRANSLATE_NOOP("FullscreenUI", "Hardware Download Mode");
TRANSLATE_NOOP("FullscreenUI", "Changes synchronization behavior for GS downloads.");
TRANSLATE_NOOP("FullscreenUI", "Allow Exclusive Fullscreen");
TRANSLATE_NOOP("FullscreenUI", "Overrides the driver's heuristics for enabling exclusive fullscreen, or direct flip/scanout.");
TRANSLATE_NOOP("FullscreenUI", "Override Texture Barriers");
TRANSLATE_NOOP("FullscreenUI", "Forces texture barrier functionality to the specified value.");
TRANSLATE_NOOP("FullscreenUI", "GS Dump Compression");
TRANSLATE_NOOP("FullscreenUI", "Sets the compression algorithm for GS dumps.");
TRANSLATE_NOOP("FullscreenUI", "Disable Framebuffer Fetch");
TRANSLATE_NOOP("FullscreenUI", "Prevents the usage of framebuffer fetch when supported by host GPU.");
TRANSLATE_NOOP("FullscreenUI", "Disable Shader Cache");
TRANSLATE_NOOP("FullscreenUI", "Prevents the loading and saving of shaders/pipelines to disk.");
TRANSLATE_NOOP("FullscreenUI", "Disable Vertex Shader Expand");
TRANSLATE_NOOP("FullscreenUI", "Falls back to the CPU for expanding sprites/lines.");
TRANSLATE_NOOP("FullscreenUI", "Texture Preloading");
TRANSLATE_NOOP("FullscreenUI", "Uploads full textures to the GPU on use, rather than only the utilized regions. Can improve performance in some games.");
TRANSLATE_NOOP("FullscreenUI", "NTSC Frame Rate");
TRANSLATE_NOOP("FullscreenUI", "Determines what frame rate NTSC games run at.");
TRANSLATE_NOOP("FullscreenUI", "PAL Frame Rate");
TRANSLATE_NOOP("FullscreenUI", "Determines what frame rate PAL games run at.");
TRANSLATE_NOOP("FullscreenUI", "On-Screen Display");
TRANSLATE_NOOP("FullscreenUI", "Determines how large the on-screen messages and monitors are.");
@@ -5377,7 +5338,6 @@ TRANSLATE_NOOP("FullscreenUI", "Determines the distance in pixels from the edges
TRANSLATE_NOOP("FullscreenUI", "Determines where on-screen display messages are positioned.");
TRANSLATE_NOOP("FullscreenUI", "Determines where performance statistics are positioned.");
TRANSLATE_NOOP("FullscreenUI", "Select OSD Font");
TRANSLATE_NOOP("FullscreenUI", "Clear OSD Font Override");
TRANSLATE_NOOP("FullscreenUI", "Use default bundled font");
TRANSLATE_NOOP("FullscreenUI", "Draws OSD text with heavier weight for improved readability.");
TRANSLATE_NOOP("FullscreenUI", "Shows the current PCSX2 version.");
@@ -5487,7 +5447,6 @@ TRANSLATE_NOOP("FullscreenUI", "Sets the compression level for savestate.");
TRANSLATE_NOOP("FullscreenUI", "Graphics");
TRANSLATE_NOOP("FullscreenUI", "Enables API-level validation of graphics commands.");
TRANSLATE_NOOP("FullscreenUI", "Settings");
TRANSLATE_NOOP("FullscreenUI", "Enable Cheats");
TRANSLATE_NOOP("FullscreenUI", "No cheats are available for this game.");
TRANSLATE_NOOP("FullscreenUI", "Cheat Codes");
TRANSLATE_NOOP("FullscreenUI", "No patches are available for this game.");
@@ -5497,41 +5456,23 @@ TRANSLATE_NOOP("FullscreenUI", "Activating game patches can cause unpredictable
TRANSLATE_NOOP("FullscreenUI", "Use patches at your own risk, the PCSX2 team will provide no support for users who have enabled game patches.");
TRANSLATE_NOOP("FullscreenUI", "Game Fixes");
TRANSLATE_NOOP("FullscreenUI", "Game fixes should not be modified unless you are aware of what each option does and the implications of doing so.");
TRANSLATE_NOOP("FullscreenUI", "FPU Multiply Hack");
TRANSLATE_NOOP("FullscreenUI", "For Tales of Destiny.");
TRANSLATE_NOOP("FullscreenUI", "Use Software Renderer For FMVs");
TRANSLATE_NOOP("FullscreenUI", "Needed for some games with complex FMV rendering.");
TRANSLATE_NOOP("FullscreenUI", "Skip MPEG Hack");
TRANSLATE_NOOP("FullscreenUI", "Skips videos/FMVs in games to avoid game hanging/freezes.");
TRANSLATE_NOOP("FullscreenUI", "Preload TLB Hack");
TRANSLATE_NOOP("FullscreenUI", "To avoid TLB miss on Goemon.");
TRANSLATE_NOOP("FullscreenUI", "EE Timing Hack");
TRANSLATE_NOOP("FullscreenUI", "General-purpose timing hack. Known to affect following games: Digital Devil Saga, SSX.");
TRANSLATE_NOOP("FullscreenUI", "Instant DMA Hack");
TRANSLATE_NOOP("FullscreenUI", "Good for cache emulation problems. Known to affect following games: Fire Pro Wrestling Z.");
TRANSLATE_NOOP("FullscreenUI", "OPH Flag Hack");
TRANSLATE_NOOP("FullscreenUI", "Known to affect following games: Bleach Blade Battlers, Growlanser II and III, Wizardry.");
TRANSLATE_NOOP("FullscreenUI", "Emulate GIF FIFO");
TRANSLATE_NOOP("FullscreenUI", "Correct but slower. Known to affect the following games: FIFA Street 2.");
TRANSLATE_NOOP("FullscreenUI", "DMA Busy Hack");
TRANSLATE_NOOP("FullscreenUI", "Known to affect following games: Mana Khemia 1, Metal Saga, Pilot Down Behind Enemy Lines.");
TRANSLATE_NOOP("FullscreenUI", "Delay VIF1 Stalls");
TRANSLATE_NOOP("FullscreenUI", "For SOCOM 2 HUD and Spy Hunter loading hang.");
TRANSLATE_NOOP("FullscreenUI", "Emulate VIF FIFO");
TRANSLATE_NOOP("FullscreenUI", "Simulate VIF1 FIFO read ahead. Known to affect following games: Test Drive Unlimited, Transformers.");
TRANSLATE_NOOP("FullscreenUI", "Full VU0 Synchronization");
TRANSLATE_NOOP("FullscreenUI", "Forces tight VU0 sync on every COP2 instruction.");
TRANSLATE_NOOP("FullscreenUI", "VU I Bit Hack");
TRANSLATE_NOOP("FullscreenUI", "Avoids constant recompilation in some games. Known to affect the following games: Scarface The World is Yours, Crash Tag Team Racing.");
TRANSLATE_NOOP("FullscreenUI", "VU Add Hack");
TRANSLATE_NOOP("FullscreenUI", "For Tri-Ace Games: Star Ocean 3, Radiata Stories, Valkyrie Profile 2.");
TRANSLATE_NOOP("FullscreenUI", "VU Overflow Hack");
TRANSLATE_NOOP("FullscreenUI", "To check for possible float overflows (Superman Returns).");
TRANSLATE_NOOP("FullscreenUI", "VU Sync");
TRANSLATE_NOOP("FullscreenUI", "Run behind. To avoid sync problems when reading or writing VU registers.");
TRANSLATE_NOOP("FullscreenUI", "VU XGKick Sync");
TRANSLATE_NOOP("FullscreenUI", "Use accurate timing for VU XGKicks (slower).");
TRANSLATE_NOOP("FullscreenUI", "Force Blit Internal FPS Detection");
TRANSLATE_NOOP("FullscreenUI", "Use alternative method to calculate internal FPS to avoid false readings in some games.");
TRANSLATE_NOOP("FullscreenUI", "{0}/{1}/{2}/{3}");
TRANSLATE_NOOP("FullscreenUI", "Automatic mapping completed for {}.");
@@ -5595,6 +5536,9 @@ TRANSLATE_NOOP("FullscreenUI", "Fill");
TRANSLATE_NOOP("FullscreenUI", "Stretch");
TRANSLATE_NOOP("FullscreenUI", "Center");
TRANSLATE_NOOP("FullscreenUI", "Tile");
TRANSLATE_NOOP("FullscreenUI", "Xbox");
TRANSLATE_NOOP("FullscreenUI", "PlayStation");
TRANSLATE_NOOP("FullscreenUI", "Nintendo");
TRANSLATE_NOOP("FullscreenUI", "Enabled");
TRANSLATE_NOOP("FullscreenUI", "Disabled");
TRANSLATE_NOOP("FullscreenUI", "2% [1 FPS (NTSC) / 1 FPS (PAL)]");
@@ -5818,6 +5762,7 @@ TRANSLATE_NOOP("FullscreenUI", "Prompt On State Load/Save Failure");
TRANSLATE_NOOP("FullscreenUI", "Confirm Shutdown");
TRANSLATE_NOOP("FullscreenUI", "Save State On Shutdown");
TRANSLATE_NOOP("FullscreenUI", "Create Save State Backups");
TRANSLATE_NOOP("FullscreenUI", "Controller Glyph Style");
TRANSLATE_NOOP("FullscreenUI", "Swap OK/Cancel in Big Picture Mode");
TRANSLATE_NOOP("FullscreenUI", "Use Legacy Nintendo Layout in Big Picture Mode");
TRANSLATE_NOOP("FullscreenUI", "Enable Discord Presence");
@@ -5835,6 +5780,7 @@ TRANSLATE_NOOP("FullscreenUI", "EE Cycle Rate");
TRANSLATE_NOOP("FullscreenUI", "EE Cycle Skipping");
TRANSLATE_NOOP("FullscreenUI", "Enable MTVU (Multi-Threaded VU1)");
TRANSLATE_NOOP("FullscreenUI", "Thread Pinning");
TRANSLATE_NOOP("FullscreenUI", "Enable Cheats");
TRANSLATE_NOOP("FullscreenUI", "Enable Host Filesystem");
TRANSLATE_NOOP("FullscreenUI", "Enable Fast CDVD");
TRANSLATE_NOOP("FullscreenUI", "Enable CDVD Precaching");
@@ -5869,6 +5815,33 @@ TRANSLATE_NOOP("FullscreenUI", "Software Rendering Threads");
TRANSLATE_NOOP("FullscreenUI", "Auto Flush (Software)");
TRANSLATE_NOOP("FullscreenUI", "Edge AA (AA1)");
TRANSLATE_NOOP("FullscreenUI", "Manual Hardware Fixes");
TRANSLATE_NOOP("FullscreenUI", "CPU Sprite Render Size");
TRANSLATE_NOOP("FullscreenUI", "CPU Sprite Render Level");
TRANSLATE_NOOP("FullscreenUI", "Software CLUT Render");
TRANSLATE_NOOP("FullscreenUI", "GPU Target CLUT");
TRANSLATE_NOOP("FullscreenUI", "Skip Draw Start");
TRANSLATE_NOOP("FullscreenUI", "Skip Draw End");
TRANSLATE_NOOP("FullscreenUI", "Auto Flush (Hardware)");
TRANSLATE_NOOP("FullscreenUI", "CPU Framebuffer Conversion");
TRANSLATE_NOOP("FullscreenUI", "Disable Depth Conversion");
TRANSLATE_NOOP("FullscreenUI", "Disable Safe Features");
TRANSLATE_NOOP("FullscreenUI", "Disable Render Fixes");
TRANSLATE_NOOP("FullscreenUI", "Preload Frame Data");
TRANSLATE_NOOP("FullscreenUI", "Disable Partial Invalidation");
TRANSLATE_NOOP("FullscreenUI", "Texture Inside RT");
TRANSLATE_NOOP("FullscreenUI", "Read Targets When Closing");
TRANSLATE_NOOP("FullscreenUI", "Estimate Texture Region");
TRANSLATE_NOOP("FullscreenUI", "GPU Palette Conversion");
TRANSLATE_NOOP("FullscreenUI", "Half Pixel Offset");
TRANSLATE_NOOP("FullscreenUI", "Native Scaling");
TRANSLATE_NOOP("FullscreenUI", "Round Sprite");
TRANSLATE_NOOP("FullscreenUI", "Bilinear Dirty Upscale");
TRANSLATE_NOOP("FullscreenUI", "Texture Offset X");
TRANSLATE_NOOP("FullscreenUI", "Texture Offset Y");
TRANSLATE_NOOP("FullscreenUI", "Align Sprite");
TRANSLATE_NOOP("FullscreenUI", "Merge Sprite");
TRANSLATE_NOOP("FullscreenUI", "Force Even Sprite Position");
TRANSLATE_NOOP("FullscreenUI", "Unscaled Palette Texture Draws");
TRANSLATE_NOOP("FullscreenUI", "Load Textures");
TRANSLATE_NOOP("FullscreenUI", "Asynchronous Texture Loading");
TRANSLATE_NOOP("FullscreenUI", "Precache Replacements");
@@ -5882,14 +5855,28 @@ TRANSLATE_NOOP("FullscreenUI", "CAS Sharpness");
TRANSLATE_NOOP("FullscreenUI", "Shade Boost");
TRANSLATE_NOOP("FullscreenUI", "Shade Boost Brightness");
TRANSLATE_NOOP("FullscreenUI", "Shade Boost Contrast");
TRANSLATE_NOOP("FullscreenUI", "Shade Boost Gamma");
TRANSLATE_NOOP("FullscreenUI", "Shade Boost Saturation");
TRANSLATE_NOOP("FullscreenUI", "TV Shaders");
TRANSLATE_NOOP("FullscreenUI", "Skip Presenting Duplicate Frames");
TRANSLATE_NOOP("FullscreenUI", "Disable Mailbox Presentation");
TRANSLATE_NOOP("FullscreenUI", "Extended Upscaling Multipliers");
TRANSLATE_NOOP("FullscreenUI", "Hardware Download Mode");
TRANSLATE_NOOP("FullscreenUI", "Allow Exclusive Fullscreen");
TRANSLATE_NOOP("FullscreenUI", "Override Texture Barriers");
TRANSLATE_NOOP("FullscreenUI", "GS Dump Compression");
TRANSLATE_NOOP("FullscreenUI", "Disable Framebuffer Fetch");
TRANSLATE_NOOP("FullscreenUI", "Disable Shader Cache");
TRANSLATE_NOOP("FullscreenUI", "Disable Vertex Shader Expand");
TRANSLATE_NOOP("FullscreenUI", "Texture Preloading");
TRANSLATE_NOOP("FullscreenUI", "NTSC Frame Rate");
TRANSLATE_NOOP("FullscreenUI", "PAL Frame Rate");
TRANSLATE_NOOP("FullscreenUI", "OSD Scale");
TRANSLATE_NOOP("FullscreenUI", "OSD Margin");
TRANSLATE_NOOP("FullscreenUI", "OSD Messages Position");
TRANSLATE_NOOP("FullscreenUI", "OSD Performance Position");
TRANSLATE_NOOP("FullscreenUI", "OSD Font File");
TRANSLATE_NOOP("FullscreenUI", "Clear OSD Font Override");
TRANSLATE_NOOP("FullscreenUI", "Bold OSD Text");
TRANSLATE_NOOP("FullscreenUI", "Show PCSX2 Version");
TRANSLATE_NOOP("FullscreenUI", "Show Speed");
@@ -6010,6 +5997,24 @@ TRANSLATE_NOOP("FullscreenUI", "Enable IOP Recompiler");
TRANSLATE_NOOP("FullscreenUI", "Compression Method");
TRANSLATE_NOOP("FullscreenUI", "Compression Level");
TRANSLATE_NOOP("FullscreenUI", "Use Debug Device");
TRANSLATE_NOOP("FullscreenUI", "FPU Multiply Hack");
TRANSLATE_NOOP("FullscreenUI", "Use Software Renderer For FMVs");
TRANSLATE_NOOP("FullscreenUI", "Skip MPEG Hack");
TRANSLATE_NOOP("FullscreenUI", "Preload TLB Hack");
TRANSLATE_NOOP("FullscreenUI", "EE Timing Hack");
TRANSLATE_NOOP("FullscreenUI", "Instant DMA Hack");
TRANSLATE_NOOP("FullscreenUI", "OPH Flag Hack");
TRANSLATE_NOOP("FullscreenUI", "Emulate GIF FIFO");
TRANSLATE_NOOP("FullscreenUI", "DMA Busy Hack");
TRANSLATE_NOOP("FullscreenUI", "Delay VIF1 Stalls");
TRANSLATE_NOOP("FullscreenUI", "Emulate VIF FIFO");
TRANSLATE_NOOP("FullscreenUI", "Full VU0 Synchronization");
TRANSLATE_NOOP("FullscreenUI", "VU I Bit Hack");
TRANSLATE_NOOP("FullscreenUI", "VU Add Hack");
TRANSLATE_NOOP("FullscreenUI", "VU Overflow Hack");
TRANSLATE_NOOP("FullscreenUI", "VU Sync");
TRANSLATE_NOOP("FullscreenUI", "VU XGKick Sync");
TRANSLATE_NOOP("FullscreenUI", "Force Blit Internal FPS Detection");
TRANSLATE_NOOP("FullscreenUI", "Memory Card Enabled");
TRANSLATE_NOOP("FullscreenUI", "Card Name");
TRANSLATE_NOOP("FullscreenUI", "Eject Card");
+1 -1
View File
@@ -709,7 +709,7 @@ __fi void psxRcntWmode32(int index, u32 value)
// Current counter *always* resets on mode write.
counter.count = 0;
counter.startCycle = psxRegs.cycle & ~(counter.rate - 1);
counter.startCycle = psxRegs.cycle & ~(static_cast<u64>(counter.rate - 1));
counter.target &= 0xffffffff;
_rcntSet(index);
}
+43 -36
View File
@@ -2195,51 +2195,58 @@ std::string EmuFolders::GetPortableModePath()
bool EmuFolders::SetDataDirectory(Error* error)
{
// Portable mode has the absolute priority.
if (!ShouldUsePortableMode())
{
// Also check if the user has overriden the DataRoot path.
if (EmuConfig.CustomDataPath.empty())
{
#if defined(_WIN32)
// On Windows, use My Documents\PCSX2 to match old installs.
PWSTR documents_directory;
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &documents_directory)))
{
if (std::wcslen(documents_directory) > 0)
DataRoot = Path::Combine(StringUtil::WideStringToUTF8String(documents_directory), "PCSX2");
CoTaskMemFree(documents_directory);
}
// On Windows, use My Documents\PCSX2 to match old installs.
PWSTR documents_directory;
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &documents_directory)))
{
if (std::wcslen(documents_directory) > 0)
DataRoot = Path::Combine(StringUtil::WideStringToUTF8String(documents_directory), "PCSX2");
CoTaskMemFree(documents_directory);
}
#elif defined(__linux__) || defined(__FreeBSD__)
// Use $XDG_CONFIG_HOME/PCSX2 if it exists.
const char* xdg_config_home = getenv("XDG_CONFIG_HOME");
if (xdg_config_home && Path::IsAbsolute(xdg_config_home))
{
DataRoot = Path::RealPath(Path::Combine(xdg_config_home, "PCSX2"));
}
else
{
// Use ~/PCSX2 for non-XDG, and ~/.config/PCSX2 for XDG.
// Use $XDG_CONFIG_HOME/PCSX2 if it exists.
const char* xdg_config_home = getenv("XDG_CONFIG_HOME");
if (xdg_config_home && Path::IsAbsolute(xdg_config_home))
{
DataRoot = Path::RealPath(Path::Combine(xdg_config_home, "PCSX2"));
}
else
{
// Use ~/PCSX2 for non-XDG, and ~/.config/PCSX2 for XDG.
const char* home_dir = getenv("HOME");
if (home_dir)
{
// ~/.config should exist, but just in case it doesn't and this is a fresh profile..
const std::string config_dir(Path::Combine(home_dir, ".config"));
if (!FileSystem::DirectoryExists(config_dir.c_str()))
FileSystem::CreateDirectoryPath(config_dir.c_str(), false);
DataRoot = Path::RealPath(Path::Combine(config_dir, "PCSX2"));
}
}
#elif defined(__APPLE__)
static constexpr char MAC_DATA_DIR[] = "Library/Application Support/PCSX2";
const char* home_dir = getenv("HOME");
if (home_dir)
{
// ~/.config should exist, but just in case it doesn't and this is a fresh profile..
const std::string config_dir(Path::Combine(home_dir, ".config"));
if (!FileSystem::DirectoryExists(config_dir.c_str()))
FileSystem::CreateDirectoryPath(config_dir.c_str(), false);
DataRoot = Path::RealPath(Path::Combine(config_dir, "PCSX2"));
}
}
#elif defined(__APPLE__)
static constexpr char MAC_DATA_DIR[] = "Library/Application Support/PCSX2";
const char* home_dir = getenv("HOME");
if (home_dir)
DataRoot = Path::RealPath(Path::Combine(home_dir, MAC_DATA_DIR));
DataRoot = Path::RealPath(Path::Combine(home_dir, MAC_DATA_DIR));
#endif
}
}
else // Otherwise use the custom path provided by the user
DataRoot = Path::RealPath(Path::Combine(EmuConfig.CustomDataPath, "PCSX2"));
}
// couldn't determine the data directory, or using portable mode? fallback to portable.
// Couldn't determine the data directory, or using portable mode? fallback to portable.
if (DataRoot.empty())
{
#if defined(__linux__)
// special check if we're on appimage
// Special check if we're on appimage
// always make sure that DataRoot
// is adjacent next to the appimage
if (getenv("APPIMAGE"))
@@ -2254,10 +2261,10 @@ bool EmuFolders::SetDataDirectory(Error* error)
#endif
}
// inis is always below the data root
// Inis is always below the data root
Settings = Path::Combine(DataRoot, "inis");
// make sure it exists
// Make sure it exists
Console.WriteLnFmt("DataRoot Directory: {}", DataRoot);
return (FileSystem::EnsureDirectoryExists(DataRoot.c_str(), false, error) &&
FileSystem::EnsureDirectoryExists(Settings.c_str(), false, error));
+1 -1
View File
@@ -3,4 +3,4 @@
/// Version number for GS and other shaders. Increment whenever any of the contents of the
/// shaders change, to invalidate the cache.
static constexpr u32 SHADER_CACHE_VERSION = 88; // Last changed in PR 14187
static constexpr u32 SHADER_CACHE_VERSION = 92; // Last changed in PR 14399
+91 -13
View File
@@ -29,6 +29,20 @@ namespace usb_msd
"00000000000PCSX2",
};
static const USBDescStrings olympus_camera_desc_strings = {
"",
"OLYMPUS",
"USB Mass Storage",
"00000000000PCSX2",
};
static const USBDescStrings olympus_mo_desc_strings = {
"",
"OLYMPUS",
"MOS3412",
"00000000000PCSX2",
};
static const USBDescStrings sony_msac_desc_strings = {
"",
"Sony",
@@ -38,14 +52,14 @@ namespace usb_msd
static const uint8_t zip100_dev_descriptor[] = {
0x12, // bLength
0x01, // bDescriptorType (Device)
0x10, 0x01, // bcdUSB 0.10
0x10, 0x01, // bcdUSB 1.10
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
0x40, // bMaxPacketSize0 8
0x9B, 0x05, // idVendor 0x059B
0x34, 0x00, // idProduct 0x0034
0x00, 0x01, // bcdDevice 0.00
0x00, 0x01, // bcdDevice 1.00
0x01, // iManufacturer (String Index)
0x02, // iProduct (String Index)
0x03, // iSerialNumber (String Index)
@@ -94,6 +108,23 @@ namespace usb_msd
0x20, // bInterval 32 (unit depends on device speed)
};
static const uint8_t olympus_dev_descriptor[] = {
0x12, // bLength
0x01, // bDescriptorType (Device)
0x10, 0x01, // bcdUSB 1.10
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
0x40, // bMaxPacketSize0 8
0xb4, 0x07, // idVendor 0x07b4
0x02, 0x01, // idProduct 0x0102
0x00, 0x01, // bcdDevice 1.00
0x01, // iManufacturer (String Index)
0x02, // iProduct (String Index)
0x03, // iSerialNumber (String Index)
0x01, // bNumConfigurations 1
};
static const uint8_t sony_msac_dev_descriptor[] = {
0x12, // bLength
0x01, // bDescriptorType (Device)
@@ -535,7 +566,7 @@ namespace usb_msd
len = std::min<size_t>(len, s->f.data_len);
//TODO No async reader/writer so do it right here
if (s->f.tag == s->f.file_op_tag)
if (s->f.file_op_tag)
{
switch (s->f.mode)
{
@@ -586,6 +617,7 @@ namespace usb_msd
s->f.result = COMMAND_PASSED;
s->f.off = 0;
s->f.file_op_tag = false;
if (cbw->cmd[0] != REQUEST_SENSE)
set_sense(s, SENSE_CODE(NO_SENSE));
@@ -698,7 +730,7 @@ namespace usb_msd
xfer_len = bswap32(*(uint32_t*)&cbw->cmd[6]);
s->f.data_len = xfer_len * LBA_BLOCK_SIZE;
s->f.file_op_tag = s->f.tag;
s->f.file_op_tag = true;
if (xfer_len == 0) // nothing to do
@@ -733,7 +765,7 @@ namespace usb_msd
xfer_len = bswap32(*(uint32_t*)&cbw->cmd[6]);
s->f.data_len = xfer_len * LBA_BLOCK_SIZE;
s->f.file_op_tag = s->f.tag;
s->f.file_op_tag = true;
if (xfer_len == 0) //nothing to do
break;
@@ -1125,6 +1157,26 @@ namespace usb_msd
s->dev.klass.handle_control = usb_msd_handle_control;
s->dev.klass.handle_data = usb_msd_handle_data;
break;
case OLYMPUS_CAMERA:
path = USB::GetConfigString(si, port, TypeName(), "ImagePathOlympusCamera");
s->desc.str = olympus_camera_desc_strings;
if (usb_desc_parse_dev(olympus_dev_descriptor, sizeof(olympus_dev_descriptor), s->desc, s->desc_dev) < 0)
goto fail;
if (usb_desc_parse_config(zip100_config_descriptor, sizeof(zip100_config_descriptor), s->desc_dev) < 0)
goto fail;
s->dev.klass.handle_control = usb_msd_handle_control;
s->dev.klass.handle_data = usb_msd_handle_data;
break;
case OLYMPUS_MO:
path = USB::GetConfigString(si, port, TypeName(), "ImagePathOlympusMD");
s->desc.str = olympus_mo_desc_strings;
if (usb_desc_parse_dev(olympus_dev_descriptor, sizeof(olympus_dev_descriptor), s->desc, s->desc_dev) < 0)
goto fail;
if (usb_desc_parse_config(zip100_config_descriptor, sizeof(zip100_config_descriptor), s->desc_dev) < 0)
goto fail;
s->dev.klass.handle_control = usb_msd_handle_control;
s->dev.klass.handle_data = usb_msd_handle_data;
break;
case SONY_MSAC_US1:
path = USB::GetConfigString(si, port, TypeName(), "ImagePathMsac");
s->desc.str = sony_msac_desc_strings;
@@ -1230,6 +1282,8 @@ namespace usb_msd
{
static const char* subtypes[] = {
TRANSLATE_NOOP("USB", "Iomega Zip-100 (Generic)"),
TRANSLATE_NOOP("USB", "Olympus Camera"),
TRANSLATE_NOOP("USB", "Olympus Turbo MO"),
TRANSLATE_NOOP("USB", "Sony MSAC-US1 (PictureParadise)")
};
return subtypes;
@@ -1241,18 +1295,42 @@ namespace usb_msd
{
case IOMEGA_ZIP_100:
{
static constexpr const SettingInfo settings[] = {
{SettingInfo::Type::Path, "ImagePathMsd", TRANSLATE_NOOP("USB", "Image Path"),
TRANSLATE_NOOP("USB", "Sets the path to the disk image which will back the virtual mass storage device.")},
};
static constexpr const SettingInfo settings[] = {{
.type = SettingInfo::Type::Path,
.name = "ImagePathMsd",
.display_name = TRANSLATE_NOOP("USB", "Image Path"),
.description = TRANSLATE_NOOP("USB", "Sets the path to the disk image which will back the virtual mass storage device.")
}};
return settings;
}
case OLYMPUS_CAMERA:
{
static constexpr const SettingInfo settings[] = {{
.type = SettingInfo::Type::Path,
.name = "ImagePathOlympusCamera",
.display_name = TRANSLATE_NOOP("USB", "Image Path"),
.description = TRANSLATE_NOOP("USB", "Sets the path to the disk image which will back the virtual mass storage device.")
}};
return settings;
}
case OLYMPUS_MO:
{
static constexpr const SettingInfo settings[] = {{
.type = SettingInfo::Type::Path,
.name = "ImagePathOlympusMD",
.display_name = TRANSLATE_NOOP("USB", "Image Path"),
.description = TRANSLATE_NOOP("USB", "Sets the path to the disk image which will back the virtual mass storage device.")
}};
return settings;
}
case SONY_MSAC_US1:
{
static constexpr const SettingInfo settings[] = {
{SettingInfo::Type::Path, "ImagePathMsac", TRANSLATE_NOOP("USB", "Image Path"),
TRANSLATE_NOOP("USB", "Sets the path to the disk image which will back the virtual mass storage device.")},
};
static constexpr const SettingInfo settings[] = {{
.type = SettingInfo::Type::Path,
.name = "ImagePathMsac",
.display_name = TRANSLATE_NOOP("USB", "Image Path"),
.description = TRANSLATE_NOOP("USB", "Sets the path to the disk image which will back the virtual mass storage device.")
}};
return settings;
}
default:
+2
View File
@@ -10,6 +10,8 @@ namespace usb_msd
enum MSDType
{
IOMEGA_ZIP_100,
OLYMPUS_CAMERA,
OLYMPUS_MO,
SONY_MSAC_US1,
};