mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-07-15 19:54:27 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38ee1e1e22 | ||
|
|
2b02a5dc78 | ||
|
|
dead00eb62 | ||
|
|
4fa2b8e45d | ||
|
|
fc90973509 | ||
|
|
ff9176f83e | ||
|
|
63d3dd9978 | ||
|
|
b704c6caaa | ||
|
|
a64465681c | ||
|
|
539357436c | ||
|
|
22cb6b0c9c | ||
|
|
81df4e5def | ||
|
|
a3f3e8733d | ||
|
|
6fc84f76bb | ||
|
|
0949f193b8 | ||
|
|
5f3dc1a76e | ||
|
|
9afea55bb6 | ||
|
|
433614ae85 | ||
|
|
00c85c4a8f | ||
|
|
f9885315bd | ||
|
|
26563dbae2 | ||
|
|
53e3838c1d | ||
|
|
4509d11daf | ||
|
|
37d5bf33e8 | ||
|
|
76842401ee | ||
|
|
bf9d38e86a | ||
|
|
7de2aad05a | ||
|
|
7d11e1f963 | ||
|
|
452e239eb4 | ||
|
|
8b82fbb449 | ||
|
|
de84c41757 | ||
|
|
3000e113e2 | ||
|
|
c5d6b28be4 | ||
|
|
c0dfb8cfa0 | ||
|
|
f9feca4f4d | ||
|
|
4fa9c4e382 | ||
|
|
93f1fcbe88 | ||
|
|
3d71e310c0 | ||
|
|
02d0f613ba | ||
|
|
3091407e9e | ||
|
|
70f6169eff | ||
|
|
a1a3cd9d1b | ||
|
|
86c5d2ae2a | ||
|
|
d759055231 | ||
|
|
c6474852d5 | ||
|
|
a2ac576711 | ||
|
|
1b6924a033 | ||
|
|
8e5e053b81 | ||
|
|
6891511a1d | ||
|
|
c186f8d366 | ||
|
|
eed53d19a0 | ||
|
|
abb218c5cf | ||
|
|
249ac51827 | ||
|
|
3da22f126e | ||
|
|
a509350646 | ||
|
|
bd486f1729 | ||
|
|
f7ef43b155 | ||
|
|
282129030f | ||
|
|
5734403ffc | ||
|
|
8d4d9ce026 | ||
|
|
390e3994ad | ||
|
|
6c41c125d8 | ||
|
|
83da1b6479 | ||
|
|
226b4d25dc | ||
|
|
27736ef47f | ||
|
|
f95b2ce24d | ||
|
|
f1d83f8c65 | ||
|
|
84c0cd8890 | ||
|
|
971382f11e | ||
|
|
ddf6fe3264 | ||
|
|
e5f0c99c76 | ||
|
|
af81d8b7da | ||
|
|
8f23e31ae4 | ||
|
|
3737b1e06a | ||
|
|
de848fe468 | ||
|
|
ce2a859ad1 | ||
|
|
8cfd583605 |
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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><html><head/><body><p align="justify">PCSX2 uses RetroAchievements as an achievement database and for tracking progress. To use achievements, please sign up for an account at <a href="https://retroachievements.org/">retroachievements.org</a>.</p><p align="justify">To view the achievement list in-game, press the hotkey for <span style=" font-weight:600;">Open Pause Menu</span> and select <span style=" font-weight:600;">Achievements</span> from the menu.</p></body></html></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><html><head/><body><p align="justify">PCSX2 uses RetroAchievements as an achievement database and for tracking progress. To use achievements, please sign up for an account at <a href="https://retroachievements.org/">retroachievements.org</a>.</p><p align="justify">To view the achievement list in-game, press the hotkey for <span style=" font-weight:600;">Open Pause Menu</span> and select <span style=" font-weight:600;">Achievements</span> from the menu.</p></body></html></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"/>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
+3065
-2888
File diff suppressed because it is too large
Load Diff
+24
-1
@@ -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)
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() :
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/AlignedMalloc.h"
|
||||
#include <climits>
|
||||
|
||||
template <class T>
|
||||
struct Element
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {};
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -82,7 +82,7 @@ protected:
|
||||
template <u32 primclass>
|
||||
void RewriteVerticesIfSTOverflow();
|
||||
|
||||
bool IsCoverageAlphaSupported() override { return true; }
|
||||
bool IsCoverageAlphaSupported() override;
|
||||
public:
|
||||
GSRendererSW(int threads);
|
||||
~GSRendererSW() override;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
@@ -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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace usb_msd
|
||||
enum MSDType
|
||||
{
|
||||
IOMEGA_ZIP_100,
|
||||
OLYMPUS_CAMERA,
|
||||
OLYMPUS_MO,
|
||||
SONY_MSAC_US1,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user