mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-07-15 19:54:27 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa04e57df5 | ||
|
|
99a8afac8d | ||
|
|
16fde28c39 | ||
|
|
ed515782f9 | ||
|
|
fa85ef3f92 | ||
|
|
12136217c4 | ||
|
|
0e5445d2a7 | ||
|
|
e83a343bab | ||
|
|
d5aeb9e20a | ||
|
|
d9128872fd | ||
|
|
6e129a69d2 | ||
|
|
6e2d17073a | ||
|
|
f622726bb1 | ||
|
|
a39a0fd268 | ||
|
|
f14e061438 | ||
|
|
0356b8a73e | ||
|
|
1d9b7c0387 | ||
|
|
2164078b2d | ||
|
|
aeb1d500df | ||
|
|
e44a388455 | ||
|
|
74044cdbce | ||
|
|
3f6ec596f0 | ||
|
|
1831d11671 | ||
|
|
362e89d668 | ||
|
|
936836e096 | ||
|
|
0e9de44c72 | ||
|
|
d3841591a5 | ||
|
|
7f13863bb8 | ||
|
|
dab29f8443 | ||
|
|
73d11a88da | ||
|
|
21320c750b | ||
|
|
35ce3c483f | ||
|
|
fdd360f919 | ||
|
|
fcb1d4ff4e | ||
|
|
815a59b6ed | ||
|
|
2097ff232c | ||
|
|
53d98a53e8 | ||
|
|
f81a95f3a9 | ||
|
|
74e190511b | ||
|
|
a86f441ac8 | ||
|
|
2557c2e887 | ||
|
|
6accade0d5 | ||
|
|
9437e1ac41 | ||
|
|
b2b87f9e87 | ||
|
|
f2ab17ff94 | ||
|
|
a1514e2c6c | ||
|
|
329c6d42be | ||
|
|
800da09b1d | ||
|
|
fb8c1b901d | ||
|
|
df817924e0 | ||
|
|
c227686d46 | ||
|
|
683ced4fe2 | ||
|
|
ad6a8a5cf3 | ||
|
|
20e3896714 | ||
|
|
084df36598 | ||
|
|
28abbc6bf5 | ||
|
|
3a125a2d7a | ||
|
|
8415a4b0d6 | ||
|
|
209e676e3a | ||
|
|
a64431c7b5 | ||
|
|
665eb3d6b3 | ||
|
|
fe7935ce0e | ||
|
|
2fba420339 | ||
|
|
e35c84c2c6 | ||
|
|
4b79645774 | ||
|
|
df4be2c170 | ||
|
|
ede25a5ecc | ||
|
|
76977da05a | ||
|
|
aca37d14a1 | ||
|
|
eee315718f | ||
|
|
a14d99e93c | ||
|
|
af3877ac7a | ||
|
|
c7f7bd6f3d | ||
|
|
dd81fbbd5f | ||
|
|
b4293a40d2 | ||
|
|
9acadb21fe | ||
|
|
e82fa0bba5 | ||
|
|
45490d903a | ||
|
|
76dadf792a | ||
|
|
204829865d | ||
|
|
84dc2959c5 | ||
|
|
a85b203689 | ||
|
|
135d40fb7f | ||
|
|
a0bc7a5d0e | ||
|
|
955b925633 | ||
|
|
cc338cdd9d | ||
|
|
082a28dc13 | ||
|
|
664e14bd6c |
@@ -17,7 +17,7 @@ jobs:
|
||||
run: ./.github/workflows/scripts/common/update_base_translation.sh
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725
|
||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0
|
||||
with:
|
||||
title: "Qt: Update Base Translation"
|
||||
commit-message: "[ci skip] Qt: Update Base Translation."
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
mv ./game_controller_db.txt ${{github.workspace}}/bin/resources/game_controller_db.txt
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725
|
||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0
|
||||
with:
|
||||
title: "PAD: Update to latest controller database"
|
||||
commit-message: "[ci skip] PAD: Update to latest controller database."
|
||||
|
||||
@@ -18,6 +18,9 @@ if [ "${INSTALLDIR:0:1}" != "/" ]; then
|
||||
INSTALLDIR="$PWD/$INSTALLDIR"
|
||||
fi
|
||||
|
||||
QT=6.10.2
|
||||
QTAPNG=1.3.0
|
||||
|
||||
FFMPEG=8.0
|
||||
FREETYPE=2.14.1
|
||||
HARFBUZZ=12.2.0
|
||||
@@ -27,8 +30,6 @@ LIBPNG=1.6.53
|
||||
LIBWEBP=1.6.0
|
||||
NVENC=13.0.19.0
|
||||
SDL=SDL3-3.4.0
|
||||
QT=6.10.1
|
||||
QTAPNG=1.3.0
|
||||
LZ4=1.10.0
|
||||
VULKAN=1.4.328.1
|
||||
ZSTD=1.5.7
|
||||
@@ -47,6 +48,14 @@ cd deps-build
|
||||
export PKG_CONFIG_PATH="$INSTALLDIR/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
|
||||
cat > SHASUMS <<EOF
|
||||
aeb78d29291a2b5fd53cb55950f8f5065b4978c25fb1d77f627d695ab9adf21e qtbase-everywhere-src-$QT.tar.xz
|
||||
8b8f9c718638081e7b3c000e7f31910140b1202a98e98df5d1b496fe6f639d67 qtimageformats-everywhere-src-$QT.tar.xz
|
||||
f07ff80f38caf235187200345392ca7479445ddf49a36c3694cd52a735dad6e1 qtsvg-everywhere-src-$QT.tar.xz
|
||||
1e3d2c07c1fd76d2425c6eaeeaa62ffaff5f79210c4e1a5bc2a6a9db668d5b24 qttools-everywhere-src-$QT.tar.xz
|
||||
b3b3813bc9d76b545716dc8b6e659fa71b6e2bc14569e9fab6dab8b30650a644 qttranslations-everywhere-src-$QT.tar.xz
|
||||
391998eb432719df26a6a67d8efdc67f8bf2afdd76c1ee3381ebff4fe7527ee2 qtwayland-everywhere-src-$QT.tar.xz
|
||||
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
|
||||
|
||||
b2751fccb6cc4c77708113cd78b561059b6fa904b24162fa0be2d60273d27b8e ffmpeg-$FFMPEG.tar.xz
|
||||
32427e8c471ac095853212a37aef816c60b42052d4d9e48230bab3bdf2936ccc freetype-$FREETYPE.tar.xz
|
||||
f63fc519f150465bd0bdafcdf3d0e9c23474f4c474171cd515ea1b3a72c081fb harfbuzz-$HARFBUZZ.tar.gz
|
||||
@@ -60,20 +69,14 @@ e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWE
|
||||
13da39edb3a40ed9713ae390ca89faa2f1202c9dda869ef306a8d4383e242bee nv-codec-headers-$NVENC.tar.gz
|
||||
c465aa56757e7746ac707f582b6e2d51546569a4a2488c1172fb543aa5fdfc2c vulkan-sdk-$VULKAN.tar.gz
|
||||
eb33e51f49a15e023950cd7825ca74a4a2b43db8354825ac24fc1b7ee09e6fa3 zstd-$ZSTD.tar.gz
|
||||
5a6226f7e23db51fdc3223121eba53f3f5447cf0cc4d6cb82a3a2df7a65d265d qtbase-everywhere-src-$QT.tar.xz
|
||||
498eabdf2381db96f808942b3e3c765f6360fe6c0e9961f0a45ff7a4c68d7a72 qtimageformats-everywhere-src-$QT.tar.xz
|
||||
c02f355a58f3bbcf404a628bf488b6aeb2d84a94c269afdb86f6e529343ab01f qtsvg-everywhere-src-$QT.tar.xz
|
||||
8148408380ffea03101a26305c812b612ea30dbc07121e58707601522404d49b qttools-everywhere-src-$QT.tar.xz
|
||||
8e49a2df88a12c376a479ae7bd272a91cf57ebb4e7c0cf7341b3565df99d2314 qttranslations-everywhere-src-$QT.tar.xz
|
||||
49bf6db800227a6b2c971f4c5d03dd1e81297e7ffb296ce4a96437304f27cb13 qtwayland-everywhere-src-$QT.tar.xz
|
||||
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
|
||||
51dbf24fe72e43dd7cb9a289d3cab47112010f1a2ed69b6fc8ac0dff31991ed2 KDDockWidgets-$KDDOCKWIDGETS.tar.gz
|
||||
7bd4e79ce18b1d47517e7e91fbb7cf19d4f01942804a519bc7c0bf32b6325dd5 plutovg-$PLUTOVG.tar.gz
|
||||
78561b571ac224030cdc450ca2986b4de915c2ba7616004a6d71a379bffd15f3 plutosvg-$PLUTOSVG.tar.gz
|
||||
|
||||
8a89fb6612ace8954470aae004623374a8fc8b7a34a4277bee5527173b064faf shaderc-$SHADERC.tar.gz
|
||||
272d2725b140e09e85b96eecdc59c2e00c1a14cda2301767e1bf3c363a44b931 shaderc-glslang-$SHADERC_GLSLANG.tar.gz
|
||||
c693867f10a7760ef1bcf85419d51783586768cc2c601d03841bc6a8b2554b9c shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
|
||||
06b0a042f2e121e954badb4fd78c9e2d4bc7ed6087eceb26ab559c23cf94334f shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz
|
||||
51dbf24fe72e43dd7cb9a289d3cab47112010f1a2ed69b6fc8ac0dff31991ed2 KDDockWidgets-$KDDOCKWIDGETS.tar.gz
|
||||
7bd4e79ce18b1d47517e7e91fbb7cf19d4f01942804a519bc7c0bf32b6325dd5 plutovg-$PLUTOVG.tar.gz
|
||||
78561b571ac224030cdc450ca2986b4de915c2ba7616004a6d71a379bffd15f3 plutosvg-$PLUTOSVG.tar.gz
|
||||
EOF
|
||||
|
||||
curl -L \
|
||||
@@ -213,7 +216,7 @@ tar xf "harfbuzz-$HARFBUZZ.tar.gz"
|
||||
cd "harfbuzz-$HARFBUZZ"
|
||||
# Add an SOVERSION to match system harfbuzz
|
||||
sed -i 's/PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE)/PROPERTIES VISIBILITY_INLINES_HIDDEN TRUE SOVERSION 0)/g' CMakeLists.txt
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DHB_BUILD_UTILS=OFF -DHB_HAVE_FREETYPE=ON -DHB_HAVE_GOBJECT=ON -B build -G Ninja
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" -DBUILD_SHARED_LIBS=ON -DHB_BUILD_UTILS=OFF -DHB_HAVE_FREETYPE=ON -B build -G Ninja
|
||||
cmake --build build --parallel
|
||||
ninja -C build install
|
||||
cd ..
|
||||
@@ -247,7 +250,7 @@ tar xf "qtbase-everywhere-src-$QT.tar.xz"
|
||||
cd "qtbase-everywhere-src-$QT"
|
||||
mkdir build
|
||||
cd build
|
||||
../configure -prefix "$INSTALLDIR" -release -dbus-linked -gui -widgets -fontconfig -qt-doubleconversion -ssl -openssl-runtime -opengl desktop -qpa xcb,wayland -xkbcommon -xcb -gtk -- --log-level=STATUS -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DFEATURE_dbus=ON -DFEATURE_icu=OFF -DFEATURE_printsupport=OFF -DFEATURE_sql=OFF -DFEATURE_system_png=ON -DFEATURE_system_jpeg=ON -DFEATURE_system_zlib=ON -DFEATURE_system_freetype=ON -DFEATURE_system_harfbuzz=ON
|
||||
../configure -prefix "$INSTALLDIR" -release -dbus-linked -gui -widgets -fontconfig -qt-doubleconversion -ssl -openssl-runtime -opengl desktop -qpa xcb,wayland -xkbcommon -xcb -- --log-level=STATUS -DCMAKE_PREFIX_PATH="$INSTALLDIR" -DFEATURE_dbus=ON -DFEATURE_icu=OFF -DFEATURE_printsupport=OFF -DFEATURE_sql=OFF -DFEATURE_system_png=ON -DFEATURE_system_jpeg=ON -DFEATURE_system_zlib=ON -DFEATURE_system_freetype=ON -DFEATURE_system_harfbuzz=ON -DFEATURE_gtk3=OFF
|
||||
cmake --build . --parallel
|
||||
ninja install
|
||||
cd ../../
|
||||
|
||||
@@ -38,6 +38,9 @@ if [ "${INSTALLDIR:0:1}" != "/" ]; then
|
||||
INSTALLDIR="$PWD/$INSTALLDIR"
|
||||
fi
|
||||
|
||||
QT=6.10.1
|
||||
QTAPNG=1.3.0
|
||||
|
||||
FREETYPE=2.14.1
|
||||
HARFBUZZ=12.2.0
|
||||
SDL=SDL3-3.4.0
|
||||
@@ -48,8 +51,6 @@ LIBJPEGTURBO=3.1.2
|
||||
LIBWEBP=1.6.0
|
||||
FFMPEG=8.0
|
||||
MOLTENVK=1.4.1
|
||||
QT=6.10.1
|
||||
QTAPNG=1.3.0
|
||||
KDDOCKWIDGETS=2.4.0
|
||||
PLUTOVG=1.3.2
|
||||
PLUTOSVG=0.0.7
|
||||
@@ -78,6 +79,13 @@ CMAKE_ARCH_ARM64=-DCMAKE_OSX_ARCHITECTURES="arm64"
|
||||
CMAKE_ARCH_UNIVERSAL=-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"
|
||||
|
||||
cat > SHASUMS <<EOF
|
||||
aeb78d29291a2b5fd53cb55950f8f5065b4978c25fb1d77f627d695ab9adf21e qtbase-everywhere-src-$QT.tar.xz
|
||||
8b8f9c718638081e7b3c000e7f31910140b1202a98e98df5d1b496fe6f639d67 qtimageformats-everywhere-src-$QT.tar.xz
|
||||
f07ff80f38caf235187200345392ca7479445ddf49a36c3694cd52a735dad6e1 qtsvg-everywhere-src-$QT.tar.xz
|
||||
1e3d2c07c1fd76d2425c6eaeeaa62ffaff5f79210c4e1a5bc2a6a9db668d5b24 qttools-everywhere-src-$QT.tar.xz
|
||||
b3b3813bc9d76b545716dc8b6e659fa71b6e2bc14569e9fab6dab8b30650a644 qttranslations-everywhere-src-$QT.tar.xz
|
||||
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
|
||||
|
||||
32427e8c471ac095853212a37aef816c60b42052d4d9e48230bab3bdf2936ccc freetype-$FREETYPE.tar.xz
|
||||
f63fc519f150465bd0bdafcdf3d0e9c23474f4c474171cd515ea1b3a72c081fb harfbuzz-$HARFBUZZ.tar.gz
|
||||
082cbf5f429e0d80820f68dc2b507a94d4cc1b4e70817b119bbb8ec6a69584b8 $SDL.tar.gz
|
||||
@@ -89,19 +97,14 @@ e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWE
|
||||
8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf libjpeg-turbo-$LIBJPEGTURBO.tar.gz
|
||||
b2751fccb6cc4c77708113cd78b561059b6fa904b24162fa0be2d60273d27b8e ffmpeg-$FFMPEG.tar.xz
|
||||
9985f141902a17de818e264d17c1ce334b748e499ee02fcb4703e4dc0038f89c v$MOLTENVK.tar.gz
|
||||
5a6226f7e23db51fdc3223121eba53f3f5447cf0cc4d6cb82a3a2df7a65d265d qtbase-everywhere-src-$QT.tar.xz
|
||||
498eabdf2381db96f808942b3e3c765f6360fe6c0e9961f0a45ff7a4c68d7a72 qtimageformats-everywhere-src-$QT.tar.xz
|
||||
c02f355a58f3bbcf404a628bf488b6aeb2d84a94c269afdb86f6e529343ab01f qtsvg-everywhere-src-$QT.tar.xz
|
||||
8148408380ffea03101a26305c812b612ea30dbc07121e58707601522404d49b qttools-everywhere-src-$QT.tar.xz
|
||||
8e49a2df88a12c376a479ae7bd272a91cf57ebb4e7c0cf7341b3565df99d2314 qttranslations-everywhere-src-$QT.tar.xz
|
||||
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
|
||||
51dbf24fe72e43dd7cb9a289d3cab47112010f1a2ed69b6fc8ac0dff31991ed2 KDDockWidgets-$KDDOCKWIDGETS.tar.gz
|
||||
7bd4e79ce18b1d47517e7e91fbb7cf19d4f01942804a519bc7c0bf32b6325dd5 plutovg-$PLUTOVG.tar.gz
|
||||
78561b571ac224030cdc450ca2986b4de915c2ba7616004a6d71a379bffd15f3 plutosvg-$PLUTOSVG.tar.gz
|
||||
|
||||
8a89fb6612ace8954470aae004623374a8fc8b7a34a4277bee5527173b064faf shaderc-$SHADERC.tar.gz
|
||||
272d2725b140e09e85b96eecdc59c2e00c1a14cda2301767e1bf3c363a44b931 shaderc-glslang-$SHADERC_GLSLANG.tar.gz
|
||||
c693867f10a7760ef1bcf85419d51783586768cc2c601d03841bc6a8b2554b9c shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
|
||||
06b0a042f2e121e954badb4fd78c9e2d4bc7ed6087eceb26ab559c23cf94334f shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz
|
||||
51dbf24fe72e43dd7cb9a289d3cab47112010f1a2ed69b6fc8ac0dff31991ed2 KDDockWidgets-$KDDOCKWIDGETS.tar.gz
|
||||
7bd4e79ce18b1d47517e7e91fbb7cf19d4f01942804a519bc7c0bf32b6325dd5 plutovg-$PLUTOVG.tar.gz
|
||||
78561b571ac224030cdc450ca2986b4de915c2ba7616004a6d71a379bffd15f3 plutosvg-$PLUTOSVG.tar.gz
|
||||
EOF
|
||||
|
||||
curl -C - -L \
|
||||
|
||||
@@ -20,6 +20,9 @@ if [ "${INSTALLDIR:0:1}" != "/" ]; then
|
||||
INSTALLDIR="$PWD/$INSTALLDIR"
|
||||
fi
|
||||
|
||||
QT=6.10.2
|
||||
QTAPNG=1.3.0
|
||||
|
||||
FREETYPE=2.14.1
|
||||
HARFBUZZ=12.2.0
|
||||
SDL=SDL3-3.4.0
|
||||
@@ -30,8 +33,6 @@ LIBJPEGTURBO=3.1.2
|
||||
LIBWEBP=1.6.0
|
||||
FFMPEG=8.0
|
||||
MOLTENVK=1.4.1
|
||||
QT=6.10.1
|
||||
QTAPNG=1.3.0
|
||||
KDDOCKWIDGETS=2.4.0
|
||||
PLUTOVG=1.3.2
|
||||
PLUTOSVG=0.0.7
|
||||
@@ -59,6 +60,13 @@ CMAKE_COMMON=(
|
||||
)
|
||||
|
||||
cat > SHASUMS <<EOF
|
||||
aeb78d29291a2b5fd53cb55950f8f5065b4978c25fb1d77f627d695ab9adf21e qtbase-everywhere-src-$QT.tar.xz
|
||||
8b8f9c718638081e7b3c000e7f31910140b1202a98e98df5d1b496fe6f639d67 qtimageformats-everywhere-src-$QT.tar.xz
|
||||
f07ff80f38caf235187200345392ca7479445ddf49a36c3694cd52a735dad6e1 qtsvg-everywhere-src-$QT.tar.xz
|
||||
1e3d2c07c1fd76d2425c6eaeeaa62ffaff5f79210c4e1a5bc2a6a9db668d5b24 qttools-everywhere-src-$QT.tar.xz
|
||||
b3b3813bc9d76b545716dc8b6e659fa71b6e2bc14569e9fab6dab8b30650a644 qttranslations-everywhere-src-$QT.tar.xz
|
||||
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
|
||||
|
||||
32427e8c471ac095853212a37aef816c60b42052d4d9e48230bab3bdf2936ccc freetype-$FREETYPE.tar.xz
|
||||
f63fc519f150465bd0bdafcdf3d0e9c23474f4c474171cd515ea1b3a72c081fb harfbuzz-$HARFBUZZ.tar.gz
|
||||
082cbf5f429e0d80820f68dc2b507a94d4cc1b4e70817b119bbb8ec6a69584b8 $SDL.tar.gz
|
||||
@@ -70,19 +78,14 @@ e4ab7009bf0629fd11982d4c2aa83964cf244cffba7347ecd39019a9e38c4564 libwebp-$LIBWE
|
||||
8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf libjpeg-turbo-$LIBJPEGTURBO.tar.gz
|
||||
b2751fccb6cc4c77708113cd78b561059b6fa904b24162fa0be2d60273d27b8e ffmpeg-$FFMPEG.tar.xz
|
||||
9985f141902a17de818e264d17c1ce334b748e499ee02fcb4703e4dc0038f89c v$MOLTENVK.tar.gz
|
||||
5a6226f7e23db51fdc3223121eba53f3f5447cf0cc4d6cb82a3a2df7a65d265d qtbase-everywhere-src-$QT.tar.xz
|
||||
498eabdf2381db96f808942b3e3c765f6360fe6c0e9961f0a45ff7a4c68d7a72 qtimageformats-everywhere-src-$QT.tar.xz
|
||||
c02f355a58f3bbcf404a628bf488b6aeb2d84a94c269afdb86f6e529343ab01f qtsvg-everywhere-src-$QT.tar.xz
|
||||
8148408380ffea03101a26305c812b612ea30dbc07121e58707601522404d49b qttools-everywhere-src-$QT.tar.xz
|
||||
8e49a2df88a12c376a479ae7bd272a91cf57ebb4e7c0cf7341b3565df99d2314 qttranslations-everywhere-src-$QT.tar.xz
|
||||
f1d3be3489f758efe1a8f12118a212febbe611aa670af32e0159fa3c1feab2a6 QtApng-$QTAPNG.tar.gz
|
||||
51dbf24fe72e43dd7cb9a289d3cab47112010f1a2ed69b6fc8ac0dff31991ed2 KDDockWidgets-$KDDOCKWIDGETS.tar.gz
|
||||
7bd4e79ce18b1d47517e7e91fbb7cf19d4f01942804a519bc7c0bf32b6325dd5 plutovg-$PLUTOVG.tar.gz
|
||||
78561b571ac224030cdc450ca2986b4de915c2ba7616004a6d71a379bffd15f3 plutosvg-$PLUTOSVG.tar.gz
|
||||
|
||||
8a89fb6612ace8954470aae004623374a8fc8b7a34a4277bee5527173b064faf shaderc-$SHADERC.tar.gz
|
||||
272d2725b140e09e85b96eecdc59c2e00c1a14cda2301767e1bf3c363a44b931 shaderc-glslang-$SHADERC_GLSLANG.tar.gz
|
||||
c693867f10a7760ef1bcf85419d51783586768cc2c601d03841bc6a8b2554b9c shaderc-spirv-headers-$SHADERC_SPIRVHEADERS.tar.gz
|
||||
06b0a042f2e121e954badb4fd78c9e2d4bc7ed6087eceb26ab559c23cf94334f shaderc-spirv-tools-$SHADERC_SPIRVTOOLS.tar.gz
|
||||
51dbf24fe72e43dd7cb9a289d3cab47112010f1a2ed69b6fc8ac0dff31991ed2 KDDockWidgets-$KDDOCKWIDGETS.tar.gz
|
||||
7bd4e79ce18b1d47517e7e91fbb7cf19d4f01942804a519bc7c0bf32b6325dd5 plutovg-$PLUTOVG.tar.gz
|
||||
78561b571ac224030cdc450ca2986b4de915c2ba7616004a6d71a379bffd15f3 plutosvg-$PLUTOSVG.tar.gz
|
||||
EOF
|
||||
|
||||
curl -L \
|
||||
|
||||
@@ -2,10 +2,15 @@
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
echo Setting environment...
|
||||
if exist "%ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsamd64_arm64.bat" (
|
||||
call "%ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsamd64_arm64.bat"
|
||||
) else if exist "%ProgramFiles%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsamd64_arm64.bat" (
|
||||
call "%ProgramFiles%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsamd64_arm64.bat"
|
||||
if exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" (
|
||||
for /f "usebackq tokens=*" %%i in (`call "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -version "[17, 18)" -latest -property installationPath`) do set "VSINSTPATH=%%i"
|
||||
if defined VSINSTPATH (
|
||||
echo VSINSTPATH=!VSINSTPATH!
|
||||
call "!VSINSTPATH!\VC\Auxiliary\Build\vcvarsamd64_arm64.bat" || goto error
|
||||
) else (
|
||||
echo Visual Studio 2022 not found.
|
||||
goto error
|
||||
)
|
||||
) else (
|
||||
echo Visual Studio 2022 not found.
|
||||
goto error
|
||||
@@ -42,15 +47,16 @@ echo INSTALLDIR=%INSTALLDIR%
|
||||
|
||||
cd "%BUILDDIR%"
|
||||
|
||||
set QT=6.10.2
|
||||
set QTMINOR=6.10
|
||||
set QTAPNG=1.3.0
|
||||
|
||||
set FREETYPE=2.14.1
|
||||
set HARFBUZZ=12.2.0
|
||||
set LIBJPEGTURBO=3.1.2
|
||||
set LIBPNG=1653
|
||||
set LIBPNGLONG=1.6.53
|
||||
set SDL=SDL3-3.4.0
|
||||
set QT=6.10.1
|
||||
set QTMINOR=6.10
|
||||
set QTAPNG=1.3.0
|
||||
set LZ4=1.10.0
|
||||
set WEBP=1.6.0
|
||||
set ZLIB=1.3.1
|
||||
@@ -67,6 +73,13 @@ set SHADERC_SPIRVTOOLS=971a7b6e8d7740035bbff089bbbf9f42951ecfd5
|
||||
|
||||
set AGILITYSDK=1.618.5
|
||||
|
||||
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 690e08ce807041150d388b5351de2e591febf0a0bc973b56e1197e2df9a2d96f || goto error
|
||||
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" a407e489ca8d68639530e881aea797f58754e355ca06024a73358bd9cc5fca17 || goto error
|
||||
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" fd9f4f6e983a55f6b0ba4c49ce13f8b014cd30d12e75a49a478a640462f9bf64 || goto error
|
||||
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" a498357aed659c66aa87ceb556eacab55e3e7d7c9b9599049066de70eb7efa59 || goto error
|
||||
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" cf5e04a2048c02369f579c1251f61af965f241cde2d69de3db90e6c9f0ee2bf3 || goto error
|
||||
call :downloadfile "QtApng-%QTAPNG%.zip" "https://github.com/jurplel/QtApng/archive/refs/tags/%QTAPNG%.zip" 5176082cdd468047a7eb1ec1f106b032f57df207aa318d559b29606b00d159ac || goto error
|
||||
|
||||
call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download 174d9e53402e1bf9ec7277e22ec199ba3e55a6be2c0740cb18c0ee9850fc8c34 || goto error
|
||||
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip 31490c781bacd2ce56862555b11c51c964977c39f14f51b817dfaecf0be089fe || goto error
|
||||
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1653.zip 140566abc64bb2320cb35f1d154d1cb3eb7174a12234d33bfdffb446bdc0a1d2 || goto error
|
||||
@@ -74,12 +87,6 @@ call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net
|
||||
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" 8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf || 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" 9ac2debb493e0d3e13dbd2729fb91f4bfeb00a0f4dff5e04b73cc9bac276b38d || goto error
|
||||
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" c43f471a808b07fc541528410e94ce89c6745bdc1d744492e19911d35fbf7d33 || goto error
|
||||
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 2d828d8c999fdd18167937c071781c22321c643b04a106c714411c2356cdb26d || goto error
|
||||
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" ddd74a417d2397eb085d047a9b6ba52b76e748055817f728fe691f8456035d23 || goto error
|
||||
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" db8e49ed50912c3c064a4f9ada7791c09eccec5a8d53463a19608eaab17679f0 || goto error
|
||||
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" 868eb651e395d48ade5932ef2c386e606e054eb5888ebe5284fbd8cb63ed935a || goto error
|
||||
call :downloadfile "QtApng-%QTAPNG%.zip" "https://github.com/jurplel/QtApng/archive/refs/tags/%QTAPNG%.zip" 5176082cdd468047a7eb1ec1f106b032f57df207aa318d559b29606b00d159ac || 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" 72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17 || goto error
|
||||
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 7897bc5d620580d9b7cd3539c44b59d78f3657d33663fe97a145e07b4ebd69a4 || goto error
|
||||
|
||||
@@ -2,10 +2,15 @@
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
echo Setting environment...
|
||||
if exist "%ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" (
|
||||
call "%ProgramFiles%\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||
) else if exist "%ProgramFiles%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" (
|
||||
call "%ProgramFiles%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"
|
||||
if exist "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" (
|
||||
for /f "usebackq tokens=*" %%i in (`call "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -version "[17, 18)" -latest -property installationPath`) do set "VSINSTPATH=%%i"
|
||||
if defined VSINSTPATH (
|
||||
echo VSINSTPATH=!VSINSTPATH!
|
||||
call "!VSINSTPATH!\VC\Auxiliary\Build\vcvars64.bat" || goto error
|
||||
) else (
|
||||
echo Visual Studio 2022 not found.
|
||||
goto error
|
||||
)
|
||||
) else (
|
||||
echo Visual Studio 2022 not found.
|
||||
goto error
|
||||
@@ -40,15 +45,16 @@ set "PATH=%PATH%;%INSTALLDIR%\bin"
|
||||
|
||||
cd "%BUILDDIR%"
|
||||
|
||||
set QT=6.10.2
|
||||
set QTMINOR=6.10
|
||||
set QTAPNG=1.3.0
|
||||
|
||||
set FREETYPE=2.14.1
|
||||
set HARFBUZZ=12.2.0
|
||||
set LIBJPEGTURBO=3.1.2
|
||||
set LIBPNG=1653
|
||||
set LIBPNGLONG=1.6.53
|
||||
set SDL=SDL3-3.4.0
|
||||
set QT=6.10.1
|
||||
set QTMINOR=6.10
|
||||
set QTAPNG=1.3.0
|
||||
set LZ4=1.10.0
|
||||
set WEBP=1.6.0
|
||||
set ZLIB=1.3.1
|
||||
@@ -65,6 +71,13 @@ set SHADERC_SPIRVTOOLS=971a7b6e8d7740035bbff089bbbf9f42951ecfd5
|
||||
|
||||
set AGILITYSDK=1.618.5
|
||||
|
||||
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 690e08ce807041150d388b5351de2e591febf0a0bc973b56e1197e2df9a2d96f || goto error
|
||||
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" a407e489ca8d68639530e881aea797f58754e355ca06024a73358bd9cc5fca17 || goto error
|
||||
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" fd9f4f6e983a55f6b0ba4c49ce13f8b014cd30d12e75a49a478a640462f9bf64 || goto error
|
||||
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" a498357aed659c66aa87ceb556eacab55e3e7d7c9b9599049066de70eb7efa59 || goto error
|
||||
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" cf5e04a2048c02369f579c1251f61af965f241cde2d69de3db90e6c9f0ee2bf3 || goto error
|
||||
call :downloadfile "QtApng-%QTAPNG%.zip" "https://github.com/jurplel/QtApng/archive/refs/tags/%QTAPNG%.zip" 5176082cdd468047a7eb1ec1f106b032f57df207aa318d559b29606b00d159ac || goto error
|
||||
|
||||
call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects/freetype/files/freetype2/%FREETYPE%/freetype-%FREETYPE%.tar.gz/download 174d9e53402e1bf9ec7277e22ec199ba3e55a6be2c0740cb18c0ee9850fc8c34 || goto error
|
||||
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip 31490c781bacd2ce56862555b11c51c964977c39f14f51b817dfaecf0be089fe || goto error
|
||||
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1653.zip 140566abc64bb2320cb35f1d154d1cb3eb7174a12234d33bfdffb446bdc0a1d2 || goto error
|
||||
@@ -72,12 +85,6 @@ call :downloadfile "lpng%LIBPNG%-apng.patch.gz" https://download.sourceforge.net
|
||||
call :downloadfile "libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/%LIBJPEGTURBO%/libjpeg-turbo-%LIBJPEGTURBO%.tar.gz" 8f0012234b464ce50890c490f18194f913a7b1f4e6a03d6644179fa0f867d0cf || 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" 9ac2debb493e0d3e13dbd2729fb91f4bfeb00a0f4dff5e04b73cc9bac276b38d || goto error
|
||||
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" c43f471a808b07fc541528410e94ce89c6745bdc1d744492e19911d35fbf7d33 || goto error
|
||||
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 2d828d8c999fdd18167937c071781c22321c643b04a106c714411c2356cdb26d || goto error
|
||||
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" ddd74a417d2397eb085d047a9b6ba52b76e748055817f728fe691f8456035d23 || goto error
|
||||
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" db8e49ed50912c3c064a4f9ada7791c09eccec5a8d53463a19608eaab17679f0 || goto error
|
||||
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" 868eb651e395d48ade5932ef2c386e606e054eb5888ebe5284fbd8cb63ed935a || goto error
|
||||
call :downloadfile "QtApng-%QTAPNG%.zip" "https://github.com/jurplel/QtApng/archive/refs/tags/%QTAPNG%.zip" 5176082cdd468047a7eb1ec1f106b032f57df207aa318d559b29606b00d159ac || 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" 72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17 || goto error
|
||||
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 7897bc5d620580d9b7cd3539c44b59d78f3657d33663fe97a145e07b4ebd69a4 || goto error
|
||||
|
||||
@@ -84,14 +84,10 @@ endif()
|
||||
if(_M_ARM64)
|
||||
message(WARNING "
|
||||
*************** UNSUPPORTED CONFIGURATION ***************
|
||||
|
||||
Apple Silicon support in PCSX2 is INCOMPLETE. There are
|
||||
currently no EE/VU/IOP recompilers, and games will run
|
||||
VERY slow. There is no date for completion yet, you
|
||||
should set -DCMAKE_OSX_ARCHITECTURES=x86_64 for now,
|
||||
unless you want to work on the recompilers.
|
||||
|
||||
We also ask that you read https://dont-ship.it/.
|
||||
|
||||
*********************************************************")
|
||||
endif()
|
||||
|
||||
Binary file not shown.
@@ -1980,6 +1980,8 @@ SCAJ-20116:
|
||||
SCAJ-20117:
|
||||
name: "Fu-un Bakumatsu-den"
|
||||
region: "NTSC-Unk"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SCAJ-20118:
|
||||
name: "ラジアータ ストーリーズ"
|
||||
name-sort: "らじあーた すとーりーず"
|
||||
@@ -6268,19 +6270,23 @@ SCES-53880:
|
||||
region: "PAL-E"
|
||||
SCES-53881:
|
||||
name: "Buzz! Le Grand Quiz"
|
||||
name-en: "Buzz! The Big Quiz"
|
||||
region: "PAL-F"
|
||||
SCES-53882:
|
||||
name: "Buzz! The Big Quiz"
|
||||
name: "Buzz! Il Superquiz"
|
||||
name-en: "Buzz! The Big Quiz"
|
||||
region: "PAL-I"
|
||||
gameFixes:
|
||||
- EETimingHack # Fixes freezes.
|
||||
SCES-53883:
|
||||
name: "Buzz! The Big Quiz"
|
||||
name: "Buzz! Das große Quiz"
|
||||
name-en: "Buzz! The Big Quiz"
|
||||
region: "PAL-G"
|
||||
gameFixes:
|
||||
- EETimingHack # Fixes freezes.
|
||||
SCES-53884:
|
||||
name: "Buzz! El GRAN Reto"
|
||||
name: "Buzz! El Gran Reto"
|
||||
name-en: "Buzz! The Big Quiz"
|
||||
region: "PAL-S"
|
||||
compat: 2
|
||||
gameFixes:
|
||||
@@ -6317,10 +6323,12 @@ SCES-53926:
|
||||
name: "Buzz! The Big Quiz"
|
||||
region: "PAL-SC"
|
||||
SCES-53927:
|
||||
name: "Buzz! The Big Quiz"
|
||||
name: "Buzz! Suuri Tietovisa"
|
||||
name-en: "Buzz! The Big Quiz"
|
||||
region: "PAL-FI"
|
||||
SCES-53929:
|
||||
name: "Buzz! The Big Quiz"
|
||||
name: "Buzz! O Grande Quiz"
|
||||
name-en: "Buzz! The Big Quiz"
|
||||
region: "PAL-P"
|
||||
SCES-53931:
|
||||
name: "Shinobido - Way of the Ninja"
|
||||
@@ -6436,19 +6444,23 @@ SCES-54258:
|
||||
name: "Buzz! The Sports Quiz"
|
||||
region: "PAL-E"
|
||||
SCES-54259:
|
||||
name: "Buzz! The Sports Quiz"
|
||||
name: "Buzz! Das Sport-Quiz"
|
||||
name-en: "Buzz! The Sports Quiz"
|
||||
region: "PAL-G"
|
||||
SCES-54260:
|
||||
name: "Buzz! Le Quiz du Sport"
|
||||
name-en: "Buzz! The Sports Quiz"
|
||||
region: "PAL-F"
|
||||
SCES-54261:
|
||||
name: "Buzz! The Sports Quiz"
|
||||
region: "PAL-I"
|
||||
SCES-54262:
|
||||
name: "Buzz! - El Gran Concurso de Deportes"
|
||||
name: "Buzz! El Gran Concurso de Deportes"
|
||||
name-en: "Buzz! The Sports Quiz"
|
||||
region: "PAL-S"
|
||||
SCES-54263:
|
||||
name: "Buzz! The Sports Quiz"
|
||||
name: "Buzz! O Quiz Desportivo"
|
||||
name-en: "Buzz! The Sports Quiz"
|
||||
region: "PAL-P"
|
||||
SCES-54264:
|
||||
name: "Buzz! The Sports Quiz"
|
||||
@@ -6460,7 +6472,8 @@ SCES-54266:
|
||||
name: "Buzz! The Sports Quiz"
|
||||
region: "PAL-SC"
|
||||
SCES-54267:
|
||||
name: "Buzz! The Sports Quiz"
|
||||
name: "Buzz! Sporttivisa"
|
||||
name-en: "Buzz! The Sports Quiz"
|
||||
region: "PAL-FI"
|
||||
SCES-54268:
|
||||
name: "Buzz! The Sports Quiz"
|
||||
@@ -6496,24 +6509,29 @@ SCES-54496:
|
||||
region: "PAL-E"
|
||||
SCES-54497:
|
||||
name: "Buzz! Le Mega Quiz"
|
||||
name-en: "Buzz! The Mega Quiz"
|
||||
region: "PAL-F"
|
||||
SCES-54498:
|
||||
name: "Buzz! The Mega Quiz"
|
||||
region: "PAL-I"
|
||||
SCES-54499:
|
||||
name: "Buzz! Das Mega-Quiz"
|
||||
name-en: "Buzz! The Mega Quiz"
|
||||
region: "PAL-G"
|
||||
SCES-54500:
|
||||
name: "Buzz! El Mega Concurso"
|
||||
name-en: "Buzz! The Mega Quiz"
|
||||
region: "PAL-S"
|
||||
SCES-54501:
|
||||
name: "Buzz! The Mega Quiz"
|
||||
region: "PAL-A"
|
||||
SCES-54503:
|
||||
name: "Buzz! Megavisa"
|
||||
name-en: "Buzz! The Mega Quiz"
|
||||
region: "PAL-FI"
|
||||
SCES-54504:
|
||||
name: "Buzz! O Mega Quiz"
|
||||
name-en: "Buzz! The Mega Quiz"
|
||||
region: "PAL-P"
|
||||
SCES-54505:
|
||||
name: "Buzz! The Mega Quiz"
|
||||
@@ -13217,9 +13235,7 @@ SLAJ-25018:
|
||||
region: "NTSC-HK"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # Improves visual clarity whilst upscaling.
|
||||
roundSprite: 1 # Reduces graphics garbage on UI whilst upscaling.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLAJ-25019:
|
||||
name: "The Lord of the Rings - The Return of the King"
|
||||
region: "NTSC-C"
|
||||
@@ -22704,9 +22720,7 @@ SLES-53191:
|
||||
name: "Kaido Racer"
|
||||
region: "PAL-M5"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes ghosting during Rain/Storm.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # De-blurs the 3D image.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLES-53192:
|
||||
name: "Tim Burton's The Nightmare Before Christmas - Oogie's Reveng"
|
||||
name-sort: "Nightmare Before Christmas, The - Tim Burton's"
|
||||
@@ -24781,6 +24795,11 @@ SLES-53820:
|
||||
SLES-53821:
|
||||
name: "Radio Helicopter II"
|
||||
region: "PAL-E"
|
||||
patches:
|
||||
9A695202:
|
||||
content: |-
|
||||
comment=Patch that nops a branch instruction causing a freeze.
|
||||
patch=1,EE,001799AC,word,00000000
|
||||
SLES-53824:
|
||||
name: "Trapt"
|
||||
region: "PAL-E"
|
||||
@@ -24964,9 +24983,7 @@ SLES-53900:
|
||||
name: "Kaido Racer 2"
|
||||
region: "PAL-M3"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes ghosting during Storm.
|
||||
alignSprite: 1 # Fixes black lines when upscaling.
|
||||
forceEvenSpritePosition: 1 # De-blurs the 3D image.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLES-53901:
|
||||
name: "Torino 2006"
|
||||
region: "PAL-M5"
|
||||
@@ -31331,9 +31348,7 @@ SLKA-25063:
|
||||
name-en: "Kaido Battle"
|
||||
region: "NTSC-K"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes ghosting during Rain.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # De-blurs the 3D image.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLKA-25064:
|
||||
name: "천주 3 천벌"
|
||||
name-en: "Tenchu - Wrath of Heaven"
|
||||
@@ -31428,9 +31443,7 @@ SLKA-25084:
|
||||
region: "NTSC-K"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # Improves visual clarity whilst upscaling.
|
||||
roundSprite: 1 # Reduces graphics garbage on UI whilst upscaling.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLKA-25085:
|
||||
name: "진・삼국무쌍 3 맹장전"
|
||||
name-en: "Jin Samguk Mussang 3 - Maengjangjeon"
|
||||
@@ -31718,9 +31731,7 @@ SLKA-25146:
|
||||
name-en: "Kaido Battle 2 - Chain Reaction"
|
||||
region: "NTSC-K"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes ghosting during Rain/Storm.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # De-blurs the 3D image.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLKA-25148:
|
||||
name: "MVP 베이스볼 2004"
|
||||
name-en: "MVP Baseball 2004"
|
||||
@@ -32307,9 +32318,11 @@ SLKA-25255:
|
||||
name-en: "Mobile Suit Gundam Seed - Never Ending Tomorrow"
|
||||
region: "NTSC-K"
|
||||
SLKA-25257:
|
||||
name: "풍운 막말전" # Undumped on ReDump as of 2025-08-28
|
||||
name-en: "Fu-un Bakumatsu Den"
|
||||
name: "풍운 막말전" # Undumped on ReDump as of 2025-08-28
|
||||
name-en: "Fu-un Bakumatsu-den"
|
||||
region: "NTSC-K"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLKA-25258:
|
||||
name: "요시츠네 영웅전"
|
||||
name-sort: "Yoshitsune Yeongungjeon"
|
||||
@@ -35570,9 +35583,7 @@ SLPM-60195:
|
||||
name-en: "Kaidō Battle - Nikko, Haruna, Rokko, Hakone [Trial]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes ghosting during Rain.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # De-blurs the 3D image.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLPM-60197:
|
||||
name: "エキサイティングプロレス 4 [体験版]"
|
||||
name-sort: "えきさいてぃんぐぷろれす4 [たいけんばん]"
|
||||
@@ -35616,9 +35627,7 @@ SLPM-60206:
|
||||
name-en: "Shutokou Battle 01 [Trial]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # Improves visual clarity whilst upscaling.
|
||||
roundSprite: 1 # Reduces graphics garbage on UI whilst upscaling.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLPM-60207:
|
||||
name: "ロックマンX7 [体験版]"
|
||||
name-sort: "ろっくまんえっくす7 [たいけんばん]"
|
||||
@@ -35713,10 +35722,12 @@ SLPM-60220:
|
||||
name-en: "Hajime no Ippo II - Victorious Road [Trial]"
|
||||
region: "NTSC-J"
|
||||
SLPM-60225:
|
||||
name: "風雲新撰組 [体験版]"
|
||||
name: "風雲 新撰組 [体験版]"
|
||||
name-sort: "ふううん しんせんぐみ [たいけんばん]"
|
||||
name-en: "Fuuun Shinsengumi [Trial]"
|
||||
name-en: "Fu-un Shinsen-gumi [Trial]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLPM-60226:
|
||||
name: "シャドウハーツⅡ [体験版]"
|
||||
name-sort: "しゃどうはーつ2 [たいけんばん]"
|
||||
@@ -35732,9 +35743,7 @@ SLPM-60228:
|
||||
name-en: "Kaidō Battle 2 - Chain Reaction [Trial]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes ghosting during Rain/Storm.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # De-blurs the 3D image.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLPM-60237:
|
||||
name: "電車でGO!FINAL [体験版]"
|
||||
name-sort: "でんしゃでごーFINAL [たいけんばん]"
|
||||
@@ -36484,10 +36493,12 @@ SLPM-61095:
|
||||
name-en: "Dengeki PS2 / DengekiPlayStation D74"
|
||||
region: "NTSC-J"
|
||||
SLPM-61096:
|
||||
name: "風雲幕末伝 [体験版]"
|
||||
name: "風雲 幕末伝 [体験版]"
|
||||
name-sort: "ふううんばくまつでん [たいけんばん]"
|
||||
name-en: "Fuuun Bakumatsu-den [Trial]"
|
||||
name-en: "Fu-un Bakumatsu-den [Trial]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLPM-61097:
|
||||
name: "電撃PS2 / 電撃PlayStation D75"
|
||||
name-sort: "でんげき PS2 でんげきPlayStation D75"
|
||||
@@ -36596,8 +36607,7 @@ SLPM-61115:
|
||||
name-en: "Racing Battle - C1 Grand Prix [Trial]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # De-blurs the 3D image.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLPM-61116:
|
||||
name: "冒険王ビィト ダークネスセンチュリー [体験版]"
|
||||
name-sort: "ぼうけんおうびぃと だーくねすせんちゅりー [たいけんばん]"
|
||||
@@ -36644,9 +36654,7 @@ SLPM-61121:
|
||||
name-en: "Kaido Touge no Densetsu [Trial]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes ghosting during Rain/Storm.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # De-blurs the 3D image.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLPM-61122:
|
||||
name: "電撃PS2 / 電撃PlayStation D81"
|
||||
name-sort: "でんげき PS2 でんげきPlayStation D81"
|
||||
@@ -40089,6 +40097,11 @@ SLPM-62624:
|
||||
name-sort: "ぷちこぷたー2"
|
||||
name-en: "Petit Copter 2"
|
||||
region: "NTSC-J"
|
||||
patches:
|
||||
9A695202:
|
||||
content: |-
|
||||
comment=Patch that nops a branch instruction causing a freeze.
|
||||
patch=1,EE,001799B8,word,00000000
|
||||
SLPM-62625:
|
||||
name: "鬼浜爆走愚連隊 激闘編"
|
||||
name-sort: "おにはまばくそうぐれんたい げきとうへん"
|
||||
@@ -42485,9 +42498,7 @@ SLPM-65246:
|
||||
name-en: "Kaidō Battle - Nikko, Haruna, Rokko, Hakone"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes ghosting during Rain.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # De-blurs the 3D image.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLPM-65247:
|
||||
name: "三國志戦記2"
|
||||
name-sort: "さんごくしせんき2"
|
||||
@@ -42862,9 +42873,7 @@ SLPM-65308:
|
||||
region: "NTSC-J"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # Improves visual clarity whilst upscaling.
|
||||
roundSprite: 1 # Reduces graphics garbage on UI whilst upscaling.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLPM-65309:
|
||||
name: "Splashdown [PlayStation2 the Best]"
|
||||
name-sort: "すぷらっしゅだうん [PlayStation2 the Best]"
|
||||
@@ -43897,10 +43906,12 @@ SLPM-65493:
|
||||
name-en: "Hurrah! Sailor [Limited Edition]"
|
||||
region: "NTSC-J"
|
||||
SLPM-65494:
|
||||
name: "風雲 新撰組"
|
||||
name: "風雲 新撰組"
|
||||
name-sort: "ふううん しんせんぐみ"
|
||||
name-en: "Fuuun Shinsengumi"
|
||||
name-en: "Fu-un Shinsen-gumi"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLPM-65495:
|
||||
name: "モンスターハンター"
|
||||
name-sort: "もんすたーはんたー"
|
||||
@@ -44038,9 +44049,7 @@ SLPM-65514:
|
||||
name-en: "Kaidō Battle 2 - Chain Reaction"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes ghosting during Rain/Storm.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # De-blurs the 3D image.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLPM-65515:
|
||||
name: "サクラ大戦物語 ~ミステリアス巴里~"
|
||||
name-sort: "さくらたいせんものがたり ~みすてりあすぱり~"
|
||||
@@ -45769,11 +45778,13 @@ SLPM-65812:
|
||||
name-en: "Bakushou! Jinsei Kaidou - NOVA Usagi ga Miteruzo! [TAITO BEST]"
|
||||
region: "NTSC-J"
|
||||
SLPM-65813:
|
||||
name: "風雲幕末伝"
|
||||
name: "風雲 幕末伝"
|
||||
name-sort: "ふううん ばくまつでん"
|
||||
name-en: "Fu-un Bakumatsu Den"
|
||||
name-en: "Fu-un Bakumatsu-den"
|
||||
region: "NTSC-J"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLPM-65815:
|
||||
name: "トム・クランシーシリーズ スプリンターセル パンドラトゥモロー"
|
||||
name-sort: "とむくらんしーしりーず すぷりんたーせる ぱんどらとぅもろー"
|
||||
@@ -46239,8 +46250,7 @@ SLPM-65897:
|
||||
name-en: "Racing Battle - C1 Grand Prix"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # De-blurs the 3D image.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLPM-65898:
|
||||
name: "キャッスルファンタジア エレンシア戦記 DXパック"
|
||||
name-sort: "きゃっするふぁんたじあ えれんしあせんき DXぱっく"
|
||||
@@ -46991,9 +47001,7 @@ SLPM-66022:
|
||||
name-en: "Kaido - Touge no Densetsu"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes ghosting during Rain/Storm.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # De-blurs the 3D image.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLPM-66023:
|
||||
name: "ふしぎ遊戯 玄武開伝 外伝 鏡の巫女 [限定版]"
|
||||
name-sort: "ふしぎゆうぎ げんぶかいでん がいでん かがみのみこ [げんていばん]"
|
||||
@@ -53742,10 +53750,12 @@ SLPM-74201:
|
||||
- "SLPM-65286"
|
||||
- "BWNETCNF"
|
||||
SLPM-74202:
|
||||
name: "風雲 新撰組 [PlayStation2 the Best]"
|
||||
name: "風雲 新撰組 [PlayStation2 the Best]"
|
||||
name-sort: "ふううん しんせんぐみ [PlayStation2 the Best]"
|
||||
name-en: "Fuuun Shinsengumi [PlayStation2 the Best]"
|
||||
name-en: "Fu-un Shinsen-gumi [PlayStation2 the Best]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLPM-74204:
|
||||
name: "首都高バトル01 [PlayStation2 the Best]"
|
||||
name-sort: "しゅとこうばとる01 [PlayStation2 the Best]"
|
||||
@@ -53753,9 +53763,7 @@ SLPM-74204:
|
||||
region: "NTSC-J"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # Improves visual clarity whilst upscaling.
|
||||
roundSprite: 1 # Reduces graphics garbage on UI whilst upscaling.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLPM-74205:
|
||||
name: "真・女神転生Ⅲ - NOCTURNE [PlayStation2 the Best]"
|
||||
name-sort: "しんめがみてんせい3 - NOCTURNE [PlayStation2 the Best]"
|
||||
@@ -53894,10 +53902,12 @@ SLPM-74227:
|
||||
halfPixelOffset: 4 # Fixes misaligned lighting and bloom.
|
||||
bilinearUpscale: 1 # Smooths out bloom.
|
||||
SLPM-74228:
|
||||
name: "風雲幕末伝 [PlayStation2 the Best]"
|
||||
name: "風雲 幕末伝 [PlayStation2 the Best]"
|
||||
name-sort: "ふううんばくまつでん [PlayStation2 the Best]"
|
||||
name-en: "Fuuun Bakumatsu-den [PlayStation2 the Best]"
|
||||
name-en: "Fu-un Bakumatsu-den [PlayStation2 the Best]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLPM-74229:
|
||||
name: "バイオハザード4 [PlayStation2 the Best]"
|
||||
name-sort: "ばいおはざーど4 [PlayStation2 the Best]"
|
||||
@@ -63478,10 +63488,12 @@ SLPS-73227:
|
||||
name-en: "Another Century's Episode [PlayStation2 the Best]"
|
||||
region: "NTSC-J"
|
||||
SLPS-73228:
|
||||
name: "風雲幕末伝 [PlayStation2 the Best]"
|
||||
name: "風雲 幕末伝 [PlayStation2 the Best]"
|
||||
name-sort: "ふううんばくまつでん [PlayStation2 the Best]"
|
||||
name-en: "Fuuun Bakumatsu-den [PlayStation2 the Best]"
|
||||
name-en: "Fu-un Bakumatsu-den [PlayStation2 the Best]"
|
||||
region: "NTSC-J"
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLPS-73229:
|
||||
name: "ベルウィックサーガ [PlayStation2 the Best]"
|
||||
name-sort: "べるうぃっくさーが [PlayStation2 the Best]"
|
||||
@@ -68121,9 +68133,7 @@ SLUS-20831:
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # Improves visual clarity whilst upscaling.
|
||||
roundSprite: 1 # Reduces graphics garbage on UI whilst upscaling.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLUS-20833:
|
||||
name: "Mega Man Anniversary Collection"
|
||||
region: "NTSC-U"
|
||||
@@ -70592,9 +70602,7 @@ SLUS-21236:
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes ghosting during Rain.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # Fixes double image.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLUS-21237:
|
||||
name: "AND 1 Streetball"
|
||||
region: "NTSC-U"
|
||||
@@ -71776,9 +71784,7 @@ SLUS-21394:
|
||||
region: "NTSC-U"
|
||||
compat: 5
|
||||
gsHWFixes:
|
||||
halfPixelOffset: 2 # Fixes ghosting during Rain/Storm.
|
||||
alignSprite: 1 # Fixes vertical lines.
|
||||
forceEvenSpritePosition: 1 # De-blurs the 3D image.
|
||||
halfPixelOffset: 5 # Fixes vertical lines.
|
||||
SLUS-21395:
|
||||
name: "Avatar - The Last Airbender"
|
||||
region: "NTSC-U"
|
||||
|
||||
@@ -236,7 +236,7 @@
|
||||
03000000ac0500005b05000000000000,GameSir G3w,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,
|
||||
03000000ac0500002d02000000000000,GameSir G4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000ac0500004d04000000000000,GameSir G4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000373500002210000000000000,GameSir G7 Pro,a:b0,b:b1,x:b3,y:b4,back:b10,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,lefty:a2,rightx:a3,righty:a4,lefttrigger:b8,righttrigger:b9,platform:Window,
|
||||
03000000373500002210000000000000,GameSir G7 Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000ac0500001a06000000000000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000373500009410000000000000,GameSir Tegenaria Lite,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
030000004c0e00001035000000000000,Gamester,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,
|
||||
@@ -246,8 +246,6 @@
|
||||
03000000b62500000100000000000000,Gametel GT004 01,a:b3,b:b0,dpdown:b10,dpleft:b9,dpright:b8,dpup:b11,leftshoulder:b4,rightshoulder:b5,start:b7,x:b1,y:b2,platform:Windows,
|
||||
030000008f0e00001411000000000000,Gamo2 Divaller,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:Windows,
|
||||
03000000120c0000a857000000000000,Gator Claw,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000c21100000791000000000000,Nacon GC101 1.03,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
|
||||
03000000c9110000f055000000000000,Nacon GC100XF,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
|
||||
030000008305000009a0000000000000,Genius,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
|
||||
030000008305000031b0000000000000,Genius Maxfire Blaze 3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
|
||||
03000000451300000010000000000000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
|
||||
@@ -367,7 +365,7 @@
|
||||
03000000fd0500002a26000000000000,InterAct Hammerhead FX,a:b3,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b0,y:b1,platform:Windows,
|
||||
03000000fd0500002f26000000000000,InterAct Hammerhead FX,a:b4,b:b5,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b1,y:b2,platform:Windows,
|
||||
03000000fd0500005302000000000000,InterAct ProPad,a:b3,b:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Windows,
|
||||
03000000ac0500002c02000000000000,Ipega Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000ac0500002c02000000000000,Ipega Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000491900000204000000000000,Ipega PG9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000491900000304000000000000,Ipega PG9087,+righty:+a5,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows,
|
||||
030000007e0500000620000000000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows,
|
||||
@@ -464,6 +462,8 @@
|
||||
03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
|
||||
03000000091200004488000000000000,MUSIA PlayStation 2 Input Display,a:b0,b:b2,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b6,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:b11,rightx:a2,righty:a3,start:b5,x:b1,y:b3,platform:Windows,
|
||||
03000000f70600000100000000000000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,platform:Windows,
|
||||
03000000c9110000f055000000000000,Nacon GC100XF,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
|
||||
03000000c21100000791000000000000,Nacon GC101 1.03,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
|
||||
030000006b140000010c000000000000,Nacon GC400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
|
||||
030000006b1400001106000000000000,Nacon Revolution 3 PS4 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:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,
|
||||
0300000085320000170d000000000000,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:Windows,
|
||||
@@ -697,7 +697,7 @@
|
||||
03000000730700000601000000000000,Sega Saturn Controller,a:b0,b:b1,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,
|
||||
03000000b40400000a01000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,
|
||||
030000003b07000004a1000000000000,SFX,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Windows,
|
||||
03000000632500002705000000000000,ShanWan Q36,a:b0,b:b1,x:b3,y:b4,leftshoulder:b6,rightshoulder:b7,lefttrigger:b8,righttrigger:b9,back:b10,start:b11,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,platform:Windows,
|
||||
03000000632500002705000000000000,ShanWan Q36,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,righttrigger:b9,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000f82100001900000000000000,Shogun Bros Chameleon X1,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
|
||||
03000000120c00001c1e000000000000,SnakeByte 4S PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,
|
||||
03000000140300000918000000000000,SNES Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
|
||||
@@ -888,6 +888,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000c82d00000851000000010000,8BitDo P30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
03000000c82d00000660000000010000,8BitDo Pro 2,a:b1,b:b0,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:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000660000000020000,8BitDo Pro 2,a:b1,b:b0,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:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000960000000000000,8BitDo Pro 3,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000131000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000231000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c82d00000331000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
@@ -1174,7 +1175,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000c82d00000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000c82d00006a28000000010000,8BitDo GameCube,a:b0,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b9,paddle2:b8,rightshoulder:b10,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b1,y:b4,platform:Linux,
|
||||
03000000c82d00001251000011010000,8BitDo Lite 2,a:b1,b:b0,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:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c82d00001251000011010000,8BitDo Lite 2,a:b1,b:b0,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:b4,y:b3,platform:Linux,
|
||||
05000000c82d00001251000000010000,8BitDo Lite 2,a:b1,b:b0,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:b4,y:b3,platform:Linux,
|
||||
03000000c82d00001151000011010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
05000000c82d00001151000000010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
@@ -1352,7 +1353,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000280400000140000000010000,Gravis GamePad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
|
||||
030000008f0e00000610000000010000,GreenAsia Electronics Controller,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:a2,start:b11,x:b3,y:b0,platform:Linux,
|
||||
030000008f0e00001200000010010000,GreenAsia Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,
|
||||
0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
0500000047532067616d657061640000,GS Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
03000000f0250000c383000010010000,GT VX2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
|
||||
030000008a2e0000dd10000011010000,Hand Held Legend GC Ultimate,a:b0,b:b2,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,misc2:b24,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b1,y:b3,platform:Linux,
|
||||
030000008a2e0000df10000011010000,Hand Held Legend ProGCC,a:b1,b:b0,back:b17,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,guide:b18,leftshoulder:b10,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b19,paddle1:b13,paddle2:b12,rightshoulder:b11,rightstick:b9,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b3,y:b2,platform:Linux,
|
||||
@@ -1397,11 +1398,12 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000242e00006a38000010010000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Linux,
|
||||
03000000242e00008816000001010000,Hyperkin X91,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,
|
||||
03000000f00300008d03000011010000,HyperX Clutch,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,
|
||||
05000000f00300008d04000000010000,HyperX Clutch,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,
|
||||
03000000830500006020000010010000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,
|
||||
03000000d80400004bea000011010000,icedragon.io STAC Dance Pad,a:b0,b:b1,x:b2,y:b3,back:b4,platform:Linux,
|
||||
03000000d80400004aea000011010000,icedragon.io STAC Dance Pad,a:b0,b:b1,x:b2,y:b3,back:b4,platform:Linux,
|
||||
030000008a2e0000d910000011010000,icedragon.io STAC2 Dance Pad,a:b0,b:b1,x:b2,y:b3,back:b4,platform:Linux,
|
||||
030000008a2e0000e910000011010000,icedragon.io STAC2 Dance Pad,a:b8,b:b9,x:b10,y:b11,back:b12,platform:Linux,
|
||||
03000000d80400004aea000011010000,icedragon.io STAC Dance Pad,a:b0,b:b1,back:b4,x:b2,y:b3,platform:Linux,
|
||||
03000000d80400004bea000011010000,icedragon.io STAC Dance Pad,a:b0,b:b1,back:b4,x:b2,y:b3,platform:Linux,
|
||||
030000008a2e0000d910000011010000,icedragon.io STAC2 Dance Pad,a:b0,b:b1,back:b4,x:b2,y:b3,platform:Linux,
|
||||
030000008a2e0000e910000011010000,icedragon.io STAC2 Dance Pad,a:b8,b:b9,back:b12,x:b10,y:b11,platform:Linux,
|
||||
030000008f0e00001330000001010000,iCode Retro Adapter,b:b3,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b1,start:b7,x:b2,y:b0,platform:Linux,
|
||||
050000006964726f69643a636f6e0000,idroidcon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000b50700001503000010010000,Impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,
|
||||
@@ -1487,6 +1489,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
030000005e040000ea02000008040000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
030000005e040000ea0200000f050000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
060000005e040000120b000009050000,Microsoft Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
060000005e040000120b000014050000,Microsoft Xbox One,a:b0,b:b1,x:b2,y:b3,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux
|
||||
030000005e040000e302000003020000,Microsoft Xbox One Elite,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
030000005e040000000b000007040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
030000005e040000000b000008040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
@@ -1504,7 +1507,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
05000000d6200000e589000001000000,Moga 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,
|
||||
05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,
|
||||
05000000d62000007162000001000000,Moga Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,
|
||||
03000000c82d00000960000000000000,8BitDo Pro 3,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Mac OS X,
|
||||
03000000c62400002b89000011010000,MOGA XP5A Plus,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,
|
||||
05000000c62400002a89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b22,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,
|
||||
05000000c62400001a89000000010000,MOGA XP5X Plus,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,
|
||||
@@ -1512,9 +1514,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
030000005e0400008e02000010020000,MSI GC20 V2,a:b0,b:b1,back:b6,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,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,
|
||||
03000000f70600000100000000010000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,platform:Linux,
|
||||
030000006b1400000906000014010000,Nacon Asymmetric Wireless PS4 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,
|
||||
030000006b140000010c000010010000,Nacon GC400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
0300000085320000030c000011010000,Nacon GC100,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,
|
||||
03000000853200000706000012010000,Nacon GC100,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,
|
||||
030000006b140000010c000010010000,Nacon GC400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
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,
|
||||
@@ -1788,9 +1790,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
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,
|
||||
03000000100800000100000010010000,Twin PS2 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,
|
||||
03000000790000002601000011010000,SNES Controller,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,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,
|
||||
03000000100800000300000010010000,USB Gamepad,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,
|
||||
03000000790000000600000007010000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,
|
||||
03000000790000000600000007010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,
|
||||
03000000790000001100000000010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux,
|
||||
03000000790000001a18000011010000,Venom PS4 Arcade Joystick,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,
|
||||
03000000790000001b18000011010000,Venom PS4 Arcade Joystick,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,
|
||||
@@ -1800,17 +1803,17 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
05000000434f4d4d414e440000000000,VX Gaming Command Series,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
0000000058626f782033363020576900,Xbox 360 Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,
|
||||
030000005e0400001907000000010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,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,
|
||||
030000005e0400008e02000000010000,Xbox 360 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,
|
||||
030000005e0400008e02000002010000,Xbox 360 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,
|
||||
030000005e0400008e02000010010000,Xbox 360 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,
|
||||
030000005e0400008e02000014010000,Xbox 360 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,
|
||||
030000005e0400008e02000047010000,Xbox 360 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,
|
||||
030000005e0400008e02000072050000,Xbox 360 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,
|
||||
030000005e0400009102000007010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,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,
|
||||
030000005e040000a102000000010000,Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,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,
|
||||
030000005e040000a102000007010000,Xbox 360 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,
|
||||
030000005e040000a102000030060000,Xbox 360 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,
|
||||
030000006f0e00001503000000020000,Xbox 360 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,
|
||||
030000005e0400008e02000000010000,Xbox 360 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,
|
||||
030000005e0400008e02000010010000,Xbox 360 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,
|
||||
030000005e0400008e02000002010000,Xbox 360 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,
|
||||
030000005e0400008e02000014010000,Xbox 360 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,
|
||||
030000005e0400008e02000047010000,Xbox 360 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,
|
||||
030000005e0400008e02000072050000,Xbox 360 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,
|
||||
030000005e040000a102000014010000,Xbox 360 Receiver,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,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,
|
||||
0000000058626f782047616d65706100,Xbox 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:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
|
||||
030000005e0400000202000000010000,Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,
|
||||
|
||||
@@ -139,8 +139,12 @@ struct PS_OUTPUT
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#if PS_ZCLAMP || PS_ZFLOOR
|
||||
float depth : SV_DepthLessEqual;
|
||||
#if (PS_ZFLOOR || PS_ZCLAMP)
|
||||
#if PS_HAS_CONSERVATIVE_DEPTH
|
||||
float depth : SV_DepthLessEqual;
|
||||
#else
|
||||
float depth : SV_Depth;
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ layout(binding = 3) uniform sampler2D img_prim_min;
|
||||
//layout(pixel_center_integer) in vec4 gl_FragCoord;
|
||||
#endif
|
||||
|
||||
#if (PS_ZFLOOR || PS_ZCLAMP) && HAS_CONSERVATIVE_DEPTH
|
||||
#if (PS_ZFLOOR || PS_ZCLAMP) && PS_HAS_CONSERVATIVE_DEPTH
|
||||
layout(depth_less) out float gl_FragDepth;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ disable_compiler_warnings_for_target(speex)
|
||||
|
||||
# Find the Qt components that we need.
|
||||
if(ENABLE_QT_UI)
|
||||
find_package(Qt6 6.10.0 COMPONENTS CoreTools Core GuiTools Gui WidgetsTools Widgets LinguistTools REQUIRED)
|
||||
find_package(Qt6 6.10.1 COMPONENTS CoreTools Core GuiTools Gui WidgetsTools Widgets LinguistTools REQUIRED)
|
||||
|
||||
if(NOT WIN32 AND NOT APPLE)
|
||||
if (Qt6_VERSION VERSION_GREATER_EQUAL 6.10.0)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "common/Threading.h"
|
||||
#include "common/WindowInfo.h"
|
||||
#include "common/HostSys.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include <csignal>
|
||||
#include <cstring>
|
||||
@@ -19,12 +20,15 @@
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <thread>
|
||||
#include <time.h>
|
||||
#include <mach/mach_init.h>
|
||||
#include <mach/mach_port.h>
|
||||
#include <mach/mach_time.h>
|
||||
#include <mach/mach_vm.h>
|
||||
#include <mach/message.h>
|
||||
#include <mach/task.h>
|
||||
#include <mach/thread_state.h>
|
||||
#include <mach/vm_map.h>
|
||||
#include <mutex>
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
@@ -294,7 +298,7 @@ static CPUInfo CalcCPUInfo()
|
||||
std::vector<DarwinMisc::CPUClass> classes = DarwinMisc::GetCPUClasses();
|
||||
out.num_clusters = static_cast<u32>(classes.size());
|
||||
out.num_big_cores = classes.empty() ? 0 : classes[0].num_physical;
|
||||
out.num_threads = classes.empty() ? 0 : classes[0].num_logical;
|
||||
out.num_threads = classes.empty() ? 0 : classes[0].num_logical;
|
||||
out.num_small_cores = 0;
|
||||
for (std::size_t i = 1; i < classes.size(); i++)
|
||||
{
|
||||
@@ -568,15 +572,206 @@ void HostSys::EndCodeWrite()
|
||||
|
||||
#endif // _M_ARM64
|
||||
|
||||
#define USE_MACH_EXCEPTION_PORTS
|
||||
|
||||
namespace PageFaultHandler
|
||||
{
|
||||
#ifdef USE_MACH_EXCEPTION_PORTS
|
||||
static void SignalHandler(mach_port_t port);
|
||||
#else
|
||||
static void SignalHandler(int sig, siginfo_t* info, void* ctx);
|
||||
#endif
|
||||
|
||||
static std::recursive_mutex s_exception_handler_mutex;
|
||||
static bool s_in_exception_handler = false;
|
||||
static bool s_installed = false;
|
||||
} // namespace PageFaultHandler
|
||||
|
||||
#ifdef USE_MACH_EXCEPTION_PORTS
|
||||
|
||||
#if defined(_M_X86)
|
||||
#define THREAD_STATE64_COUNT x86_THREAD_STATE64_COUNT
|
||||
#define THREAD_STATE64 x86_THREAD_STATE64
|
||||
#define thread_state64_t x86_thread_state64_t
|
||||
#elif defined(_M_ARM64)
|
||||
#define THREAD_STATE64_COUNT ARM_THREAD_STATE64_COUNT
|
||||
#define THREAD_STATE64 ARM_THREAD_STATE64
|
||||
#define thread_state64_t arm_thread_state64_t
|
||||
#else
|
||||
#error Unknown Darwin Platform
|
||||
#endif
|
||||
|
||||
void PageFaultHandler::SignalHandler(mach_port_t port)
|
||||
{
|
||||
Threading::SetNameOfCurrentThread("Mach Exception Thread");
|
||||
|
||||
#pragma pack(4)
|
||||
struct
|
||||
{
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[THREAD_STATE64_COUNT];
|
||||
mach_msg_trailer_t trailer;
|
||||
} msg_in;
|
||||
|
||||
struct
|
||||
{
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[THREAD_STATE64_COUNT];
|
||||
} msg_out;
|
||||
#pragma pack()
|
||||
memset(&msg_in, 0xee, sizeof(msg_in));
|
||||
memset(&msg_out, 0xee, sizeof(msg_out));
|
||||
mach_msg_size_t send_size = 0;
|
||||
mach_msg_option_t option = MACH_RCV_MSG;
|
||||
while (true)
|
||||
{
|
||||
kern_return_t r;
|
||||
if ((r = mach_msg_overwrite(&msg_out.Head, option, send_size, sizeof(msg_in), port,
|
||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL, &msg_in.Head, 0)))
|
||||
{
|
||||
pxFail(fmt::format("CRITICAL: mach_msg_overwrite: {:x}", r).c_str());
|
||||
}
|
||||
|
||||
if (msg_in.Head.msgh_id == MACH_NOTIFY_NO_SENDERS)
|
||||
{
|
||||
// the other thread exited
|
||||
mach_port_deallocate(mach_task_self(), port);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg_in.Head.msgh_id != 2406)
|
||||
{
|
||||
pxFailRel("unknown message received");
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg_in.flavor != THREAD_STATE64)
|
||||
{
|
||||
pxFailRel(fmt::format("unknown flavour {}, expected {}", msg_in.flavor, THREAD_STATE64).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
s_exception_handler_mutex.lock();
|
||||
thread_state64_t* state = (thread_state64_t*)msg_in.old_state;
|
||||
|
||||
HandlerResult result = HandlerResult::ExecuteNextHandler;
|
||||
if (!s_in_exception_handler)
|
||||
{
|
||||
s_in_exception_handler = true;
|
||||
|
||||
#ifdef _M_ARM64
|
||||
result = HandlePageFault(reinterpret_cast<void*>(state->__pc), reinterpret_cast<void*>(msg_in.code[1]), (msg_in.code[0] & 2) != 0);
|
||||
#else
|
||||
result = HandlePageFault(reinterpret_cast<void*>(state->__rip), reinterpret_cast<void*>(msg_in.code[1]), (msg_in.code[0] & 2) != 0);
|
||||
#endif
|
||||
s_in_exception_handler = false;
|
||||
}
|
||||
|
||||
// Set up the reply.
|
||||
msg_out.Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(msg_in.Head.msgh_bits), 0);
|
||||
msg_out.Head.msgh_remote_port = msg_in.Head.msgh_remote_port;
|
||||
msg_out.Head.msgh_local_port = MACH_PORT_NULL;
|
||||
msg_out.Head.msgh_id = msg_in.Head.msgh_id + 100;
|
||||
msg_out.NDR = msg_in.NDR;
|
||||
|
||||
if (result != HandlerResult::ContinueExecution) // cooked
|
||||
{
|
||||
msg_out.RetCode = KERN_FAILURE;
|
||||
msg_out.flavor = 0;
|
||||
msg_out.new_stateCnt = 0;
|
||||
|
||||
// The crash handler on macOS or Linux doesn't use context passed to it
|
||||
// Stubbing it here is fine
|
||||
CrashHandler::CrashSignalHandler(-1, nullptr, nullptr);
|
||||
|
||||
pxFailRel("CrashSignalHandler returned when it should have terminated us!");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Resumes execution right where we left off (re-executes instruction that caused the SIGSEGV)
|
||||
msg_out.RetCode = KERN_SUCCESS;
|
||||
msg_out.flavor = THREAD_STATE64;
|
||||
msg_out.new_stateCnt = THREAD_STATE64_COUNT;
|
||||
memcpy(msg_out.new_state, msg_in.old_state, THREAD_STATE64_COUNT * sizeof(natural_t));
|
||||
}
|
||||
|
||||
msg_out.Head.msgh_size =
|
||||
offsetof(__typeof__(msg_out), new_state) + msg_out.new_stateCnt * sizeof(natural_t);
|
||||
send_size = msg_out.Head.msgh_size;
|
||||
option |= MACH_SEND_MSG;
|
||||
|
||||
s_exception_handler_mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
bool PageFaultHandler::Install(Error* error)
|
||||
{
|
||||
exception_mask_t masks[EXC_TYPES_COUNT];
|
||||
mach_port_t ports[EXC_TYPES_COUNT];
|
||||
exception_behavior_t behaviors[EXC_TYPES_COUNT];
|
||||
thread_state_flavor_t flavors[EXC_TYPES_COUNT];
|
||||
mach_msg_type_number_t count = EXC_TYPES_COUNT;
|
||||
|
||||
kern_return_t r = task_get_exception_ports(mach_task_self(), EXC_MASK_ALL,
|
||||
masks, &count, ports, behaviors, flavors);
|
||||
|
||||
mach_port_t port;
|
||||
if ((r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port)))
|
||||
{
|
||||
pxFailRel(fmt::format("mach_port_allocate: {:x}", r).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::thread sig_thread(PageFaultHandler::SignalHandler, port);
|
||||
sig_thread.detach();
|
||||
|
||||
if ((r = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND)))
|
||||
{
|
||||
mach_port_deallocate(mach_task_self(), port);
|
||||
pxFailRel(fmt::format("mach_port_insert_right: {:x}", r).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, MACH_PORT_NULL, EXCEPTION_DEFAULT, THREAD_STATE_NONE);
|
||||
|
||||
if ((r = thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS, port, EXCEPTION_STATE | MACH_EXCEPTION_CODES, THREAD_STATE64)))
|
||||
{
|
||||
mach_port_deallocate(mach_task_self(), port);
|
||||
pxFailRel(fmt::format("thread_set_exception_ports: {:x}", r).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((r = mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1)))
|
||||
{
|
||||
mach_port_deallocate(mach_task_self(), port);
|
||||
pxFailRel(fmt::format("mach_port_mod_refs: {:x}", r).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
mach_port_t previous;
|
||||
if ((r = mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_NO_SENDERS, 0, port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous)))
|
||||
{
|
||||
mach_port_deallocate(mach_task_self(), port);
|
||||
pxFailRel(fmt::format("mach_port_mod_refs: {:x}", r).c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
s_installed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void PageFaultHandler::SignalHandler(int sig, siginfo_t* info, void* ctx)
|
||||
{
|
||||
#if defined(_M_X86)
|
||||
@@ -644,3 +839,4 @@ bool PageFaultHandler::Install(Error* error)
|
||||
s_installed = true;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -184,7 +184,7 @@ if __name__ == "__main__":
|
||||
args = parser.parse_args()
|
||||
MAX_DIFF_FRAMES = args.maxframes
|
||||
|
||||
outfile = open(args.outfile, "w")
|
||||
outfile = open(args.outfile, "w", encoding="utf-8")
|
||||
write(FILE_HEADER)
|
||||
|
||||
if not check_regression_tests(os.path.realpath(args.baselinedir), os.path.realpath(args.testdir)):
|
||||
|
||||
@@ -144,7 +144,7 @@ void AboutDialog::showHTMLDialog(QWidget* parent, const QString& title, const QS
|
||||
{
|
||||
tb->setSource(QUrl::fromLocalFile(path));
|
||||
}
|
||||
|
||||
|
||||
layout->addWidget(tb, 1);
|
||||
|
||||
QDialogButtonBox* bb = new QDialogButtonBox(QDialogButtonBox::Close, &dialog);
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "AsyncDialogs.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
|
||||
#include <QtWidgets/QPushButton>
|
||||
|
||||
template <typename Value>
|
||||
static std::function<void(std::optional<Value>)> wrapValueCallback(std::function<void(Value)> callback)
|
||||
{
|
||||
return [callback = std::move(callback)](std::optional<Value> input) {
|
||||
if (input.has_value())
|
||||
callback(std::move(*input));
|
||||
};
|
||||
}
|
||||
|
||||
void AsyncDialogs::getText(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
const QString& text,
|
||||
std::function<void(QString)> value_callback)
|
||||
{
|
||||
getText(parent, title, label, QLineEdit::Normal, text, {}, Qt::ImhNone, wrapValueCallback(std::move(value_callback)));
|
||||
}
|
||||
|
||||
void AsyncDialogs::getText(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
QLineEdit::EchoMode echo,
|
||||
const QString& text,
|
||||
Qt::WindowFlags flags,
|
||||
Qt::InputMethodHints input_method_hints,
|
||||
std::function<void(std::optional<QString>)> callback)
|
||||
{
|
||||
QInputDialog* dialog = new QInputDialog(parent, flags);
|
||||
dialog->setWindowTitle(title);
|
||||
dialog->setLabelText(label);
|
||||
dialog->setTextValue(text);
|
||||
dialog->setTextEchoMode(echo);
|
||||
dialog->setInputMethodHints(input_method_hints);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
if (callback)
|
||||
{
|
||||
QObject::connect(dialog, &QDialog::finished, [dialog, callback = std::move(callback)](int result) {
|
||||
callback(result ? std::make_optional<QString>(dialog->textValue()) : std::nullopt);
|
||||
});
|
||||
}
|
||||
|
||||
dialog->open();
|
||||
}
|
||||
|
||||
void AsyncDialogs::getMultiLineText(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
const QString& text,
|
||||
std::function<void(QString)> value_callback)
|
||||
{
|
||||
getMultiLineText(parent, title, label, text, {}, Qt::ImhNone, wrapValueCallback(std::move(value_callback)));
|
||||
}
|
||||
|
||||
void AsyncDialogs::getMultiLineText(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
const QString& text,
|
||||
Qt::WindowFlags flags,
|
||||
Qt::InputMethodHints input_method_hints,
|
||||
std::function<void(std::optional<QString>)> callback)
|
||||
{
|
||||
QInputDialog* dialog = new QInputDialog(parent, flags);
|
||||
dialog->setOptions(QInputDialog::UsePlainTextEditForTextInput);
|
||||
dialog->setWindowTitle(title);
|
||||
dialog->setLabelText(label);
|
||||
dialog->setTextValue(text);
|
||||
dialog->setInputMethodHints(input_method_hints);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
if (callback)
|
||||
{
|
||||
QObject::connect(dialog, &QDialog::finished, [dialog, callback = std::move(callback)](int result) {
|
||||
callback(result ? std::make_optional<QString>(dialog->textValue()) : std::nullopt);
|
||||
});
|
||||
}
|
||||
|
||||
dialog->open();
|
||||
}
|
||||
|
||||
void AsyncDialogs::getItem(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
const QStringList& items,
|
||||
int current,
|
||||
std::function<void(QString)> value_callback)
|
||||
{
|
||||
getItem(parent, title, label, items, current, true, {}, Qt::ImhNone, wrapValueCallback(std::move(value_callback)));
|
||||
}
|
||||
|
||||
void AsyncDialogs::getItem(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
const QStringList& items,
|
||||
int current,
|
||||
bool editable,
|
||||
Qt::WindowFlags flags,
|
||||
Qt::InputMethodHints input_method_hints,
|
||||
std::function<void(std::optional<QString>)> callback)
|
||||
{
|
||||
const QString text(items.value(current));
|
||||
|
||||
QInputDialog* dialog = new QInputDialog(parent, flags);
|
||||
dialog->setWindowTitle(title);
|
||||
dialog->setLabelText(label);
|
||||
dialog->setComboBoxItems(items);
|
||||
dialog->setTextValue(text);
|
||||
dialog->setComboBoxEditable(editable);
|
||||
dialog->setInputMethodHints(input_method_hints);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
if (callback)
|
||||
{
|
||||
QObject::connect(dialog, &QDialog::finished, [dialog, callback = std::move(callback)](int result) {
|
||||
callback(result ? std::make_optional<QString>(dialog->textValue()) : std::nullopt);
|
||||
});
|
||||
}
|
||||
|
||||
dialog->open();
|
||||
}
|
||||
|
||||
void AsyncDialogs::getInt(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
int value,
|
||||
std::function<void(int)> value_callback)
|
||||
{
|
||||
getInt(parent, title, label, value, -2147483647, 2147483647, 1, {}, wrapValueCallback(std::move(value_callback)));
|
||||
}
|
||||
|
||||
void AsyncDialogs::getInt(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
int value,
|
||||
int min_value,
|
||||
int max_value,
|
||||
int step,
|
||||
Qt::WindowFlags flags,
|
||||
std::function<void(std::optional<int>)> callback)
|
||||
{
|
||||
QInputDialog* dialog = new QInputDialog(parent, flags);
|
||||
dialog->setWindowTitle(title);
|
||||
dialog->setLabelText(label);
|
||||
dialog->setIntRange(min_value, max_value);
|
||||
dialog->setIntValue(value);
|
||||
dialog->setIntStep(step);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
if (callback)
|
||||
{
|
||||
QObject::connect(dialog, &QDialog::finished, [dialog, callback = std::move(callback)](int result) {
|
||||
callback(result ? std::make_optional<int>(dialog->intValue()) : std::nullopt);
|
||||
});
|
||||
}
|
||||
|
||||
dialog->open();
|
||||
}
|
||||
|
||||
void AsyncDialogs::getDouble(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
double value,
|
||||
std::function<void(double)> callback)
|
||||
{
|
||||
getDouble(parent, title, label, value, -2147483647, 2147483647, 1, {}, 1, wrapValueCallback(std::move(callback)));
|
||||
}
|
||||
|
||||
void AsyncDialogs::getDouble(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
double value,
|
||||
double min_value,
|
||||
double max_value,
|
||||
int decimals,
|
||||
Qt::WindowFlags flags,
|
||||
double step,
|
||||
std::function<void(std::optional<double>)> callback)
|
||||
{
|
||||
QInputDialog* dialog = new QInputDialog(parent, flags);
|
||||
dialog->setWindowTitle(title);
|
||||
dialog->setLabelText(label);
|
||||
dialog->setDoubleDecimals(decimals);
|
||||
dialog->setDoubleRange(min_value, max_value);
|
||||
dialog->setDoubleValue(value);
|
||||
dialog->setDoubleStep(step);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
if (callback)
|
||||
{
|
||||
QObject::connect(dialog, &QDialog::finished, [dialog, callback = std::move(callback)](int result) {
|
||||
callback(result ? std::make_optional<double>(dialog->doubleValue()) : std::nullopt);
|
||||
});
|
||||
}
|
||||
|
||||
dialog->open();
|
||||
}
|
||||
|
||||
// Based on the QMessageBoxPrivate::showNewMessageBox function from Qt Widgets.
|
||||
static void openAsyncMessageBox(QWidget* parent,
|
||||
QMessageBox::Icon icon,
|
||||
const QString& title,
|
||||
const QString& text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
QMessageBox::StandardButton default_button,
|
||||
std::function<void(QMessageBox::StandardButton)> callback)
|
||||
{
|
||||
// Don't support the fallback for Qt 4.
|
||||
pxAssert(!(default_button && !(buttons & default_button)));
|
||||
|
||||
QMessageBox* message_box = new QMessageBox(icon, title, text, QMessageBox::NoButton, parent);
|
||||
message_box->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
const QDialogButtonBox* button_box = message_box->findChild<QDialogButtonBox*>();
|
||||
pxAssert(button_box);
|
||||
|
||||
u32 mask = QMessageBox::FirstButton;
|
||||
while (mask <= QMessageBox::LastButton)
|
||||
{
|
||||
QMessageBox::StandardButton standard_button =
|
||||
static_cast<QMessageBox::StandardButton>(static_cast<u32>(buttons) & mask);
|
||||
|
||||
mask <<= 1;
|
||||
|
||||
if (standard_button == QMessageBox::NoButton)
|
||||
continue;
|
||||
|
||||
QPushButton* button = message_box->addButton(standard_button);
|
||||
|
||||
if (message_box->defaultButton())
|
||||
continue;
|
||||
|
||||
if ((default_button == QMessageBox::NoButton &&
|
||||
button_box->buttonRole(button) == QDialogButtonBox::AcceptRole) ||
|
||||
(default_button != QMessageBox::NoButton &&
|
||||
standard_button == default_button))
|
||||
message_box->setDefaultButton(button);
|
||||
}
|
||||
|
||||
if (callback)
|
||||
{
|
||||
QObject::connect(message_box, &QMessageBox::finished, [message_box, callback = std::move(callback)](int result) {
|
||||
callback(message_box->standardButton(message_box->clickedButton()));
|
||||
});
|
||||
}
|
||||
|
||||
message_box->open();
|
||||
}
|
||||
|
||||
void AsyncDialogs::information(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& text,
|
||||
std::function<void(QMessageBox::StandardButton)> callback)
|
||||
{
|
||||
information(parent, title, text, QMessageBox::Ok, QMessageBox::NoButton, std::move(callback));
|
||||
}
|
||||
|
||||
void AsyncDialogs::information(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
QMessageBox::StandardButton default_button,
|
||||
std::function<void(QMessageBox::StandardButton)> callback)
|
||||
{
|
||||
openAsyncMessageBox(parent, QMessageBox::Information, title, text, buttons, default_button, std::move(callback));
|
||||
}
|
||||
|
||||
void AsyncDialogs::question(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& text,
|
||||
std::function<void()> yes_callback)
|
||||
{
|
||||
std::function<void(QMessageBox::StandardButton)> callback;
|
||||
if (yes_callback)
|
||||
{
|
||||
callback = [yes_callback = std::move(yes_callback)](QMessageBox::StandardButton result) {
|
||||
if (result == QMessageBox::Yes)
|
||||
yes_callback();
|
||||
};
|
||||
}
|
||||
|
||||
const QMessageBox::StandardButtons buttons(QMessageBox::Yes | QMessageBox::No);
|
||||
question(parent, title, text, buttons, QMessageBox::NoButton, std::move(callback));
|
||||
}
|
||||
|
||||
void AsyncDialogs::question(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
QMessageBox::StandardButton default_button,
|
||||
std::function<void(QMessageBox::StandardButton)> callback)
|
||||
{
|
||||
openAsyncMessageBox(parent, QMessageBox::Question, title, text, buttons, default_button, std::move(callback));
|
||||
}
|
||||
|
||||
void AsyncDialogs::warning(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& text,
|
||||
std::function<void(QMessageBox::StandardButton)> callback)
|
||||
{
|
||||
warning(parent, title, text, QMessageBox::Ok, QMessageBox::NoButton, std::move(callback));
|
||||
}
|
||||
|
||||
void AsyncDialogs::warning(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
QMessageBox::StandardButton default_button,
|
||||
std::function<void(QMessageBox::StandardButton)> callback)
|
||||
{
|
||||
openAsyncMessageBox(parent, QMessageBox::Warning, title, text, buttons, default_button, std::move(callback));
|
||||
}
|
||||
|
||||
void AsyncDialogs::critical(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& text,
|
||||
std::function<void(QMessageBox::StandardButton)> callback)
|
||||
{
|
||||
critical(parent, title, text, QMessageBox::Ok, QMessageBox::NoButton, std::move(callback));
|
||||
}
|
||||
|
||||
void AsyncDialogs::critical(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
QMessageBox::StandardButton default_button,
|
||||
std::function<void(QMessageBox::StandardButton)> callback)
|
||||
{
|
||||
openAsyncMessageBox(parent, QMessageBox::Critical, title, text, buttons, default_button, std::move(callback));
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtWidgets/QInputDialog>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
#include <functional>
|
||||
|
||||
/// A collection of helper functions for opening asynchronous dialog boxes.
|
||||
/// These should be used in place of Qt's built-in blocking functions as those
|
||||
/// are prone to crashing if the parent is destroyed while the dialog is open.
|
||||
namespace AsyncDialogs
|
||||
{
|
||||
/// Replacement for QInputDialog::getText.
|
||||
void getText(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
const QString& text,
|
||||
std::function<void(QString)> value_callback);
|
||||
|
||||
/// Replacement for QInputDialog::getText.
|
||||
void getText(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
QLineEdit::EchoMode echo,
|
||||
const QString& text,
|
||||
Qt::WindowFlags flags,
|
||||
Qt::InputMethodHints input_method_hints,
|
||||
std::function<void(std::optional<QString>)> callback);
|
||||
|
||||
/// Replacement for QInputDialog::getMultiLineText.
|
||||
void getMultiLineText(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
const QString& text,
|
||||
std::function<void(QString)> value_callback);
|
||||
|
||||
/// Replacement for QInputDialog::getMultiLineText.
|
||||
void getMultiLineText(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
const QString& text,
|
||||
Qt::WindowFlags flags,
|
||||
Qt::InputMethodHints input_method_hints,
|
||||
std::function<void(std::optional<QString>)> callback);
|
||||
|
||||
/// Replacement for QInputDialog::getItem.
|
||||
void getItem(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
const QStringList& items,
|
||||
int current,
|
||||
std::function<void(QString)> value_callback);
|
||||
|
||||
/// Replacement for QInputDialog::getItem.
|
||||
void getItem(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
const QStringList& items,
|
||||
int current,
|
||||
bool editable,
|
||||
Qt::WindowFlags flags,
|
||||
Qt::InputMethodHints input_method_hints,
|
||||
std::function<void(std::optional<QString>)> callback);
|
||||
|
||||
/// Replacement for QInputDialog::getInt.
|
||||
void getInt(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
int value,
|
||||
std::function<void(int)> value_callback);
|
||||
|
||||
/// Replacement for QInputDialog::getInt.
|
||||
void getInt(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
int value,
|
||||
int min_value,
|
||||
int max_value,
|
||||
int step,
|
||||
Qt::WindowFlags flags,
|
||||
std::function<void(std::optional<int>)> callback);
|
||||
|
||||
/// Replacement for QInputDialog::getDouble.
|
||||
void getDouble(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
double value,
|
||||
std::function<void(double)> value_callback);
|
||||
|
||||
/// Replacement for QInputDialog::getDouble.
|
||||
void getDouble(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& label,
|
||||
double value,
|
||||
double min_value,
|
||||
double max_value,
|
||||
int decimals,
|
||||
Qt::WindowFlags flags,
|
||||
double step,
|
||||
std::function<void(std::optional<double>)> callback);
|
||||
|
||||
/// Replacement for QMessageBox::information.
|
||||
void information(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& text,
|
||||
std::function<void(QMessageBox::StandardButton)> callback = {});
|
||||
|
||||
/// Replacement for QMessageBox::information.
|
||||
void information(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
QMessageBox::StandardButton default_button,
|
||||
std::function<void(QMessageBox::StandardButton)> callback = {});
|
||||
|
||||
/// Replacement for QMessageBox::question.
|
||||
void question(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& text,
|
||||
std::function<void()> yes_callback);
|
||||
|
||||
/// Replacement for QMessageBox::question.
|
||||
void question(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
QMessageBox::StandardButton default_button,
|
||||
std::function<void(QMessageBox::StandardButton)> callback);
|
||||
|
||||
/// Replacement for QMessageBox::warning.
|
||||
void warning(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& text,
|
||||
std::function<void(QMessageBox::StandardButton)> callback = {});
|
||||
|
||||
/// Replacement for QMessageBox::warning.
|
||||
void warning(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
QMessageBox::StandardButton default_button,
|
||||
std::function<void(QMessageBox::StandardButton)> callback = {});
|
||||
|
||||
/// Replacement for QMessageBox::critical.
|
||||
void critical(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& text,
|
||||
std::function<void(QMessageBox::StandardButton)> callback = {});
|
||||
|
||||
/// Replacement for QMessageBox::critical.
|
||||
void critical(
|
||||
QWidget* parent,
|
||||
const QString& title,
|
||||
const QString& text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
QMessageBox::StandardButton default_button,
|
||||
std::function<void(QMessageBox::StandardButton)> callback = {});
|
||||
} // namespace AsyncDialogs
|
||||
@@ -10,6 +10,8 @@ target_sources(pcsx2-qt PRIVATE
|
||||
AboutDialog.cpp
|
||||
AboutDialog.h
|
||||
AboutDialog.ui
|
||||
AsyncDialogs.cpp
|
||||
AsyncDialogs.h
|
||||
AutoUpdaterDialog.cpp
|
||||
AutoUpdaterDialog.h
|
||||
AutoUpdaterDialog.ui
|
||||
|
||||
@@ -112,7 +112,8 @@ void CoverDownloadDialog::cancelThread()
|
||||
}
|
||||
|
||||
CoverDownloadDialog::CoverDownloadThread::CoverDownloadThread(QWidget* parent, const QString& urls, bool use_serials)
|
||||
: QtAsyncProgressThread(parent), m_use_serials(use_serials)
|
||||
: QtAsyncProgressThread(parent)
|
||||
, m_use_serials(use_serials)
|
||||
{
|
||||
for (const QString& str : urls.split(QChar('\n')))
|
||||
m_urls.push_back(str.toStdString());
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "BreakpointDialog.h"
|
||||
#include "DebugTools/Breakpoints.h"
|
||||
|
||||
#include "AsyncDialogs.h"
|
||||
#include "QtUtils.h"
|
||||
#include "QtHost.h"
|
||||
#include <QtWidgets/QDialog>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include "Debugger/DebuggerWindow.h"
|
||||
|
||||
#include "DebugTools/Breakpoints.h"
|
||||
|
||||
BreakpointDialog::BreakpointDialog(QWidget* parent, DebugInterface* cpu, BreakpointModel& model)
|
||||
: QDialog(parent)
|
||||
@@ -100,7 +101,7 @@ void BreakpointDialog::accept()
|
||||
u64 address;
|
||||
if (!m_cpu->evaluateExpression(m_ui.txtAddress->text().toStdString().c_str(), address, error))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid Address"), QString::fromStdString(error));
|
||||
AsyncDialogs::warning(g_debugger_window, tr("Invalid Address"), QString::fromStdString(error));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -116,7 +117,7 @@ void BreakpointDialog::accept()
|
||||
|
||||
if (!m_cpu->initExpression(m_ui.txtCondition->text().toStdString().c_str(), expr, error))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid Condition"), QString::fromStdString(error));
|
||||
AsyncDialogs::warning(g_debugger_window, tr("Invalid Condition"), QString::fromStdString(error));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -134,14 +135,14 @@ void BreakpointDialog::accept()
|
||||
u64 startAddress;
|
||||
if (!m_cpu->evaluateExpression(m_ui.txtAddress->text().toStdString().c_str(), startAddress, error))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid Address"), QString::fromStdString(error));
|
||||
AsyncDialogs::warning(g_debugger_window, tr("Invalid Address"), QString::fromStdString(error));
|
||||
return;
|
||||
}
|
||||
|
||||
u64 size;
|
||||
if (!m_cpu->evaluateExpression(m_ui.txtSize->text().toStdString().c_str(), size, error) || !size)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid Size"), QString::fromStdString(error));
|
||||
AsyncDialogs::warning(g_debugger_window, tr("Invalid Size"), QString::fromStdString(error));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -157,7 +158,7 @@ void BreakpointDialog::accept()
|
||||
PostfixExpression expr;
|
||||
if (!m_cpu->initExpression(m_ui.txtCondition->text().toStdString().c_str(), expr, error))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid Condition"), QString::fromStdString(error));
|
||||
AsyncDialogs::warning(g_debugger_window, tr("Invalid Condition"), QString::fromStdString(error));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
|
||||
#include "BreakpointModel.h"
|
||||
|
||||
#include "AsyncDialogs.h"
|
||||
#include "QtHost.h"
|
||||
#include "QtUtils.h"
|
||||
#include "Debugger/DebuggerSettingsManager.h"
|
||||
#include "Debugger/DebuggerWindow.h"
|
||||
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
#include "DebugTools/Breakpoints.h"
|
||||
@@ -13,7 +15,6 @@
|
||||
#include "common/Console.h"
|
||||
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -385,7 +386,7 @@ bool BreakpointModel::setData(const QModelIndex& index, const QVariant& value, i
|
||||
std::string error;
|
||||
if (!m_cpu.initExpression(condValue.toLocal8Bit().constData(), expr, error))
|
||||
{
|
||||
QMessageBox::warning(nullptr, "Condition Error", QString::fromStdString(error));
|
||||
AsyncDialogs::warning(g_debugger_window, "Condition Error", QString::fromStdString(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -419,7 +420,7 @@ bool BreakpointModel::setData(const QModelIndex& index, const QVariant& value, i
|
||||
std::string error;
|
||||
if (!m_cpu.initExpression(condValue.toLocal8Bit().constData(), expr, error))
|
||||
{
|
||||
QMessageBox::warning(nullptr, "Condition Error", QString::fromStdString(error));
|
||||
AsyncDialogs::warning(g_debugger_window, "Condition Error", QString::fromStdString(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "DebuggerWindow.h"
|
||||
|
||||
#include "AsyncDialogs.h"
|
||||
#include "Debugger/DebuggerView.h"
|
||||
#include "Debugger/Docking/DockManager.h"
|
||||
|
||||
@@ -15,8 +16,6 @@
|
||||
#include "MainWindow.h"
|
||||
#include "AnalysisOptionsDialog.h"
|
||||
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
DebuggerWindow* g_debugger_window = nullptr;
|
||||
|
||||
DebuggerWindow::DebuggerWindow(QWidget* parent)
|
||||
@@ -70,19 +69,21 @@ DebuggerWindow::DebuggerWindow(QWidget* parent)
|
||||
});
|
||||
|
||||
connect(m_ui.actionResetAllLayouts, &QAction::triggered, this, [this]() {
|
||||
QString text = tr("Are you sure you want to reset all layouts?");
|
||||
if (QMessageBox::question(g_debugger_window, tr("Confirmation"), text) != QMessageBox::Yes)
|
||||
return;
|
||||
const QString title = tr("Confirmation");
|
||||
const QString text = tr("Are you sure you want to reset all layouts?");
|
||||
|
||||
m_dock_manager->resetAllLayouts();
|
||||
AsyncDialogs::question(this, title, text, [this]() {
|
||||
m_dock_manager->resetAllLayouts();
|
||||
});
|
||||
});
|
||||
|
||||
connect(m_ui.actionResetDefaultLayouts, &QAction::triggered, this, [this]() {
|
||||
QString text = tr("Are you sure you want to reset the default layouts?");
|
||||
if (QMessageBox::question(g_debugger_window, tr("Confirmation"), text) != QMessageBox::Yes)
|
||||
return;
|
||||
const QString title = tr("Confirmation");
|
||||
const QString text = tr("Are you sure you want to reset the default layouts?");
|
||||
|
||||
m_dock_manager->resetDefaultLayouts();
|
||||
AsyncDialogs::question(this, title, text, [this]() {
|
||||
m_dock_manager->resetDefaultLayouts();
|
||||
});
|
||||
});
|
||||
|
||||
connect(g_emu_thread, &EmuThread::onVMPaused, this, []() {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "DisassemblyView.h"
|
||||
|
||||
#include "AsyncDialogs.h"
|
||||
#include "Debugger/DebuggerWindow.h"
|
||||
#include "Debugger/JsonValueWrapper.h"
|
||||
#include "Debugger/Breakpoints/BreakpointModel.h"
|
||||
@@ -18,8 +19,6 @@
|
||||
#include <QtGui/QMouseEvent>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtWidgets/QInputDialog>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include "SymbolTree/NewSymbolDialogs.h"
|
||||
#include "common/StringUtil.h"
|
||||
|
||||
@@ -111,7 +110,7 @@ void DisassemblyView::contextPasteInstructionText()
|
||||
{
|
||||
if (!cpu().isCpuPaused())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Assemble Error"), tr("Unable to change assembly while core is running"));
|
||||
AsyncDialogs::warning(this, tr("Assemble Error"), tr("Unable to change assembly while core is running"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -130,7 +129,7 @@ void DisassemblyView::contextPasteInstructionText()
|
||||
bool valid = MipsAssembleOpcode(newInstructions[instructionIdx].c_str(), &cpu(), replaceAddress, encodedInstruction, errorText);
|
||||
if (!valid)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Assemble Error"), QString("%1 %2").arg(errorText.c_str()).arg(newInstructions[instructionIdx].c_str()));
|
||||
AsyncDialogs::warning(this, tr("Assemble Error"), QString("%1 %2").arg(errorText.c_str()).arg(newInstructions[instructionIdx].c_str()));
|
||||
return;
|
||||
}
|
||||
encodedInstructions.push_back(encodedInstruction);
|
||||
@@ -148,29 +147,28 @@ void DisassemblyView::contextAssembleInstruction()
|
||||
{
|
||||
if (!cpu().isCpuPaused())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Assemble Error"), tr("Unable to change assembly while core is running"));
|
||||
AsyncDialogs::warning(this, tr("Assemble Error"), tr("Unable to change assembly while core is running"));
|
||||
return;
|
||||
}
|
||||
|
||||
DisassemblyLineInfo line;
|
||||
bool ok;
|
||||
m_disassemblyManager.getLine(m_selectedAddressStart, false, line);
|
||||
QString instruction = QInputDialog::getText(this, tr("Assemble Instruction"), "",
|
||||
QLineEdit::Normal, QString("%1 %2").arg(line.name.c_str()).arg(line.params.c_str()), &ok);
|
||||
|
||||
if (!ok)
|
||||
return;
|
||||
const QString title = tr("Assemble Instruction");
|
||||
const QString text = QString("%1 %2").arg(line.name.c_str()).arg(line.params.c_str());
|
||||
|
||||
u32 encodedInstruction;
|
||||
std::string errorText;
|
||||
bool valid = MipsAssembleOpcode(instruction.toLocal8Bit().constData(), &cpu(), m_selectedAddressStart, encodedInstruction, errorText);
|
||||
if (!valid)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Assemble Error"), QString::fromStdString(errorText));
|
||||
return;
|
||||
}
|
||||
AsyncDialogs::getText(this, title, "", text, [this](QString instruction) {
|
||||
u32 encodedInstruction;
|
||||
std::string errorText;
|
||||
bool valid = MipsAssembleOpcode(instruction.toLocal8Bit().constData(), &cpu(), m_selectedAddressStart, encodedInstruction, errorText);
|
||||
if (!valid)
|
||||
{
|
||||
AsyncDialogs::warning(this, tr("Assemble Error"), QString::fromStdString(errorText));
|
||||
return;
|
||||
}
|
||||
|
||||
setInstructions(m_selectedAddressStart, m_selectedAddressEnd, encodedInstruction);
|
||||
setInstructions(m_selectedAddressStart, m_selectedAddressEnd, encodedInstruction);
|
||||
});
|
||||
}
|
||||
|
||||
void DisassemblyView::contextNoopInstruction()
|
||||
@@ -249,34 +247,34 @@ void DisassemblyView::contextFollowBranch()
|
||||
|
||||
void DisassemblyView::contextGoToAddress()
|
||||
{
|
||||
bool ok;
|
||||
const QString targetString = QInputDialog::getText(this, tr("Go To In Disassembly"), "",
|
||||
QLineEdit::Normal, "", &ok);
|
||||
const QString title = tr("Go To In Disassembly");
|
||||
|
||||
if (!ok)
|
||||
return;
|
||||
AsyncDialogs::getText(this, title, "", "", [this](QString expression) {
|
||||
u64 address = 0;
|
||||
std::string error;
|
||||
if (!cpu().evaluateExpression(expression.toStdString().c_str(), address, error))
|
||||
{
|
||||
AsyncDialogs::warning(this, tr("Cannot Go To"), QString::fromStdString(error));
|
||||
return;
|
||||
}
|
||||
|
||||
u64 address = 0;
|
||||
std::string error;
|
||||
if (!cpu().evaluateExpression(targetString.toStdString().c_str(), address, error))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Cannot Go To"), QString::fromStdString(error));
|
||||
return;
|
||||
}
|
||||
|
||||
gotoAddressAndSetFocus(static_cast<u32>(address) & ~3);
|
||||
gotoAddressAndSetFocus(static_cast<u32>(address) & ~3);
|
||||
});
|
||||
}
|
||||
|
||||
void DisassemblyView::contextAddFunction()
|
||||
{
|
||||
NewFunctionDialog* dialog = new NewFunctionDialog(cpu(), this);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
dialog->setName(QString("func_%1").arg(m_selectedAddressStart, 8, 16, QChar('0')));
|
||||
dialog->setAddress(m_selectedAddressStart);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
if (m_selectedAddressEnd != m_selectedAddressStart)
|
||||
dialog->setCustomSize(m_selectedAddressEnd - m_selectedAddressStart + 4);
|
||||
if (dialog->exec() == QDialog::Accepted)
|
||||
update();
|
||||
|
||||
connect(dialog, &QDialog::accepted, this, [this]() { update(); });
|
||||
|
||||
dialog->open();
|
||||
}
|
||||
|
||||
void DisassemblyView::contextCopyFunctionName()
|
||||
@@ -304,28 +302,26 @@ void DisassemblyView::contextRemoveFunction()
|
||||
void DisassemblyView::contextRenameFunction()
|
||||
{
|
||||
const FunctionInfo curFunc = cpu().GetSymbolGuardian().FunctionOverlappingAddress(m_selectedAddressStart);
|
||||
|
||||
if (!curFunc.address.valid())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Rename Function Error"), tr("No function / symbol is currently selected."));
|
||||
AsyncDialogs::warning(this, tr("Rename Function Error"), tr("No function / symbol is currently selected."));
|
||||
return;
|
||||
}
|
||||
|
||||
QString oldName = QString::fromStdString(curFunc.name);
|
||||
const QString title = tr("Rename Function");
|
||||
const QString label = tr("Function name");
|
||||
const QString oldName = QString::fromStdString(curFunc.name);
|
||||
|
||||
bool ok;
|
||||
QString newName = QInputDialog::getText(this, tr("Rename Function"), tr("Function name"), QLineEdit::Normal, oldName, &ok);
|
||||
if (!ok)
|
||||
return;
|
||||
AsyncDialogs::getText(this, title, label, oldName, [this, curFunc](QString newName) {
|
||||
if (newName.isEmpty())
|
||||
{
|
||||
AsyncDialogs::warning(this, tr("Rename Function Error"), tr("Function name cannot be nothing."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (newName.isEmpty())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Rename Function Error"), tr("Function name cannot be nothing."));
|
||||
return;
|
||||
}
|
||||
|
||||
cpu().GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
database.functions.rename_symbol(curFunc.handle, newName.toStdString());
|
||||
cpu().GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
database.functions.rename_symbol(curFunc.handle, newName.toStdString());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -375,7 +371,7 @@ void DisassemblyView::contextRestoreFunction()
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::warning(this, tr("Restore Function Error"), tr("Unable to stub selected address."));
|
||||
AsyncDialogs::warning(this, tr("Restore Function Error"), tr("Unable to stub selected address."));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ public slots:
|
||||
void contextCopyInstructionHex();
|
||||
void contextCopyInstructionText();
|
||||
void contextCopyFunctionName();
|
||||
void contextPasteInstructionText();
|
||||
void contextPasteInstructionText();
|
||||
void contextAssembleInstruction();
|
||||
void contextNoopInstruction();
|
||||
void contextRestoreInstruction();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "DockManager.h"
|
||||
|
||||
#include "AsyncDialogs.h"
|
||||
#include "Debugger/DebuggerView.h"
|
||||
#include "Debugger/DebuggerWindow.h"
|
||||
#include "Debugger/Docking/DockTables.h"
|
||||
@@ -24,7 +25,6 @@
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QtTranslation>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QProxyStyle>
|
||||
#include <QtWidgets/QStyleFactory>
|
||||
|
||||
@@ -518,17 +518,19 @@ void DockManager::newLayoutClicked()
|
||||
if (m_menu_bar)
|
||||
m_menu_bar->onCurrentLayoutChanged(m_current_layout);
|
||||
|
||||
auto name_validator = [this](const QString& name) {
|
||||
const auto name_validator = [this](const QString& name) {
|
||||
return !hasNameConflict(name, DockLayout::INVALID_INDEX);
|
||||
};
|
||||
|
||||
bool can_clone_current_layout = m_current_layout != DockLayout::INVALID_INDEX;
|
||||
const bool can_clone_current_layout = m_current_layout != DockLayout::INVALID_INDEX;
|
||||
|
||||
QPointer<LayoutEditorDialog> dialog = new LayoutEditorDialog(
|
||||
name_validator, can_clone_current_layout, g_debugger_window);
|
||||
LayoutEditorDialog* dialog = new LayoutEditorDialog(name_validator, can_clone_current_layout, g_debugger_window);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
connect(dialog, &QDialog::accepted, this, [this, dialog, name_validator]() {
|
||||
if (!name_validator(dialog->name()))
|
||||
return;
|
||||
|
||||
if (dialog->exec() == QDialog::Accepted && name_validator(dialog->name()))
|
||||
{
|
||||
DockLayout::Index new_layout = DockLayout::INVALID_INDEX;
|
||||
|
||||
const auto [mode, index] = dialog->initialState();
|
||||
@@ -565,9 +567,9 @@ void DockManager::newLayoutClicked()
|
||||
updateLayoutSwitcher();
|
||||
switchToLayout(new_layout);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
delete dialog.get();
|
||||
dialog->open();
|
||||
}
|
||||
|
||||
void DockManager::openLayoutSwitcherContextMenu(const QPoint& pos, QTabBar* layout_switcher)
|
||||
@@ -605,26 +607,33 @@ void DockManager::editLayoutClicked(DockLayout::Index layout_index)
|
||||
if (layout_index >= m_layouts.size())
|
||||
return;
|
||||
|
||||
DockLayout& layout = m_layouts[layout_index];
|
||||
const DockLayout& layout = m_layouts[layout_index];
|
||||
|
||||
auto name_validator = [this, layout_index](const QString& name) {
|
||||
const auto name_validator = [this, layout_index](const QString& name) {
|
||||
return !hasNameConflict(name, layout_index);
|
||||
};
|
||||
|
||||
QPointer<LayoutEditorDialog> dialog = new LayoutEditorDialog(
|
||||
layout.name(), layout.cpu(), name_validator, g_debugger_window);
|
||||
LayoutEditorDialog* dialog = new LayoutEditorDialog(layout.name(), layout.cpu(), name_validator, g_debugger_window);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
if (dialog->exec() != QDialog::Accepted || !name_validator(dialog->name()))
|
||||
return;
|
||||
connect(dialog, &QDialog::accepted, this, [this, layout_index, dialog, name_validator]() {
|
||||
if (layout_index >= m_layouts.size())
|
||||
return;
|
||||
|
||||
layout.setName(dialog->name());
|
||||
layout.setCpu(dialog->cpu());
|
||||
DockLayout& layout = m_layouts[layout_index];
|
||||
|
||||
layout.save(layout_index);
|
||||
if (!name_validator(dialog->name()))
|
||||
return;
|
||||
|
||||
delete dialog.get();
|
||||
layout.setName(dialog->name());
|
||||
layout.setCpu(dialog->cpu());
|
||||
|
||||
updateLayoutSwitcher();
|
||||
layout.save(layout_index);
|
||||
|
||||
updateLayoutSwitcher();
|
||||
});
|
||||
|
||||
dialog->open();
|
||||
}
|
||||
|
||||
void DockManager::resetLayoutClicked(DockLayout::Index layout_index)
|
||||
@@ -636,20 +645,28 @@ void DockManager::resetLayoutClicked(DockLayout::Index layout_index)
|
||||
if (!layout.canReset())
|
||||
return;
|
||||
|
||||
QString text = tr("Are you sure you want to reset layout '%1'?").arg(layout.name());
|
||||
if (QMessageBox::question(g_debugger_window, tr("Confirmation"), text) != QMessageBox::Yes)
|
||||
return;
|
||||
const QString title = tr("Confirmation");
|
||||
const QString text = tr("Are you sure you want to reset layout '%1'?").arg(layout.name());
|
||||
|
||||
bool current_layout = layout_index == m_current_layout;
|
||||
AsyncDialogs::question(g_debugger_window, title, text, [this, layout_index]() {
|
||||
if (layout_index >= m_layouts.size())
|
||||
return;
|
||||
|
||||
if (current_layout)
|
||||
switchToLayout(DockLayout::INVALID_INDEX);
|
||||
DockLayout& layout = m_layouts[layout_index];
|
||||
if (!layout.canReset())
|
||||
return;
|
||||
|
||||
layout.reset();
|
||||
layout.save(layout_index);
|
||||
bool current_layout = layout_index == m_current_layout;
|
||||
|
||||
if (current_layout)
|
||||
switchToLayout(layout_index);
|
||||
if (current_layout)
|
||||
switchToLayout(DockLayout::INVALID_INDEX);
|
||||
|
||||
layout.reset();
|
||||
layout.save(layout_index);
|
||||
|
||||
if (current_layout)
|
||||
switchToLayout(layout_index);
|
||||
});
|
||||
}
|
||||
|
||||
void DockManager::deleteLayoutClicked(DockLayout::Index layout_index)
|
||||
@@ -659,12 +676,16 @@ void DockManager::deleteLayoutClicked(DockLayout::Index layout_index)
|
||||
|
||||
DockLayout& layout = m_layouts[layout_index];
|
||||
|
||||
QString text = tr("Are you sure you want to delete layout '%1'?").arg(layout.name());
|
||||
if (QMessageBox::question(g_debugger_window, tr("Confirmation"), text) != QMessageBox::Yes)
|
||||
return;
|
||||
const QString title = tr("Confirmation");
|
||||
const QString text = tr("Are you sure you want to delete layout '%1'?").arg(layout.name());
|
||||
|
||||
deleteLayout(layout_index);
|
||||
updateLayoutSwitcher();
|
||||
AsyncDialogs::question(g_debugger_window, title, text, [this, layout_index]() {
|
||||
if (layout_index >= m_layouts.size())
|
||||
return;
|
||||
|
||||
deleteLayout(layout_index);
|
||||
updateLayoutSwitcher();
|
||||
});
|
||||
}
|
||||
|
||||
void DockManager::layoutSwitcherTabMoved(DockLayout::Index from_index, DockLayout::Index to_index)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "DockViews.h"
|
||||
|
||||
#include "AsyncDialogs.h"
|
||||
#include "QtUtils.h"
|
||||
#include "Debugger/DebuggerView.h"
|
||||
#include "Debugger/DebuggerWindow.h"
|
||||
@@ -17,8 +18,6 @@
|
||||
|
||||
#include <QtGui/QActionGroup>
|
||||
#include <QtGui/QPalette>
|
||||
#include <QtWidgets/QInputDialog>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QStyleFactory>
|
||||
|
||||
@@ -176,19 +175,22 @@ void DockTabBar::openContextMenu(QPoint pos)
|
||||
if (!widget)
|
||||
return;
|
||||
|
||||
bool ok;
|
||||
QString new_name = QInputDialog::getText(
|
||||
this, tr("Rename Window"), tr("New name:"), QLineEdit::Normal, widget->displayNameWithoutSuffix(), &ok);
|
||||
if (!ok)
|
||||
return;
|
||||
const QString title = tr("Rename Window");
|
||||
const QString label = tr("New name:");
|
||||
const QString text = widget->displayNameWithoutSuffix();
|
||||
|
||||
if (!widget->setCustomDisplayName(new_name))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid Name"), tr("The specified name is too long."));
|
||||
return;
|
||||
}
|
||||
AsyncDialogs::getText(this, title, label, text, [this, widget = QPointer(widget)](QString name) {
|
||||
if (!widget || !g_debugger_window)
|
||||
return;
|
||||
|
||||
g_debugger_window->dockManager().updateDockWidgetTitles();
|
||||
if (!widget->setCustomDisplayName(name))
|
||||
{
|
||||
AsyncDialogs::warning(this, tr("Invalid Name"), tr("The specified name is too long."));
|
||||
return;
|
||||
}
|
||||
|
||||
g_debugger_window->dockManager().updateDockWidgetTitles();
|
||||
});
|
||||
});
|
||||
|
||||
QAction* reset_name_action = menu->addAction(tr("Reset Name"));
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
|
||||
#include "MemorySearchView.h"
|
||||
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
|
||||
#include "AsyncDialogs.h"
|
||||
#include "QtUtils.h"
|
||||
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
|
||||
#include "common/Console.h"
|
||||
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QScrollBar>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
#include <QtCore/QFutureWatcher>
|
||||
#include <QtGui/QPainter>
|
||||
@@ -478,7 +478,7 @@ void MemorySearchView::onSearchButtonClicked()
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
QMessageBox::critical(this, tr("Debugger"), tr("Invalid start address"));
|
||||
AsyncDialogs::critical(this, tr("Debugger"), tr("Invalid start address"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -486,13 +486,13 @@ void MemorySearchView::onSearchButtonClicked()
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
QMessageBox::critical(this, tr("Debugger"), tr("Invalid end address"));
|
||||
AsyncDialogs::critical(this, tr("Debugger"), tr("Invalid end address"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (searchStart >= searchEnd)
|
||||
{
|
||||
QMessageBox::critical(this, tr("Debugger"), tr("Start address can't be equal to or greater than the end address"));
|
||||
AsyncDialogs::critical(this, tr("Debugger"), tr("Start address can't be equal to or greater than the end address"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -527,7 +527,7 @@ void MemorySearchView::onSearchButtonClicked()
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
QMessageBox::critical(this, tr("Debugger"), tr("Invalid search value"));
|
||||
AsyncDialogs::critical(this, tr("Debugger"), tr("Invalid search value"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -551,7 +551,7 @@ void MemorySearchView::onSearchButtonClicked()
|
||||
if (value <= std::numeric_limits<unsigned char>::max())
|
||||
break;
|
||||
default:
|
||||
QMessageBox::critical(this, tr("Debugger"), tr("Value is larger than type"));
|
||||
AsyncDialogs::critical(this, tr("Debugger"), tr("Value is larger than type"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -565,7 +565,7 @@ void MemorySearchView::onSearchButtonClicked()
|
||||
searchComparison == SearchComparison::IncreasedBy ||
|
||||
searchComparison == SearchComparison::NotChanged))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Debugger"), tr("This search comparison can only be used with filter searches."));
|
||||
AsyncDialogs::critical(this, tr("Debugger"), tr("This search comparison can only be used with filter searches."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -578,7 +578,7 @@ void MemorySearchView::onSearchButtonClicked()
|
||||
searchComparison == SearchComparison::IncreasedBy ||
|
||||
searchComparison == SearchComparison::NotChanged))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Debugger"), tr("This search comparison can only be used with filter searches."));
|
||||
AsyncDialogs::critical(this, tr("Debugger"), tr("This search comparison can only be used with filter searches."));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "MemoryView.h"
|
||||
|
||||
#include "AsyncDialogs.h"
|
||||
#include "Debugger/JsonValueWrapper.h"
|
||||
|
||||
#include "QtHost.h"
|
||||
@@ -12,8 +13,6 @@
|
||||
#include <QtGui/QActionGroup>
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtGui/QMouseEvent>
|
||||
#include <QtWidgets/QInputDialog>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
using namespace QtUtils;
|
||||
|
||||
@@ -305,36 +304,33 @@ bool MemoryViewTable::InsertFloatIntoSelectedHexView(DebugInterface& cpu)
|
||||
std::memcpy(¤tFloatVal, ¤tIntVal, sizeof(currentFloatVal));
|
||||
const QString currentfloatStr = QString("%1").arg(QString::number(currentFloatVal, 'g'), 14).trimmed();
|
||||
|
||||
// Prompt user to enter a new float value
|
||||
bool isValidInput = false;
|
||||
QString newFloatStr = QInputDialog::getText(parent, tr("Input New Float"), "",
|
||||
QLineEdit::Normal, currentfloatStr, &isValidInput);
|
||||
if (!isValidInput)
|
||||
return false;
|
||||
const QString title = tr("Input New Float");
|
||||
|
||||
// Convert string into float value
|
||||
bool isValidFloat = false;
|
||||
const float newFloatVal = newFloatStr.toFloat(&isValidFloat);
|
||||
if (!isValidFloat)
|
||||
{
|
||||
QMessageBox::warning(parent, tr("Input Error"), tr("Invalid float value"));
|
||||
return false;
|
||||
}
|
||||
AsyncDialogs::getText(parent, title, "", currentfloatStr, [this, &cpu](QString newFloatStr) {
|
||||
// Convert string into float value
|
||||
bool isValidFloat = false;
|
||||
const float newFloatVal = newFloatStr.toFloat(&isValidFloat);
|
||||
if (!isValidFloat)
|
||||
{
|
||||
AsyncDialogs::warning(parent, tr("Input Error"), tr("Invalid float value"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Write new float value back to memory
|
||||
u32 newIntVal = 0;
|
||||
std::memcpy(&newIntVal, &newFloatVal, sizeof(newIntVal));
|
||||
newIntVal = convertEndian(newIntVal);
|
||||
// Write new float value back to memory
|
||||
u32 newIntVal = 0;
|
||||
std::memcpy(&newIntVal, &newFloatVal, sizeof(newIntVal));
|
||||
newIntVal = convertEndian(newIntVal);
|
||||
|
||||
const QPointer<MemoryViewTable> table(this);
|
||||
Host::RunOnCPUThread([table, address = selectedAddress, &cpu, val = newIntVal] {
|
||||
cpu.write32(address, val);
|
||||
const QPointer<MemoryViewTable> table(this);
|
||||
Host::RunOnCPUThread([table, address = selectedAddress, &cpu, val = newIntVal] {
|
||||
cpu.write32(address, val);
|
||||
|
||||
QtHost::RunOnUIThread([table] {
|
||||
if (!table)
|
||||
return;
|
||||
QtHost::RunOnUIThread([table] {
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
table->parent->update();
|
||||
table->parent->update();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -353,7 +349,7 @@ void MemoryViewTable::InsertAtCurrentSelection(const QString& text, DebugInterfa
|
||||
const float newFloatVal = text.toFloat(&isValidFloat);
|
||||
if (!isValidFloat)
|
||||
{
|
||||
QMessageBox::warning(parent, tr("Input Error"), tr("Invalid float value"));
|
||||
AsyncDialogs::warning(parent, tr("Input Error"), tr("Invalid float value"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -899,22 +895,19 @@ void MemoryView::contextPaste()
|
||||
|
||||
void MemoryView::contextGoToAddress()
|
||||
{
|
||||
bool ok;
|
||||
QString targetString = QInputDialog::getText(this, tr("Go To In Memory View"), "",
|
||||
QLineEdit::Normal, "", &ok);
|
||||
const QString title = tr("Go To In Memory View");
|
||||
|
||||
if (!ok)
|
||||
return;
|
||||
AsyncDialogs::getText(this, title, "", "", [this](QString expression) {
|
||||
u64 address = 0;
|
||||
std::string error;
|
||||
if (!cpu().evaluateExpression(expression.toStdString().c_str(), address, error))
|
||||
{
|
||||
AsyncDialogs::warning(this, tr("Cannot Go To"), QString::fromStdString(error));
|
||||
return;
|
||||
}
|
||||
|
||||
u64 address = 0;
|
||||
std::string error;
|
||||
if (!cpu().evaluateExpression(targetString.toStdString().c_str(), address, error))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Cannot Go To"), QString::fromStdString(error));
|
||||
return;
|
||||
}
|
||||
|
||||
gotoAddress(static_cast<u32>(address));
|
||||
gotoAddress(static_cast<u32>(address));
|
||||
});
|
||||
}
|
||||
|
||||
void MemoryView::contextFollowAddress()
|
||||
|
||||
@@ -30,7 +30,8 @@ SavedAddressesView::SavedAddressesView(const DebuggerViewParameters& parameters)
|
||||
DebuggerSettingsManager::loadGameSettings(m_model);
|
||||
});
|
||||
|
||||
DebuggerSettingsManager::loadGameSettings(m_model);
|
||||
if (m_model->rowCount() == 0)
|
||||
DebuggerSettingsManager::loadGameSettings(m_model);
|
||||
|
||||
for (std::size_t i = 0; auto mode : SavedAddressesModel::HeaderResizeModes)
|
||||
{
|
||||
|
||||
@@ -101,7 +101,7 @@ QVariant ModuleModel::headerData(int section, Qt::Orientation orientation, int r
|
||||
//: Warning: short space limit. Abbreviate if needed.
|
||||
return tr("NAME");
|
||||
case ModuleColumns::VERSION:
|
||||
//: Warning: short space limit. Abbreviate if needed.
|
||||
//: Warning: short space limit. Abbreviate if needed.
|
||||
return tr("VERSION");
|
||||
case ModuleColumns::ENTRY:
|
||||
//: Warning: short space limit. Abbreviate if needed. // Entrypoint of the executable
|
||||
|
||||
@@ -3,17 +3,16 @@
|
||||
|
||||
#include "RegisterView.h"
|
||||
|
||||
#include "AsyncDialogs.h"
|
||||
#include "QtUtils.h"
|
||||
#include "Debugger/JsonValueWrapper.h"
|
||||
|
||||
#include "QtUtils.h"
|
||||
#include <QtGui/QMouseEvent>
|
||||
#include <QtWidgets/QTabBar>
|
||||
#include <QtWidgets/QStylePainter>
|
||||
#include <QtWidgets/QStyleOptionTab>
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtWidgets/QInputDialog>
|
||||
#include <QtWidgets/QProxyStyle>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
#include <bit>
|
||||
|
||||
@@ -351,12 +350,11 @@ void RegisterView::contextCopySegment()
|
||||
QApplication::clipboard()->setText(FilledQStringFromValue(val._u32[3 - m_selected128Field], 16));
|
||||
}
|
||||
|
||||
bool RegisterView::contextFetchNewValue(u64& out, u64 currentValue, bool segment)
|
||||
void RegisterView::fetchNewValue(u64 currentValue, bool segment, std::function<void(u64)> callback)
|
||||
{
|
||||
const int categoryIndex = ui.registerTabs->currentIndex();
|
||||
const bool floatingPoint = CAT_SHOW_FLOAT && segment;
|
||||
const int regSize = cpu().getRegisterSize(categoryIndex);
|
||||
bool ok = false;
|
||||
|
||||
QString existingValue("%1");
|
||||
|
||||
@@ -366,79 +364,89 @@ bool RegisterView::contextFetchNewValue(u64& out, u64 currentValue, bool segment
|
||||
existingValue = existingValue.arg(std::bit_cast<float>((u32)currentValue));
|
||||
|
||||
//: Changing the value in a CPU register (e.g. "Change t0")
|
||||
QString input = QInputDialog::getText(this, tr("Change %1").arg(cpu().getRegisterName(categoryIndex, m_selectedRow)), "",
|
||||
QLineEdit::Normal, existingValue, &ok);
|
||||
const QString title = tr("Change %1").arg(cpu().getRegisterName(categoryIndex, m_selectedRow));
|
||||
|
||||
if (!ok)
|
||||
return false;
|
||||
|
||||
if (!floatingPoint) // Get input as hexadecimal
|
||||
{
|
||||
out = input.toULongLong(&ok, 16);
|
||||
if (!ok)
|
||||
AsyncDialogs::getText(this, title, "", existingValue, [this, callback, floatingPoint](QString input) {
|
||||
u64 value;
|
||||
if (!floatingPoint) // Get input as hexadecimal
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid register value"), tr("Invalid hexadecimal register value."));
|
||||
return false;
|
||||
bool ok;
|
||||
value = input.toULongLong(&ok, 16);
|
||||
if (!ok)
|
||||
{
|
||||
AsyncDialogs::warning(this, tr("Invalid register value"), tr("Invalid hexadecimal register value."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out = std::bit_cast<u32>(input.toFloat(&ok));
|
||||
if (!ok)
|
||||
else
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid register value"), tr("Invalid floating-point register value."));
|
||||
return false;
|
||||
bool ok;
|
||||
value = std::bit_cast<u32>(input.toFloat(&ok));
|
||||
if (!ok)
|
||||
{
|
||||
AsyncDialogs::warning(this, tr("Invalid register value"), tr("Invalid floating-point register value."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
callback(value);
|
||||
});
|
||||
}
|
||||
|
||||
void RegisterView::contextChangeValue()
|
||||
{
|
||||
const int categoryIndex = ui.registerTabs->currentIndex();
|
||||
u64 newVal;
|
||||
if (contextFetchNewValue(newVal, cpu().getRegister(categoryIndex, m_selectedRow).lo))
|
||||
{
|
||||
cpu().setRegister(categoryIndex, m_selectedRow, u128::From64(newVal));
|
||||
const int category_index = ui.registerTabs->currentIndex();
|
||||
const s32 row = m_selectedRow;
|
||||
const u32 old_value = cpu().getRegister(category_index, row).lo;
|
||||
|
||||
fetchNewValue(old_value, false, [this, category_index, row](u64 input) {
|
||||
cpu().setRegister(category_index, row, u128::From64(input));
|
||||
DebuggerView::broadcastEvent(DebuggerEvents::VMUpdate());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void RegisterView::contextChangeTop()
|
||||
{
|
||||
u64 newVal;
|
||||
u128 oldVal = cpu().getRegister(ui.registerTabs->currentIndex(), m_selectedRow);
|
||||
if (contextFetchNewValue(newVal, oldVal.hi))
|
||||
{
|
||||
oldVal.hi = newVal;
|
||||
cpu().setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal);
|
||||
const int category_index = ui.registerTabs->currentIndex();
|
||||
const s32 row = m_selectedRow;
|
||||
const u128 old_value = cpu().getRegister(category_index, row);
|
||||
|
||||
fetchNewValue(old_value.hi, false, [this, category_index, row, old_value](u32 input) {
|
||||
u128 new_value = old_value;
|
||||
new_value.hi = input;
|
||||
cpu().setRegister(category_index, row, new_value);
|
||||
DebuggerView::broadcastEvent(DebuggerEvents::VMUpdate());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void RegisterView::contextChangeBottom()
|
||||
{
|
||||
u64 newVal;
|
||||
u128 oldVal = cpu().getRegister(ui.registerTabs->currentIndex(), m_selectedRow);
|
||||
if (contextFetchNewValue(newVal, oldVal.lo))
|
||||
{
|
||||
oldVal.lo = newVal;
|
||||
cpu().setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal);
|
||||
const int category_index = ui.registerTabs->currentIndex();
|
||||
const s32 row = m_selectedRow;
|
||||
const u128 old_value = cpu().getRegister(category_index, row);
|
||||
|
||||
fetchNewValue(old_value.lo, false, [this, category_index, row, old_value](u32 input) {
|
||||
u128 new_value = old_value;
|
||||
new_value.lo = input;
|
||||
cpu().setRegister(category_index, row, new_value);
|
||||
DebuggerView::broadcastEvent(DebuggerEvents::VMUpdate());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void RegisterView::contextChangeSegment()
|
||||
{
|
||||
u64 newVal;
|
||||
u128 oldVal = cpu().getRegister(ui.registerTabs->currentIndex(), m_selectedRow);
|
||||
if (contextFetchNewValue(newVal, oldVal._u32[3 - m_selected128Field], true))
|
||||
{
|
||||
oldVal._u32[3 - m_selected128Field] = (u32)newVal;
|
||||
cpu().setRegister(ui.registerTabs->currentIndex(), m_selectedRow, oldVal);
|
||||
const int category_index = ui.registerTabs->currentIndex();
|
||||
const s32 row = m_selectedRow;
|
||||
const s32 field = m_selected128Field;
|
||||
const u128 old_value = cpu().getRegister(category_index, row);
|
||||
const u32 segment = old_value._u32[3 - field];
|
||||
|
||||
fetchNewValue(segment, false, [this, category_index, row, field, old_value](u32 input) {
|
||||
u128 new_value = old_value;
|
||||
new_value._u32[3 - field] = input;
|
||||
cpu().setRegister(category_index, row, new_value);
|
||||
DebuggerView::broadcastEvent(DebuggerEvents::VMUpdate());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::optional<DebuggerEvents::GoToAddress> RegisterView::contextCreateGotoEvent()
|
||||
@@ -454,7 +462,7 @@ std::optional<DebuggerEvents::GoToAddress> RegisterView::contextCreateGotoEvent(
|
||||
|
||||
if (!cpu().isValidAddress(addr))
|
||||
{
|
||||
QMessageBox::warning(
|
||||
AsyncDialogs::warning(
|
||||
this,
|
||||
tr("Invalid target address"),
|
||||
tr("This register holds an invalid address."));
|
||||
|
||||
@@ -49,8 +49,7 @@ public slots:
|
||||
private:
|
||||
Ui::RegisterView ui;
|
||||
|
||||
// Returns true on success
|
||||
bool contextFetchNewValue(u64& out, u64 currentValue, bool segment = false);
|
||||
void fetchNewValue(u64 currentValue, bool segment, std::function<void(u64)> callback);
|
||||
|
||||
// Used for the height offset the tab bar creates
|
||||
// because we share a widget
|
||||
|
||||
@@ -25,8 +25,7 @@ public:
|
||||
COLUMN_COUNT
|
||||
};
|
||||
|
||||
static constexpr QHeaderView::ResizeMode HeaderResizeModes[StackColumns::COLUMN_COUNT] =
|
||||
{
|
||||
static constexpr QHeaderView::ResizeMode HeaderResizeModes[StackColumns::COLUMN_COUNT] = {
|
||||
QHeaderView::ResizeMode::ResizeToContents,
|
||||
QHeaderView::ResizeMode::Stretch,
|
||||
QHeaderView::ResizeMode::ResizeToContents,
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
|
||||
#include "NewSymbolDialogs.h"
|
||||
|
||||
#include "AsyncDialogs.h"
|
||||
#include "Debugger/DebuggerWindow.h"
|
||||
#include "Debugger/SymbolTree/TypeString.h"
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QMetaMethod>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QPushButton>
|
||||
|
||||
#include "TypeString.h"
|
||||
|
||||
NewSymbolDialog::NewSymbolDialog(u32 flags, u32 alignment, DebugInterface& cpu, QWidget* parent)
|
||||
: QDialog(parent)
|
||||
, m_cpu(cpu)
|
||||
@@ -359,7 +360,7 @@ void NewFunctionDialog::createSymbol()
|
||||
});
|
||||
|
||||
if (!error_message.isEmpty())
|
||||
QMessageBox::warning(this, tr("Cannot Create Function"), error_message);
|
||||
AsyncDialogs::warning(g_debugger_window, tr("Cannot Create Function"), error_message);
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
@@ -416,7 +417,7 @@ void NewGlobalVariableDialog::createSymbol()
|
||||
});
|
||||
|
||||
if (!error_message.isEmpty())
|
||||
QMessageBox::warning(this, tr("Cannot Create Global Variable"), error_message);
|
||||
AsyncDialogs::warning(g_debugger_window, tr("Cannot Create Global Variable"), error_message);
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
@@ -534,7 +535,7 @@ void NewLocalVariableDialog::createSymbol()
|
||||
});
|
||||
|
||||
if (!error_message.isEmpty())
|
||||
QMessageBox::warning(this, tr("Cannot Create Local Variable"), error_message);
|
||||
AsyncDialogs::warning(g_debugger_window, tr("Cannot Create Local Variable"), error_message);
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
@@ -648,5 +649,5 @@ void NewParameterVariableDialog::createSymbol()
|
||||
});
|
||||
|
||||
if (!error_message.isEmpty())
|
||||
QMessageBox::warning(this, tr("Cannot Create Parameter Variable"), error_message);
|
||||
AsyncDialogs::warning(g_debugger_window, tr("Cannot Create Parameter Variable"), error_message);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "SymbolTreeDelegates.h"
|
||||
|
||||
#include "AsyncDialogs.h"
|
||||
#include "Debugger/SymbolTree/SymbolTreeModel.h"
|
||||
#include "Debugger/SymbolTree/TypeString.h"
|
||||
|
||||
@@ -10,7 +11,6 @@
|
||||
#include <QtWidgets/QComboBox>
|
||||
#include <QtWidgets/QDoubleSpinBox>
|
||||
#include <QtWidgets/QLineEdit>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
|
||||
SymbolTreeValueDelegate::SymbolTreeValueDelegate(
|
||||
DebugInterface& cpu,
|
||||
@@ -495,7 +495,7 @@ void SymbolTreeTypeDelegate::setModelData(QWidget* editor, QAbstractItemModel* m
|
||||
symbol_tree_model->resetChildren(index);
|
||||
}
|
||||
else
|
||||
QMessageBox::warning(editor, tr("Cannot Change Type"), error_message);
|
||||
AsyncDialogs::warning(editor, tr("Cannot Change Type"), error_message);
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
|
||||
@@ -19,7 +19,7 @@ struct SymbolTreeLocation
|
||||
MEMORY,
|
||||
NONE // Put NONE last so nodes of this type sort to the bottom.
|
||||
};
|
||||
|
||||
|
||||
Type type = NONE;
|
||||
u32 address = 0;
|
||||
|
||||
|
||||
@@ -3,15 +3,14 @@
|
||||
|
||||
#include "SymbolTreeViews.h"
|
||||
|
||||
#include "AsyncDialogs.h"
|
||||
#include "Debugger/JsonValueWrapper.h"
|
||||
#include "Debugger/SymbolTree/NewSymbolDialogs.h"
|
||||
#include "Debugger/SymbolTree/SymbolTreeDelegates.h"
|
||||
|
||||
#include <QtGui/QActionGroup>
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtWidgets/QInputDialog>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/QScrollBar>
|
||||
|
||||
static bool testName(const QString& name, const QString& filter);
|
||||
@@ -643,14 +642,16 @@ void SymbolTreeView::onDeleteButtonPressed()
|
||||
if (!node->symbol.valid())
|
||||
return;
|
||||
|
||||
if (QMessageBox::question(this, tr("Confirm Deletion"), tr("Delete '%1'?").arg(node->name)) != QMessageBox::Yes)
|
||||
return;
|
||||
const QString title = tr("Confirm Deletion");
|
||||
const QString text = tr("Delete '%1'?").arg(node->name);
|
||||
|
||||
cpu().GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
node->symbol.destroy_symbol(database, true);
|
||||
AsyncDialogs::question(this, title, text, [this, handle = node->symbol]() {
|
||||
cpu().GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
handle.destroy_symbol(database, true);
|
||||
});
|
||||
|
||||
reset();
|
||||
});
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void SymbolTreeView::onCopyName()
|
||||
@@ -701,13 +702,10 @@ void SymbolTreeView::onRenameSymbol()
|
||||
text = QString::fromStdString(symbol->name());
|
||||
});
|
||||
|
||||
bool ok;
|
||||
std::string name = QInputDialog::getText(this, title, label, QLineEdit::Normal, text, &ok).toStdString();
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
cpu().GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
node->symbol.rename_symbol(name, database);
|
||||
AsyncDialogs::getText(this, title, label, text, [this, handle = node->symbol](QString new_name) {
|
||||
cpu().GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
handle.rename_symbol(new_name.toStdString(), database);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -737,18 +735,15 @@ void SymbolTreeView::onChangeTypeTemporarily()
|
||||
std::optional<QString> old_type = m_model->typeFromModelIndexToString(index);
|
||||
if (!old_type.has_value())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Cannot Change Type"), tr("That node cannot have a type."));
|
||||
AsyncDialogs::warning(this, tr("Cannot Change Type"), tr("That node cannot have a type."));
|
||||
return;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
QString type_string = QInputDialog::getText(this, title, label, QLineEdit::Normal, *old_type, &ok);
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
std::optional<QString> error_message = m_model->changeTypeTemporarily(index, type_string.toStdString());
|
||||
if (error_message.has_value() && !error_message->isEmpty())
|
||||
QMessageBox::warning(this, tr("Cannot Change Type"), *error_message);
|
||||
AsyncDialogs::getText(this, title, label, *old_type, [this, index](QString type_string) {
|
||||
std::optional<QString> error_message = m_model->changeTypeTemporarily(index, type_string.toStdString());
|
||||
if (error_message.has_value() && !error_message->isEmpty())
|
||||
AsyncDialogs::warning(this, tr("Cannot Change Type"), *error_message);
|
||||
});
|
||||
}
|
||||
|
||||
void SymbolTreeView::onTreeViewClicked(const QModelIndex& index)
|
||||
@@ -862,8 +857,10 @@ void FunctionTreeView::onNewButtonPressed()
|
||||
{
|
||||
NewFunctionDialog* dialog = new NewFunctionDialog(cpu(), this);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
if (dialog->exec() == QDialog::Accepted)
|
||||
reset();
|
||||
|
||||
connect(dialog, &QDialog::accepted, this, &FunctionTreeView::reset);
|
||||
|
||||
dialog->open();
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
@@ -1005,8 +1002,10 @@ void GlobalVariableTreeView::onNewButtonPressed()
|
||||
{
|
||||
NewGlobalVariableDialog* dialog = new NewGlobalVariableDialog(cpu(), this);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
if (dialog->exec() == QDialog::Accepted)
|
||||
reset();
|
||||
|
||||
connect(dialog, &QDialog::accepted, this, &GlobalVariableTreeView::reset);
|
||||
|
||||
dialog->open();
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
@@ -1134,8 +1133,10 @@ void LocalVariableTreeView::onNewButtonPressed()
|
||||
{
|
||||
NewLocalVariableDialog* dialog = new NewLocalVariableDialog(cpu(), this);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
if (dialog->exec() == QDialog::Accepted)
|
||||
reset();
|
||||
|
||||
connect(dialog, &QDialog::accepted, this, &LocalVariableTreeView::reset);
|
||||
|
||||
dialog->open();
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
@@ -1261,8 +1262,10 @@ void ParameterVariableTreeView::onNewButtonPressed()
|
||||
{
|
||||
NewParameterVariableDialog* dialog = new NewParameterVariableDialog(cpu(), this);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
if (dialog->exec() == QDialog::Accepted)
|
||||
reset();
|
||||
|
||||
connect(dialog, &QDialog::accepted, this, &ParameterVariableTreeView::reset);
|
||||
|
||||
dialog->open();
|
||||
}
|
||||
|
||||
static bool testName(const QString& name, const QString& filter)
|
||||
|
||||
@@ -27,8 +27,7 @@ public:
|
||||
COLUMN_COUNT
|
||||
};
|
||||
|
||||
static constexpr QHeaderView::ResizeMode HeaderResizeModes[ThreadColumns::COLUMN_COUNT] =
|
||||
{
|
||||
static constexpr QHeaderView::ResizeMode HeaderResizeModes[ThreadColumns::COLUMN_COUNT] = {
|
||||
QHeaderView::ResizeMode::ResizeToContents,
|
||||
QHeaderView::ResizeMode::ResizeToContents,
|
||||
QHeaderView::ResizeMode::ResizeToContents,
|
||||
|
||||
@@ -479,8 +479,15 @@ bool DisplaySurface::eventFilter(QObject* object, QEvent* event)
|
||||
// macOS: When we (the display window) get focus from another window with a toolbar we update to the MainWindow toolbar.
|
||||
// This is because we are a different native window from our MainWindow. So, whenever we get focus, focus our MainWindow.
|
||||
// That way macOS will show the MainWindow toolbar when you click from the debugger / log window to the game.
|
||||
if (auto* w = qobject_cast<QWidget*>(object))
|
||||
|
||||
// Don't try to steal focus when we're showing a modal dialog
|
||||
// We end up ping ponging focus in a feedback loop
|
||||
if (QApplication::activeModalWidget() != nullptr)
|
||||
return false;
|
||||
|
||||
if (const auto* w = qobject_cast<QWidget*>(object))
|
||||
w->window()->activateWindow();
|
||||
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
|
||||
@@ -160,7 +160,6 @@ void GameListModel::loadOrGenerateCover(const GameList::Entry* ge)
|
||||
|
||||
// Final check that scale is unchanged before we send out the produced image.
|
||||
return m_cover_scale_counter.load(std::memory_order_acquire) == counter ? image : QPixmap();
|
||||
|
||||
});
|
||||
|
||||
// Context must be 'this' so we run on the UI thread.
|
||||
@@ -221,7 +220,7 @@ QString GameListModel::formatTimespan(const time_t timespan)
|
||||
const u32 hours = static_cast<u32>(timespan / 3600);
|
||||
if (hours > 0)
|
||||
return qApp->translate("GameList", "%n hours", "", hours);
|
||||
|
||||
|
||||
const u32 minutes = static_cast<u32>((timespan % 3600) / 60);
|
||||
if (minutes > 0)
|
||||
return qApp->translate("GameList", "%n minutes", "", minutes);
|
||||
|
||||
@@ -282,7 +282,7 @@ void GameListWidget::initialize()
|
||||
connect(m_table_view, &QTableView::customContextMenuRequested, this,
|
||||
&GameListWidget::onTableViewContextMenuRequested);
|
||||
connect(m_table_view->horizontalHeader(), &QHeaderView::customContextMenuRequested, this,
|
||||
&GameListWidget::onTableViewHeaderContextMenuRequested);
|
||||
&GameListWidget::onTableViewHeaderContextMenuRequested);
|
||||
|
||||
// Save state when header state changes (hiding and showing handled within onTableViewHeaderContextMenuRequested).
|
||||
connect(m_table_view->horizontalHeader(), &QHeaderView::sectionMoved, this, &GameListWidget::onTableHeaderStateChanged);
|
||||
@@ -292,13 +292,27 @@ void GameListWidget::initialize()
|
||||
|
||||
// Load the last session's header state or create a new one.
|
||||
if (Host::ContainsBaseSettingValue("GameListTableView", "HeaderState"))
|
||||
{
|
||||
loadTableHeaderState();
|
||||
// Enforce at least one column is visible immediately after loading.
|
||||
// This handles cases where a config (perhaps from an older version) has 0 columns and
|
||||
// no games are visible to be changed (such as per-game config) or played as you can't click on any.
|
||||
// Will automatically repair a broken header state from config (PCSX2.ini) file.
|
||||
ensureMinimumOneColumnVisible();
|
||||
}
|
||||
else
|
||||
{
|
||||
applyTableHeaderDefaults();
|
||||
}
|
||||
|
||||
// After header state load to account for user-specified sort.
|
||||
m_table_view->setSortingEnabled(true);
|
||||
|
||||
// Safety Fallback: Ensure the header is actually visible and
|
||||
// force it to stretch correctly on the first launch. This is an edgecase in case it already broke for some people or broke on older versions
|
||||
m_table_view->horizontalHeader()->show();
|
||||
resizeTableViewColumnsToFit();
|
||||
|
||||
m_ui.stack->insertWidget(0, m_table_view);
|
||||
|
||||
m_list_view = new GameListGridListView(m_ui.stack);
|
||||
@@ -408,7 +422,7 @@ void GameListWidget::setCustomBackground()
|
||||
// Selected Custom background is valid, connect the signals and start animation in gamelist
|
||||
connect(m_background_movie, &QMovie::frameChanged, this, &GameListWidget::processBackgroundFrames, Qt::UniqueConnection);
|
||||
m_ui.stack->setAutoFillBackground(false);
|
||||
|
||||
|
||||
m_table_view->viewport()->setAutoFillBackground(false);
|
||||
m_list_view->viewport()->setAutoFillBackground(false);
|
||||
updateCustomBackgroundState(true);
|
||||
@@ -562,30 +576,35 @@ void GameListWidget::onListViewContextMenuRequested(const QPoint& point)
|
||||
|
||||
void GameListWidget::onTableViewHeaderContextMenuRequested(const QPoint& point)
|
||||
{
|
||||
QMenu menu;
|
||||
QHeaderView* header = m_table_view->horizontalHeader();
|
||||
if (!header)
|
||||
return;
|
||||
|
||||
int column_visual = 0;
|
||||
QHeaderView* const header = m_table_view->horizontalHeader();
|
||||
QMenu menu(this);
|
||||
// Iterate through all available columns defined in the model.
|
||||
for (int column = 0; column < GameListModel::Column_Count; column++)
|
||||
{
|
||||
// The "cover" column is the game grid and cannot be hidden.
|
||||
// Skip the cover art column as it shouldn't be toggled manually.
|
||||
if (column == GameListModel::Column_Cover)
|
||||
continue;
|
||||
|
||||
column_visual = header->visualIndex(column);
|
||||
QAction* action = menu.addAction(m_model->getColumnDisplayName(column_visual));
|
||||
// Create a checkable menu item for each column title.
|
||||
const QString title = m_model->headerData(column, Qt::Horizontal, Qt::DisplayRole).toString();
|
||||
QAction* const action = menu.addAction(title);
|
||||
action->setCheckable(true);
|
||||
action->setChecked(!m_table_view->isColumnHidden(column_visual));
|
||||
connect(action, &QAction::toggled, [this, column_visual](bool enabled) {
|
||||
m_table_view->setColumnHidden(column_visual, !enabled);
|
||||
action->setChecked(!header->isSectionHidden(column));
|
||||
// Update the GUI when the user toggles a column with left-click actions in the right-click menu on the column.
|
||||
connect(action, &QAction::triggered, [this, header, column, action]() {
|
||||
header->setSectionHidden(column, !action->isChecked());
|
||||
// Safety check: prevent the user from hiding every single column.
|
||||
ensureMinimumOneColumnVisible();
|
||||
// Lastly push the new header state to settings.
|
||||
onTableHeaderStateChanged();
|
||||
resizeTableViewColumnsToFit();
|
||||
});
|
||||
}
|
||||
|
||||
menu.exec(m_table_view->mapToGlobal(point));
|
||||
menu.addSeparator();
|
||||
// Add a "panic button" that fully restores the default column layout.
|
||||
// This allows users to recover without editing configuration files such as [GameListTableView] has a key with
|
||||
// and variable HeaderState which you can remove the line to also do the same effect but old method is not user-friendly.
|
||||
menu.addAction(tr("Reset All Columns"), this, &GameListWidget::resetTableHeaderToDefault);
|
||||
menu.exec(m_table_view->viewport()->mapToGlobal(point));
|
||||
}
|
||||
|
||||
void GameListWidget::onCoverScaleChanged()
|
||||
@@ -789,6 +808,34 @@ void GameListWidget::loadTableHeaderState()
|
||||
header->restoreState(QByteArray::fromBase64(QByteArray::fromStdString(state_setting)));
|
||||
}
|
||||
|
||||
void GameListWidget::ensureMinimumOneColumnVisible()
|
||||
{
|
||||
QHeaderView* header = m_table_view->horizontalHeader();
|
||||
if (!header)
|
||||
return;
|
||||
|
||||
bool any_visible = false;
|
||||
for (int column = 0; column < GameListModel::Column_Count; column++)
|
||||
{
|
||||
if (column != GameListModel::Column_Cover && !header->isSectionHidden(column))
|
||||
{
|
||||
any_visible = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If absolutely everything is hidden, force the Title column to be visible.
|
||||
// This ensures there is always a right-click menu on the column available to restore
|
||||
// other columns or access the "Reset All Columns" option or even re-order them with drag and drop.
|
||||
// By default Qt will hide everything if it sees 0 viable columns, so just enforce atleast 1 column.
|
||||
// Adding ghost columns would be hacky and ugly so let's not do that.
|
||||
if (!any_visible)
|
||||
{
|
||||
header->setSectionHidden(GameListModel::Column_Title, false);
|
||||
onTableHeaderStateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void GameListWidget::onTableHeaderStateChanged()
|
||||
{
|
||||
QHeaderView* header = m_table_view->horizontalHeader();
|
||||
@@ -824,6 +871,8 @@ void GameListWidget::applyTableHeaderDefaults()
|
||||
}
|
||||
|
||||
// TODO (Tech): Create a button for this in the minibar. Currently unused.
|
||||
// TODO (Red): Not sure if I should integrate it in the minibar for now when I made sure they can't break their order and there is a reset function now when you right-click the column.
|
||||
// They could accidentaly press on it when they didn't want to, could be revised later still because people without mouses can't do it such as controller mode on the TV.
|
||||
void GameListWidget::resetTableHeaderToDefault()
|
||||
{
|
||||
QHeaderView* header = m_table_view->horizontalHeader();
|
||||
@@ -848,6 +897,8 @@ void GameListWidget::resetTableHeaderToDefault()
|
||||
}
|
||||
|
||||
Host::SetBaseStringSettingValue("GameListTableView", "HeaderState", header->saveState().toBase64());
|
||||
// This makes the columns expand to fill the window right now.
|
||||
resizeTableViewColumnsToFit();
|
||||
}
|
||||
|
||||
void GameListWidget::saveSortSettings(const int column, const Qt::SortOrder sort_order)
|
||||
|
||||
@@ -109,6 +109,7 @@ private:
|
||||
void loadTableHeaderState();
|
||||
void applyTableHeaderDefaults();
|
||||
void resetTableHeaderToDefault();
|
||||
void ensureMinimumOneColumnVisible();
|
||||
void saveSortSettings(int column, Qt::SortOrder sort_order);
|
||||
void listZoom(float delta);
|
||||
void updateToolbar();
|
||||
|
||||
+9
-12
@@ -52,7 +52,7 @@ void LogWindow::updateSettings()
|
||||
const bool curr_enabled = Log::IsHostOutputEnabled();
|
||||
const bool input_enabled = Host::GetBaseBoolSettingValue("Logging", "ShowEESIOInput");
|
||||
|
||||
if(g_log_window && g_log_window->m_line_input)
|
||||
if (g_log_window && g_log_window->m_line_input)
|
||||
{
|
||||
g_log_window->m_input_widget->setVisible(input_enabled);
|
||||
}
|
||||
@@ -199,18 +199,16 @@ void LogWindow::createUi()
|
||||
|
||||
m_line_input = new QLineEdit(this);
|
||||
connect(m_line_input, &QLineEdit::returnPressed, this, &LogWindow::onInputEntered);
|
||||
|
||||
|
||||
m_local_echo_checkbox = new QCheckBox(tr("Local Echo"), this);
|
||||
m_local_echo_checkbox->setChecked(m_local_echo);
|
||||
connect(m_local_echo_checkbox, &QCheckBox::checkStateChanged, this, [&](Qt::CheckState state)
|
||||
{
|
||||
connect(m_local_echo_checkbox, &QCheckBox::checkStateChanged, this, [&](Qt::CheckState state) {
|
||||
m_local_echo = state == Qt::CheckState::Checked;
|
||||
});
|
||||
|
||||
|
||||
m_newline_on_enter_checkbox = new QCheckBox(tr("Newline on send"), this);
|
||||
m_newline_on_enter_checkbox->setChecked(m_newline_on_enter);
|
||||
connect(m_newline_on_enter_checkbox, &QCheckBox::checkStateChanged, this, [&](Qt::CheckState state)
|
||||
{
|
||||
connect(m_newline_on_enter_checkbox, &QCheckBox::checkStateChanged, this, [&](Qt::CheckState state) {
|
||||
m_newline_on_enter = state == Qt::CheckState::Checked;
|
||||
});
|
||||
|
||||
@@ -243,7 +241,6 @@ void LogWindow::createUi()
|
||||
vlayout->addWidget(m_text);
|
||||
vlayout->addWidget(m_input_widget);
|
||||
|
||||
central_widget->setLayout(vlayout);
|
||||
setCentralWidget(central_widget);
|
||||
}
|
||||
|
||||
@@ -417,18 +414,18 @@ void LogWindow::onInputEntered()
|
||||
if (text.isEmpty() && !m_newline_on_enter)
|
||||
return;
|
||||
|
||||
if(m_newline_on_enter)
|
||||
if (m_newline_on_enter)
|
||||
text.append('\n');
|
||||
|
||||
std::string str = text.toUtf8().toStdString();
|
||||
|
||||
if(VMManager::WriteBytesToEESIORXFIFO({reinterpret_cast<const u8*>(str.data()), str.size()}))
|
||||
if (VMManager::WriteBytesToEESIORXFIFO({reinterpret_cast<const u8*>(str.data()), str.size()}))
|
||||
{
|
||||
m_line_input->clear();
|
||||
|
||||
if(m_local_echo)
|
||||
if (m_local_echo)
|
||||
// appendMessage expects a newline to be at the end of the string
|
||||
appendMessage(0, 0, m_newline_on_enter ? text : (text + '\n') );
|
||||
appendMessage(0, 0, m_newline_on_enter ? text : (text + '\n'));
|
||||
|
||||
QTextCursor cursor(m_text->textCursor());
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
|
||||
+2
-3
@@ -195,7 +195,7 @@ void EmuThread::stopFullscreenUI()
|
||||
{
|
||||
m_run_fullscreen_ui.store(false, std::memory_order_release);
|
||||
emit onFullscreenUIStateChange(false);
|
||||
|
||||
|
||||
// Resume and refresh background when FullscreenUI exits
|
||||
QMetaObject::invokeMethod(g_main_window, "updateGameListBackground", Qt::QueuedConnection);
|
||||
}
|
||||
@@ -1160,7 +1160,7 @@ void Host::OpenHostFileSelectorAsync(std::string_view title, bool select_directo
|
||||
if (!filters.empty())
|
||||
{
|
||||
filters_str.append(QStringLiteral("All File Types (%1)")
|
||||
.arg(QString::fromStdString(StringUtil::JoinString(filters.begin(), filters.end(), " "))));
|
||||
.arg(QString::fromStdString(StringUtil::JoinString(filters.begin(), filters.end(), " "))));
|
||||
for (const std::string& filter : filters)
|
||||
{
|
||||
filters_str.append(
|
||||
@@ -1369,7 +1369,6 @@ bool QtHost::InitializeConfig()
|
||||
.arg(QString::fromStdString(error.GetDescription())));
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Setup wizard was incomplete last time?
|
||||
|
||||
@@ -1357,8 +1357,18 @@ namespace SettingWidgetBinder
|
||||
widget->connect(widget, &QLineEdit::editingFinished, widget, std::move(value_changed));
|
||||
}
|
||||
|
||||
// No need to pass a section or key since this is only used once and has six keys associated with it
|
||||
static inline void BindWidgetToDateTimeSetting(SettingsInterface* sif, QDateTimeEdit* widget, std::string section)
|
||||
struct DateTimeKeys
|
||||
{
|
||||
const char* year = nullptr;
|
||||
const char* month = nullptr;
|
||||
const char* day = nullptr;
|
||||
const char* hour = nullptr;
|
||||
const char* minute = nullptr;
|
||||
const char* second = nullptr;
|
||||
};
|
||||
|
||||
static inline void BindWidgetToDateTimeSetting(
|
||||
SettingsInterface* sif, QDateTimeEdit* widget, std::string section, const DateTimeKeys& keys)
|
||||
{
|
||||
using Accessor = SettingAccessor<QDateTimeEdit>;
|
||||
|
||||
@@ -1370,58 +1380,36 @@ namespace SettingWidgetBinder
|
||||
const int DEFAULT_MINUTE = 0;
|
||||
const int DEFAULT_SECOND = 0;
|
||||
|
||||
const char* YEAR_KEY = "RtcYear";
|
||||
const char* MONTH_KEY = "RtcMonth";
|
||||
const char* DAY_KEY = "RtcDay";
|
||||
const char* HOUR_KEY = "RtcHour";
|
||||
const char* MINUTE_KEY = "RtcMinute";
|
||||
const char* SECOND_KEY = "RtcSecond";
|
||||
|
||||
// Fetch settings from .ini
|
||||
const s32 year_value =
|
||||
Host::GetBaseIntSettingValue(section.c_str(), YEAR_KEY, static_cast<s32>(DEFAULT_YEAR));
|
||||
const s32 month_value =
|
||||
Host::GetBaseIntSettingValue(section.c_str(), MONTH_KEY, static_cast<s32>(DEFAULT_MONTH));
|
||||
const s32 day_value =
|
||||
Host::GetBaseIntSettingValue(section.c_str(), DAY_KEY, static_cast<s32>(DEFAULT_DAY));
|
||||
const s32 hour_value =
|
||||
Host::GetBaseIntSettingValue(section.c_str(), HOUR_KEY, static_cast<s32>(DEFAULT_HOUR));
|
||||
const s32 minute_value =
|
||||
Host::GetBaseIntSettingValue(section.c_str(), MINUTE_KEY, static_cast<s32>(DEFAULT_MINUTE));
|
||||
const s32 second_value =
|
||||
Host::GetBaseIntSettingValue(section.c_str(), SECOND_KEY, static_cast<s32>(DEFAULT_SECOND));
|
||||
const int year_value = Host::GetBaseIntSettingValue(section.c_str(), keys.year, DEFAULT_YEAR);
|
||||
const int month_value = Host::GetBaseIntSettingValue(section.c_str(), keys.month, DEFAULT_MONTH);
|
||||
const int day_value = Host::GetBaseIntSettingValue(section.c_str(), keys.day, DEFAULT_DAY);
|
||||
const int hour_value = Host::GetBaseIntSettingValue(section.c_str(), keys.hour, DEFAULT_HOUR);
|
||||
const int minute_value = Host::GetBaseIntSettingValue(section.c_str(), keys.minute, DEFAULT_MINUTE);
|
||||
const int second_value = Host::GetBaseIntSettingValue(section.c_str(), keys.second, DEFAULT_SECOND);
|
||||
|
||||
if (sif)
|
||||
{
|
||||
int sif_year_value = DEFAULT_YEAR;
|
||||
int sif_month_value = DEFAULT_MONTH;
|
||||
int sif_day_value = DEFAULT_DAY;
|
||||
int sif_hour_value = DEFAULT_HOUR;
|
||||
int sif_minute_value = DEFAULT_MINUTE;
|
||||
int sif_second_value = DEFAULT_SECOND;
|
||||
|
||||
// Get Settings Interface values or default if that fails
|
||||
if (!sif->GetIntValue(section.c_str(), YEAR_KEY, &sif_year_value)) { sif_year_value = DEFAULT_YEAR; }
|
||||
if (!sif->GetIntValue(section.c_str(), MONTH_KEY, &sif_month_value)) { sif_month_value = DEFAULT_MONTH; }
|
||||
if (!sif->GetIntValue(section.c_str(), DAY_KEY, &sif_day_value)) { sif_day_value = DEFAULT_DAY; }
|
||||
if (!sif->GetIntValue(section.c_str(), HOUR_KEY, &sif_hour_value)) { sif_hour_value = DEFAULT_HOUR; }
|
||||
if (!sif->GetIntValue(section.c_str(), MINUTE_KEY, &sif_minute_value)) { sif_minute_value = DEFAULT_MINUTE; }
|
||||
if (!sif->GetIntValue(section.c_str(), SECOND_KEY, &sif_second_value)) { sif_second_value = DEFAULT_SECOND; }
|
||||
int sif_year_value = sif->GetIntValue(section.c_str(), keys.year, DEFAULT_YEAR);
|
||||
int sif_month_value = sif->GetIntValue(section.c_str(), keys.month, DEFAULT_MONTH);
|
||||
int sif_day_value = sif->GetIntValue(section.c_str(), keys.day, DEFAULT_DAY);
|
||||
int sif_hour_value = sif->GetIntValue(section.c_str(), keys.hour, DEFAULT_HOUR);
|
||||
int sif_minute_value = sif->GetIntValue(section.c_str(), keys.minute, DEFAULT_MINUTE);
|
||||
int sif_second_value = sif->GetIntValue(section.c_str(), keys.second, DEFAULT_SECOND);
|
||||
|
||||
// No need to check for valid date since QDateTime resets to minimum upon becoming invalid
|
||||
Accessor::setDateTime(widget, QDate(static_cast<int>(sif_year_value + YEAR_OFFSET), static_cast<int>(sif_month_value), static_cast<int>(sif_day_value)),
|
||||
QTime(static_cast<int>(sif_hour_value), static_cast<int>(sif_minute_value), static_cast<int>(sif_second_value)));
|
||||
QDate date(sif_year_value + YEAR_OFFSET, sif_month_value, sif_day_value);
|
||||
QTime time(sif_hour_value, sif_minute_value, sif_second_value);
|
||||
Accessor::setDateTime(widget, date, time);
|
||||
|
||||
// Update the settings interface and reload the game settings when changed
|
||||
Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), YEAR_KEY = std::move(YEAR_KEY), MONTH_KEY = std::move(MONTH_KEY),
|
||||
DAY_KEY = std::move(DAY_KEY), HOUR_KEY = std::move(HOUR_KEY), MINUTE_KEY = std::move(MINUTE_KEY), SECOND_KEY = std::move(SECOND_KEY), YEAR_OFFSET = std::move(YEAR_OFFSET)]() {
|
||||
|
||||
sif->SetIntValue(section.c_str(), YEAR_KEY, Accessor::getYear(widget) - YEAR_OFFSET);
|
||||
sif->SetIntValue(section.c_str(), MONTH_KEY, Accessor::getMonth(widget));
|
||||
sif->SetIntValue(section.c_str(), DAY_KEY, Accessor::getDay(widget));
|
||||
sif->SetIntValue(section.c_str(), HOUR_KEY, Accessor::getHour(widget));
|
||||
sif->SetIntValue(section.c_str(), MINUTE_KEY, Accessor::getMinute(widget));
|
||||
sif->SetIntValue(section.c_str(), SECOND_KEY, Accessor::getSecond(widget));
|
||||
Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), keys]() {
|
||||
sif->SetIntValue(section.c_str(), keys.year, Accessor::getYear(widget) - YEAR_OFFSET);
|
||||
sif->SetIntValue(section.c_str(), keys.month, Accessor::getMonth(widget));
|
||||
sif->SetIntValue(section.c_str(), keys.day, Accessor::getDay(widget));
|
||||
sif->SetIntValue(section.c_str(), keys.hour, Accessor::getHour(widget));
|
||||
sif->SetIntValue(section.c_str(), keys.minute, Accessor::getMinute(widget));
|
||||
sif->SetIntValue(section.c_str(), keys.second, Accessor::getSecond(widget));
|
||||
|
||||
QtHost::SaveGameSettings(sif, true);
|
||||
g_emu_thread->reloadGameSettings();
|
||||
@@ -1431,13 +1419,12 @@ namespace SettingWidgetBinder
|
||||
else
|
||||
{
|
||||
// No need to check for valid date since QDateTime resets to minimum upon becoming invalid
|
||||
Accessor::setDateTime(widget, QDate(static_cast<int>(year_value + YEAR_OFFSET), static_cast<int>(month_value), static_cast<int>(day_value)),
|
||||
QTime(static_cast<int>(hour_value), static_cast<int>(minute_value), static_cast<int>(second_value)));
|
||||
QDate date(year_value + YEAR_OFFSET, month_value, day_value);
|
||||
QTime time(hour_value, minute_value, second_value);
|
||||
Accessor::setDateTime(widget, date, time);
|
||||
|
||||
// Update and apply base settings with values from widget when user changes it in UI
|
||||
Accessor::connectValueChanged(widget, [widget, section = std::move(section), YEAR_KEY = std::move(YEAR_KEY), MONTH_KEY = std::move(MONTH_KEY),
|
||||
DAY_KEY = std::move(DAY_KEY), HOUR_KEY = std::move(HOUR_KEY), MINUTE_KEY = std::move(MINUTE_KEY), SECOND_KEY = std::move(SECOND_KEY), YEAR_OFFSET = std::move(YEAR_OFFSET)]() {
|
||||
|
||||
Accessor::connectValueChanged(widget, [widget, section = std::move(section), keys]() {
|
||||
const int new_year_value = Accessor::getYear(widget);
|
||||
const int new_month_value = Accessor::getMonth(widget);
|
||||
const int new_day_value = Accessor::getDay(widget);
|
||||
@@ -1445,16 +1432,15 @@ namespace SettingWidgetBinder
|
||||
const int new_minute_value = Accessor::getMinute(widget);
|
||||
const int new_second_value = Accessor::getSecond(widget);
|
||||
|
||||
Host::SetBaseIntSettingValue(section.c_str(), YEAR_KEY, new_year_value - YEAR_OFFSET);
|
||||
Host::SetBaseIntSettingValue(section.c_str(), MONTH_KEY, new_month_value);
|
||||
Host::SetBaseIntSettingValue(section.c_str(), DAY_KEY, new_day_value);
|
||||
Host::SetBaseIntSettingValue(section.c_str(), HOUR_KEY, new_hour_value);
|
||||
Host::SetBaseIntSettingValue(section.c_str(), MINUTE_KEY, new_minute_value);
|
||||
Host::SetBaseIntSettingValue(section.c_str(), SECOND_KEY, new_second_value);
|
||||
Host::SetBaseIntSettingValue(section.c_str(), keys.year, new_year_value - YEAR_OFFSET);
|
||||
Host::SetBaseIntSettingValue(section.c_str(), keys.month, new_month_value);
|
||||
Host::SetBaseIntSettingValue(section.c_str(), keys.day, new_day_value);
|
||||
Host::SetBaseIntSettingValue(section.c_str(), keys.hour, new_hour_value);
|
||||
Host::SetBaseIntSettingValue(section.c_str(), keys.minute, new_minute_value);
|
||||
Host::SetBaseIntSettingValue(section.c_str(), keys.second, new_second_value);
|
||||
Host::CommitBaseSettingChanges();
|
||||
g_emu_thread->applySettings();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace SettingWidgetBinder
|
||||
|
||||
@@ -133,19 +133,19 @@ void AchievementLoginDialog::processLoginResult(bool result, const QString& mess
|
||||
QMessageBox::information(
|
||||
this, tr("Login Successful"),
|
||||
tr("Successfully logged in to RetroAchievements as %1.").arg(QString::fromStdString(username)));
|
||||
|
||||
|
||||
m_ui.status->setText(tr("Successfully logged in as %1.").arg(QString::fromStdString(username)));
|
||||
m_ui.status->setStyleSheet("color: green; font-weight: bold;");
|
||||
|
||||
|
||||
disconnect(m_ui.buttonBox, &QDialogButtonBox::accepted, this, &AchievementLoginDialog::loginClicked);
|
||||
|
||||
|
||||
m_login->setVisible(false);
|
||||
QPushButton* dismissButton = m_ui.buttonBox->addButton(tr("&Dismiss"), QDialogButtonBox::AcceptRole);
|
||||
dismissButton->setDefault(true);
|
||||
dismissButton->setFocus();
|
||||
|
||||
|
||||
connect(dismissButton, &QPushButton::clicked, this, [this]() { done(0); });
|
||||
|
||||
|
||||
enableUI(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -214,8 +214,8 @@ void AchievementSettingsWidget::updateLoginState()
|
||||
StringUtil::FromChars<u64>(Host::GetBaseStringSettingValue("Achievements", "LoginTimestamp", "0")).value_or(0);
|
||||
const QDateTime login_timestamp(QDateTime::fromSecsSinceEpoch(static_cast<qint64>(login_unix_timestamp)));
|
||||
m_ui.loginStatus->setText(tr("Username: %1\nLogin token generated on %2.")
|
||||
.arg(QString::fromStdString(username))
|
||||
.arg(login_timestamp.toString(Qt::TextDate)));
|
||||
.arg(QString::fromStdString(username))
|
||||
.arg(login_timestamp.toString(Qt::TextDate)));
|
||||
m_ui.loginButton->setText(tr("Logout"));
|
||||
}
|
||||
else
|
||||
|
||||
@@ -262,17 +262,17 @@ void AudioSettingsWidget::updateLatencyLabel()
|
||||
if (expand_buffer_ms > 0)
|
||||
{
|
||||
m_ui.bufferingLabel->setText(tr("Maximum Latency: %1 ms (%2 ms buffer + %3 ms expand + %4 ms output)")
|
||||
.arg(config_buffer_ms + expand_buffer_ms + output_latency_ms)
|
||||
.arg(config_buffer_ms)
|
||||
.arg(expand_buffer_ms)
|
||||
.arg(output_latency_ms));
|
||||
.arg(config_buffer_ms + expand_buffer_ms + output_latency_ms)
|
||||
.arg(config_buffer_ms)
|
||||
.arg(expand_buffer_ms)
|
||||
.arg(output_latency_ms));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui.bufferingLabel->setText(tr("Maximum Latency: %1 ms (%2 ms buffer + %3 ms output)")
|
||||
.arg(config_buffer_ms + output_latency_ms)
|
||||
.arg(config_buffer_ms)
|
||||
.arg(output_latency_ms));
|
||||
.arg(config_buffer_ms + output_latency_ms)
|
||||
.arg(config_buffer_ms)
|
||||
.arg(output_latency_ms));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -280,8 +280,8 @@ void AudioSettingsWidget::updateLatencyLabel()
|
||||
if (expand_buffer_ms > 0)
|
||||
{
|
||||
m_ui.bufferingLabel->setText(tr("Maximum Latency: %1 ms (%2 ms expand, minimum output latency unknown)")
|
||||
.arg(expand_buffer_ms + config_buffer_ms)
|
||||
.arg(expand_buffer_ms));
|
||||
.arg(expand_buffer_ms + config_buffer_ms)
|
||||
.arg(expand_buffer_ms));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -203,7 +203,7 @@ void ControllerBindingWidget::onAutomaticBindingClicked()
|
||||
{
|
||||
// we set it as data, because the device list could get invalidated while the menu is up
|
||||
QAction* action;
|
||||
if(dev.first.compare(dev.second, Qt::CaseInsensitive) == 0)
|
||||
if (dev.first.compare(dev.second, Qt::CaseInsensitive) == 0)
|
||||
action = menu.addAction(dev.first);
|
||||
else
|
||||
action = menu.addAction(QStringLiteral("%1: %2").arg(dev.first).arg(dev.second));
|
||||
@@ -1157,7 +1157,7 @@ void USBDeviceWidget::onAutomaticBindingClicked()
|
||||
{
|
||||
// we set it as data, because the device list could get invalidated while the menu is up
|
||||
QAction* action;
|
||||
if(dev.first.compare(dev.second, Qt::CaseInsensitive) == 0)
|
||||
if (dev.first.compare(dev.second, Qt::CaseInsensitive) == 0)
|
||||
action = menu.addAction(dev.first);
|
||||
else
|
||||
action = menu.addAction(QStringLiteral("%1: %2").arg(dev.first).arg(dev.second));
|
||||
|
||||
@@ -88,7 +88,7 @@ ControllerGlobalSettingsWidget::~ControllerGlobalSettingsWidget() = default;
|
||||
void ControllerGlobalSettingsWidget::addDeviceToList(const QString& identifier, const QString& name)
|
||||
{
|
||||
QListWidgetItem* item = new QListWidgetItem();
|
||||
if(identifier.compare(name,Qt::CaseInsensitive) == 0)
|
||||
if (identifier.compare(name, Qt::CaseInsensitive) == 0)
|
||||
item->setText(identifier);
|
||||
else
|
||||
item->setText(QStringLiteral("%1: %2").arg(identifier).arg(name));
|
||||
|
||||
@@ -89,10 +89,10 @@ void ControllerSettingsWindow::onCurrentProfileChanged(int index)
|
||||
|
||||
void ControllerSettingsWindow::onNewProfileClicked()
|
||||
{
|
||||
const QString profile_name(QInputDialog::getText(this, tr("Create Input Profile"),
|
||||
tr("Custom input profiles are used to override the Shared input profile for specific games.\n"
|
||||
"To apply a custom input profile to a game, go to its Game Properties, then change the 'Input Profile' on the Summary tab.\n\n"
|
||||
"Enter the name for the new input profile:")));
|
||||
const QString profile_name(QInputDialog::getText(this, tr("Create Input Profile"),
|
||||
tr("Custom input profiles are used to override the Shared input profile for specific games.\n"
|
||||
"To apply a custom input profile to a game, go to its Game Properties, then change the 'Input Profile' on the Summary tab.\n\n"
|
||||
"Enter the name for the new input profile:")));
|
||||
if (profile_name.isEmpty())
|
||||
return;
|
||||
|
||||
@@ -457,10 +457,16 @@ void ControllerSettingsWindow::createWidgets()
|
||||
const QString display_name(QString::fromUtf8(ci ? ci->GetLocalizedName() : "Unknown"));
|
||||
|
||||
QListWidgetItem* item = new QListWidgetItem();
|
||||
//: Controller Port is an official term from Sony. Find the official translation for your language inside the console's manual.
|
||||
item->setText(mtap_enabled[port] ? (tr("Controller Port %1%2\n%3").arg(port + 1).arg(s_mtap_slot_names[slot]).arg(display_name))
|
||||
//: Controller Port is an official term from Sony. Find the official translation for your language inside the console's manual.
|
||||
: tr("Controller Port %1\n%2").arg(port + 1).arg(display_name));
|
||||
if (mtap_enabled[port])
|
||||
{
|
||||
//: Controller Port is an official term from Sony. Find the official translation for your language inside the console's manual.
|
||||
item->setText(tr("Controller Port %1%2\n%3").arg(port + 1).arg(s_mtap_slot_names[slot]).arg(display_name));
|
||||
}
|
||||
else
|
||||
{
|
||||
//: Controller Port is an official term from Sony. Find the official translation for your language inside the console's manual.
|
||||
item->setText(tr("Controller Port %1\n%2").arg(port + 1).arg(display_name));
|
||||
}
|
||||
item->setIcon(m_port_bindings[global_slot]->getIcon());
|
||||
item->setData(Qt::UserRole, QVariant(global_slot));
|
||||
m_ui.settingsCategory->addItem(item);
|
||||
@@ -513,10 +519,16 @@ void ControllerSettingsWindow::updateListDescription(u32 global_slot, Controller
|
||||
const Pad::ControllerInfo* ci = Pad::GetControllerInfo(widget->getControllerType());
|
||||
const QString display_name = QString::fromUtf8(ci ? ci->GetLocalizedName() : "Unknown");
|
||||
|
||||
//: Controller Port is an official term from Sony. Find the official translation for your language inside the console's manual.
|
||||
item->setText(mtap_enabled ? (tr("Controller Port %1%2\n%3").arg(port + 1).arg(s_mtap_slot_names[slot]).arg(display_name))
|
||||
//: Controller Port is an official term from Sony. Find the official translation for your language inside the console's manual.
|
||||
: tr("Controller Port %1\n%2").arg(port + 1).arg(display_name));
|
||||
if (mtap_enabled)
|
||||
{
|
||||
//: Controller Port is an official term from Sony. Find the official translation for your language inside the console's manual.
|
||||
item->setText(tr("Controller Port %1%2\n%3").arg(port + 1).arg(s_mtap_slot_names[slot]).arg(display_name));
|
||||
}
|
||||
else
|
||||
{
|
||||
//: Controller Port is an official term from Sony. Find the official translation for your language inside the console's manual.
|
||||
item->setText(tr("Controller Port %1\n%2").arg(port + 1).arg(display_name));
|
||||
}
|
||||
item->setIcon(widget->getIcon());
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,15 @@ EmulationSettingsWidget::EmulationSettingsWidget(SettingsWindow* settings_dialog
|
||||
|
||||
if (dialog()->isPerGameSettings())
|
||||
{
|
||||
SettingWidgetBinder::BindWidgetToDateTimeSetting(sif, m_ui.rtcDateTime, "EmuCore");
|
||||
SettingWidgetBinder::DateTimeKeys rtc_keys;
|
||||
rtc_keys.year = "RtcYear";
|
||||
rtc_keys.month = "RtcMonth";
|
||||
rtc_keys.day = "RtcDay";
|
||||
rtc_keys.hour = "RtcHour";
|
||||
rtc_keys.minute = "RtcMinute";
|
||||
rtc_keys.second = "RtcSecond";
|
||||
|
||||
SettingWidgetBinder::BindWidgetToDateTimeSetting(sif, m_ui.rtcDateTime, "EmuCore", rtc_keys);
|
||||
m_ui.rtcDateTime->setDateRange(QDate(2000, 1, 1), QDate(2099, 12, 31));
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.manuallySetRealTimeClock, "EmuCore", "ManuallySetRealTimeClock", false);
|
||||
connect(m_ui.manuallySetRealTimeClock, &QCheckBox::checkStateChanged, this, &EmulationSettingsWidget::onManuallySetRealTimeClockChanged);
|
||||
|
||||
@@ -25,7 +25,7 @@ FolderSettingsWidget::FolderSettingsWidget(SettingsWindow* settings_dialog, QWid
|
||||
SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.videoDumpingDirectory, m_ui.videoDumpingDirectoryBrowse, m_ui.videoDumpingDirectoryOpen, m_ui.videoDumpingDirectoryReset,
|
||||
"Folders", "Videos", Path::Combine(EmuFolders::DataRoot, "videos"));
|
||||
dialog()->registerWidgetHelp(m_ui.organizeSnapshotsByGame, tr("Organize Snapshots by Game"), tr("Unchecked"),
|
||||
tr("Saves snapshots to per-game subfolders instead of a shared folder."));
|
||||
tr("Saves snapshots to per-game subfolders instead of a shared folder."));
|
||||
}
|
||||
|
||||
FolderSettingsWidget::~FolderSettingsWidget() = default;
|
||||
|
||||
@@ -118,9 +118,8 @@ void GameCheatSettingsWidget::onCheatListItemHovered(const QModelIndex& index)
|
||||
const QModelIndex source_index = m_model_proxy->mapToSource(index);
|
||||
const QModelIndex sibling_index = source_index.siblingAtColumn(0);
|
||||
QStandardItem* item = m_model->itemFromIndex(sibling_index);
|
||||
if (!item)
|
||||
if (!item || !item->isCheckable())
|
||||
{
|
||||
// No item is selected.
|
||||
m_ui.appliedLabel->clear();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -342,15 +342,15 @@ void GameSummaryWidget::onVerifyClicked()
|
||||
if (!hentry->version.empty())
|
||||
{
|
||||
setVerifyResult(tr("Verified as %1 [%2] (Version %3).")
|
||||
.arg(QString::fromStdString(hentry->name))
|
||||
.arg(QString::fromStdString(hentry->serial))
|
||||
.arg(QString::fromStdString(hentry->version)));
|
||||
.arg(QString::fromStdString(hentry->name))
|
||||
.arg(QString::fromStdString(hentry->serial))
|
||||
.arg(QString::fromStdString(hentry->version)));
|
||||
}
|
||||
else
|
||||
{
|
||||
setVerifyResult(tr("Verified as %1 [%2].")
|
||||
.arg(QString::fromStdString(hentry->name))
|
||||
.arg(QString::fromStdString(hentry->serial)));
|
||||
.arg(QString::fromStdString(hentry->name))
|
||||
.arg(QString::fromStdString(hentry->serial)));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -11,7 +11,7 @@ class HddCreateQt : public HddCreate
|
||||
{
|
||||
public:
|
||||
HddCreateQt(QWidget* parent);
|
||||
virtual ~HddCreateQt(){};
|
||||
virtual ~HddCreateQt() {}
|
||||
|
||||
private:
|
||||
QWidget* m_parent;
|
||||
|
||||
@@ -197,8 +197,8 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog
|
||||
"and unpauses when you switch back."));
|
||||
dialog()->registerWidgetHelp(m_ui.pauseOnControllerDisconnection, tr("Pause On Controller Disconnection"),
|
||||
tr("Unchecked"), tr("Pauses the emulator when a controller with bindings is disconnected."));
|
||||
dialog()->registerWidgetHelp(m_ui.promptOnStateLoadSaveFailure, tr("Pause On State Load/Save Failure"),
|
||||
tr("Checked"), tr("Display a modal dialog when a save state load/save operation fails."));
|
||||
dialog()->registerWidgetHelp(m_ui.promptOnStateLoadSaveFailure, tr("Prompt On State Load/Save Failure"),
|
||||
tr("Checked"), tr("Displays a modal dialog when a save state load/save operation fails."));
|
||||
dialog()->registerWidgetHelp(m_ui.startFullscreen, tr("Start Fullscreen"), tr("Unchecked"),
|
||||
tr("Automatically switches to fullscreen mode when a game is started."));
|
||||
dialog()->registerWidgetHelp(m_ui.hideMouseCursor, tr("Hide Cursor In Fullscreen"), tr("Unchecked"),
|
||||
|
||||
@@ -37,41 +37,39 @@ MemoryCardConvertDialog::MemoryCardConvertDialog(QWidget* parent, QString select
|
||||
m_ui.progressBar->setRange(0, 100);
|
||||
m_ui.progressBar->setValue(0);
|
||||
|
||||
connect(m_ui.conversionTypeSelect, &QComboBox::currentIndexChanged, this, [this]()
|
||||
connect(m_ui.conversionTypeSelect, &QComboBox::currentIndexChanged, this, [this]() {
|
||||
switch (m_srcCardInfo.type)
|
||||
{
|
||||
switch (m_srcCardInfo.type)
|
||||
{
|
||||
case MemoryCardType::File:
|
||||
SetType(MemoryCardType::Folder, MemoryCardFileType::Unknown, tr("Uses a folder on your PC filesystem, instead of a file. Infinite capacity, while keeping the same compatibility as an 8 MB Memory Card."));
|
||||
break;
|
||||
case MemoryCardType::Folder:
|
||||
switch (m_ui.conversionTypeSelect->currentData().toInt())
|
||||
{
|
||||
case 8:
|
||||
SetType(MemoryCardType::File, MemoryCardFileType::PS2_8MB, tr("A standard, 8 MB Memory Card. Most compatible, but smallest capacity."));
|
||||
break;
|
||||
case 16:
|
||||
SetType(MemoryCardType::File, MemoryCardFileType::PS2_16MB, tr("2x larger than a standard Memory Card. May have some compatibility issues."));
|
||||
break;
|
||||
case 32:
|
||||
SetType(MemoryCardType::File, MemoryCardFileType::PS2_32MB, tr("4x larger than a standard Memory Card. Likely to have compatibility issues."));
|
||||
break;
|
||||
case 64:
|
||||
SetType(MemoryCardType::File, MemoryCardFileType::PS2_64MB, tr("8x larger than a standard Memory Card. Likely to have compatibility issues."));
|
||||
break;
|
||||
default:
|
||||
//: MemoryCardType should be left as-is.
|
||||
QMessageBox::critical(this, tr("Convert Memory Card Failed"), tr("Invalid MemoryCardType"));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//: MemoryCardType should be left as-is.
|
||||
QMessageBox::critical(this, tr("Convert Memory Card Failed"), tr("Invalid MemoryCardType"));
|
||||
return;
|
||||
}
|
||||
case MemoryCardType::File:
|
||||
SetType(MemoryCardType::Folder, MemoryCardFileType::Unknown, tr("Uses a folder on your PC filesystem, instead of a file. Infinite capacity, while keeping the same compatibility as an 8 MB Memory Card."));
|
||||
break;
|
||||
case MemoryCardType::Folder:
|
||||
switch (m_ui.conversionTypeSelect->currentData().toInt())
|
||||
{
|
||||
case 8:
|
||||
SetType(MemoryCardType::File, MemoryCardFileType::PS2_8MB, tr("A standard, 8 MB Memory Card. Most compatible, but smallest capacity."));
|
||||
break;
|
||||
case 16:
|
||||
SetType(MemoryCardType::File, MemoryCardFileType::PS2_16MB, tr("2x larger than a standard Memory Card. May have some compatibility issues."));
|
||||
break;
|
||||
case 32:
|
||||
SetType(MemoryCardType::File, MemoryCardFileType::PS2_32MB, tr("4x larger than a standard Memory Card. Likely to have compatibility issues."));
|
||||
break;
|
||||
case 64:
|
||||
SetType(MemoryCardType::File, MemoryCardFileType::PS2_64MB, tr("8x larger than a standard Memory Card. Likely to have compatibility issues."));
|
||||
break;
|
||||
default:
|
||||
//: MemoryCardType should be left as-is.
|
||||
QMessageBox::critical(this, tr("Convert Memory Card Failed"), tr("Invalid MemoryCardType"));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//: MemoryCardType should be left as-is.
|
||||
QMessageBox::critical(this, tr("Convert Memory Card Failed"), tr("Invalid MemoryCardType"));
|
||||
return;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
disconnect(m_ui.buttonBox, &QDialogButtonBox::accepted, this, nullptr);
|
||||
|
||||
@@ -88,7 +86,6 @@ bool MemoryCardConvertDialog::IsSetup()
|
||||
|
||||
void MemoryCardConvertDialog::onStatusUpdated()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MemoryCardConvertDialog::onProgressUpdated(int value, int range)
|
||||
|
||||
@@ -64,4 +64,4 @@ namespace CardCapacity
|
||||
static constexpr size_t _16_MB = 0x3e80 * 512 * 2; //(0x3fa7 - 0x49) * 2 * 512;
|
||||
static constexpr size_t _32_MB = 0x7d00 * 512 * 2; //(0x7f67 - 0x89) * 2 * 512;
|
||||
static constexpr size_t _64_MB = 0xfde8 * 512 * 2; //(0xfee7 - 0x0109) * 2 * 512;
|
||||
}
|
||||
} // namespace CardCapacity
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "common/FileSystem.h"
|
||||
|
||||
MemoryCardConvertWorker::MemoryCardConvertWorker(QWidget* parent, MemoryCardType type, MemoryCardFileType fileType, const std::string& srcFileName, const std::string& destFileName)
|
||||
: QtAsyncProgressThread(parent)
|
||||
: QtAsyncProgressThread(parent)
|
||||
{
|
||||
this->type = type;
|
||||
this->fileType = fileType;
|
||||
|
||||
@@ -99,9 +99,8 @@ void MemoryCardCreateDialog::updateState()
|
||||
void MemoryCardCreateDialog::createCard()
|
||||
{
|
||||
const QString name = m_ui.name->text();
|
||||
const std::string name_str = QStringLiteral("%1.%2").arg(name)
|
||||
.arg((m_fileType == MemoryCardFileType::PS1) ? QStringLiteral("mcr") : QStringLiteral("ps2"))
|
||||
.toStdString();
|
||||
const QString type = (m_fileType == MemoryCardFileType::PS1) ? QStringLiteral("mcr") : QStringLiteral("ps2");
|
||||
const std::string name_str = QStringLiteral("%1.%2").arg(name).arg(type).toStdString();
|
||||
if (!Path::IsValidFileName(name_str, false))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Create Memory Card"),
|
||||
|
||||
@@ -357,7 +357,6 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
||||
{
|
||||
executable_path = "flatpak run net.pcsx2.PCSX2";
|
||||
icon_name = "net.pcsx2.PCSX2";
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -395,9 +394,12 @@ void ShortcutCreationDialog::CreateShortcut(const std::string name, const std::s
|
||||
"Type=Application\n"
|
||||
"Terminal=false\n"
|
||||
"StartupWMClass=PCSX2\n"
|
||||
"Exec=" + final_args + "\n"
|
||||
"Name=" + clean_name + "\n"
|
||||
"Icon=" + icon_name + "\n"
|
||||
"Exec=" +
|
||||
final_args + "\n" +
|
||||
"Name=" +
|
||||
clean_name + "\n" +
|
||||
"Icon=" +
|
||||
icon_name + "\n" +
|
||||
"Categories=Game;Emulator;\n";
|
||||
std::string_view sv(file_content);
|
||||
|
||||
|
||||
@@ -341,11 +341,12 @@ bool QtHost::DownloadMissingFont(QWidget* dialog_parent, const char* font_name,
|
||||
msgbox.setWindowModality(Qt::WindowModal);
|
||||
msgbox.setIcon(QMessageBox::Critical);
|
||||
msgbox.setTextFormat(Qt::RichText);
|
||||
msgbox.setText(qApp->translate("MainWindow",
|
||||
"The font file '%1' is required for the On-Screen Display and Big Picture Mode to show messages in your language.<br><br>"
|
||||
"Do you want to download this file now? These files are usually less than 10 megabytes in size.<br><br>"
|
||||
"<strong>If you do not download this file, on-screen messages will not be readable.</strong>")
|
||||
.arg(QLatin1StringView(font_name)));
|
||||
msgbox.setText(
|
||||
qApp->translate("MainWindow",
|
||||
"The font file '%1' is required for the On-Screen Display and Big Picture Mode to show messages in your language.<br><br>"
|
||||
"Do you want to download this file now? These files are usually less than 10 megabytes in size.<br><br>"
|
||||
"<strong>If you do not download this file, on-screen messages will not be readable.</strong>")
|
||||
.arg(QLatin1StringView(font_name)));
|
||||
msgbox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
if (msgbox.exec() != QMessageBox::Yes)
|
||||
return false;
|
||||
|
||||
+1579
-1525
File diff suppressed because it is too large
Load Diff
@@ -89,6 +89,7 @@
|
||||
<Manifest Include="..\pcsx2\windows\PCSX2.manifest" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AsyncDialogs.cpp" />
|
||||
<ClCompile Include="ColorPickerButton.cpp" />
|
||||
<ClCompile Include="Debugger\SymbolTree\NewSymbolDialogs.cpp" />
|
||||
<ClCompile Include="Debugger\SymbolTree\SymbolTreeDelegates.cpp" />
|
||||
@@ -183,6 +184,7 @@
|
||||
<ClCompile Include="VCRuntimeChecker.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AsyncDialogs.h" />
|
||||
<QtMoc Include="SetupWizardDialog.h" />
|
||||
<QtMoc Include="Settings\InterfaceSettingsWidget.h" />
|
||||
<QtMoc Include="Settings\GameListSettingsWidget.h" />
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="PrecompiledHeader.cpp" />
|
||||
<ClCompile Include="AsyncDialogs.cpp" />
|
||||
<ClCompile Include="QtHost.cpp" />
|
||||
<ClCompile Include="MainWindow.cpp" />
|
||||
<ClCompile Include="$(IntDir)moc_MainWindow.cpp">
|
||||
@@ -518,6 +519,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="PrecompiledHeader.h" />
|
||||
<ClInclude Include="AsyncDialogs.h" />
|
||||
<ClInclude Include="QtUtils.h" />
|
||||
<ClInclude Include="SettingWidgetBinder.h" />
|
||||
<ClInclude Include="Settings\HddCreateQt.h">
|
||||
|
||||
+331
-56
@@ -143,6 +143,7 @@ namespace Achievements
|
||||
static void HandleResetEvent(const rc_client_event_t* event);
|
||||
static void HandleUnlockEvent(const rc_client_event_t* event);
|
||||
static void HandleGameCompleteEvent(const rc_client_event_t* event);
|
||||
static void HandleSubsetCompleteEvent(const rc_client_event_t* event);
|
||||
static void HandleLeaderboardStartedEvent(const rc_client_event_t* event);
|
||||
static void HandleLeaderboardFailedEvent(const rc_client_event_t* event);
|
||||
static void HandleLeaderboardSubmittedEvent(const rc_client_event_t* event);
|
||||
@@ -169,6 +170,7 @@ namespace Achievements
|
||||
static void UpdateNotificationPosition();
|
||||
|
||||
static std::string GetAchievementBadgePath(const rc_client_achievement_t* achievement, int state);
|
||||
static std::string GetSubsetBadgePath(const rc_client_subset_t* subset);
|
||||
static std::string GetUserBadgePath(const std::string_view username);
|
||||
static std::string GetLeaderboardUserBadgePath(const rc_client_leaderboard_entry_t* entry);
|
||||
|
||||
@@ -176,6 +178,7 @@ namespace Achievements
|
||||
static void DrawLeaderboardListEntry(const rc_client_leaderboard_t* lboard);
|
||||
static void DrawLeaderboardEntry(const rc_client_leaderboard_entry_t& entry, bool is_self, float rank_column_width,
|
||||
float name_column_width, float time_column_width, float column_spacing);
|
||||
static void OpenSubset(const rc_client_subset_t* subset);
|
||||
static void OpenLeaderboard(const rc_client_leaderboard_t* lboard);
|
||||
static void LeaderboardFetchNearbyCallback(
|
||||
int result, const char* error_message, rc_client_leaderboard_entry_list_t* list, rc_client_t* client, void* callback_userdata);
|
||||
@@ -183,6 +186,7 @@ namespace Achievements
|
||||
int result, const char* error_message, rc_client_leaderboard_entry_list_t* list, rc_client_t* client, void* callback_userdata);
|
||||
static void FetchNextLeaderboardEntries();
|
||||
static void CloseLeaderboard();
|
||||
static void CloseSubset();
|
||||
|
||||
static bool s_hardcore_mode = false;
|
||||
|
||||
@@ -213,9 +217,11 @@ namespace Achievements
|
||||
static rc_client_async_handle_t* s_load_game_request;
|
||||
|
||||
static rc_client_achievement_list_t* s_achievement_list;
|
||||
static rc_client_subset_list_t* s_subset_list;
|
||||
static rc_client_leaderboard_list_t* s_leaderboard_list;
|
||||
static std::vector<std::pair<const void*, std::string>> s_achievement_badge_paths;
|
||||
static const rc_client_leaderboard_t* s_open_leaderboard = nullptr;
|
||||
static const rc_client_subset_t* s_open_subset = nullptr;
|
||||
static rc_client_async_handle_t* s_leaderboard_fetch_handle = nullptr;
|
||||
static std::vector<rc_client_leaderboard_entry_list_t*> s_leaderboard_entry_lists;
|
||||
static rc_client_leaderboard_entry_list_t* s_leaderboard_nearby_entries;
|
||||
@@ -820,6 +826,10 @@ void Achievements::ClientEventHandler(const rc_client_event_t* event, rc_client_
|
||||
HandleGameCompleteEvent(event);
|
||||
break;
|
||||
|
||||
case RC_CLIENT_EVENT_SUBSET_COMPLETED:
|
||||
HandleSubsetCompleteEvent(event);
|
||||
break;
|
||||
|
||||
case RC_CLIENT_EVENT_LEADERBOARD_STARTED:
|
||||
HandleLeaderboardStartedEvent(event);
|
||||
break;
|
||||
@@ -1211,6 +1221,33 @@ void Achievements::HandleGameCompleteEvent(const rc_client_event_t* event)
|
||||
}
|
||||
}
|
||||
|
||||
void Achievements::HandleSubsetCompleteEvent(const rc_client_event_t* event)
|
||||
{
|
||||
const rc_client_subset_t* subset = event->subset;
|
||||
Console.WriteLn("Achievements: Subset '%s' (%u) complete", subset->title, subset->id);
|
||||
UpdateGameSummary();
|
||||
|
||||
if (EmuConfig.Achievements.Notifications)
|
||||
{
|
||||
std::string title = fmt::format(TRANSLATE_FS("Achievements", "Mastered {}"), subset->title);
|
||||
std::string message = fmt::format(
|
||||
TRANSLATE_FS("Achievements", "{0}, {1}"),
|
||||
TRANSLATE_PLURAL_STR("Achievements", "%n achievements", "Mastery popup",
|
||||
s_game_summary.num_unlocked_achievements),
|
||||
TRANSLATE_PLURAL_STR("Achievements", "%n points", "Mastery popup", s_game_summary.points_unlocked));
|
||||
|
||||
std::string badge_path = GetSubsetBadgePath(subset);
|
||||
|
||||
MTGS::RunOnGSThread([title = std::move(title), message = std::move(message), badge_path = std::move(badge_path)]() {
|
||||
if (ImGuiManager::InitializeFullscreenUI())
|
||||
{
|
||||
ImGuiFullscreen::AddNotification(
|
||||
"achievement_subset_mastery", GAME_COMPLETE_NOTIFICATION_TIME, std::move(title), std::move(message), std::move(badge_path));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Achievements::HandleLeaderboardStartedEvent(const rc_client_event_t* event)
|
||||
{
|
||||
DevCon.WriteLn("Achievements: Leaderboard %u (%s) started", event->leaderboard->id, event->leaderboard->title);
|
||||
@@ -2333,6 +2370,15 @@ bool Achievements::PrepareAchievementsWindow()
|
||||
|
||||
s_achievement_badge_paths = {};
|
||||
|
||||
if (s_subset_list)
|
||||
{
|
||||
rc_client_destroy_subset_list(s_subset_list);
|
||||
s_subset_list = nullptr;
|
||||
}
|
||||
CloseSubset();
|
||||
|
||||
s_subset_list = rc_client_create_subset_list(s_client);
|
||||
|
||||
if (s_achievement_list)
|
||||
rc_client_destroy_achievement_list(s_achievement_list);
|
||||
s_achievement_list = rc_client_create_achievement_list(s_client, RC_CLIENT_ACHIEVEMENT_CATEGORY_CORE_AND_UNOFFICIAL,
|
||||
@@ -2360,6 +2406,9 @@ void Achievements::DrawAchievementsWindow()
|
||||
// ensure image downloads still happen while we're paused
|
||||
Achievements::IdleUpdate();
|
||||
|
||||
const bool has_multiple_subsets = (s_subset_list && s_subset_list->num_subsets > 1);
|
||||
bool s_sidebar_has_focus = false;
|
||||
|
||||
static constexpr float alpha = 0.8f;
|
||||
static constexpr float heading_alpha = 0.95f;
|
||||
static constexpr float heading_height_unscaled = 110.0f;
|
||||
@@ -2409,7 +2458,7 @@ void Achievements::DrawAchievementsWindow()
|
||||
}
|
||||
|
||||
const ImRect title_bb(ImVec2(left, top), ImVec2(right, top + g_large_font.second));
|
||||
text.assign(s_game_title);
|
||||
text.assign(s_open_subset ? s_open_subset->title : s_game_title);
|
||||
|
||||
if (s_hardcore_mode)
|
||||
text.append(TRANSLATE_SV("Achievements", " (Hardcore Mode)"));
|
||||
@@ -2421,18 +2470,28 @@ void Achievements::DrawAchievementsWindow()
|
||||
ImGui::PopFont();
|
||||
|
||||
const ImRect summary_bb(ImVec2(left, top), ImVec2(right, top + g_medium_font.second));
|
||||
if (s_game_summary.num_core_achievements > 0)
|
||||
UpdateGameSummary();
|
||||
|
||||
rc_client_user_game_summary_t display_summary = {};
|
||||
const rc_client_user_game_summary_t* summary_ptr = &s_game_summary;
|
||||
if (s_open_subset)
|
||||
{
|
||||
if (s_game_summary.num_unlocked_achievements == s_game_summary.num_core_achievements)
|
||||
rc_client_get_user_subset_summary(s_client, s_open_subset->id, &display_summary);
|
||||
summary_ptr = &display_summary;
|
||||
}
|
||||
|
||||
if (summary_ptr->num_core_achievements > 0)
|
||||
{
|
||||
if (summary_ptr->num_unlocked_achievements == summary_ptr->num_core_achievements)
|
||||
{
|
||||
text.format(TRANSLATE_FS("Achievements", "You have unlocked all achievements and earned {} points!"),
|
||||
s_game_summary.points_unlocked);
|
||||
summary_ptr->points_unlocked);
|
||||
}
|
||||
else
|
||||
{
|
||||
text.format(TRANSLATE_FS("Achievements", "You have unlocked {0} of {1} achievements, earning {2} of {3} possible points."),
|
||||
s_game_summary.num_unlocked_achievements, s_game_summary.num_core_achievements, s_game_summary.points_unlocked,
|
||||
s_game_summary.points_core);
|
||||
summary_ptr->num_unlocked_achievements, summary_ptr->num_core_achievements, summary_ptr->points_unlocked,
|
||||
summary_ptr->points_core);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2447,12 +2506,12 @@ void Achievements::DrawAchievementsWindow()
|
||||
summary_bb.Min, summary_bb.Max, text.c_str(), text.end_ptr(), nullptr, ImVec2(0.0f, 0.0f), &summary_bb);
|
||||
ImGui::PopFont();
|
||||
|
||||
if (s_game_summary.num_core_achievements > 0)
|
||||
if (summary_ptr->num_core_achievements > 0)
|
||||
{
|
||||
const float progress_height = ImGuiFullscreen::LayoutScale(20.0f);
|
||||
const ImRect progress_bb(ImVec2(left, top), ImVec2(right, top + progress_height));
|
||||
const float fraction =
|
||||
static_cast<float>(s_game_summary.num_unlocked_achievements) / static_cast<float>(s_game_summary.num_core_achievements);
|
||||
static_cast<float>(summary_ptr->num_unlocked_achievements) / static_cast<float>(summary_ptr->num_core_achievements);
|
||||
dl->AddRectFilled(progress_bb.Min, progress_bb.Max, ImGui::GetColorU32(ImGuiFullscreen::UIPrimaryDarkColor));
|
||||
dl->AddRectFilled(progress_bb.Min, ImVec2(progress_bb.Min.x + fraction * progress_bb.GetWidth(), progress_bb.Max.y),
|
||||
ImGui::GetColorU32(ImGuiFullscreen::UISecondaryColor));
|
||||
@@ -2473,68 +2532,203 @@ void Achievements::DrawAchievementsWindow()
|
||||
|
||||
ImGui::SetNextWindowBgAlpha(alpha);
|
||||
|
||||
const float sidebar_width = has_multiple_subsets ? LayoutScale(200.0f) : 0.0f;
|
||||
|
||||
if (has_multiple_subsets && !s_open_subset)
|
||||
{
|
||||
OpenSubset(s_subset_list->subsets[0]);
|
||||
}
|
||||
|
||||
if (ImGuiFullscreen::BeginFullscreenWindow(
|
||||
ImVec2(0.0f, heading_height),
|
||||
ImVec2(display_size.x, display_size.y - heading_height - LayoutScale(ImGuiFullscreen::LAYOUT_FOOTER_HEIGHT)),
|
||||
"achievements", background, 0.0f, ImVec2(ImGuiFullscreen::LAYOUT_MENU_WINDOW_X_PADDING, 0.0f), 0))
|
||||
"achievements", background, 0.0f, ImVec2(0.0f, 0.0f), 0))
|
||||
{
|
||||
static std::map<std::pair<u32, u32>, bool> buckets_collapsed;
|
||||
static const char* bucket_names[NUM_RC_CLIENT_ACHIEVEMENT_BUCKETS] = {
|
||||
TRANSLATE_NOOP("Achievements", "Unknown"),
|
||||
TRANSLATE_NOOP("Achievements", "Locked"),
|
||||
TRANSLATE_NOOP("Achievements", "Unlocked"),
|
||||
TRANSLATE_NOOP("Achievements", "Unsupported"),
|
||||
TRANSLATE_NOOP("Achievements", "Unofficial"),
|
||||
TRANSLATE_NOOP("Achievements", "Recently Unlocked"),
|
||||
TRANSLATE_NOOP("Achievements", "Active Challenges"),
|
||||
TRANSLATE_NOOP("Achievements", "Almost There"),
|
||||
};
|
||||
|
||||
ImGuiFullscreen::BeginMenuButtons();
|
||||
|
||||
for (u32 bucket_type : {RC_CLIENT_ACHIEVEMENT_BUCKET_ACTIVE_CHALLENGE, RC_CLIENT_ACHIEVEMENT_BUCKET_RECENTLY_UNLOCKED,
|
||||
RC_CLIENT_ACHIEVEMENT_BUCKET_UNLOCKED, RC_CLIENT_ACHIEVEMENT_BUCKET_ALMOST_THERE, RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED,
|
||||
RC_CLIENT_ACHIEVEMENT_BUCKET_UNOFFICIAL, RC_CLIENT_ACHIEVEMENT_BUCKET_UNSUPPORTED})
|
||||
if (has_multiple_subsets)
|
||||
{
|
||||
for (u32 bucket_idx = 0; bucket_idx < s_achievement_list->num_buckets; bucket_idx++)
|
||||
const ImVec4 sidebar_bg(0.10f, 0.10f, 0.10f, alpha);
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, sidebar_bg);
|
||||
|
||||
if (ImGui::BeginChild("subset_sidebar", ImVec2(sidebar_width, 0.0f), ImGuiChildFlags_NavFlattened, 0))
|
||||
{
|
||||
const rc_client_achievement_bucket_t& bucket = s_achievement_list->buckets[bucket_idx];
|
||||
if (bucket.bucket_type != bucket_type)
|
||||
continue;
|
||||
ImGuiFullscreen::BeginMenuButtons();
|
||||
|
||||
pxAssert(bucket.bucket_type < NUM_RC_CLIENT_ACHIEVEMENT_BUCKETS);
|
||||
|
||||
bool& bucket_collapsed = buckets_collapsed[std::make_pair(bucket.subset_id, bucket.bucket_type)];
|
||||
const char* translated_bucket_name = Host::TranslateToCString("Achievements", bucket_names[bucket.bucket_type]);
|
||||
|
||||
if (bucket.subset_id != 0)
|
||||
for (u32 i = 0; i < s_subset_list->num_subsets; i++)
|
||||
{
|
||||
const rc_client_subset_t* subset_info = rc_client_get_subset_info(s_client, bucket.subset_id);
|
||||
bucket_collapsed ^=
|
||||
ImGuiFullscreen::MenuHeadingButton(TinyString::from_format("{} - {}",
|
||||
subset_info != nullptr ? subset_info->title : TRANSLATE("Achievements", "Unknown Subset"),
|
||||
translated_bucket_name), bucket_collapsed ? ICON_FA_CHEVRON_DOWN : ICON_FA_CHEVRON_UP);
|
||||
const rc_client_subset_t* subset = s_subset_list->subsets[i];
|
||||
TinyString id_str;
|
||||
id_str.format("sidebar_subset_{}", subset->id);
|
||||
|
||||
ImRect bb;
|
||||
bool visible, hovered;
|
||||
ImGuiFullscreen::MenuButtonFrame(id_str, true, ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY,
|
||||
&visible, &hovered, &bb.Min, &bb.Max, 0, 1.0f);
|
||||
|
||||
if (ImGui::IsItemHovered() || ImGui::IsItemFocused())
|
||||
s_sidebar_has_focus = true;
|
||||
|
||||
const float padding = LayoutScale(10.0f);
|
||||
const float spacing = LayoutScale(8.0f);
|
||||
const float icon_size = LayoutScale(28.0f);
|
||||
|
||||
if (s_open_subset && s_open_subset->id == subset->id)
|
||||
{
|
||||
const float line_width = LayoutScale(4.0f);
|
||||
const ImVec2 line_min(bb.Min.x, bb.Min.y);
|
||||
const ImVec2 line_max(bb.Min.x + line_width, bb.Max.y);
|
||||
ImGui::GetWindowDrawList()->AddRectFilled(line_min, line_max, IM_COL32(52, 152, 219, 255));
|
||||
}
|
||||
|
||||
const ImVec2 icon_min(bb.Min.x + padding, bb.Min.y + (bb.Max.y - bb.Min.y - icon_size) * 0.5f);
|
||||
const ImVec2 icon_max(icon_min.x + icon_size, icon_min.y + icon_size);
|
||||
|
||||
std::string badge_path = GetSubsetBadgePath(subset);
|
||||
if (!badge_path.empty())
|
||||
{
|
||||
GSTexture* badge = ImGuiFullscreen::GetCachedTextureAsync(badge_path);
|
||||
if (badge)
|
||||
{
|
||||
ImGui::GetWindowDrawList()->AddImage(reinterpret_cast<ImTextureID>(badge->GetNativeHandle()),
|
||||
icon_min, icon_max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f), IM_COL32(255, 255, 255, 255));
|
||||
}
|
||||
}
|
||||
|
||||
const float text_x = icon_max.x + spacing;
|
||||
const ImRect text_bb(ImVec2(text_x, bb.Min.y), ImVec2(bb.Max.x - padding, bb.Max.y));
|
||||
|
||||
ImGui::PushFont(g_medium_font.first, g_medium_font.second);
|
||||
ImGui::RenderTextClipped(text_bb.Min, text_bb.Max, subset->title, nullptr, nullptr, ImVec2(0.0f, 0.5f), &text_bb);
|
||||
ImGui::PopFont();
|
||||
|
||||
if (ImGui::IsItemClicked() || ImGui::IsItemActivated())
|
||||
OpenSubset(subset);
|
||||
}
|
||||
else
|
||||
|
||||
ImGuiFullscreen::EndMenuButtons();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
if (ImGui::IsItemHovered() || ImGui::IsItemFocused())
|
||||
s_sidebar_has_focus = true;
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
const float content_width = has_multiple_subsets ? (display_size.x - sidebar_width) : display_size.x;
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(ImGuiFullscreen::LAYOUT_MENU_WINDOW_X_PADDING, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, background);
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, ImGui::GetColorU32(ImGuiFullscreen::UIPrimaryDarkColor));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImGui::GetColorU32(ImGuiFullscreen::UIPrimaryColor));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImGui::GetColorU32(ImGuiFullscreen::UISecondaryColor));
|
||||
|
||||
if (ImGui::BeginChild("achievements_content", ImVec2(content_width, 0.0f), ImGuiChildFlags_NavFlattened, 0))
|
||||
{
|
||||
if (ImGui::IsWindowHovered() || ImGui::IsWindowFocused())
|
||||
s_sidebar_has_focus = false;
|
||||
|
||||
static std::map<std::pair<u32, u32>, bool> buckets_collapsed;
|
||||
static const char* bucket_names[NUM_RC_CLIENT_ACHIEVEMENT_BUCKETS] = {
|
||||
TRANSLATE_NOOP("Achievements", "Unknown"),
|
||||
TRANSLATE_NOOP("Achievements", "Locked"),
|
||||
TRANSLATE_NOOP("Achievements", "Unlocked"),
|
||||
TRANSLATE_NOOP("Achievements", "Unsupported"),
|
||||
TRANSLATE_NOOP("Achievements", "Unofficial"),
|
||||
TRANSLATE_NOOP("Achievements", "Recently Unlocked"),
|
||||
TRANSLATE_NOOP("Achievements", "Active Challenges"),
|
||||
TRANSLATE_NOOP("Achievements", "Almost There"),
|
||||
};
|
||||
|
||||
ImGuiFullscreen::BeginMenuButtons();
|
||||
|
||||
for (u32 bucket_type : {RC_CLIENT_ACHIEVEMENT_BUCKET_ACTIVE_CHALLENGE, RC_CLIENT_ACHIEVEMENT_BUCKET_RECENTLY_UNLOCKED,
|
||||
RC_CLIENT_ACHIEVEMENT_BUCKET_UNLOCKED, RC_CLIENT_ACHIEVEMENT_BUCKET_ALMOST_THERE, RC_CLIENT_ACHIEVEMENT_BUCKET_LOCKED,
|
||||
RC_CLIENT_ACHIEVEMENT_BUCKET_UNOFFICIAL, RC_CLIENT_ACHIEVEMENT_BUCKET_UNSUPPORTED})
|
||||
{
|
||||
for (u32 bucket_idx = 0; bucket_idx < s_achievement_list->num_buckets; bucket_idx++)
|
||||
{
|
||||
// Game without subsets
|
||||
const rc_client_achievement_bucket_t& bucket = s_achievement_list->buckets[bucket_idx];
|
||||
if (bucket.bucket_type != bucket_type)
|
||||
continue;
|
||||
|
||||
if (s_open_subset && bucket.subset_id != 0 && bucket.subset_id != s_open_subset->id)
|
||||
continue;
|
||||
|
||||
pxAssert(bucket.bucket_type < NUM_RC_CLIENT_ACHIEVEMENT_BUCKETS);
|
||||
|
||||
bool& bucket_collapsed = buckets_collapsed[std::make_pair(bucket.subset_id, bucket.bucket_type)];
|
||||
const char* translated_bucket_name = Host::TranslateToCString("Achievements", bucket_names[bucket.bucket_type]);
|
||||
|
||||
bucket_collapsed ^=
|
||||
ImGuiFullscreen::MenuHeadingButton(translated_bucket_name,
|
||||
bucket_collapsed ? ICON_FA_CHEVRON_DOWN : ICON_FA_CHEVRON_UP);
|
||||
}
|
||||
|
||||
if (!bucket_collapsed)
|
||||
{
|
||||
for (u32 i = 0; i < bucket.num_achievements; i++)
|
||||
DrawAchievement(bucket.achievements[i]);
|
||||
if (!bucket_collapsed)
|
||||
{
|
||||
for (u32 i = 0; i < bucket.num_achievements; i++)
|
||||
DrawAchievement(bucket.achievements[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiFullscreen::EndMenuButtons();
|
||||
ImGuiFullscreen::EndMenuButtons();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::PopStyleColor(4);
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
ImGuiFullscreen::EndFullscreenWindow();
|
||||
FullscreenUI::SetStandardSelectionFooterText(true);
|
||||
|
||||
if (has_multiple_subsets)
|
||||
{
|
||||
if (ImGuiFullscreen::IsGamepadInputSource())
|
||||
{
|
||||
const bool circleOK = ImGui::GetIO().ConfigNavSwapGamepadButtons;
|
||||
if (s_sidebar_has_focus)
|
||||
{
|
||||
ImGuiFullscreen::SetFullscreenFooterText(std::array{
|
||||
std::make_pair(ICON_PF_DPAD_UP_DOWN, TRANSLATE_SV("Achievements", "Navigate Subsets")),
|
||||
std::make_pair(ICON_PF_DPAD_LEFT_RIGHT, TRANSLATE_SV("Achievements", "Back to List")),
|
||||
std::make_pair(circleOK ? ICON_PF_BUTTON_CIRCLE : ICON_PF_BUTTON_CROSS, TRANSLATE_SV("Achievements", "Select")),
|
||||
std::make_pair(circleOK ? ICON_PF_BUTTON_CROSS : ICON_PF_BUTTON_CIRCLE, TRANSLATE_SV("Achievements", "Back")),
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGuiFullscreen::SetFullscreenFooterText(std::array{
|
||||
std::make_pair(ICON_PF_DPAD_LEFT_RIGHT, TRANSLATE_SV("Achievements", "Navigate Subsets")),
|
||||
std::make_pair(ICON_PF_DPAD_UP_DOWN, TRANSLATE_SV("Achievements", "Change Selection")),
|
||||
std::make_pair(circleOK ? ICON_PF_BUTTON_CROSS : ICON_PF_BUTTON_CIRCLE, TRANSLATE_SV("Achievements", "Back")),
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s_sidebar_has_focus)
|
||||
{
|
||||
ImGuiFullscreen::SetFullscreenFooterText(std::array{
|
||||
std::make_pair(ICON_PF_ARROW_UP ICON_PF_ARROW_DOWN, TRANSLATE_SV("Achievements", "Navigate Subsets")),
|
||||
std::make_pair(ICON_PF_ARROW_LEFT ICON_PF_ARROW_RIGHT, TRANSLATE_SV("Achievements", "Back to List")),
|
||||
std::make_pair(ICON_PF_ENTER, TRANSLATE_SV("Achievements", "Select")),
|
||||
std::make_pair(ICON_PF_ESC, TRANSLATE_SV("Achievements", "Back")),
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGuiFullscreen::SetFullscreenFooterText(std::array{
|
||||
std::make_pair(ICON_PF_ARROW_LEFT ICON_PF_ARROW_RIGHT, TRANSLATE_SV("Achievements", "Navigate Subsets")),
|
||||
std::make_pair(ICON_PF_ARROW_UP ICON_PF_ARROW_DOWN, TRANSLATE_SV("Achievements", "Change Selection")),
|
||||
std::make_pair(ICON_PF_ESC, TRANSLATE_SV("Achievements", "Back")),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FullscreenUI::SetStandardSelectionFooterText(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo)
|
||||
@@ -2610,23 +2804,36 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo)
|
||||
const float points_start = points_template_start + ((points_template_size.x - points_size.x) * 0.5f);
|
||||
|
||||
const char* right_icon_text;
|
||||
const char* badge_text;
|
||||
const char* badge_icon;
|
||||
ImVec4 badge_color;
|
||||
switch (cheevo->type)
|
||||
{
|
||||
case RC_CLIENT_ACHIEVEMENT_TYPE_MISSABLE:
|
||||
right_icon_text = ICON_PF_ACHIEVEMENTS_MISSABLE; // Missable
|
||||
badge_text = TRANSLATE("Achievements", "Missable");
|
||||
badge_icon = ICON_PF_ACHIEVEMENTS_MISSABLE;
|
||||
badge_color = ImVec4(0.78f, 0.55f, 0.30f, 1.0f);
|
||||
right_icon_text = is_unlocked ? ICON_FA_LOCK_OPEN : ICON_FA_LOCK;
|
||||
break;
|
||||
|
||||
case RC_CLIENT_ACHIEVEMENT_TYPE_PROGRESSION:
|
||||
right_icon_text = ICON_PF_ACHIEVEMENTS_PROGRESSION; // Progression
|
||||
badge_text = TRANSLATE("Achievements", "Progression");
|
||||
badge_icon = ICON_PF_ACHIEVEMENTS_PROGRESSION;
|
||||
badge_color = ImVec4(0.62f, 0.48f, 0.80f, 1.0f);
|
||||
right_icon_text = is_unlocked ? ICON_FA_LOCK_OPEN : ICON_FA_LOCK;
|
||||
break;
|
||||
|
||||
case RC_CLIENT_ACHIEVEMENT_TYPE_WIN:
|
||||
right_icon_text = ICON_PF_ACHIEVEMENTS_WIN; // Win Condition
|
||||
badge_text = TRANSLATE("Achievements", "Win Condition");
|
||||
badge_icon = ICON_PF_ACHIEVEMENTS_WIN;
|
||||
badge_color = ImVec4(0.36f, 0.62f, 0.36f, 1.0f);
|
||||
right_icon_text = is_unlocked ? ICON_FA_LOCK_OPEN : ICON_FA_LOCK;
|
||||
break;
|
||||
|
||||
// Just use the lock for standard achievements.
|
||||
case RC_CLIENT_ACHIEVEMENT_TYPE_STANDARD:
|
||||
default:
|
||||
badge_text = nullptr;
|
||||
badge_icon = nullptr;
|
||||
right_icon_text = is_unlocked ? ICON_FA_LOCK_OPEN : ICON_FA_LOCK;
|
||||
break;
|
||||
}
|
||||
@@ -2634,7 +2841,34 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo)
|
||||
const ImVec2 right_icon_size(g_large_font.first->CalcTextSizeA(g_large_font.second, FLT_MAX, 0.0f, right_icon_text));
|
||||
|
||||
const float text_start_x = bb.Min.x + image_size.x + LayoutScale(15.0f);
|
||||
const ImRect title_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(points_start, midpoint));
|
||||
|
||||
const float badge_padding = LayoutScale(6.0f);
|
||||
const float icon_text_spacing = LayoutScale(4.0f);
|
||||
const float badge_title_spacing = LayoutScale(6.0f);
|
||||
ImVec2 badge_size = ImVec2(0.0f, 0.0f);
|
||||
ImVec2 badge_icon_size = ImVec2(0.0f, 0.0f);
|
||||
ImVec2 badge_text_size = ImVec2(0.0f, 0.0f);
|
||||
if (badge_text && badge_icon)
|
||||
{
|
||||
badge_icon_size = g_medium_font.first->CalcTextSizeA(g_medium_font.second, FLT_MAX, 0.0f, badge_icon);
|
||||
badge_text_size = g_medium_font.first->CalcTextSizeA(g_medium_font.second, FLT_MAX, 0.0f, badge_text);
|
||||
badge_size = ImVec2(badge_icon_size.x + icon_text_spacing + badge_text_size.x + (badge_padding * 2.0f),
|
||||
g_medium_font.second + badge_padding);
|
||||
}
|
||||
|
||||
const ImVec2 title_size(g_large_font.first->CalcTextSizeA(g_large_font.second, FLT_MAX, 0.0f, cheevo->title));
|
||||
const float title_text_end_x = text_start_x + title_size.x;
|
||||
|
||||
float title_max_x = points_start;
|
||||
float badge_x = 0.0f;
|
||||
if (badge_text && badge_icon)
|
||||
{
|
||||
const float effective_title_max_x = points_start - (badge_size.x + badge_title_spacing);
|
||||
badge_x = std::min(title_text_end_x + badge_title_spacing, effective_title_max_x);
|
||||
title_max_x = badge_x - badge_title_spacing;
|
||||
}
|
||||
|
||||
const ImRect title_bb(ImVec2(text_start_x, bb.Min.y), ImVec2(title_max_x, midpoint));
|
||||
const ImRect summary_bb(ImVec2(text_start_x, midpoint), ImVec2(points_start, midpoint + g_medium_font.second + extra_summary_height));
|
||||
const ImRect points_bb(ImVec2(points_start, midpoint), bb.Max);
|
||||
const ImRect lock_bb(
|
||||
@@ -2645,6 +2879,24 @@ void Achievements::DrawAchievement(const rc_client_achievement_t* cheevo)
|
||||
ImGui::RenderTextClipped(lock_bb.Min, lock_bb.Max, right_icon_text, nullptr, &right_icon_size, ImVec2(0.0f, 0.0f), &lock_bb);
|
||||
ImGui::PopFont();
|
||||
|
||||
if (badge_text && badge_icon)
|
||||
{
|
||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||
const ImVec2 badge_min(badge_x, bb.Min.y + ((midpoint - bb.Min.y) / 2.0f) - (badge_size.y / 2.0f));
|
||||
const ImVec2 badge_max(badge_min.x + badge_size.x, badge_min.y + badge_size.y);
|
||||
|
||||
dl->AddRectFilled(badge_min, badge_max, ImGui::GetColorU32(badge_color), LayoutScale(3.0f));
|
||||
|
||||
const float icon_y = badge_min.y + ((badge_size.y - badge_icon_size.y) * 0.5f);
|
||||
const float text_y = badge_min.y + ((badge_size.y - badge_text_size.y) * 0.5f);
|
||||
|
||||
const ImVec2 icon_pos(badge_min.x + badge_padding, icon_y);
|
||||
dl->AddText(g_medium_font.first, g_medium_font.second, icon_pos, IM_COL32(255, 255, 255, 255), badge_icon);
|
||||
|
||||
const ImVec2 text_pos(icon_pos.x + badge_icon_size.x + icon_text_spacing, text_y);
|
||||
dl->AddText(g_medium_font.first, g_medium_font.second, text_pos, IM_COL32(255, 255, 255, 255), badge_text);
|
||||
}
|
||||
|
||||
ImGui::PushFont(g_medium_font.first, g_medium_font.second);
|
||||
if (cheevo->description && summary_length > 0)
|
||||
{
|
||||
@@ -3286,6 +3538,29 @@ void Achievements::CloseLeaderboard()
|
||||
s_open_leaderboard = nullptr;
|
||||
}
|
||||
|
||||
std::string Achievements::GetSubsetBadgePath(const rc_client_subset_t* subset)
|
||||
{
|
||||
if (!subset || !subset->badge_name[0])
|
||||
return {};
|
||||
|
||||
std::string path = Path::Combine(s_image_directory, fmt::format("subset_{}.png", subset->badge_name));
|
||||
if (!path.empty() && subset->badge_url && !FileSystem::FileExists(path.c_str()))
|
||||
DownloadImage(subset->badge_url, path);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
void Achievements::OpenSubset(const rc_client_subset_t* subset)
|
||||
{
|
||||
Console.WriteLn("Achievements: Opening subset '%s' (%u)", subset->title, subset->id);
|
||||
s_open_subset = subset;
|
||||
}
|
||||
|
||||
void Achievements::CloseSubset()
|
||||
{
|
||||
s_open_subset = nullptr;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_RAINTEGRATION
|
||||
|
||||
#include "RA_Consoles.h"
|
||||
|
||||
+8
-2
@@ -102,10 +102,16 @@ static void CDVD_INT(int eCycle)
|
||||
// test (which will cause the exception to be handled).
|
||||
static void cdvdSetIrq(uint id = (1 << Irq_CommandComplete))
|
||||
{
|
||||
if (!(cdvd.IntrStat & id))
|
||||
{
|
||||
iopIntcIrq(2);
|
||||
psxSetNextBranchDelta(20);
|
||||
}
|
||||
else
|
||||
DevCon.Warning("CDVD trying to double issue IRQ %x", id);
|
||||
|
||||
cdvd.IntrStat |= id;
|
||||
cdvd.AbortRequested = false;
|
||||
iopIntcIrq(2);
|
||||
psxSetNextBranchDelta(20);
|
||||
}
|
||||
|
||||
static int mg_BIToffset(u8* buffer)
|
||||
|
||||
@@ -247,7 +247,6 @@ set(pcsx2SPU2Sources
|
||||
SPU2/ADSR.cpp
|
||||
SPU2/Debug.cpp
|
||||
SPU2/Dma.cpp
|
||||
SPU2/Mixer.cpp
|
||||
SPU2/spu2.cpp
|
||||
SPU2/ReadInput.cpp
|
||||
SPU2/RegTable.cpp
|
||||
@@ -258,6 +257,7 @@ set(pcsx2SPU2Sources
|
||||
)
|
||||
|
||||
set(pcsx2SPU2SourcesUnshared
|
||||
SPU2/Mixer.cpp
|
||||
SPU2/ReverbResample.cpp
|
||||
)
|
||||
|
||||
@@ -830,6 +830,7 @@ set(pcsx2HostHeaders
|
||||
|
||||
set(pcsx2ImGuiSources
|
||||
ImGui/FullscreenUI.cpp
|
||||
ImGui/FullscreenUI_Settings.cpp
|
||||
ImGui/ImGuiFullscreen.cpp
|
||||
ImGui/ImGuiManager.cpp
|
||||
ImGui/ImGuiOverlays.cpp
|
||||
@@ -837,6 +838,7 @@ set(pcsx2ImGuiSources
|
||||
|
||||
set(pcsx2ImGuiHeaders
|
||||
ImGui/FullscreenUI.h
|
||||
ImGui/FullscreenUI_Internal.h
|
||||
ImGui/ImGuiAnimated.h
|
||||
ImGui/ImGuiFullscreen.h
|
||||
ImGui/ImGuiManager.h
|
||||
|
||||
+11
-2
@@ -139,12 +139,15 @@ The clamp modes are also numerically based.
|
||||
|
||||
### GS Hardware Mipmap Fixes
|
||||
|
||||
* mipmap [`0` or `1` or `2`] {Off, Basic, Full} Default: Automatic (No value, looks up GameDB)
|
||||
* mipmap [`0` or `1`] {Off, On} Default: On (looks up GameDB)
|
||||
* trilinearFiltering [`0` or `1` or `2`] {None, Trilinear, Trilinear Ultra} Default: None (`0`)
|
||||
|
||||
### GS Hardware General Fixes
|
||||
|
||||
* beforeDraw {`OI` with suffix } {None unless specific game GSC} Default: Automatic (No value, looks up GameDB) with valid variable name (ex. OI_BurnoutGames)
|
||||
|
||||
* moveHandler {`MV` with suffix } {None unless specific game GSC} Default: Automatic (No value, looks up GameDB) with valid variable name (ex. MV_Ico)
|
||||
|
||||
* afterDraw {`OO` with suffix } {None unless specific game GSC} Default: Automatic (No value, looks up GameDB) with valid variable name
|
||||
* conservativeFramebuffer [`0` or `1`] {Off or On} Default: On (`1`)
|
||||
* texturePreloading [`0` or `1` or `2`] {None, Partial or Full Hash Cache} Default: None (`0`)
|
||||
@@ -153,11 +156,17 @@ The clamp modes are also numerically based.
|
||||
### GS Hardware Renderer Fixes
|
||||
|
||||
* autoFlush [`0` or `1` or `2`] {Disabled, Enabled (Sprites Only), Enabled (All Primitives)} Default: Off (`0`)
|
||||
* partialTargetInvalidation [`0` or `1`] {Off, On} Default: Off (`0`)
|
||||
* PCRTCOffsets [`0` or `1`] {Off, On} Default: Off (`0`)
|
||||
|
||||
* PCRTCOverscan [`0` or `1`] {Off, On} Default: Off (`0`)
|
||||
|
||||
* disableDepthSupport [`0` or `1`] {Off, On} Default: Off (`0`)
|
||||
* disablePartialInvalidation [`0` or `1`] {Off, On} Default: Off (`0`)
|
||||
* cpuFramebufferConversion [`0` or `1`] {Off, On} Default: Off (`0`)
|
||||
* preloadFrameData [`0` or `1`] {Off, On} Default: Off (`0`)
|
||||
* textureInsideRT [`0` or `1`] {Disabled, Inside Targets, Merge Targets} Default: Off (`0`)
|
||||
* textureInsideRT [`0` or `1`or `2`] {Disabled, Inside Targets, Merge Targets} Default: Off (`0`)
|
||||
* PCRTCOverscan [`0` or `1`] {Off, On} Default: Off (`0`)
|
||||
* PCRTCOverscan [`0` or `1`] {Off, On} Default: Off (`0`)
|
||||
* cpuCLUTRender [`0` or `1` or `2`] {Disabled, Normal, Aggressive} Default: Disabled (`0`)
|
||||
* cpuSpriteRenderBW [Value between `0` to `10`] {Disabled, 1 (64), 2 (128), 3 (192), 4 (256), 5 (320), 6 (384), 7 (448), 8 (512), 9 (576), 10 (640)} Default: Off (`0`)
|
||||
|
||||
@@ -229,11 +229,6 @@
|
||||
"minimum": 0,
|
||||
"maximum": 100000
|
||||
},
|
||||
"halfBottomOverride": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 1
|
||||
},
|
||||
"halfPixelOffset": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
|
||||
+352
-88
@@ -3754,12 +3754,11 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
|
||||
const u16* RESTRICT index = m_index.buff;
|
||||
const u32 count = m_index.tail;
|
||||
|
||||
// Optimize out using indices for sprites and points; probably not much difference.
|
||||
const auto GetIndex = [&](int i) {
|
||||
const auto GetPoint = [v, index](int i) -> GSVector4i {
|
||||
if constexpr (primclass == GS_SPRITE_CLASS || primclass == GS_POINT_CLASS)
|
||||
return i;
|
||||
return GSVector4i(v[i].m[1]).upl16(); // Optimize out using the indices.
|
||||
else
|
||||
return index[i];
|
||||
return GSVector4i(v[index[i]].m[1]).upl16();
|
||||
};
|
||||
|
||||
// Batch prim into groups so that within each group the cumulative bboxes are non-overlapping.
|
||||
@@ -3783,8 +3782,15 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
|
||||
|
||||
u32 i = 0;
|
||||
u32 skip = 0; // Number of indices to skip if we have the bbox from the previous iteration.
|
||||
|
||||
// To cache a tristrip for the next iteration if we cannot use it in this iteration.
|
||||
struct SavedTristrip {
|
||||
bool saved = false;
|
||||
u32 skip;
|
||||
BoundingOct bbox;
|
||||
} saved_tristrip;
|
||||
|
||||
GSVector4i all(INT_MAX, INT_MAX, -INT_MAX, -INT_MAX);
|
||||
BoundingOct all;
|
||||
|
||||
while (i < count)
|
||||
{
|
||||
@@ -3792,149 +3798,394 @@ GSState::PRIM_OVERLAP GSState::GetPrimitiveOverlapDrawlistImpl(bool save_drawlis
|
||||
|
||||
skip = 0;
|
||||
|
||||
GSVector4i bbox(INT_MAX, INT_MAX, -INT_MAX, -INT_MAX);
|
||||
BoundingOct bbox;
|
||||
|
||||
while (j < count)
|
||||
{
|
||||
bool got_bbox = false;
|
||||
|
||||
// Assuming that indices 0-5 represent two triangles:
|
||||
// Triangle strips: indices 1, 2 are identical to indices 3, 4. Indices 0, 5 are different.
|
||||
// Triangles fans: indices 0, 2 are identical to indices 3, 4. Indices 1, 5 are different.
|
||||
// Warning: this depends on how the vertices are arranged in the vertex kick.
|
||||
// if that changes this detection will break.
|
||||
constexpr std::array<std::array<std::array<int, 3>, 2>, 2> tri_order({
|
||||
// Triangle strip expected indices.
|
||||
std::array{ std::array<int, 3>{ 1, 2, 0 }, std::array<int, 3>{ 3, 4, 5 } },
|
||||
// Test overlap of two adjacent triangles give the indices of the
|
||||
// shared edge and two unshared points.
|
||||
const auto TrianglesOverlap = [v, index, GetPoint]
|
||||
(u32 s0, u32 s1, u32 u0, u32 u1, bool& sign_out) -> bool {
|
||||
|
||||
// Triangle fan expected indices.
|
||||
std::array{ std::array<int, 3>{ 0, 2, 1 }, std::array<int, 3>{ 3, 4, 5 } },
|
||||
});
|
||||
const GSVector4i shared0 = GetPoint(s0);
|
||||
const GSVector4i shared1 = GetPoint(s1) - shared0;
|
||||
const GSVector4i unshared0 = GetPoint(u0) - shared0;
|
||||
const GSVector4i unshared1 = GetPoint(u1) - shared0;
|
||||
|
||||
// Determine which side of the shared edge each triangle is on.
|
||||
const bool sign0 = unshared0.x * shared1.y - unshared0.y * shared1.x >= 0;
|
||||
const bool sign1 = unshared1.x * shared1.y - unshared1.y * shared1.x >= 0;
|
||||
|
||||
sign_out = sign0; // Output side of the first triangle with respect to the shared edge.
|
||||
|
||||
// Cross product signs comparison. If true, triangles are on same side of the shared edge.
|
||||
return sign0 == sign1;
|
||||
};
|
||||
|
||||
// Helper to detect triangles strips/fans (template 0 for strips, 1 for fans).
|
||||
const auto CheckTriangleQuads = [v, index, count, TrianglesOverlap, GetPoint]
|
||||
<int type>(u32 i, u32& skip, BoundingOct& bbox) -> bool {
|
||||
|
||||
// Assuming that indices 0-5 represent two triangles:
|
||||
// Triangle strips: indices 1, 2 are identical to indices 3, 4. Indices 0, 5 are different.
|
||||
// Triangles fans: indices 0, 2 are identical to indices 3, 4. Indices 1, 5 are different.
|
||||
// Warning: this depends on how the vertices are arranged in the vertex kick.
|
||||
// if that changes this detection will break.
|
||||
constexpr std::array<std::array<std::array<int, 3>, 2>, 2> tri_order({
|
||||
// Triangle strip expected indices.
|
||||
std::array{ std::array<int, 3>{ 1, 2, 0 }, std::array<int, 3>{ 3, 4, 5 } },
|
||||
|
||||
// Triangle fan expected indices.
|
||||
std::array{ std::array<int, 3>{ 0, 2, 1 }, std::array<int, 3>{ 3, 4, 5 } },
|
||||
});
|
||||
|
||||
const auto CheckTriangleQuads = [&]<int type>() {
|
||||
constexpr std::array<int, 3> tri0 = tri_order[type][0];
|
||||
constexpr std::array<int, 3> tri1 = tri_order[type][1];
|
||||
|
||||
if (!got_bbox && primclass == GS_TRIANGLE_CLASS && j + 3 < count &&
|
||||
index[j + tri0[0]] == index[j + tri1[0]] &&
|
||||
index[j + tri0[1]] == index[j + tri1[1]])
|
||||
// Check that the initial two triangles form a strip.
|
||||
if (!(primclass == GS_TRIANGLE_CLASS && i + 6 <= count &&
|
||||
index[i + tri0[0]] == index[i + tri1[0]] &&
|
||||
index[i + tri0[1]] == index[i + tri1[1]]))
|
||||
{
|
||||
// Get the initial triangle bbox.
|
||||
bbox = GSVector4i(v[index[j + 0]].m[1]).upl16().xyxy();
|
||||
bbox = bbox.runion(GSVector4i(v[index[j + 1]].m[1]).upl16().xyxy());
|
||||
bbox = bbox.runion(GSVector4i(v[index[j + 2]].m[1]).upl16().xyxy());
|
||||
return false;
|
||||
}
|
||||
|
||||
while (true)
|
||||
u32 j = i;
|
||||
|
||||
// Get the initial triangle bbox.
|
||||
bbox = BoundingOct::FromPoint(GetPoint(j + 0));
|
||||
bbox = bbox.Union(GetPoint(j + 1));
|
||||
bbox = bbox.Union(GetPoint(j + 2));
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Check if the two triangles overlap.
|
||||
[[maybe_unused]] bool sign;// ignore
|
||||
if (TrianglesOverlap(j + tri1[0], j + tri1[1], j + tri0[2], j + tri1[2], sign))
|
||||
{
|
||||
// There is a shared edge between this and next triangle.
|
||||
// Check if the unshared point is on opposite sides of the shared edge.
|
||||
const GSVector4i shared0 = GSVector4i(v[index[j + skip + tri0[0]]].m[1]).upl16();
|
||||
const GSVector4i shared1 = GSVector4i(v[index[j + skip + tri0[1]]].m[1]).upl16() - shared0;
|
||||
const GSVector4i unshared0 = GSVector4i(v[index[j + skip + tri0[2]]].m[1]).upl16() - shared0;
|
||||
const GSVector4i unshared1 = GSVector4i(v[index[j + skip + tri1[2]]].m[1]).upl16() - shared0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Cross product signs comparison.
|
||||
if ((unshared0.x * shared1.y - unshared0.y * shared1.x >= 0) ==
|
||||
(unshared1.x * shared1.y - unshared1.y * shared1.x >= 0))
|
||||
// Corners are on opposite sides so we can assume a non-axis-aligned quad.
|
||||
// Take union with the single unshared point.
|
||||
bbox = bbox.Union(GetPoint(j + tri1[2]));
|
||||
j += 3;
|
||||
|
||||
if (!(j + 6 <= count &&
|
||||
index[j + tri0[0]] == index[j + tri1[0]] &&
|
||||
index[j + tri0[1]] == index[j + tri1[1]]))
|
||||
{
|
||||
// Cannot continue the strip/fan.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (j == i)
|
||||
{
|
||||
return false; // Only a single triangle in the strip.
|
||||
}
|
||||
|
||||
skip = j - i + 3; // Number of vertices in the loop + first triangle.
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Helper functions to find a common edge between two triangles and check if they overlap.
|
||||
// Template parameter indicates if the triangle is at the end of the strip or beginning.
|
||||
const auto MatchTriangles = [v, index, TrianglesOverlap]<bool end0, bool end1>
|
||||
(u32 tri0, u32 tri1, bool& sign) -> bool {
|
||||
|
||||
// For the end triangle only consider the last edge of the triangle.
|
||||
// For the start triangle only consider the first edge of the triangle.
|
||||
const u32 base0 = end0 ? 1 : 0;
|
||||
const u32 base1 = end1 ? 1 : 0;
|
||||
|
||||
for (int i0 = base0; i0 < base0 + 2; i0++)
|
||||
{
|
||||
for (int i1 = base1; i1 < base1 + 2; i1++)
|
||||
{
|
||||
if (v[index[tri0 + i0]].XYZ.U32[0] == v[index[tri1 + i1]].XYZ.U32[0])
|
||||
{
|
||||
break; // Triangles are on same side of the shared edge so there's overlap.
|
||||
}
|
||||
// Get the index that should be shared between the adjacent triangles.
|
||||
const int j0 = end0 ? (i0 == 2 ? 0 : i0 - 4) : (i0 == 0 ? 2 : i0 + 4);
|
||||
const int j1 = end1 ? (i1 == 2 ? 0 : i1 - 4) : (i1 == 0 ? 2 : i1 + 4);
|
||||
|
||||
// Corners are on opposite sides so we can assume a non-axis-aligned quad.
|
||||
// Take union with the single unshared point.
|
||||
bbox = bbox.runion(GSVector4i(v[index[j + skip + tri1[2]]].m[1]).upl16().xyxy());
|
||||
if (v[index[tri0 + j0]].XYZ.U32[0] != v[index[tri1 + j1]].XYZ.U32[0])
|
||||
{
|
||||
return false; // Edge is not shared.
|
||||
}
|
||||
|
||||
skip += 3;
|
||||
// Get the indices that should be unshared.
|
||||
const int k0 = end0 ? 3 - i0 : 1 - i0;
|
||||
const int k1 = end1 ? 3 - i1 : 1 - i1;
|
||||
|
||||
got_bbox = true;
|
||||
// Get the shared edge in the order of the first strip's increasing indices.
|
||||
// Consistency is important for the sign check to work correctly.
|
||||
const int s0 = std::min(i0, j0);
|
||||
const int t0 = std::max(i0, j0);
|
||||
|
||||
if (!(j + skip + 3 < count &&
|
||||
index[j + skip + tri0[0]] == index[j + skip + tri1[0]] &&
|
||||
index[j + skip + tri0[1]] == index[j + skip + tri1[1]]))
|
||||
{
|
||||
// Cannot continue the strip/fan. Consume the last triangle and exit.
|
||||
skip += 3;
|
||||
break;
|
||||
return !TrianglesOverlap(tri0 + s0, tri0 + t0, tri0 + k0, tri1 + k1, sign);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// First check: see if the triangles are part of triangle strip/fan.
|
||||
if (primclass == GS_TRIANGLE_CLASS)
|
||||
{
|
||||
CheckTriangleQuads.template operator()<0>(); // Check triangle strips.
|
||||
CheckTriangleQuads.template operator()<1>(); // Check triangle fans.
|
||||
}
|
||||
// Helper function to detect triangles strips and merge them together into
|
||||
// a grid of triangles strips.
|
||||
const auto CheckTriangleStrips = [index, v, count, CheckTriangleQuads, MatchTriangles, GetPoint]
|
||||
(u32 i, u32& skip, BoundingOct& bbox_all, SavedTristrip& saved_tristrip) -> bool {
|
||||
|
||||
// Second check: see if triangles form an axis-aligned quad.
|
||||
if (!got_bbox && primclass == GS_TRIANGLE_CLASS && check_quads && j + 3 < count)
|
||||
{
|
||||
const u16* RESTRICT idx0 = &index[j + 0];
|
||||
const u16* RESTRICT idx1 = &index[j + 3];
|
||||
if (!(primclass == GS_TRIANGLE_CLASS && i + 6 <= count))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 j = i;
|
||||
|
||||
u32 prev_tri0; // First triangle of previous tristrip.
|
||||
u32 prev_tri1; // Last triangle of previous tristrip.
|
||||
|
||||
bool all_small; // Whether all strips so far have 2 triangles only.
|
||||
|
||||
u32 n_tristrips = 0; // Number of tristrips merged so far.
|
||||
|
||||
// Used to make sure the tristrips are adjacent in the same direction so there's not overlap.
|
||||
bool expected_sign;
|
||||
|
||||
// For another heuristic to determine if the tristrips are overlapping themselves.
|
||||
// Stores the maximum difference in X/Y from the starting point.
|
||||
GSVector4i start_pt;
|
||||
GSVector4i max_delta(0, 0);
|
||||
|
||||
// Whether the current tristrip is oriented in the same direction as the first.
|
||||
bool orientation = true;
|
||||
|
||||
BoundingOct bbox; // BBox of current strip.
|
||||
|
||||
// Check for the first tristrip in the chain.
|
||||
if (saved_tristrip.saved)
|
||||
{
|
||||
// Have a tristrip saved from a previous iteration.
|
||||
skip = saved_tristrip.skip;
|
||||
bbox = saved_tristrip.bbox;
|
||||
saved_tristrip.saved = false;
|
||||
}
|
||||
else if (!CheckTriangleQuads.template operator()<0>(j, skip, bbox))
|
||||
{
|
||||
return false;// Could not find a new tristrip.
|
||||
}
|
||||
|
||||
// Initialize loop variables.
|
||||
prev_tri0 = j;
|
||||
prev_tri1 = j + skip - 3;
|
||||
all_small = (skip <= 6);
|
||||
bbox_all = bbox;
|
||||
start_pt = GetPoint(prev_tri0);
|
||||
n_tristrips++;
|
||||
j += skip;
|
||||
|
||||
while (j < count)
|
||||
{
|
||||
if (!CheckTriangleQuads.template operator()<0>(j, skip, bbox))
|
||||
{
|
||||
break; // Cannot continue tristrip grid.
|
||||
}
|
||||
|
||||
// Save the tristrip in case it can be used on the next iteration.
|
||||
saved_tristrip.saved = true;
|
||||
saved_tristrip.skip = skip;
|
||||
saved_tristrip.bbox = bbox;
|
||||
|
||||
// Get first/last triangle of current tristrip.
|
||||
const u32 tri0 = j;
|
||||
const u32 tri1 = j + skip - 3;
|
||||
|
||||
all_small = all_small && (skip <= 6);
|
||||
|
||||
// First heuristic: check if the previous and this tristrip are part of a grid by
|
||||
// checking if the first/last triangles in each strip have a common edge and do not overlap.
|
||||
bool sign0, sign1; // Orientation of how the first and last triangles are adjacent.
|
||||
bool flip; // Whether to flip the expected orientation because the two strips are in opposite directions.
|
||||
|
||||
// Check the first two vertices of first triangles and last two vertices of last triangles.
|
||||
if (MatchTriangles.template operator()<false, false>(prev_tri0, tri0, sign0) &&
|
||||
MatchTriangles.template operator()<true, true>(prev_tri1, tri1, sign1))
|
||||
{
|
||||
flip = false; // Tristrips are in the same directions.
|
||||
}
|
||||
else if (MatchTriangles.template operator()<false, true>(prev_tri0, tri1, sign0) &&
|
||||
MatchTriangles.template operator()<true, false>(prev_tri1, tri0, sign1))
|
||||
{
|
||||
flip = true; // Tristrips are in opposite directions.
|
||||
}
|
||||
else
|
||||
{
|
||||
break; // Cannot continue the tristrip chain.
|
||||
}
|
||||
|
||||
// Second heuristic: make sure the new tristrip is always on the same side as the previous one.
|
||||
if (n_tristrips >= 2)
|
||||
{
|
||||
if (!(sign0 == expected_sign && sign1 == expected_sign))
|
||||
{
|
||||
break; // New tristrip is on the wrong side.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sign0 != sign1)
|
||||
{
|
||||
break; // First/last triangles are on different sides.
|
||||
}
|
||||
expected_sign = sign0;
|
||||
}
|
||||
|
||||
if (flip)
|
||||
{
|
||||
expected_sign = !expected_sign;
|
||||
orientation = !orientation;
|
||||
}
|
||||
|
||||
// Third heuristic: for small tristrips make sure that we never move closer to
|
||||
// the first tristrip in the grid. Small tristrips appears to be more likely to
|
||||
// overlap themself.
|
||||
const GSVector4i curr_pt = GetPoint(orientation ? tri0 : tri1 + 2);
|
||||
const GSVector4i curr_delta = (curr_pt - start_pt).abs32();
|
||||
|
||||
// Check if we moved closer.
|
||||
if (n_tristrips >= 2 && all_small)
|
||||
{
|
||||
if (((curr_delta <= max_delta).mask() & 0xFF) == 0xFF)
|
||||
{
|
||||
break; // We moved closer to the start point in both X/Y so end the merging.
|
||||
}
|
||||
}
|
||||
|
||||
// Update maximum displacement from start.
|
||||
max_delta = max_delta.max_i32(curr_delta);
|
||||
|
||||
prev_tri0 = tri0;
|
||||
prev_tri1 = tri1;
|
||||
bbox_all = bbox_all.Union(bbox);
|
||||
j += skip;
|
||||
saved_tristrip.saved = false; // We consumed the new tristrip.
|
||||
n_tristrips++;
|
||||
}
|
||||
|
||||
skip = j - i;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Helper functions to detect when two triangles form an axis-aligned quad.
|
||||
const auto GetBBoxAxisAlignedTriangles = [v, index, count, GetPoint]
|
||||
(u32 i, u32& skip, BoundingOct& bbox) -> bool {
|
||||
|
||||
if (!(primclass == GS_TRIANGLE_CLASS && i + 6 <= count))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const u32 off0 = i + 0;
|
||||
const u32 off1 = i + 3;
|
||||
TriangleOrdering tri0;
|
||||
TriangleOrdering tri1;
|
||||
|
||||
if (AreTrianglesQuad<0, 0>(v, idx0, idx1, &tri0, &tri1))
|
||||
if (!AreTrianglesQuad<0, 0>(v, index + off0, index + off1, &tri0, &tri1))
|
||||
{
|
||||
// tri.b is right angle corner
|
||||
bbox = GSVector4i(v[idx0[tri0.b]].m[1]).upl16().xyxy();
|
||||
bbox = bbox.runion(GSVector4i(v[idx1[tri1.b]].m[1]).upl16().xyxy());
|
||||
|
||||
skip = 6;
|
||||
return false;
|
||||
}
|
||||
|
||||
got_bbox = true;
|
||||
// tri.b is right angle corner
|
||||
const GSVector4i pt0 = GetPoint(off0 + tri0.b);
|
||||
const GSVector4i pt1 = GetPoint(off1 + tri1.b);
|
||||
bbox = BoundingOct::FromSprite(pt0, pt1);
|
||||
skip = 6;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Helper functions to just get the individual prim bbox.
|
||||
const auto GetBBox = [v, count, GetPoint](u32 i, u32& skip, BoundingOct& bbox) -> bool {
|
||||
if constexpr (primclass == GS_SPRITE_CLASS)
|
||||
{
|
||||
bbox = BoundingOct::FromSprite(GetPoint(i + 0), GetPoint(i + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we fail a quad check assume the rest are not quads since the check is relatively expensive.
|
||||
check_quads = false;
|
||||
bbox = BoundingOct::FromPoint(GetPoint(i + 0));
|
||||
for (u32 j = 1; j < n; j++) // Unroll
|
||||
{
|
||||
bbox = bbox.Union(GetPoint(i + j));
|
||||
}
|
||||
}
|
||||
skip = n;
|
||||
return true;
|
||||
};
|
||||
|
||||
// First check: see if the triangles are part of a triangle strip.
|
||||
if (!got_bbox)
|
||||
{
|
||||
got_bbox = CheckTriangleStrips(j, skip, bbox, saved_tristrip);
|
||||
}
|
||||
|
||||
// Second check: see if the triangles are part of triangle fan.
|
||||
if (!got_bbox)
|
||||
{
|
||||
got_bbox = CheckTriangleQuads.template operator()<1>(j, skip, bbox);
|
||||
}
|
||||
|
||||
// Third check: see if a pair of triangles are an axis-aligned quad.
|
||||
// This doesn't require indices to match like the tristrip check.
|
||||
if (!got_bbox && check_quads)
|
||||
{
|
||||
got_bbox = GetBBoxAxisAlignedTriangles(j, skip, bbox);
|
||||
|
||||
// If we fail a quad check assume the rest are not quads since the check is relatively expensive.
|
||||
check_quads = got_bbox;
|
||||
}
|
||||
|
||||
// Default case: just take the bbox of the prim vertices.
|
||||
if (!got_bbox)
|
||||
{
|
||||
bbox = GSVector4i(v[GetIndex(j)].m[1]).upl16().xyxy();
|
||||
for (int k = 1; k < n; k++) // Unroll
|
||||
bbox = bbox.runion(GSVector4i(v[GetIndex(j + k)].m[1]).upl16().xyxy());
|
||||
|
||||
skip = n;
|
||||
|
||||
got_bbox = true;
|
||||
got_bbox = GetBBox(j, skip, bbox);
|
||||
}
|
||||
|
||||
// Avoid degenerate bbox.
|
||||
bbox = bbox.blend(bbox + GSVector4i(0, 0, 1, 1), bbox.xyxy() == bbox.zwzw());
|
||||
bbox = bbox.FixDegenerate();
|
||||
|
||||
if (all.rintersects(bbox))
|
||||
if (all.Intersects(bbox))
|
||||
{
|
||||
overlap = PRIM_OVERLAP_YES;
|
||||
break;
|
||||
}
|
||||
|
||||
all = all.runion(bbox);
|
||||
|
||||
all = all.Union(bbox);
|
||||
j += skip;
|
||||
|
||||
skip = 0;
|
||||
}
|
||||
|
||||
if (save_drawlist)
|
||||
{
|
||||
m_drawlist.push_back((j - i) / n); // Prim count
|
||||
}
|
||||
else if (j < count)
|
||||
{
|
||||
return PRIM_OVERLAP_YES; // Early exit if not saving drawlist.
|
||||
}
|
||||
|
||||
if (save_bbox)
|
||||
{
|
||||
m_drawlist_bbox.push_back(ProcessBBox(all));
|
||||
m_drawlist_bbox.push_back(ProcessBBox(all.ToBBox()));
|
||||
}
|
||||
|
||||
all = bbox;
|
||||
|
||||
i = j;
|
||||
}
|
||||
|
||||
return overlap;
|
||||
}
|
||||
|
||||
@@ -6114,13 +6365,26 @@ void GSState::GSPCRTCRegs::CalculateFramebufferOffset(bool scanmask, GSRegDISPFB
|
||||
{
|
||||
if (framebuffer0Reg.DBY != PCRTCDisplays[0].prevFramebufferReg.DBY)
|
||||
{
|
||||
PCRTCDisplays[0].framebufferRect.y = PCRTCDisplays[1].framebufferRect.y;
|
||||
PCRTCDisplays[0].framebufferRect.w = PCRTCDisplays[1].framebufferRect.w;
|
||||
if (framebuffer1Reg.DBY == PCRTCDisplays[1].prevFramebufferReg.DBY)
|
||||
{
|
||||
const int offset = PCRTCDisplays[1].framebufferRect.y - PCRTCDisplays[0].framebufferRect.y;
|
||||
|
||||
if (std::abs(offset) <= 4)
|
||||
{
|
||||
PCRTCDisplays[0].framebufferRect.y += offset;
|
||||
PCRTCDisplays[0].framebufferRect.w += offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PCRTCDisplays[1].framebufferRect.y = PCRTCDisplays[0].framebufferRect.y;
|
||||
PCRTCDisplays[1].framebufferRect.w = PCRTCDisplays[0].framebufferRect.w;
|
||||
const int offset = PCRTCDisplays[0].framebufferRect.y - PCRTCDisplays[1].framebufferRect.y;
|
||||
|
||||
if (std::abs(offset) <= 4)
|
||||
{
|
||||
PCRTCDisplays[1].framebufferRect.y += offset;
|
||||
PCRTCDisplays[1].framebufferRect.w += offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,3 +76,89 @@ public:
|
||||
return GetClassVertexCount(GetPrimClass(prim));
|
||||
}
|
||||
};
|
||||
|
||||
// Class that represents an octogonal bounding area with sides at 45 degree increments.
|
||||
class BoundingOct
|
||||
{
|
||||
private:
|
||||
GSVector4i bbox0; // Standard bbox.
|
||||
GSVector4i bbox1; // Bounding diamond (rotated 45 degrees axes and scaled, so (x, y) becomes (x + y, x - y)).
|
||||
|
||||
// Assumes that v is of the form { x, y, x, y }.
|
||||
static GSVector4i Rotate45(const GSVector4i& v)
|
||||
{
|
||||
const GSVector4i swap = v.yxwz();
|
||||
return (v + swap).blend32<0xa>(swap - v);
|
||||
}
|
||||
|
||||
BoundingOct(const GSVector4i& bbox0, const GSVector4i& bbox1)
|
||||
: bbox0(bbox0)
|
||||
, bbox1(bbox1)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
// Initialize to null bounding area.
|
||||
BoundingOct()
|
||||
: bbox0(GSVector4i(INT_MAX, INT_MAX, INT_MIN, INT_MIN))
|
||||
, bbox1(GSVector4i(INT_MAX, INT_MAX, INT_MIN, INT_MIN))
|
||||
{
|
||||
}
|
||||
|
||||
static BoundingOct FromPoint(GSVector4i v)
|
||||
{
|
||||
v = v.xyxy();
|
||||
return { v, Rotate45(v) };
|
||||
}
|
||||
|
||||
// The two inputs are assumed to be diagonally opposite to each other in an axis-aligned quad (i.e. sprite).
|
||||
static BoundingOct FromSprite(GSVector4i v0, GSVector4i v1)
|
||||
{
|
||||
const GSVector4i min = v0.min_i32(v1);
|
||||
const GSVector4i max = v0.max_i32(v1);
|
||||
const GSVector4i bbox = min.upl64(max);
|
||||
// Rotate45(x, y) => (x + y, x - y), we want the (min, max) result for any pair of (x, y)
|
||||
// Min: (min.x + min.y, min.x - max.y)
|
||||
// Max: (max.x + max.y, max.x - min.y)
|
||||
const GSVector4i x = GSVector4i::cast(GSVector4::cast(min).xxxx(GSVector4::cast(max))); // Don't use bbox immediately to help the compiler optimize.
|
||||
const GSVector4i y = bbox.ywwy();
|
||||
return {
|
||||
bbox,
|
||||
(x + y).blend32<0xa>(x - y),
|
||||
};
|
||||
}
|
||||
|
||||
BoundingOct Union(GSVector4i v) const
|
||||
{
|
||||
v = v.xyxy();
|
||||
return { bbox0.runion(v), bbox1.runion(Rotate45(v)) };
|
||||
}
|
||||
|
||||
BoundingOct Union(const BoundingOct& other) const
|
||||
{
|
||||
return { bbox0.runion(other.bbox0), bbox1.runion(other.bbox1) };
|
||||
}
|
||||
|
||||
BoundingOct UnionSprite(GSVector4i pt0, GSVector4i pt1) const
|
||||
{
|
||||
return Union(FromSprite(pt0, pt1));
|
||||
}
|
||||
|
||||
bool Intersects(const BoundingOct& other) const
|
||||
{
|
||||
return bbox0.rintersects(other.bbox0) && bbox1.rintersects(other.bbox1);
|
||||
}
|
||||
|
||||
BoundingOct FixDegenerate() const
|
||||
{
|
||||
return {
|
||||
bbox0.blend(bbox0 + GSVector4i(0, 0, 1, 1), bbox0.xyxy() == bbox0.zwzw()),
|
||||
bbox1.blend(bbox1 + GSVector4i(0, 0, 1, 1), bbox1.xyxy() == bbox1.zwzw()),
|
||||
};
|
||||
}
|
||||
|
||||
const GSVector4i& ToBBox()
|
||||
{
|
||||
return bbox0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -494,6 +494,16 @@ public:
|
||||
return GSVector4i(_mm_packus_epi32(m, m));
|
||||
}
|
||||
|
||||
__forceinline GSVector4i pkl32(const GSVector4i& a) const
|
||||
{
|
||||
return GSVector4i(_mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(m), _mm_castsi128_ps(a.m), _MM_SHUFFLE(2, 0, 2, 0))));
|
||||
}
|
||||
|
||||
__forceinline GSVector4i pku32(const GSVector4i& a) const
|
||||
{
|
||||
return GSVector4i(_mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(m), _mm_castsi128_ps(a.m), _MM_SHUFFLE(3, 1, 3, 1))));
|
||||
}
|
||||
|
||||
__forceinline GSVector4i upl8(const GSVector4i& a) const
|
||||
{
|
||||
return GSVector4i(_mm_unpacklo_epi8(m, a));
|
||||
@@ -840,6 +850,11 @@ public:
|
||||
return GSVector4i(_mm_srli_epi64(m, i));
|
||||
}
|
||||
|
||||
__forceinline GSVector4i abs32() const
|
||||
{
|
||||
return GSVector4i(_mm_abs_epi32(m));
|
||||
}
|
||||
|
||||
__forceinline GSVector4i add8(const GSVector4i& v) const
|
||||
{
|
||||
return GSVector4i(_mm_add_epi8(m, v.m));
|
||||
@@ -855,6 +870,11 @@ public:
|
||||
return GSVector4i(_mm_add_epi32(m, v.m));
|
||||
}
|
||||
|
||||
__forceinline GSVector4i hadd32(const GSVector4i& v) const
|
||||
{
|
||||
return GSVector4i(_mm_hadd_epi32(m, v.m));
|
||||
}
|
||||
|
||||
__forceinline GSVector4i adds8(const GSVector4i& v) const
|
||||
{
|
||||
return GSVector4i(_mm_adds_epi8(m, v.m));
|
||||
@@ -895,6 +915,11 @@ public:
|
||||
return GSVector4i(_mm_sub_epi32(m, v.m));
|
||||
}
|
||||
|
||||
__forceinline GSVector4i hsub32(const GSVector4i& v) const
|
||||
{
|
||||
return GSVector4i(_mm_hsub_epi32(m, v.m));
|
||||
}
|
||||
|
||||
__forceinline GSVector4i subs8(const GSVector4i& v) const
|
||||
{
|
||||
return GSVector4i(_mm_subs_epi8(m, v.m));
|
||||
@@ -1967,6 +1992,11 @@ public:
|
||||
return v ^ GSVector4i(i);
|
||||
}
|
||||
|
||||
__forceinline friend GSVector4i operator-(const GSVector4i& v)
|
||||
{
|
||||
return GSVector4i(_mm_sub_epi32(_mm_setzero_si128(), v));
|
||||
}
|
||||
|
||||
__forceinline friend GSVector4i operator~(const GSVector4i& v)
|
||||
{
|
||||
return v ^ (v == v);
|
||||
|
||||
@@ -506,6 +506,16 @@ public:
|
||||
return GSVector4i(vreinterpretq_s32_u16(vcombine_u16(vqmovun_s32(v4s), vqmovun_s32(v4s))));
|
||||
}
|
||||
|
||||
__forceinline GSVector4i pkl32(const GSVector4i& a) const
|
||||
{
|
||||
return GSVector4i(vuzp1q_u32(v4s, a.v4s));
|
||||
}
|
||||
|
||||
__forceinline GSVector4i pku32(const GSVector4i& a) const
|
||||
{
|
||||
return GSVector4i(vuzp2q_u32(v4s, a.v4s));
|
||||
}
|
||||
|
||||
__forceinline GSVector4i upl8(const GSVector4i& a) const
|
||||
{
|
||||
return GSVector4i(vreinterpretq_s32_s8(vzip1q_s8(vreinterpretq_s8_s32(v4s), vreinterpretq_s8_s32(a.v4s))));
|
||||
@@ -807,6 +817,11 @@ public:
|
||||
vreinterpretq_s32_u64(vshlq_u64(vreinterpretq_u64_s32(v4s), vnegq_s64(vreinterpretq_s64_s32(v.v4s)))));
|
||||
}
|
||||
|
||||
__forceinline GSVector4i abs32() const
|
||||
{
|
||||
return GSVector4i(vabsq_s32(v4s));
|
||||
}
|
||||
|
||||
__forceinline GSVector4i add8(const GSVector4i& v) const
|
||||
{
|
||||
return GSVector4i(vreinterpretq_s32_s8(vaddq_s8(vreinterpretq_s8_s32(v4s), vreinterpretq_s8_s32(v.v4s))));
|
||||
@@ -822,6 +837,11 @@ public:
|
||||
return GSVector4i(vaddq_s32(v4s, v.v4s));
|
||||
}
|
||||
|
||||
__forceinline GSVector4i hadd32(const GSVector4i& v) const
|
||||
{
|
||||
return GSVector4i(vpaddq_s32(v4s, v.v4s));
|
||||
}
|
||||
|
||||
__forceinline GSVector4i adds8(const GSVector4i& v) const
|
||||
{
|
||||
return GSVector4i(vreinterpretq_s32_s8(vqaddq_s8(vreinterpretq_s8_s32(v4s), vreinterpretq_s8_s32(v.v4s))));
|
||||
@@ -866,6 +886,11 @@ public:
|
||||
return GSVector4i(vsubq_s32(v4s, v.v4s));
|
||||
}
|
||||
|
||||
__forceinline GSVector4i hsub32(const GSVector4i& v) const
|
||||
{
|
||||
return GSVector4i(vsubq_u32(vuzp1q_u32(v4s, v.v4s), vuzp2q_u32(v4s, v.v4s)));
|
||||
}
|
||||
|
||||
__forceinline GSVector4i subs8(const GSVector4i& v) const
|
||||
{
|
||||
return GSVector4i(vreinterpretq_s32_s8(vqsubq_s8(vreinterpretq_s8_s32(v4s), vreinterpretq_s8_s32(v.v4s))));
|
||||
@@ -1920,6 +1945,11 @@ public:
|
||||
return v ^ GSVector4i(i);
|
||||
}
|
||||
|
||||
__forceinline friend GSVector4i operator-(const GSVector4i& v)
|
||||
{
|
||||
return GSVector4i(vnegq_s32(v.v4s));
|
||||
}
|
||||
|
||||
__forceinline friend GSVector4i operator~(const GSVector4i& v)
|
||||
{
|
||||
return GSVector4i(vmvnq_s32(v.v4s));
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
#include "IconsFontAwesome.h"
|
||||
|
||||
#include <appmodel.h>
|
||||
#include <array>
|
||||
#include <d3d11.h>
|
||||
#include <d3d12.h>
|
||||
@@ -382,29 +381,7 @@ GSRendererType D3D::GetPreferredRenderer()
|
||||
return device;
|
||||
};
|
||||
#ifdef ENABLE_VULKAN
|
||||
static constexpr auto check_for_mapping_layers = []() {
|
||||
PCWSTR familyName = L"Microsoft.D3DMappingLayers_8wekyb3d8bbwe";
|
||||
UINT32 numPackages = 0, bufferLength = 0;
|
||||
const DWORD error = GetPackagesByPackageFamily(familyName, &numPackages, nullptr, &bufferLength, nullptr);
|
||||
if (error == ERROR_INSUFFICIENT_BUFFER || numPackages > 0)
|
||||
{
|
||||
Host::AddIconOSDMessage("VKDriverUnsupported", ICON_FA_TV,
|
||||
TRANSLATE_STR("GS",
|
||||
"Your system has the \"OpenCL, OpenGL, and Vulkan Compatibility Pack\" installed.\n"
|
||||
"This Vulkan driver crashes PCSX2 on some GPUs.\n"
|
||||
"To use the Vulkan renderer, you should remove this app package."),
|
||||
Host::OSD_WARNING_DURATION);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
static constexpr auto check_vulkan_supported = []() {
|
||||
// Don't try to enumerate Vulkan devices if the DX12 Vulkan driver is present.
|
||||
// It crashes on AMD GPUs.
|
||||
if (check_for_mapping_layers())
|
||||
return false;
|
||||
|
||||
if (!GSDeviceVK::EnumerateGPUs().empty())
|
||||
return true;
|
||||
|
||||
|
||||
@@ -38,6 +38,15 @@ static bool SupportsTextureFormat(ID3D11Device* dev, DXGI_FORMAT format)
|
||||
return (support & D3D11_FORMAT_SUPPORT_TEXTURE2D) != 0;
|
||||
}
|
||||
|
||||
static bool IsTextureFormatHWBlendable(ID3D11Device* dev, DXGI_FORMAT format)
|
||||
{
|
||||
UINT support;
|
||||
if (FAILED(dev->CheckFormatSupport(format, &support)))
|
||||
return false;
|
||||
|
||||
return (support & D3D11_FORMAT_SUPPORT_RENDER_TARGET) && (support & D3D11_FORMAT_SUPPORT_BLENDABLE);
|
||||
}
|
||||
|
||||
GSDevice11::GSDevice11()
|
||||
{
|
||||
memset(&m_state, 0, sizeof(m_state));
|
||||
@@ -599,6 +608,15 @@ void GSDevice11::SetFeatures(IDXGIAdapter1* adapter)
|
||||
m_max_texture_size = (m_feature_level >= D3D_FEATURE_LEVEL_11_0) ?
|
||||
D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION :
|
||||
D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
||||
|
||||
m_conservative_depth = (m_feature_level >= D3D_FEATURE_LEVEL_11_0);
|
||||
m_rgba16_unorm_hw_blend = IsTextureFormatHWBlendable(m_dev.get(), DXGI_FORMAT_R16G16B16A16_UNORM);
|
||||
|
||||
// Let the user know if said features are available.
|
||||
Console.WriteLnFmt("D3D11: DXTn Texture Compression: {}", m_features.dxt_textures ? "Supported" : "Not Supported");
|
||||
Console.WriteLnFmt("D3D11: BC6/7 Texture Compression: {}", m_features.bptc_textures ? "Supported" : "Not Supported");
|
||||
Console.WriteLnFmt("D3D11: Conservative Depth: {}", m_conservative_depth ? "Supported" : "Not Supported");
|
||||
Console.WriteLnFmt("D3D11: RGBA16 UNORM Hardware Blending: {}", m_rgba16_unorm_hw_blend ? "Supported" : "Not Supported");
|
||||
}
|
||||
|
||||
bool GSDevice11::HasSurface() const
|
||||
@@ -972,15 +990,15 @@ void GSDevice11::EndPresent()
|
||||
if (m_vsync_mode != GSVSyncMode::FIFO && m_gpu_timing_enabled)
|
||||
PopTimestampQuery();
|
||||
|
||||
// clear out the swap chain view, it might get resized..
|
||||
OMSetRenderTargets(nullptr, nullptr, nullptr, nullptr);
|
||||
|
||||
const UINT sync_interval = static_cast<UINT>(m_vsync_mode == GSVSyncMode::FIFO);
|
||||
const UINT flags = (m_vsync_mode == GSVSyncMode::Disabled && m_using_allow_tearing) ? DXGI_PRESENT_ALLOW_TEARING : 0;
|
||||
m_swap_chain->Present(sync_interval, flags);
|
||||
|
||||
if (m_gpu_timing_enabled)
|
||||
KickTimestampQuery();
|
||||
|
||||
// clear out the swap chain view, it might get resized..
|
||||
OMSetRenderTargets(nullptr, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool GSDevice11::CreateTimestampQueries()
|
||||
@@ -1296,6 +1314,8 @@ void GSDevice11::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTextur
|
||||
|
||||
void GSDevice11::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ID3D11PixelShader* ps, ID3D11Buffer* ps_cb, ID3D11BlendState* bs, bool linear)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
CommitClear(sTex);
|
||||
|
||||
const bool draw_in_depth = dTex && dTex->IsDepthStencil();
|
||||
@@ -1303,6 +1323,10 @@ void GSDevice11::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTextur
|
||||
GSVector2i ds;
|
||||
if (dTex)
|
||||
{
|
||||
// preemptively bind svr if possible
|
||||
if (m_state.cached_rt_view != sTex && m_state.cached_dsv != sTex)
|
||||
PSSetShaderResource(0, sTex);
|
||||
|
||||
// ps unbind conflicting srvs
|
||||
PSUnbindConflictingSRVs(dTex);
|
||||
|
||||
@@ -1363,11 +1387,17 @@ void GSDevice11::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTextur
|
||||
|
||||
void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
CommitClear(sTex);
|
||||
|
||||
GSVector2i ds;
|
||||
if (dTex)
|
||||
{
|
||||
// preemptively bind svr if possible
|
||||
if (m_state.cached_rt_view != sTex && m_state.cached_dsv != sTex)
|
||||
PSSetShaderResource(0, sTex);
|
||||
|
||||
// ps unbind conflicting srvs
|
||||
PSUnbindConflictingSRVs(dTex);
|
||||
|
||||
@@ -1383,7 +1413,7 @@ void GSDevice11::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
|
||||
cb.SetSource(sRect, sTex->GetSize());
|
||||
cb.SetTarget(dRect, ds);
|
||||
cb.SetTime(shaderTime);
|
||||
m_ctx->UpdateSubresource(m_present.ps_cb.get(), 0, nullptr, &cb, 0, 0);
|
||||
UpdateSubresource(m_present.ps_cb.get(), &cb, &m_present.cb_uniforms, sizeof(cb));
|
||||
|
||||
// om
|
||||
|
||||
@@ -1435,7 +1465,7 @@ void GSDevice11::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u
|
||||
u32 pad2;
|
||||
};
|
||||
const Uniforms cb = {sScale, {}, offsetX, offsetY, dOffset, 0};
|
||||
m_ctx->UpdateSubresource(m_merge.cb.get(), 0, nullptr, &cb, 0, 0);
|
||||
UpdateSubresource(m_merge.cb.get(), &cb, &m_merge.cb_uniforms, sizeof(cb));
|
||||
|
||||
const GSVector4 dRect(0, 0, dSize, 1);
|
||||
const ShaderConvert shader = (dSize == 16) ? ShaderConvert::CLUT_4 : ShaderConvert::CLUT_8;
|
||||
@@ -1454,7 +1484,7 @@ void GSDevice11::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offs
|
||||
};
|
||||
|
||||
const Uniforms cb = {sScale, {}, SBW, DBW, SPSM, 0};
|
||||
m_ctx->UpdateSubresource(m_merge.cb.get(), 0, nullptr, &cb, 0, 0);
|
||||
UpdateSubresource(m_merge.cb.get(), &cb, &m_merge.cb_uniforms, sizeof(cb));
|
||||
|
||||
const GSVector4 dRect(0, 0, dTex->GetWidth(), dTex->GetHeight());
|
||||
const ShaderConvert shader = ((SPSM & 0xE) == 0) ? ShaderConvert::RGBA_TO_8I : ShaderConvert::RGB5A1_TO_8I;
|
||||
@@ -1475,7 +1505,7 @@ void GSDevice11::FilteredDownsampleTexture(GSTexture* sTex, GSTexture* dTex, u32
|
||||
|
||||
const Uniforms cb = {
|
||||
static_cast<float>(downsample_factor * downsample_factor), (GSConfig.UserHacks_NativeScaling > GSNativeScaling::Aggressive) ? 2.0f : 1.0f, {}, clamp_min, static_cast<int>(downsample_factor), 0};
|
||||
m_ctx->UpdateSubresource(m_merge.cb.get(), 0, nullptr, &cb, 0, 0);
|
||||
UpdateSubresource(m_merge.cb.get(), &cb, &m_merge.cb_uniforms, sizeof(cb));
|
||||
|
||||
const ShaderConvert shader = ShaderConvert::DOWNSAMPLE_COPY;
|
||||
DoStretchRect(sTex, GSVector4::zero(), dTex, dRect, m_convert.ps[static_cast<int>(shader)].get(), m_merge.cb.get(), nullptr, false);
|
||||
@@ -1522,6 +1552,8 @@ void GSDevice11::DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_re
|
||||
|
||||
void GSDevice11::DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, const GSVector2& ds)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
// Don't use primitive restart here, it ends up slower on some drivers.
|
||||
const u32 vertex_reserve_size = num_rects * 4;
|
||||
const u32 index_reserve_size = num_rects * 6;
|
||||
@@ -1567,7 +1599,6 @@ void GSDevice11::DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rect
|
||||
DrawIndexedPrimitive();
|
||||
}
|
||||
|
||||
|
||||
void GSDevice11::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, u32 c, const bool linear)
|
||||
{
|
||||
const GSVector4 full_r(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
@@ -1584,7 +1615,7 @@ void GSDevice11::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
if (feedback_write_2 || feedback_write_1 || sTex[0])
|
||||
{
|
||||
const MergeConstantBuffer cb = {GSVector4::unorm8(c), EXTBUF.EMODA, EXTBUF.EMODC};
|
||||
m_ctx->UpdateSubresource(m_merge.cb.get(), 0, nullptr, &cb, 0, 0);
|
||||
UpdateSubresource(m_merge.cb.get(), &cb, &m_merge.cb_uniforms, sizeof(cb));
|
||||
}
|
||||
|
||||
if (sTex[1] && (PMODE.SLBG == 0 || feedback_write_2_but_blend_bg))
|
||||
@@ -1620,7 +1651,7 @@ void GSDevice11::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
||||
|
||||
void GSDevice11::DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb)
|
||||
{
|
||||
m_ctx->UpdateSubresource(m_interlace.cb.get(), 0, nullptr, &cb, 0, 0);
|
||||
UpdateSubresource(m_interlace.cb.get(), &cb, &m_interlace.cb_uniforms, sizeof(cb));
|
||||
|
||||
DoStretchRect(sTex, sRect, dTex, dRect, m_interlace.ps[static_cast<int>(shader)].get(), m_interlace.cb.get(), linear);
|
||||
}
|
||||
@@ -1658,7 +1689,7 @@ void GSDevice11::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float para
|
||||
const GSVector4 sRect(0, 0, 1, 1);
|
||||
const GSVector4 dRect(0, 0, s.x, s.y);
|
||||
|
||||
m_ctx->UpdateSubresource(m_shadeboost.cb.get(), 0, nullptr, params, 0, 0);
|
||||
UpdateSubresource(m_shadeboost.cb.get(), params, &m_shadeboost.cb_uniforms, sizeof(float) * 4);
|
||||
|
||||
DoStretchRect(sTex, sRect, dTex, dRect, m_shadeboost.ps.get(), m_shadeboost.cb.get(), false);
|
||||
}
|
||||
@@ -1721,6 +1752,7 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
|
||||
ShaderMacro sm;
|
||||
|
||||
sm.AddMacro("PIXEL_SHADER", 1);
|
||||
sm.AddMacro("PS_HAS_CONSERVATIVE_DEPTH", m_conservative_depth);
|
||||
sm.AddMacro("PS_FST", sel.fst);
|
||||
sm.AddMacro("PS_WMS", sel.wms);
|
||||
sm.AddMacro("PS_WMT", sel.wmt);
|
||||
@@ -2080,7 +2112,7 @@ void GSDevice11::RenderImGui()
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
m_ctx->UpdateSubresource(m_imgui.vs_cb.get(), 0, nullptr, ortho_projection, 0, 0);
|
||||
UpdateSubresource(m_imgui.vs_cb.get(), ortho_projection, &m_imgui.vs_cb_uniforms, sizeof(ortho_projection));
|
||||
|
||||
const UINT vb_stride = sizeof(ImDrawVert);
|
||||
const UINT vb_offset = 0;
|
||||
@@ -2162,6 +2194,8 @@ void GSDevice11::RenderImGui()
|
||||
|
||||
void GSDevice11::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSVector4i& bbox)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
// sfex3 (after the capcom logo), vf4 (first menu fading in), ffxii shadows, rumble roses shadows, persona4 shadows
|
||||
|
||||
CommitClear(rt);
|
||||
@@ -2169,6 +2203,11 @@ void GSDevice11::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSV
|
||||
|
||||
m_ctx->ClearDepthStencilView(*static_cast<GSTexture11*>(ds), D3D11_CLEAR_STENCIL, 0.0f, 0);
|
||||
|
||||
// preemptively bind svr if possible
|
||||
|
||||
if (m_state.cached_rt_view != rt && m_state.cached_dsv != rt)
|
||||
PSSetShaderResource(0, rt);
|
||||
|
||||
// ps unbind conflicting srvs
|
||||
|
||||
PSUnbindConflictingSRVs(ds);
|
||||
@@ -2382,6 +2421,7 @@ void GSDevice11::VSSetShader(ID3D11VertexShader* vs, ID3D11Buffer* vs_cb)
|
||||
|
||||
void GSDevice11::PSSetShaderResource(int i, GSTexture* sr)
|
||||
{
|
||||
// Update local state only, PSUpdateShaderState updates gpu state.
|
||||
m_state.ps_sr_views[i] = *static_cast<GSTexture11*>(sr);
|
||||
}
|
||||
|
||||
@@ -2418,7 +2458,7 @@ void GSDevice11::PSUpdateShaderState(const bool sr_update, const bool ss_update)
|
||||
if (sr_update)
|
||||
{
|
||||
bool sr_changed = false;
|
||||
for (size_t i = 0; i < m_state.ps_sr_views.size(); ++i)
|
||||
for (size_t i = 0; i < MAX_TEXTURES; ++i)
|
||||
{
|
||||
if (m_state.ps_cached_sr_views[i] != m_state.ps_sr_views[i])
|
||||
{
|
||||
@@ -2430,14 +2470,14 @@ void GSDevice11::PSUpdateShaderState(const bool sr_update, const bool ss_update)
|
||||
if (sr_changed)
|
||||
{
|
||||
m_state.ps_cached_sr_views = m_state.ps_sr_views;
|
||||
m_ctx->PSSetShaderResources(0, m_state.ps_sr_views.size(), m_state.ps_sr_views.data());
|
||||
m_ctx->PSSetShaderResources(0, MAX_TEXTURES, m_state.ps_sr_views.data());
|
||||
}
|
||||
}
|
||||
|
||||
if (ss_update)
|
||||
{
|
||||
bool ss_changed = false;
|
||||
for (size_t i = 0; i < m_state.ps_ss.size(); ++i)
|
||||
for (size_t i = 0; i < MAX_SAMPLERS; ++i)
|
||||
{
|
||||
if (m_state.ps_cached_ss[i] != m_state.ps_ss[i])
|
||||
{
|
||||
@@ -2449,7 +2489,7 @@ void GSDevice11::PSUpdateShaderState(const bool sr_update, const bool ss_update)
|
||||
if (ss_changed)
|
||||
{
|
||||
m_state.ps_cached_ss = m_state.ps_ss;
|
||||
m_ctx->PSSetSamplers(0, m_state.ps_ss.size(), m_state.ps_ss.data());
|
||||
m_ctx->PSSetSamplers(0, MAX_SAMPLERS, m_state.ps_ss.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2458,9 +2498,10 @@ void GSDevice11::PSUnbindConflictingSRVs(GSTexture* tex1, GSTexture* tex2)
|
||||
{
|
||||
// Make sure no SRVs are bound using the same texture before binding it to a RTV.
|
||||
bool changed = false;
|
||||
for (size_t i = 0; i < m_state.ps_sr_views.size(); i++)
|
||||
for (size_t i = 0; i < MAX_TEXTURES; i++)
|
||||
{
|
||||
if ((tex1 && m_state.ps_sr_views[i] == *static_cast<GSTexture11*>(tex1)) || (tex2 && m_state.ps_sr_views[i] == *static_cast<GSTexture11*>(tex2)))
|
||||
// We chech against what's currently bound (cached_sr_views), then update local state (ps_sr_views) which calls PSUpdateShaderState to update gpu state.
|
||||
if ((tex1 && m_state.ps_cached_sr_views[i] == *static_cast<GSTexture11*>(tex1)) || (tex2 && m_state.ps_cached_sr_views[i] == *static_cast<GSTexture11*>(tex2)))
|
||||
{
|
||||
m_state.ps_sr_views[i] = nullptr;
|
||||
changed = true;
|
||||
@@ -2570,6 +2611,15 @@ void GSDevice11::SetScissor(const GSVector4i& scissor)
|
||||
}
|
||||
}
|
||||
|
||||
void GSDevice11::UpdateSubresource(ID3D11Buffer* buffer, const void* cb_uniforms, void* cached_cb_uniforms, size_t cb_uniforms_size)
|
||||
{
|
||||
if (memcmp(cb_uniforms, cached_cb_uniforms, cb_uniforms_size) != 0)
|
||||
{
|
||||
memcpy(cached_cb_uniforms, cb_uniforms, cb_uniforms_size);
|
||||
m_ctx->UpdateSubresource(buffer, 0, nullptr, cached_cb_uniforms, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void GSDevice11::ShaderMacro::AddMacro(const char* n, int d)
|
||||
{
|
||||
AddMacro(n, std::to_string(d));
|
||||
@@ -2604,7 +2654,6 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
const GSVector4 dRect(config.colclip_update_area);
|
||||
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
|
||||
StretchRect(colclip_rt, sRect, config.rt, dRect, ShaderConvert::COLCLIP_RESOLVE, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
Recycle(colclip_rt);
|
||||
|
||||
g_gs_device->SetColorClipTexture(nullptr);
|
||||
@@ -2621,7 +2670,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
{
|
||||
config.colclip_update_area = config.drawarea;
|
||||
|
||||
colclip_rt = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::ColorClip, false);
|
||||
colclip_rt = CreateRenderTarget(rtsize.x, rtsize.y, m_rgba16_unorm_hw_blend ? GSTexture::Format::ColorClip : GSTexture::Format::ColorHDR, false);
|
||||
if (!colclip_rt)
|
||||
{
|
||||
Console.Warning("D3D11: Failed to allocate ColorClip render target, aborting draw.");
|
||||
@@ -2633,7 +2682,6 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
const GSVector4 dRect = GSVector4((config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
|
||||
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
|
||||
StretchRect(config.rt, sRect, colclip_rt, dRect, ShaderConvert::COLCLIP_INIT, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2704,20 +2752,29 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
if (config.tex && config.tex == config.ds)
|
||||
read_only_dsv = static_cast<GSTexture11*>(config.ds)->ReadOnlyDepthStencilView();
|
||||
|
||||
// Should be called before changing local srv state.
|
||||
PSUnbindConflictingSRVs(colclip_rt ? colclip_rt : config.rt, read_only_dsv ? nullptr : config.ds);
|
||||
|
||||
// Preemptively bind svr if possible.
|
||||
// We update the local state, then if there are srv conflicts PSUnbindConflictingSRVs will update the gpu state.
|
||||
if (config.tex)
|
||||
{
|
||||
CommitClear(config.tex);
|
||||
PSSetShaderResource(0, config.tex);
|
||||
if (m_state.cached_rt_view != config.tex && m_state.cached_dsv != config.tex)
|
||||
PSSetShaderResource(0, config.tex);
|
||||
}
|
||||
if (config.pal)
|
||||
{
|
||||
CommitClear(config.pal);
|
||||
PSSetShaderResource(1, config.pal);
|
||||
if (m_state.cached_rt_view != config.pal && m_state.cached_dsv != config.pal)
|
||||
PSSetShaderResource(1, config.pal);
|
||||
}
|
||||
|
||||
// Should be called before changing current gpu state.
|
||||
PSUnbindConflictingSRVs(colclip_rt ? colclip_rt : config.rt, read_only_dsv ? nullptr : config.ds);
|
||||
|
||||
if (config.tex)
|
||||
PSSetShaderResource(0, config.tex);
|
||||
if (config.pal)
|
||||
PSSetShaderResource(1, config.pal);
|
||||
|
||||
SetupVS(config.vs, &config.cb_vs);
|
||||
SetupPS(config.ps, &config.cb_ps, config.sampler);
|
||||
|
||||
@@ -2818,7 +2875,6 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
|
||||
const GSVector4 dRect(config.colclip_update_area);
|
||||
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
|
||||
StretchRect(colclip_rt, sRect, config.rt, dRect, ShaderConvert::COLCLIP_RESOLVE, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
Recycle(colclip_rt);
|
||||
|
||||
g_gs_device->SetColorClipTexture(nullptr);
|
||||
@@ -2847,6 +2903,7 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config, GSTexture* draw_rt_clo
|
||||
{
|
||||
const u32 draw_list_size = static_cast<u32>(config.drawlist->size());
|
||||
const u32 indices_per_prim = config.indices_per_prim;
|
||||
const GSVector4i rtsize(0, 0, draw_rt->GetWidth(), draw_rt->GetHeight());
|
||||
|
||||
pxAssert(config.drawlist && !config.drawlist->empty());
|
||||
pxAssert(config.drawlist_bbox && static_cast<u32>(config.drawlist_bbox->size()) == draw_list_size);
|
||||
@@ -2855,10 +2912,28 @@ void GSDevice11::SendHWDraw(const GSHWDrawConfig& config, GSTexture* draw_rt_clo
|
||||
{
|
||||
const u32 count = (*config.drawlist)[n] * indices_per_prim;
|
||||
|
||||
GSVector4i bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea);
|
||||
const GSVector4i original_bbox = (*config.drawlist_bbox)[n].rintersect(config.drawarea);
|
||||
GSVector4i snapped_bbox = original_bbox;
|
||||
|
||||
// Copy only the part needed by the draw.
|
||||
CopyAndBind(bbox);
|
||||
// We don't want the snapped box adjustments when the rect is empty as it might make the copy to pass.
|
||||
// The empty rect itself needs to be handled in renderer properly.
|
||||
if (!snapped_bbox.rempty())
|
||||
{
|
||||
// Aligning bbox to 4 pixel boundaries so copies will be faster using Direct Memory Access,
|
||||
// otherwise it may stall as more commands need to be issued.
|
||||
snapped_bbox.left &= ~3;
|
||||
snapped_bbox.top &= ~3;
|
||||
snapped_bbox.right = (snapped_bbox.right + 3) & ~3;
|
||||
snapped_bbox.bottom = (snapped_bbox.bottom + 3) & ~3;
|
||||
|
||||
// Ensure the new sizes are within bounds.
|
||||
snapped_bbox.left = std::max(0, snapped_bbox.left);
|
||||
snapped_bbox.top = std::max(0, snapped_bbox.top);
|
||||
snapped_bbox.right = std::min(snapped_bbox.right, rtsize.right);
|
||||
snapped_bbox.bottom = std::min(snapped_bbox.bottom, rtsize.bottom);
|
||||
}
|
||||
|
||||
CopyAndBind(snapped_bbox);
|
||||
|
||||
DrawIndexedPrimitive(p, count);
|
||||
p += count;
|
||||
|
||||
@@ -136,6 +136,8 @@ private:
|
||||
bool m_using_flip_model_swap_chain = true;
|
||||
bool m_using_allow_tearing = false;
|
||||
bool m_is_exclusive_fullscreen = false;
|
||||
bool m_conservative_depth = false;
|
||||
bool m_rgba16_unorm_hw_blend = false;
|
||||
|
||||
struct
|
||||
{
|
||||
@@ -189,6 +191,7 @@ private:
|
||||
wil::com_ptr_nothrow<ID3D11VertexShader> vs;
|
||||
wil::com_ptr_nothrow<ID3D11PixelShader> ps[static_cast<int>(PresentShader::Count)];
|
||||
wil::com_ptr_nothrow<ID3D11Buffer> ps_cb;
|
||||
DisplayConstantBuffer cb_uniforms;
|
||||
} m_present;
|
||||
|
||||
struct
|
||||
@@ -196,12 +199,14 @@ private:
|
||||
wil::com_ptr_nothrow<ID3D11PixelShader> ps[2];
|
||||
wil::com_ptr_nothrow<ID3D11Buffer> cb;
|
||||
wil::com_ptr_nothrow<ID3D11BlendState> bs;
|
||||
MergeConstantBuffer cb_uniforms;
|
||||
} m_merge;
|
||||
|
||||
struct
|
||||
{
|
||||
wil::com_ptr_nothrow<ID3D11PixelShader> ps[NUM_INTERLACE_SHADERS];
|
||||
wil::com_ptr_nothrow<ID3D11Buffer> cb;
|
||||
InterlaceConstantBuffer cb_uniforms;
|
||||
} m_interlace;
|
||||
|
||||
wil::com_ptr_nothrow<ID3D11PixelShader> m_fxaa_ps;
|
||||
@@ -210,6 +215,7 @@ private:
|
||||
{
|
||||
wil::com_ptr_nothrow<ID3D11PixelShader> ps;
|
||||
wil::com_ptr_nothrow<ID3D11Buffer> cb;
|
||||
float cb_uniforms[4];
|
||||
} m_shadeboost;
|
||||
|
||||
struct
|
||||
@@ -233,6 +239,7 @@ private:
|
||||
wil::com_ptr_nothrow<ID3D11PixelShader> ps;
|
||||
wil::com_ptr_nothrow<ID3D11BlendState> bs;
|
||||
wil::com_ptr_nothrow<ID3D11Buffer> vs_cb;
|
||||
GSVector4 vs_cb_uniforms[4];
|
||||
} m_imgui;
|
||||
|
||||
// Shaders...
|
||||
@@ -340,6 +347,8 @@ public:
|
||||
void SetViewport(const GSVector2i& viewport);
|
||||
void SetScissor(const GSVector4i& scissor);
|
||||
|
||||
void UpdateSubresource(ID3D11Buffer* buffer, const void* cb_uniforms, void* cached_cb_uniforms, size_t cb_uniforms_size);
|
||||
|
||||
void SetupVS(VSSelector sel, const GSHWDrawConfig::VSConstantBuffer* cb);
|
||||
void SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstantBuffer* cb, PSSamplerSelector ssel);
|
||||
void SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, u8 afix);
|
||||
|
||||
@@ -1407,6 +1407,11 @@ bool GSDevice12::CheckFeatures(const u32& vendor_id)
|
||||
DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, sizeof(allow_tearing_supported));
|
||||
m_allow_tearing_supported = (SUCCEEDED(hr) && allow_tearing_supported == TRUE);
|
||||
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS3 device_options3 = {};
|
||||
hr = m_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS3, &device_options3, sizeof(device_options3));
|
||||
m_typed_casting_supported = SUCCEEDED(hr) && device_options3.CastingFullyTypedFormatSupported;
|
||||
Console.WriteLnFmt("D3D12: Casting Fully Typed Formats: {}", m_typed_casting_supported ? "Supported" : "Not Supported");
|
||||
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS12 device_options12 = {};
|
||||
hr = m_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS12, &device_options12, sizeof(device_options12));
|
||||
if (SUCCEEDED(hr))
|
||||
@@ -1470,7 +1475,7 @@ void GSDevice12::LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_f
|
||||
|
||||
const auto& mapping = s_format_mapping[static_cast<int>(format)];
|
||||
if (d3d_format)
|
||||
*d3d_format = mapping[0];
|
||||
*d3d_format = (format == GSTexture::Format::DepthStencil && !m_typed_casting_supported) ? DXGI_FORMAT_R32G8X24_TYPELESS : mapping[0];
|
||||
if (srv_format)
|
||||
*srv_format = mapping[1];
|
||||
if (rtv_format)
|
||||
@@ -1743,6 +1748,8 @@ void GSDevice12::DrawMultiStretchRects(
|
||||
void GSDevice12::DoMultiStretchRects(
|
||||
const MultiStretchRect* rects, u32 num_rects, GSTexture12* dTex, ShaderConvert shader)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
// Set up vertices first.
|
||||
const u32 vertex_reserve_size = num_rects * 4 * sizeof(GSVertexPT1);
|
||||
const u32 index_reserve_size = num_rects * 6 * sizeof(u16);
|
||||
@@ -1880,6 +1887,8 @@ void GSDevice12::DoStretchRect(GSTexture12* sTex, const GSVector4& sRect, GSText
|
||||
|
||||
void GSDevice12::DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
// ia
|
||||
const float left = dRect.x * 2 / ds.x - 1.0f;
|
||||
const float top = 1.0f - dRect.y * 2 / ds.y;
|
||||
@@ -3048,6 +3057,7 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector&
|
||||
|
||||
ShaderMacro sm;
|
||||
sm.AddMacro("PIXEL_SHADER", 1);
|
||||
sm.AddMacro("PS_HAS_CONSERVATIVE_DEPTH", 1);
|
||||
sm.AddMacro("PS_FST", sel.fst);
|
||||
sm.AddMacro("PS_WMS", sel.wms);
|
||||
sm.AddMacro("PS_WMT", sel.wmt);
|
||||
@@ -3900,6 +3910,8 @@ void GSDevice12::SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb)
|
||||
|
||||
void GSDevice12::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSVector4i& bbox)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
GL_PUSH("SetupDATE {%d,%d} %dx%d", bbox.left, bbox.top, bbox.width(), bbox.height());
|
||||
|
||||
const GSVector2i size(ds->GetSize());
|
||||
@@ -3932,6 +3944,8 @@ void GSDevice12::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSV
|
||||
|
||||
GSTexture12* GSDevice12::SetupPrimitiveTrackingDATE(GSHWDrawConfig& config, PipelineSelector& pipe)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
// How this is done:
|
||||
// - can't put a barrier for the image in the middle of the normal render pass, so that's out
|
||||
// - so, instead of just filling the int texture with INT_MAX, we sample the RT and use -1 for failing values
|
||||
@@ -4078,7 +4092,6 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
SetPipeline(m_colclip_finish_pipelines[pipe.ds].get());
|
||||
SetUtilityTexture(colclip_rt, m_point_sampler_cpu);
|
||||
DrawStretchRect(sRect, GSVector4(config.colclip_update_area), rtsize);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
Recycle(colclip_rt);
|
||||
g_gs_device->SetColorClipTexture(nullptr);
|
||||
@@ -4280,7 +4293,6 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
const GSVector4 drawareaf = GSVector4((config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
|
||||
const GSVector4 sRect(drawareaf / GSVector4(rtsize.x, rtsize.y).xyxy());
|
||||
DrawStretchRect(sRect, GSVector4(drawareaf), rtsize);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
GL_POP();
|
||||
|
||||
@@ -4358,7 +4370,6 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
SetPipeline(m_colclip_finish_pipelines[pipe.ds].get());
|
||||
SetUtilityTexture(colclip_rt, m_point_sampler_cpu);
|
||||
DrawStretchRect(sRect, GSVector4(config.colclip_update_area), rtsize);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
Recycle(colclip_rt);
|
||||
g_gs_device->SetColorClipTexture(nullptr);
|
||||
|
||||
@@ -314,7 +314,8 @@ private:
|
||||
bool m_allow_tearing_supported = false;
|
||||
bool m_using_allow_tearing = false;
|
||||
bool m_is_exclusive_fullscreen = false;
|
||||
bool m_enhanced_barriers = true;
|
||||
bool m_enhanced_barriers = false;
|
||||
bool m_typed_casting_supported = false;
|
||||
bool m_device_lost = false;
|
||||
|
||||
ComPtr<ID3D12RootSignature> m_tfx_root_signature;
|
||||
|
||||
@@ -756,7 +756,7 @@ bool GSHwHack::GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip)
|
||||
config.ps.channel = ChannelFetch_RGB;
|
||||
config.colormask.wrgba = 1 | 2 | 4;
|
||||
r.EndHLEHardwareDraw(false);
|
||||
|
||||
src->m_last_draw = r.s_n;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -814,6 +814,7 @@ bool GSHwHack::GSC_PolyphonyDigitalGames(GSRendererHW& r, int& skip)
|
||||
config.ps.channel = ChannelFetch_RED + channel;
|
||||
config.colormask.wrgba = 8;
|
||||
r.EndHLEHardwareDraw(false);
|
||||
dst->m_last_draw = r.s_n;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -3846,7 +3846,6 @@ void GSRendererHW::Draw()
|
||||
|
||||
GL_CACHE("HW: RT in RT Z copy back draw %d z_vert_offset %d z_offset %d", s_n, z_vertical_offset, vertical_offset);
|
||||
g_gs_device->StretchRect(g_texture_cache->GetTemporaryZ(), sRect, ds->m_texture, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
}
|
||||
|
||||
g_texture_cache->InvalidateTemporaryZ();
|
||||
@@ -3863,7 +3862,6 @@ void GSRendererHW::Draw()
|
||||
dRect = dRect.min_u32(GSVector4i(ds->m_unscaled_size.x * ds->m_scale, ds->m_unscaled_size.y * ds->m_scale).xyxy());
|
||||
|
||||
g_gs_device->StretchRect(ds->m_texture, sRect, g_texture_cache->GetTemporaryZ(), GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
z_address_info.rect_since = GSVector4i::zero();
|
||||
g_texture_cache->SetTemporaryZInfo(z_address_info);
|
||||
}
|
||||
@@ -3911,7 +3909,6 @@ void GSRendererHW::Draw()
|
||||
if (m_cached_ctx.TEST.ZTST > ZTST_ALWAYS || !dRect.rintersect(GSVector4i(GSVector4(m_r) * ds->m_scale)).eq(dRect))
|
||||
{
|
||||
g_gs_device->StretchRect(ds->m_texture, sRect, tex, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
}
|
||||
g_texture_cache->SetTemporaryZ(tex);
|
||||
g_texture_cache->SetTemporaryZInfo(ds->m_TEX0.TBP0, page_offset, rt_page_offset);
|
||||
@@ -4523,7 +4520,6 @@ void GSRendererHW::Draw()
|
||||
{
|
||||
const GSVector4i dRect = GSVector4i(0, 0, g_texture_cache->GetTemporaryZ()->GetWidth(), g_texture_cache->GetTemporaryZ()->GetHeight());
|
||||
g_gs_device->StretchRect(g_texture_cache->GetTemporaryZ(), GSVector4(0.0f, 0.0f, 1.0f, 1.0f), tex, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
g_texture_cache->InvalidateTemporaryZ();
|
||||
g_texture_cache->SetTemporaryZ(tex);
|
||||
}
|
||||
@@ -4830,7 +4826,6 @@ void GSRendererHW::Draw()
|
||||
|
||||
GL_CACHE("HW: RT in RT Z copy back draw %d z_vert_offset %d rt_vert_offset %d z_horz_offset %d rt_horz_offset %d", s_n, z_vertical_offset, vertical_offset, z_horizontal_offset, horizontal_offset);
|
||||
g_gs_device->StretchRect(g_texture_cache->GetTemporaryZ(), sRect, ds->m_texture, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
}
|
||||
else if (m_temp_z_full_copy)
|
||||
{
|
||||
@@ -4843,7 +4838,6 @@ void GSRendererHW::Draw()
|
||||
|
||||
GL_CACHE("HW: RT in RT Z copy back draw %d z_vert_offset %d z_offset %d", s_n, z_vertical_offset, vertical_offset);
|
||||
g_gs_device->StretchRect(g_texture_cache->GetTemporaryZ(), sRect, ds->m_texture, GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
}
|
||||
|
||||
m_temp_z_full_copy = false;
|
||||
@@ -5910,6 +5904,7 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, const boo
|
||||
{
|
||||
case AccBlendLevel::Maximum:
|
||||
sw_blending |= true;
|
||||
accumulation_blend &= (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;
|
||||
@@ -7091,8 +7086,6 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
|
||||
|
||||
if (m_downscale_source)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
// Can't use box filtering on depth (yet), or fractional scales.
|
||||
if (src_target->m_texture->IsDepthStencil() || std::floor(src_target->GetScale()) != src_target->GetScale())
|
||||
{
|
||||
@@ -7120,9 +7113,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
|
||||
}
|
||||
else
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
const GSVector4i offset = copy_range - GSVector4i(copy_dst_offset).xyxy();
|
||||
|
||||
// Adjust for bilinear, must be done after calculating offset.
|
||||
copy_range.x -= 1;
|
||||
copy_range.y -= 1;
|
||||
@@ -9229,11 +9220,7 @@ bool GSRendererHW::OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Sourc
|
||||
const GSVector4 sRect(m_vt.m_min.t.x / tw, m_vt.m_min.t.y / th, m_vt.m_max.t.x / tw, m_vt.m_max.t.y / th);
|
||||
const GSVector4i r_full_new(0, 0, tw, th);
|
||||
g_gs_device->StretchRect(tex->m_texture, sRect, rt, dRect, ShaderConvert::COPY, m_vt.IsRealLinear());
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
g_gs_device->CopyRect(rt, tex->m_texture, r_full_new, 0, 0);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
g_gs_device->Recycle(rt);
|
||||
}
|
||||
|
||||
|
||||
@@ -2404,250 +2404,289 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
};
|
||||
|
||||
Target* dst = nullptr;
|
||||
auto& list = m_dst[type];
|
||||
Target* dst_match = nullptr;
|
||||
auto* list = &m_dst[type];
|
||||
|
||||
const GSVector4i min_rect = draw_rect.max_u32(GSVector4i(0, 0, draw_rect.x, draw_rect.y));
|
||||
// TODO: Move all frame stuff to its own routine too.
|
||||
if (!is_frame)
|
||||
{
|
||||
for (auto i = list.begin(); i != list.end();)
|
||||
for (int iteration = 0; iteration < 2; iteration++)
|
||||
{
|
||||
Target* t = *i;
|
||||
if (bp == t->m_TEX0.TBP0)
|
||||
{
|
||||
bool can_use = true;
|
||||
if (dst != nullptr)
|
||||
break;
|
||||
|
||||
if (dst && ((GSState::s_n - dst->m_last_draw) < (GSState::s_n - t->m_last_draw) && dst->m_TEX0.TBP0 <= bp))
|
||||
auto& new_dst = iteration == 0 ? dst : dst_match;
|
||||
list = &m_dst[iteration == 0 ? type : (1 - type)];
|
||||
for (auto i = list->begin(); i != list->end();)
|
||||
{
|
||||
Target* t = *i;
|
||||
if (bp == t->m_TEX0.TBP0)
|
||||
{
|
||||
DevCon.Warning("Ignoring target at %x as one at %x is newer", t->m_TEX0.TBP0, dst->m_TEX0.TBP0);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// if It's an old target and it's being completely overwritten, kill it.
|
||||
// Dragon Quest 8 reuses a render-target sized buffer as a single-page buffer, without clearing it. But,
|
||||
// it does dirty it by writing over the 64x64 region. So while we can't use this heuristic for tossing
|
||||
// targets at BW=1 because it breaks other games, we can when the *new* buffer area is completely dirty.
|
||||
if (((!preserve_rgb && !preserve_alpha) || (t->m_was_dst_matched && fbmask == 0xffffff)) && TEX0.TBW != t->m_TEX0.TBW)
|
||||
{
|
||||
// Old targets or shrunk targets where Y draw height goes outside the page.
|
||||
if (TEX0.TBW > 1 && (t->m_age >= 1 || (type == RenderTarget && draw_rect.w > GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y && TEX0.TBW < t->m_TEX0.TBW)))
|
||||
bool can_use = true;
|
||||
|
||||
if (new_dst && ((GSState::s_n - new_dst->m_last_draw) < (GSState::s_n - t->m_last_draw) && new_dst->m_TEX0.TBP0 <= bp))
|
||||
{
|
||||
DevCon.Warning("Ignoring target at %x as one at %x is newer", t->m_TEX0.TBP0, new_dst->m_TEX0.TBP0);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If there's no valid RGB, it can't be interchanged between RT and Depth
|
||||
if (iteration == 1)
|
||||
{
|
||||
const u32 valid_mask = (t->m_valid_rgb ? 0x7 : 0x0) | ((t->m_valid_alpha_low || t->m_valid_alpha_high) ? 0x8 : 0x0);
|
||||
if ((!(valid_mask & GSUtil::GetChannelMask(TEX0.PSM)) || (!is_shuffle && TEX0.TBW < (t->m_TEX0.TBW / 2))) ||
|
||||
(!is_shuffle && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp != GSLocalMemory::m_psm[TEX0.PSM].bpp))
|
||||
{
|
||||
if (!preserve_rgb && !preserve_alpha && (!src || src->m_from_target != t) && (valid_mask & GSUtil::GetChannelMask(TEX0.PSM)))
|
||||
{
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list->erase(i);
|
||||
delete t;
|
||||
}
|
||||
else
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// if It's an old target and it's being completely overwritten, kill it.
|
||||
// Dragon Quest 8 reuses a render-target sized buffer as a single-page buffer, without clearing it. But,
|
||||
// it does dirty it by writing over the 64x64 region. So while we can't use this heuristic for tossing
|
||||
// targets at BW=1 because it breaks other games, we can when the *new* buffer area is completely dirty.
|
||||
if (((!preserve_rgb && !preserve_alpha) || (t->m_was_dst_matched && fbmask == 0xffffff)) && TEX0.TBW != t->m_TEX0.TBW)
|
||||
{
|
||||
// Old targets or shrunk targets where Y draw height goes outside the page.
|
||||
if (TEX0.TBW > 1 && (t->m_age >= 1 || (type == RenderTarget && draw_rect.w > GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y && TEX0.TBW < t->m_TEX0.TBW)))
|
||||
{
|
||||
can_use = false;
|
||||
}
|
||||
else if (!t->m_dirty.empty())
|
||||
{
|
||||
const GSVector4i size_rect = GSVector4i::loadh(size);
|
||||
can_use = !t->m_dirty.GetTotalRect(TEX0, size).rintersect(size_rect).eq(size_rect);
|
||||
}
|
||||
}
|
||||
else if (type == RenderTarget && (fbmask == 0xffffff && !t->m_was_dst_matched && TEX0.TBW != t->m_TEX0.TBW))
|
||||
{
|
||||
// When returning to being matched with the Z buffer in width, we need to make sure the RGB is up to date as it could get used later (Hitman Contracts).
|
||||
auto& rev_list = m_dst[1 - type];
|
||||
for (auto j = rev_list.begin(); j != rev_list.end(); ++j)
|
||||
{
|
||||
Target* ds = *j;
|
||||
|
||||
if (t->m_TEX0.TBP0 != ds->m_TEX0.TBP0 || !ds->m_valid_rgb || TEX0.TBW != ds->m_TEX0.TBW)
|
||||
continue;
|
||||
|
||||
t->m_was_dst_matched = true;
|
||||
t->m_valid_rgb = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO: What might be a nicer solution than this, is to rearrange the targets to match the new layout, however this comes with some caviets:
|
||||
// 1. They can draw wider than the FBW
|
||||
// 2. The dirty+valid rects will need to also be rearranged
|
||||
// 3. This could mean larger targets hanging around more
|
||||
// 4. Sources which reference a target may become invalid and will need to be removed
|
||||
// 5. Potential performance implications from additional render passes/copying
|
||||
//
|
||||
// But the bonuses are:
|
||||
// 1. Rearranging the page layout will fix quite a few games which do this
|
||||
// 2. Preserved data will be in the correct place (in most cases)
|
||||
// 3. Less deleting sources/targets
|
||||
// 4. We can basically do clears in hardware, if they aren't insane ones
|
||||
bool dirtied_area = t->m_dirty.size() >= 1;
|
||||
|
||||
// Check it covers the whole area of the new draw
|
||||
if (!is_shuffle && dirtied_area)
|
||||
{
|
||||
const u32 draw_start = GSLocalMemory::GetStartBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, draw_rect);
|
||||
const u32 draw_end = GSLocalMemory::GetEndBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, draw_rect);
|
||||
|
||||
const GSVector4i dirty_rect = t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size);
|
||||
const u32 dirty_start = GSLocalMemory::GetStartBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, dirty_rect);
|
||||
const u32 dirty_end = GSLocalMemory::GetEndBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, dirty_rect);
|
||||
|
||||
if (dirty_end < draw_end || dirty_start > draw_start)
|
||||
dirtied_area = false;
|
||||
}
|
||||
|
||||
if (can_use && ((!is_shuffle && dirtied_area) || (is_shuffle && src && GSLocalMemory::m_psm[src->m_TEX0.PSM].bpp == 8 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 16)) && ((preserve_alpha && preserve_rgb) || (draw_rect.w > GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y && !possible_clear)) && TEX0.TBW != t->m_TEX0.TBW)
|
||||
{
|
||||
can_use = false;
|
||||
}
|
||||
else if (!t->m_dirty.empty())
|
||||
|
||||
if (can_use)
|
||||
{
|
||||
const GSVector4i size_rect = GSVector4i::loadh(size);
|
||||
can_use = !t->m_dirty.GetTotalRect(TEX0, size).rintersect(size_rect).eq(size_rect);
|
||||
}
|
||||
}
|
||||
else if (type == RenderTarget && (fbmask == 0xffffff && !t->m_was_dst_matched && TEX0.TBW != t->m_TEX0.TBW))
|
||||
{
|
||||
// When returning to being matched with the Z buffer in width, we need to make sure the RGB is up to date as it could get used later (Hitman Contracts).
|
||||
auto& rev_list = m_dst[1 - type];
|
||||
for (auto j = rev_list.begin(); j != rev_list.end(); ++j)
|
||||
{
|
||||
Target* ds = *j;
|
||||
|
||||
if (t->m_TEX0.TBP0 != ds->m_TEX0.TBP0 || !ds->m_valid_rgb || TEX0.TBW != ds->m_TEX0.TBW)
|
||||
continue;
|
||||
|
||||
t->m_was_dst_matched = true;
|
||||
t->m_valid_rgb = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO: What might be a nicer solution than this, is to rearrange the targets to match the new layout, however this comes with some caviets:
|
||||
// 1. They can draw wider than the FBW
|
||||
// 2. The dirty+valid rects will need to also be rearranged
|
||||
// 3. This could mean larger targets hanging around more
|
||||
// 4. Sources which reference a target may become invalid and will need to be removed
|
||||
// 5. Potential performance implications from additional render passes/copying
|
||||
//
|
||||
// But the bonuses are:
|
||||
// 1. Rearranging the page layout will fix quite a few games which do this
|
||||
// 2. Preserved data will be in the correct place (in most cases)
|
||||
// 3. Less deleting sources/targets
|
||||
// 4. We can basically do clears in hardware, if they aren't insane ones
|
||||
bool dirtied_area = t->m_dirty.size() >= 1;
|
||||
|
||||
// Check it covers the whole area of the new draw
|
||||
if (!is_shuffle && dirtied_area)
|
||||
{
|
||||
const u32 draw_start = GSLocalMemory::GetStartBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, draw_rect);
|
||||
const u32 draw_end = GSLocalMemory::GetEndBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, draw_rect);
|
||||
|
||||
const GSVector4i dirty_rect = t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size);
|
||||
const u32 dirty_start = GSLocalMemory::GetStartBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, dirty_rect);
|
||||
const u32 dirty_end = GSLocalMemory::GetEndBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, dirty_rect);
|
||||
|
||||
if (dirty_end < draw_end || dirty_start > draw_start)
|
||||
dirtied_area = false;
|
||||
}
|
||||
|
||||
if (can_use && ((!is_shuffle && dirtied_area) || (is_shuffle && src && GSLocalMemory::m_psm[src->m_TEX0.PSM].bpp == 8 && GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp == 16)) && ((preserve_alpha && preserve_rgb) || (draw_rect.w > GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y && !possible_clear)) && TEX0.TBW != t->m_TEX0.TBW)
|
||||
{
|
||||
can_use = false;
|
||||
}
|
||||
|
||||
if (can_use)
|
||||
{
|
||||
if (used)
|
||||
list.MoveFront(i.Index());
|
||||
dst = t;
|
||||
|
||||
dst->m_32_bits_fmt |= (psm_s.bpp != 16);
|
||||
break;
|
||||
}
|
||||
else if (!(src && src->m_from_target == t))
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to change in target", t->m_TEX0.TBP0, t->m_TEX0.TBW, GSUtil::GetPSMName(t->m_TEX0.PSM));
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list.erase(i);
|
||||
delete t;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Probably pointing to half way through the target
|
||||
else if (!min_rect.rempty() && GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets)
|
||||
{
|
||||
// Some games misuse the scissor so it ends up valid 1 pixel over, which causes hell for us. So check if it still overlaps without the extra pixel.
|
||||
const GSVector4i adjusted_valid = GSVector4i(t->m_valid.x, t->m_valid.y, std::min(t->m_valid.z, static_cast<int>(t->m_TEX0.TBW) * 64), t->m_valid.w - 1);
|
||||
const u32 adjusted_endblock = GSLocalMemory::GetEndBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, adjusted_valid);
|
||||
if (adjusted_endblock <= bp)
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const u32 widthpage_offset = (std::abs(static_cast<int>(bp - t->m_TEX0.TBP0)) >> 5) % std::max(t->m_TEX0.TBW, 1U);
|
||||
const bool is_aligned_ok = widthpage_offset == 0 || ((min_rect.width() <= static_cast<int>((t->m_TEX0.TBW - widthpage_offset) * 64) && (t->m_TEX0.TBW == TEX0.TBW || TEX0.TBW == 1)) && bp >= t->m_TEX0.TBP0);
|
||||
const bool no_target_or_newer = (!dst || ((GSState::s_n - dst->m_last_draw) < (GSState::s_n - t->m_last_draw)));
|
||||
const bool width_match = (t->m_TEX0.TBW == TEX0.TBW || (TEX0.TBW == 1 && draw_rect.w <= GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y));
|
||||
const bool ds_offset = !ds || offset != 0;
|
||||
const bool is_double_buffer = TEX0.TBP0 == ((((t->m_end_block + 1) - t->m_TEX0.TBP0) / 2) + t->m_TEX0.TBP0);
|
||||
const bool source_match = src && src->m_TEX0.TBP0 <= bp && src->m_end_block > bp && src->m_TEX0.TBW == TEX0.TBW && src->m_from_target && src->m_from_target == t && t->Inside(bp, TEX0.TBW, TEX0.PSM, min_rect);
|
||||
const bool was_used_last_draw = t->m_last_draw == (GSState::s_n - 1);
|
||||
// if it's a shuffle, some games tend to offset back by a page, such as Tomb Raider, for no disernable reason, but it then causes problems.
|
||||
// This can also happen horizontally (Catwoman moves everything one page left with shuffles), but this is too messy to deal with right now.
|
||||
const bool overlaps = t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect) || (is_shuffle && src && GSLocalMemory::m_psm[src->m_TEX0.PSM].bpp == 8 && t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect + GSVector4i(0, 0, 0, 32)));
|
||||
if (source_match || (no_target_or_newer && is_aligned_ok && width_match && overlaps && (is_shuffle || ds_offset || is_double_buffer || was_used_last_draw)))
|
||||
{
|
||||
const GSLocalMemory::psm_t& s_psm = GSLocalMemory::m_psm[TEX0.PSM];
|
||||
|
||||
// If it overlaps but the target is huge and the Z isn't offset, we need to split the buffer, so let's shrink this one down.
|
||||
// 896 is just 448 * 2,just gives the buffer chance to be larger than normal, in case they do something like 640x640, or something ridiculous.
|
||||
if (!is_shuffle && (ds && offset == 0 && (t->m_valid.w >= 896) && ((((t->m_end_block + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) <= bp))
|
||||
{
|
||||
const u32 local_offset = (((bp - t->m_TEX0.TBP0) >> 5) / std::max(t->m_TEX0.TBW, 1U)) * s_psm.pgs.y;
|
||||
if ((dst = CreateTarget(TEX0, GSVector2i(t->m_valid.z, t->m_valid.w - local_offset), GSVector2i(t->m_valid.z, t->m_valid.w - local_offset), scale, type, true, fbmask, false, false, preserve_rgb || preserve_alpha, GSVector4i::zero(), src)))
|
||||
dst->m_32_bits_fmt |= (psm_s.bpp != 16);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// I know what you're thinking, and I hate the guy who wrote it too (me). Project Snowblind, Tomb Raider etc decide to offset where they're drawing using a channel shuffle, and this gets messy, so best just to kill the old target.
|
||||
if (is_shuffle && src && src->m_TEX0.PSM == PSMT8 && GSRendererHW::GetInstance()->m_context->FRAME.FBW == 1 && t->m_last_draw != (GSState::s_n - 1) && src->m_from_target && (src->m_from_target->m_TEX0.TBP0 == src->m_TEX0.TBP0 || (((src->m_TEX0.TBP0 - src->m_from_target->m_TEX0.TBP0) >> 5) % std::max(src->m_from_target->m_TEX0.TBW, 1U) == 0)) && widthpage_offset && src->m_from_target != t)
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s offset overwrite shuffle", t->m_TEX0.TBP0, t->m_TEX0.TBW, GSUtil::GetPSMName(t->m_TEX0.PSM));
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list.erase(i);
|
||||
delete t;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_shuffle && (!GSUtil::HasSameSwizzleBits(t->m_TEX0.PSM, TEX0.PSM) ||
|
||||
((widthpage_offset % std::max(t->m_TEX0.TBW, 1U)) != 0 && ((widthpage_offset + (min_rect.width() + (s_psm.pgs.x - 1)) / s_psm.pgs.x)) > t->m_TEX0.TBW)))
|
||||
{
|
||||
const int page_offset = TEX0.TBP0 - t->m_TEX0.TBP0;
|
||||
const int number_pages = page_offset / 32;
|
||||
const u32 tbw = std::max(t->m_TEX0.TBW, 1u);
|
||||
const int row_offset = number_pages / tbw;
|
||||
const int page_height = GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y;
|
||||
const int vertical_position = row_offset * page_height;
|
||||
|
||||
if (src && src->m_from_target == t && src->m_target_direct && vertical_position >= t->m_valid.w / 2)
|
||||
{
|
||||
// Valids and drawn since last read doesn't match, keep the target but resize it.
|
||||
src->m_valid_rect.w = std::min(vertical_position, src->m_valid_rect.w);
|
||||
t->m_valid.w = std::min(vertical_position, t->m_valid.w);
|
||||
t->ResizeValidity(t->m_valid);
|
||||
t->ResizeDrawn(t->m_valid);
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to change in target", t->m_TEX0.TBP0, t->m_TEX0.TBW, GSUtil::GetPSMName(t->m_TEX0.PSM));
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list.erase(i);
|
||||
delete t;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
GSVector4i lookup_rect = min_rect;
|
||||
|
||||
if (is_shuffle)
|
||||
lookup_rect = lookup_rect & GSVector4i(~8);
|
||||
|
||||
const GSVector4i translated_rect = GSVector4i(0, 0, 0, 0).max_i32(TranslateAlignedRectByPage(t, TEX0.TBP0, TEX0.PSM, TEX0.TBW, lookup_rect));
|
||||
const GSVector4i dirty_rect = t->m_dirty.empty() ? GSVector4i::zero() : t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size);
|
||||
const bool all_dirty = dirty_rect.eq(t->m_valid);
|
||||
|
||||
|
||||
if (!is_shuffle && !dirty_rect.rempty() && (!preserve_alpha && !preserve_rgb) && (GSState::s_n - 3) > t->m_last_draw)
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to dirty areas not preserved (Likely change in target)", t->m_TEX0.TBP0, t->m_TEX0.TBW, GSUtil::GetPSMName(t->m_TEX0.PSM));
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list.erase(i);
|
||||
delete t;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!all_dirty && ((translated_rect.w <= t->m_valid.w) || widthpage_offset == 0 || (GSState::s_n - 3) <= t->m_last_draw))
|
||||
{
|
||||
if (TEX0.TBW == t->m_TEX0.TBW && !is_shuffle && widthpage_offset == 0 && ((min_rect.w + 63) / 64) > 1)
|
||||
{
|
||||
// Beyond Good and Evil does this awful thing where it puts one framebuffer at 0xf00, with the first row of pages blanked out, and the whole thing goes down to 0x2080
|
||||
// which is a problem, because it then puts the Z buffer at 0x1fc0, then offsets THAT by 1 row of pages, so it starts at, you guessed it, 2080.
|
||||
// So let's check the *real* start.
|
||||
u32 real_start_address = GSLocalMemory::GetStartBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, t->m_drawn_since_read);
|
||||
u32 new_end_address = GSLocalMemory::GetEndBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, min_rect);
|
||||
|
||||
// Not really overlapping.
|
||||
if (real_start_address > new_end_address)
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//DevCon.Warning("Here draw %d wanted %x PSM %x got %x PSM %x offset of %d pages width %d pages draw width %d", GSState::s_n, bp, TEX0.PSM, t->m_TEX0.TBP0, t->m_TEX0.PSM, (bp - t->m_TEX0.TBP0) >> 5, t->m_TEX0.TBW, draw_rect.width());
|
||||
dst = t;
|
||||
|
||||
dst->m_32_bits_fmt |= (psm_s.bpp != 16);
|
||||
//Continue just in case there's a newer target
|
||||
if (used)
|
||||
list.MoveFront(i.Index());
|
||||
if (t->m_TEX0.TBP0 <= bp || GSLocalMemory::GetStartBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, min_rect) >= bp)
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
list->MoveFront(i.Index());
|
||||
|
||||
new_dst = t;
|
||||
new_dst->m_32_bits_fmt |= (psm_s.bpp != 16);
|
||||
|
||||
break;
|
||||
}
|
||||
else if (!(src && src->m_from_target == t))
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to change in target", t->m_TEX0.TBP0, t->m_TEX0.TBW, GSUtil::GetPSMName(t->m_TEX0.PSM));
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list->erase(i);
|
||||
delete t;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Probably pointing to half way through the target
|
||||
else if (!min_rect.rempty() && GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets)
|
||||
{
|
||||
// Some games misuse the scissor so it ends up valid 1 pixel over, which causes hell for us. So check if it still overlaps without the extra pixel.
|
||||
const GSVector4i adjusted_valid = GSVector4i(t->m_valid.x, t->m_valid.y, std::min(t->m_valid.z, static_cast<int>(t->m_TEX0.TBW) * 64), t->m_valid.w - 1);
|
||||
const u32 adjusted_endblock = GSLocalMemory::GetEndBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, adjusted_valid);
|
||||
if (adjusted_endblock <= bp)
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
i++;
|
||||
const u32 widthpage_offset = (std::abs(static_cast<int>(bp - t->m_TEX0.TBP0)) >> 5) % std::max(t->m_TEX0.TBW, 1U);
|
||||
const bool is_aligned_ok = widthpage_offset == 0 || ((min_rect.width() <= static_cast<int>((t->m_TEX0.TBW - widthpage_offset) * 64) && (t->m_TEX0.TBW == TEX0.TBW || TEX0.TBW == 1)) && bp >= t->m_TEX0.TBP0);
|
||||
const bool no_target_or_newer = (!new_dst || ((GSState::s_n - new_dst->m_last_draw) < (GSState::s_n - t->m_last_draw)));
|
||||
const bool width_match = (t->m_TEX0.TBW == TEX0.TBW || (TEX0.TBW == 1 && draw_rect.w <= GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y));
|
||||
const bool ds_offset = !ds || offset != 0;
|
||||
const bool is_double_buffer = TEX0.TBP0 == ((((t->m_end_block + 1) - t->m_TEX0.TBP0) / 2) + t->m_TEX0.TBP0);
|
||||
const bool source_match = src && src->m_TEX0.TBP0 <= bp && src->m_end_block > bp && src->m_TEX0.TBW == TEX0.TBW && src->m_from_target && src->m_from_target == t && t->Inside(bp, TEX0.TBW, TEX0.PSM, min_rect);
|
||||
const bool was_used_last_draw = t->m_last_draw == (GSState::s_n - 1);
|
||||
// if it's a shuffle, some games tend to offset back by a page, such as Tomb Raider, for no disernable reason, but it then causes problems.
|
||||
// This can also happen horizontally (Catwoman moves everything one page left with shuffles), but this is too messy to deal with right now.
|
||||
const bool overlaps = t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect) || (is_shuffle && src && GSLocalMemory::m_psm[src->m_TEX0.PSM].bpp == 8 && t->Overlaps(bp, TEX0.TBW, TEX0.PSM, min_rect + GSVector4i(0, 0, 0, 32)));
|
||||
if (source_match || (no_target_or_newer && is_aligned_ok && width_match && overlaps && (is_shuffle || ds_offset || is_double_buffer || was_used_last_draw)))
|
||||
{
|
||||
const GSLocalMemory::psm_t& s_psm = GSLocalMemory::m_psm[TEX0.PSM];
|
||||
|
||||
// If it overlaps but the target is huge and the Z isn't offset, we need to split the buffer, so let's shrink this one down.
|
||||
// 896 is just 448 * 2,just gives the buffer chance to be larger than normal, in case they do something like 640x640, or something ridiculous.
|
||||
if (!is_shuffle && (ds && offset == 0 && (t->m_valid.w >= 896) && ((((t->m_end_block + 1) - t->m_TEX0.TBP0) >> 1) + t->m_TEX0.TBP0) <= bp))
|
||||
{
|
||||
const u32 local_offset = (((bp - t->m_TEX0.TBP0) >> 5) / std::max(t->m_TEX0.TBW, 1U)) * s_psm.pgs.y;
|
||||
if ((new_dst = CreateTarget(TEX0, GSVector2i(t->m_valid.z, t->m_valid.w - local_offset), GSVector2i(t->m_valid.z, t->m_valid.w - local_offset), scale, type, true, fbmask, false, false, preserve_rgb || preserve_alpha, GSVector4i::zero(), src)))
|
||||
new_dst->m_32_bits_fmt |= (psm_s.bpp != 16);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// I know what you're thinking, and I hate the guy who wrote it too (me). Project Snowblind, Tomb Raider etc decide to offset where they're drawing using a channel shuffle, and this gets messy, so best just to kill the old target.
|
||||
if (is_shuffle && src && src->m_TEX0.PSM == PSMT8 && GSRendererHW::GetInstance()->m_context->FRAME.FBW == 1 && t->m_last_draw != (GSState::s_n - 1) && src->m_from_target && (src->m_from_target->m_TEX0.TBP0 == src->m_TEX0.TBP0 || (((src->m_TEX0.TBP0 - src->m_from_target->m_TEX0.TBP0) >> 5) % std::max(src->m_from_target->m_TEX0.TBW, 1U) == 0)) && widthpage_offset && src->m_from_target != t)
|
||||
{
|
||||
if (iteration == 0)
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s offset overwrite shuffle", t->m_TEX0.TBP0, t->m_TEX0.TBW, GSUtil::GetPSMName(t->m_TEX0.PSM));
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list->erase(i);
|
||||
delete t;
|
||||
}
|
||||
else
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_shuffle && (!GSUtil::HasSameSwizzleBits(t->m_TEX0.PSM, TEX0.PSM) ||
|
||||
((widthpage_offset % std::max(t->m_TEX0.TBW, 1U)) != 0 && ((widthpage_offset + (min_rect.width() + (s_psm.pgs.x - 1)) / s_psm.pgs.x)) > t->m_TEX0.TBW)))
|
||||
{
|
||||
const int page_offset = TEX0.TBP0 - t->m_TEX0.TBP0;
|
||||
const int number_pages = page_offset / 32;
|
||||
const u32 tbw = std::max(t->m_TEX0.TBW, 1u);
|
||||
const int row_offset = number_pages / tbw;
|
||||
const int page_height = GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y;
|
||||
const int vertical_position = row_offset * page_height;
|
||||
|
||||
if (src && src->m_from_target == t && src->m_target_direct && vertical_position >= t->m_valid.w / 2)
|
||||
{
|
||||
// Valids and drawn since last read doesn't match, keep the target but resize it.
|
||||
src->m_valid_rect.w = std::min(vertical_position, src->m_valid_rect.w);
|
||||
t->m_valid.w = std::min(vertical_position, t->m_valid.w);
|
||||
t->ResizeValidity(t->m_valid);
|
||||
t->ResizeDrawn(t->m_valid);
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iteration == 0)
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to change in target", t->m_TEX0.TBP0, t->m_TEX0.TBW, GSUtil::GetPSMName(t->m_TEX0.PSM));
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list->erase(i);
|
||||
delete t;
|
||||
}
|
||||
else
|
||||
i++;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
GSVector4i lookup_rect = min_rect;
|
||||
|
||||
if (is_shuffle)
|
||||
lookup_rect = lookup_rect & GSVector4i(~8);
|
||||
|
||||
const GSVector4i translated_rect = GSVector4i(0, 0, 0, 0).max_i32(TranslateAlignedRectByPage(t, TEX0.TBP0, TEX0.PSM, TEX0.TBW, lookup_rect));
|
||||
const GSVector4i dirty_rect = t->m_dirty.empty() ? GSVector4i::zero() : t->m_dirty.GetTotalRect(t->m_TEX0, t->m_unscaled_size);
|
||||
const bool all_dirty = dirty_rect.eq(t->m_valid);
|
||||
|
||||
|
||||
if (!is_shuffle && !dirty_rect.rempty() && (!preserve_alpha && !preserve_rgb) && (GSState::s_n - 3) > t->m_last_draw)
|
||||
{
|
||||
GL_INS("TC: Deleting RT BP 0x%x BW %d PSM %s due to dirty areas not preserved (Likely change in target)", t->m_TEX0.TBP0, t->m_TEX0.TBW, GSUtil::GetPSMName(t->m_TEX0.PSM));
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list->erase(i);
|
||||
delete t;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!all_dirty && ((translated_rect.w <= t->m_valid.w) || widthpage_offset == 0 || (GSState::s_n - 3) <= t->m_last_draw))
|
||||
{
|
||||
if (TEX0.TBW == t->m_TEX0.TBW && !is_shuffle && widthpage_offset == 0 && ((min_rect.w + 63) / 64) > 1)
|
||||
{
|
||||
// Beyond Good and Evil does this awful thing where it puts one framebuffer at 0xf00, with the first row of pages blanked out, and the whole thing goes down to 0x2080
|
||||
// which is a problem, because it then puts the Z buffer at 0x1fc0, then offsets THAT by 1 row of pages, so it starts at, you guessed it, 2080.
|
||||
// So let's check the *real* start.
|
||||
u32 real_start_address = GSLocalMemory::GetStartBlockAddress(t->m_TEX0.TBP0, t->m_TEX0.TBW, t->m_TEX0.PSM, t->m_drawn_since_read);
|
||||
u32 new_end_address = GSLocalMemory::GetEndBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, min_rect);
|
||||
|
||||
// Not really overlapping.
|
||||
if (real_start_address > new_end_address)
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//DevCon.Warning("Here draw %d wanted %x PSM %x got %x PSM %x offset of %d pages width %d pages draw width %d", GSState::s_n, bp, TEX0.PSM, t->m_TEX0.TBP0, t->m_TEX0.PSM, (bp - t->m_TEX0.TBP0) >> 5, t->m_TEX0.TBW, draw_rect.width());
|
||||
new_dst = t;
|
||||
new_dst->m_32_bits_fmt |= (psm_s.bpp != 16);
|
||||
|
||||
//Continue just in case there's a newer target
|
||||
if (used)
|
||||
list->MoveFront(i.Index());
|
||||
if (t->m_TEX0.TBP0 <= bp || GSLocalMemory::GetStartBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, min_rect) >= bp)
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pxAssert(type == RenderTarget);
|
||||
// Let's try to find a perfect frame that contains valid data
|
||||
for (auto i = list.begin(); i != list.end(); ++i)
|
||||
for (auto i = list->begin(); i != list->end(); ++i)
|
||||
{
|
||||
Target* t = *i;
|
||||
|
||||
@@ -2673,7 +2712,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
{
|
||||
DevCon.Warning("Wanted %x psm %x bw %x, got %x psm %x bw %x, deleting", TEX0.TBP0, TEX0.PSM, TEX0.TBW, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list.erase(i);
|
||||
i = list->erase(i);
|
||||
delete t;
|
||||
continue;
|
||||
}
|
||||
@@ -2691,7 +2730,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
// 2nd try ! Try to find a frame at the requested bp -> bp + size is inside of (or equal to)
|
||||
if (!dst)
|
||||
{
|
||||
for (auto i = list.begin(); i != list.end(); ++i)
|
||||
for (auto i = list->begin(); i != list->end(); ++i)
|
||||
{
|
||||
Target* t = *i;
|
||||
const u32 end_block = GSLocalMemory::GetEndBlockAddress(bp, TEX0.TBW, TEX0.PSM, GSVector4i(0, size.y, size.x, size.y + 1));
|
||||
@@ -2711,7 +2750,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
{
|
||||
DevCon.Warning("2 Wanted %x psm %x bw %x, got %x psm %x bw %x, deleting", TEX0.TBP0, TEX0.PSM, TEX0.TBW, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list.erase(i);
|
||||
i = list->erase(i);
|
||||
delete t;
|
||||
continue;
|
||||
}
|
||||
@@ -2731,7 +2770,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
// 3rd try ! Try to find a frame that doesn't contain valid data (honestly I'm not sure we need to do it)
|
||||
if (!dst)
|
||||
{
|
||||
for (auto i = list.begin(); i != list.end(); ++i)
|
||||
for (auto i = list->begin(); i != list->end(); ++i)
|
||||
{
|
||||
Target* t = *i;
|
||||
if (bp == t->m_TEX0.TBP0 && TEX0.TBW == t->m_TEX0.TBW)
|
||||
@@ -2743,7 +2782,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
{
|
||||
DevCon.Warning("3 Wanted %x psm %x bw %x, got %x psm %x bw %x, deleting", TEX0.TBP0, TEX0.PSM, TEX0.TBW, t->m_TEX0.TBP0, t->m_TEX0.PSM, t->m_TEX0.TBW);
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = list.erase(i);
|
||||
i = list->erase(i);
|
||||
delete t;
|
||||
continue;
|
||||
}
|
||||
@@ -2795,11 +2834,10 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
else
|
||||
g_gs_device->StretchRect(dst->m_texture, sRect, tex, dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, type == RenderTarget && !preserve_scale);
|
||||
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
m_target_memory_usage = (m_target_memory_usage - dst->m_texture->GetMemUsage()) + tex->GetMemUsage();
|
||||
|
||||
// If we're changing resolution scale, just toss the texture, it's not going to get reused.
|
||||
if ((!GSConfig.UserHacks_NativePaletteDraw && !dst->m_downscaled) || (dst->m_scale != 1.0f && scale != 1.0f))
|
||||
if ((!GSConfig.UserHacks_NativePaletteDraw && !dst->m_downscaled && scale != 1.0f) || (dst->m_scale != 1.0f && scale != 1.0f))
|
||||
delete dst->m_texture;
|
||||
else
|
||||
g_gs_device->Recycle(dst->m_texture);
|
||||
@@ -2826,7 +2864,6 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
if (!tex)
|
||||
return nullptr;
|
||||
g_gs_device->StretchRect(dst->m_texture, sRect, tex, dRect, ShaderConvert::FLOAT32_TO_FLOAT24, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
g_gs_device->Recycle(dst->m_texture);
|
||||
|
||||
dst->m_texture = tex;
|
||||
@@ -2899,7 +2936,6 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
|
||||
g_gs_device->StretchRect(dst->m_texture, source_rect, tex, dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, false);
|
||||
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
m_target_memory_usage = m_target_memory_usage + tex->GetMemUsage();
|
||||
|
||||
// Don't kill the target here as it's being used for the source.
|
||||
@@ -2923,7 +2959,6 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
else
|
||||
g_gs_device->StretchRect(dst->m_texture, source_rect, tex, dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, false);
|
||||
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
m_target_memory_usage = (m_target_memory_usage - dst->m_texture->GetMemUsage()) + tex->GetMemUsage();
|
||||
|
||||
g_gs_device->Recycle(dst->m_texture);
|
||||
@@ -3006,7 +3041,6 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
|
||||
g_gs_device->StretchRect(dst_match->m_texture, GSVector4(0, 0, 1, 1),
|
||||
dst->m_texture, GSVector4(dst->GetUnscaledRect()) * GSVector4(dst->GetScale()), shader, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
dst_match->m_valid_rgb = !used;
|
||||
dst_match->m_was_dst_matched = true;
|
||||
@@ -3088,67 +3122,28 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
}
|
||||
else if (!is_frame && !GSConfig.UserHacks_DisableDepthSupport)
|
||||
{
|
||||
const int rev_type = (type == DepthStencil) ? RenderTarget : DepthStencil;
|
||||
|
||||
// Depth stencil/RT can be an older RT/DS but only check recent RT/DS to avoid to pick
|
||||
// some bad data.
|
||||
auto& rev_list = m_dst[rev_type];
|
||||
Target* dst_match = nullptr;
|
||||
for (auto i = rev_list.begin(); i != rev_list.end(); ++i)
|
||||
if (!dst_match)
|
||||
{
|
||||
Target* t = *i;
|
||||
// Don't pull in targets without valid lower 24 bits unless the Z is 32bits and the alpha is valid, it makes no sense to convert them otherwise.
|
||||
// FIXME: Technically the difference in size is fine, but if the target gets reinterpreted, the hw renderer doesn't rearrange the target.
|
||||
// This does cause some extra uploads in some games (like Burnout), but without this, bad data gets displayed in games like Transformers.
|
||||
if (bp != t->m_TEX0.TBP0 || (!t->m_valid_rgb && (!(GSUtil::GetChannelMask(TEX0.PSM) & 0x8) || !(t->m_valid_alpha_low || t->m_valid_alpha_high))) ||
|
||||
(!is_shuffle && t->m_TEX0.TBW != TEX0.TBW && (possible_clear || ((~GSLocalMemory::m_psm[t->m_TEX0.PSM].fmsk | fbmask) == 0xffffffff))))
|
||||
const int rev_type = (type == DepthStencil) ? RenderTarget : DepthStencil;
|
||||
// Depth stencil/RT can be an older RT/DS but only check recent RT/DS to avoid to pick
|
||||
// some bad data.
|
||||
auto& rev_list = m_dst[rev_type];
|
||||
for (auto i = rev_list.begin(); i != rev_list.end(); ++i)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// If the format is completely different, but it's the same location, it's likely just overwriting it, so get rid.
|
||||
// Make sure it's not currently in use, that could be bad.
|
||||
if (!is_shuffle && (!ds || (ds != t)) &&
|
||||
t->m_TEX0.TBW != TEX0.TBW && TEX0.TBW != 1 && !preserve_rgb && min_rect.w > GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y)
|
||||
{
|
||||
if (src && src->m_target && src->m_from_target == t && src->m_target_direct)
|
||||
Target* t = *i;
|
||||
// Don't pull in targets without valid lower 24 bits unless the Z is 32bits and the alpha is valid, it makes no sense to convert them otherwise.
|
||||
// FIXME: Technically the difference in size is fine, but if the target gets reinterpreted, the hw renderer doesn't rearrange the target.
|
||||
// This does cause some extra uploads in some games (like Burnout), but without this, bad data gets displayed in games like Transformers.
|
||||
if (bp != t->m_TEX0.TBP0 || (!t->m_valid_rgb && (!(GSUtil::GetChannelMask(TEX0.PSM) & 0x8) || !(t->m_valid_alpha_low || t->m_valid_alpha_high))) ||
|
||||
(!is_shuffle && t->m_TEX0.TBW != TEX0.TBW && (possible_clear || ((~GSLocalMemory::m_psm[t->m_TEX0.PSM].fmsk | fbmask) == 0xffffffff))))
|
||||
{
|
||||
src->m_target_direct = false;
|
||||
src->m_shared_texture = false;
|
||||
t->m_texture = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
GL_CACHE("TC: Deleting Z draw %d", GSState::s_n);
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = rev_list.erase(i);
|
||||
delete t;
|
||||
continue;
|
||||
}
|
||||
const GSLocalMemory::psm_t& t_psm_s = GSLocalMemory::m_psm[t->m_TEX0.PSM];
|
||||
if (t_psm_s.bpp != psm_s.bpp)
|
||||
{
|
||||
bool remove_target = possible_clear || (used && !is_shuffle);
|
||||
|
||||
// If we have a BW change, and it's not a multiple of 2 (for a shuffle), the game's going to get a jigsaw
|
||||
// puzzle of pages and can't be expecting to have legitimate data. Tokimeki Memorial 3 reuses a BW 17
|
||||
// buffer as BW 10, and if we don't discard the BW 17 buffer, the BW 10 buffer ends up twice the size.
|
||||
const u32 shuffle_bw = (psm_s.bpp > t_psm_s.bpp) ? (TEX0.TBW / 2u) : (TEX0.TBW * 2u);
|
||||
if (t->m_TEX0.TBW != TEX0.TBW && (t->m_TEX0.TBW != shuffle_bw && !is_shuffle))
|
||||
// If the format is completely different, but it's the same location, it's likely just overwriting it, so get rid.
|
||||
// Make sure it's not currently in use, that could be bad.
|
||||
if (!is_shuffle && (!ds || (ds != t)) &&
|
||||
t->m_TEX0.TBW != TEX0.TBW && TEX0.TBW != 1 && !preserve_rgb && min_rect.w > GSLocalMemory::m_psm[t->m_TEX0.PSM].pgs.y)
|
||||
{
|
||||
// But we'll make sure the whole existing target's actually being drawn over to be safe.
|
||||
const u32 end_block = GSLocalMemory::GetUnwrappedEndBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, draw_rect);
|
||||
if (end_block >= t->UnwrappedEndBlock())
|
||||
{
|
||||
GL_CACHE("TC: Not converting %s at %x TBW %u with end block of %x when we're drawing through %x",
|
||||
to_string(rev_type), t->m_TEX0.TBP0, t->m_TEX0.TBW, t->UnwrappedEndBlock(), end_block);
|
||||
remove_target = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Probably an old target, get rid of it.
|
||||
if (remove_target)
|
||||
{
|
||||
// DT Racer hits this path and causes a crash when RT in RT is disabled,
|
||||
// so let's make sure source and target texture isn't linked/shared before deleting the target.
|
||||
if (src && src->m_target && src->m_from_target == t && src->m_target_direct)
|
||||
{
|
||||
src->m_target_direct = false;
|
||||
@@ -3156,26 +3151,68 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
t->m_texture = nullptr;
|
||||
}
|
||||
|
||||
GL_CACHE("TC: Deleting Z draw %d", GSState::s_n);
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = rev_list.erase(i);
|
||||
delete t;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const GSLocalMemory::psm_t& t_psm_s = GSLocalMemory::m_psm[t->m_TEX0.PSM];
|
||||
if (t_psm_s.bpp != psm_s.bpp)
|
||||
{
|
||||
bool remove_target = possible_clear || (used && !is_shuffle);
|
||||
|
||||
if (t->m_age == 0)
|
||||
{
|
||||
dst_match = t;
|
||||
break;
|
||||
}
|
||||
else if (t->m_age == 1 && (preserve_rgb || (preserve_alpha && (t->m_valid_alpha_low || t->m_valid_alpha_high))))
|
||||
{
|
||||
dst_match = t;
|
||||
// If we have a BW change, and it's not a multiple of 2 (for a shuffle), the game's going to get a jigsaw
|
||||
// puzzle of pages and can't be expecting to have legitimate data. Tokimeki Memorial 3 reuses a BW 17
|
||||
// buffer as BW 10, and if we don't discard the BW 17 buffer, the BW 10 buffer ends up twice the size.
|
||||
const u32 shuffle_bw = (psm_s.bpp > t_psm_s.bpp) ? (TEX0.TBW / 2u) : (TEX0.TBW * 2u);
|
||||
if (t->m_TEX0.TBW != TEX0.TBW && (t->m_TEX0.TBW != shuffle_bw && !is_shuffle))
|
||||
{
|
||||
// But we'll make sure the whole existing target's actually being drawn over to be safe.
|
||||
const u32 end_block = GSLocalMemory::GetUnwrappedEndBlockAddress(TEX0.TBP0, TEX0.TBW, TEX0.PSM, draw_rect);
|
||||
if (end_block >= t->UnwrappedEndBlock())
|
||||
{
|
||||
GL_CACHE("TC: Not converting %s at %x TBW %u with end block of %x when we're drawing through %x",
|
||||
to_string(rev_type), t->m_TEX0.TBP0, t->m_TEX0.TBW, t->UnwrappedEndBlock(), end_block);
|
||||
remove_target = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Probably an old target, get rid of it.
|
||||
if (remove_target)
|
||||
{
|
||||
// DT Racer hits this path and causes a crash when RT in RT is disabled,
|
||||
// so let's make sure source and target texture isn't linked/shared before deleting the target.
|
||||
if (src && src->m_target && src->m_from_target == t && src->m_target_direct)
|
||||
{
|
||||
src->m_target_direct = false;
|
||||
src->m_shared_texture = false;
|
||||
t->m_texture = nullptr;
|
||||
}
|
||||
|
||||
InvalidateSourcesFromTarget(t);
|
||||
i = rev_list.erase(i);
|
||||
delete t;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (t->m_age == 0)
|
||||
{
|
||||
dst_match = t;
|
||||
break;
|
||||
}
|
||||
else if (t->m_age == 1 && (preserve_rgb || (preserve_alpha && (t->m_valid_alpha_low || t->m_valid_alpha_high))))
|
||||
{
|
||||
dst_match = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
// We only want to use a matched target if it's actually being used.
|
||||
|
||||
if (dst_match)
|
||||
{
|
||||
// dst_match, we only want to use a matched target if it's actually being used.
|
||||
|
||||
calcRescale(dst_match);
|
||||
|
||||
// If we don't need A, and the existing target doesn't have valid alpha, don't bother converting it.
|
||||
@@ -3192,7 +3229,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
// Clear instead of invalidating if there is anything which isn't touched.
|
||||
clear |= (!preserve_target && fbmask != 0);
|
||||
GIFRegTEX0 new_TEX0;
|
||||
new_TEX0.TBP0 = TEX0.TBP0;
|
||||
new_TEX0.TBP0 = GSConfig.UserHacks_TextureInsideRt >= GSTextureInRtMode::InsideTargets ? dst_match->m_TEX0.TBP0 : TEX0.TBP0;
|
||||
new_TEX0.TBW = (!half_width) ? dst_match->m_TEX0.TBW : TEX0.TBW;
|
||||
new_TEX0.PSM = is_shuffle ? dst_match->m_TEX0.PSM : TEX0.PSM;
|
||||
|
||||
@@ -3207,8 +3244,7 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
dst->m_valid_alpha_low = dst_match->m_valid_alpha_low; //&& psm_s.trbpp != 24;
|
||||
dst->m_valid_alpha_high = dst_match->m_valid_alpha_high; //&& psm_s.trbpp != 24;
|
||||
dst->m_valid_rgb = dst_match->m_valid_rgb && (dst->m_TEX0.TBW == TEX0.TBW || min_rect.w <= GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].pgs.y);
|
||||
dst->m_was_dst_matched = true;
|
||||
dst_match->m_was_dst_matched = true;
|
||||
dst->m_was_dst_matched = !dst_match->m_was_dst_matched;
|
||||
dst_match->m_valid_rgb = preserve_rgb;
|
||||
|
||||
if (GSLocalMemory::m_psm[dst->m_TEX0.PSM].bpp == 16 && GSLocalMemory::m_psm[dst_match->m_TEX0.PSM].bpp > 16)
|
||||
@@ -3285,7 +3321,6 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
|
||||
{
|
||||
dst_match->UnscaleRTAlpha();
|
||||
g_gs_device->StretchRect(dst_match->m_texture, sRect, dst->m_texture, dRect, shader, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3981,7 +4016,6 @@ void GSTextureCache::Target::ScaleRTAlpha()
|
||||
const GSVector4 dRect(m_texture->GetRect().rintersect(valid_rect));
|
||||
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
|
||||
g_gs_device->StretchRect(m_texture, sRect, temp_rt, dRect, ShaderConvert::RTA_CORRECTION, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
g_gs_device->Recycle(m_texture);
|
||||
m_texture = temp_rt;
|
||||
}
|
||||
@@ -4007,7 +4041,6 @@ void GSTextureCache::Target::UnscaleRTAlpha()
|
||||
const GSVector4 dRect(m_texture->GetRect().rintersect(valid_rect));
|
||||
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
|
||||
g_gs_device->StretchRect(m_texture, sRect, temp_rt, dRect, ShaderConvert::RTA_DECORRECTION, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
g_gs_device->Recycle(m_texture);
|
||||
m_texture = temp_rt;
|
||||
}
|
||||
@@ -4067,7 +4100,6 @@ void GSTextureCache::ScaleTargetForDisplay(Target* t, const GIFRegTEX0& dispfb,
|
||||
|
||||
// Fill the new texture with the old data, and discard the old texture.
|
||||
g_gs_device->StretchRect(old_texture, new_texture, GSVector4(old_texture->GetSize()).zwxy(), ShaderConvert::COPY, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
m_target_memory_usage = (m_target_memory_usage - old_texture->GetMemUsage()) + new_texture->GetMemUsage();
|
||||
g_gs_device->Recycle(old_texture);
|
||||
t->m_texture = new_texture;
|
||||
@@ -4201,7 +4233,6 @@ bool GSTextureCache::CopyRGBFromDepthToColor(Target* dst, Target* depth_src)
|
||||
const GSVector4 convert_rect = GSVector4(depth_src->GetUnscaledRect().rintersect(GSVector4i::loadh(new_size)));
|
||||
g_gs_device->StretchRect(depth_src->m_texture, convert_rect / GSVector4(depth_src->GetUnscaledSize()).xyxy(),
|
||||
tex, convert_rect * GSVector4(dst->GetScale()), shader, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
}
|
||||
|
||||
// Copy in alpha if we're a new texture.
|
||||
@@ -4212,7 +4243,6 @@ bool GSTextureCache::CopyRGBFromDepthToColor(Target* dst, Target* depth_src)
|
||||
const GSVector4 copy_rect = GSVector4(tex->GetRect().rintersect(dst->m_texture->GetRect()));
|
||||
g_gs_device->StretchRect(dst->m_texture, copy_rect / GSVector4(GSVector4i(dst->m_texture->GetSize()).xyxy()), tex,
|
||||
copy_rect, false, false, false, true);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
}
|
||||
|
||||
g_gs_device->Recycle(dst->m_texture);
|
||||
@@ -4377,6 +4407,7 @@ void GSTextureCache::InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 wr
|
||||
t->m_valid_alpha_low &= preserve_alpha;
|
||||
t->m_valid_alpha_high &= preserve_alpha;
|
||||
t->m_valid_rgb &= (fb_mask & 0x00FFFFFF) != 0;
|
||||
t->m_was_dst_matched = false;
|
||||
|
||||
// Don't keep partial depth buffers around.
|
||||
if ((!t->m_valid_alpha_low && !t->m_valid_alpha_high && !t->m_valid_rgb) || type == DepthStencil)
|
||||
@@ -4387,7 +4418,22 @@ void GSTextureCache::InvalidateContainedTargets(u32 start_bp, u32 end_bp, u32 wr
|
||||
Target* const rev_t = *j;
|
||||
if (rev_t->m_TEX0.TBP0 == t->m_TEX0.TBP0 && GSLocalMemory::m_psm[rev_t->m_TEX0.PSM].bpp == GSLocalMemory::m_psm[t->m_TEX0.PSM].bpp)
|
||||
{
|
||||
rev_t->m_was_dst_matched = false;
|
||||
if (t->m_last_draw > rev_t->m_last_draw && GSUtil::GetChannelMask(t->m_TEX0.PSM) == GSUtil::GetChannelMask(rev_t->m_TEX0.PSM))
|
||||
{
|
||||
if (type == DepthStencil && GSUtil::GetChannelMask(t->m_TEX0.PSM) == 0x7)
|
||||
{
|
||||
rev_t->m_valid_rgb = false;
|
||||
rev_t->m_was_dst_matched = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
GL_CACHE("TC: InvalidateContainedTargets: Remove Target %s[%x, %s]", to_string(1 - type), rev_t->m_TEX0.TBP0, GSUtil::GetPSMName(rev_t->m_TEX0.PSM));
|
||||
rev_list.erase(j);
|
||||
delete rev_t;
|
||||
}
|
||||
}
|
||||
else
|
||||
rev_t->m_was_dst_matched = false;
|
||||
break;
|
||||
}
|
||||
++j;
|
||||
@@ -5234,7 +5280,6 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
|
||||
const GSVector4 src_rect = GSVector4(scaled_sx, scaled_sy, scaled_sx + scaled_w, scaled_sy + scaled_h);
|
||||
const GSVector4 tmp_rect = src_rect / (GSVector4(tmp_texture->GetSize()).xyxy());
|
||||
const GSVector4 dst_rect = GSVector4(scaled_dx, scaled_dy, (scaled_dx + scaled_w), (scaled_dy + scaled_h));
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 2);
|
||||
g_gs_device->StretchRect(src->m_texture, tmp_rect, tmp_texture, src_rect, ShaderConvert::DEPTH_COPY, false);
|
||||
g_gs_device->StretchRect(tmp_texture, tmp_rect, dst->m_texture, dst_rect, ShaderConvert::DEPTH_COPY, false);
|
||||
}
|
||||
@@ -5245,7 +5290,6 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
|
||||
const GSVector4 src_rect = GSVector4(scaled_sx, scaled_sy, scaled_sx + scaled_w, scaled_sy + scaled_h);
|
||||
const GSVector4 tmp_rect = src_rect / (GSVector4(tmp_texture->GetSize()).xyxy());
|
||||
const GSVector4 dst_rect = GSVector4(scaled_dx, scaled_dy, (scaled_dx + scaled_w), (scaled_dy + scaled_h));
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 2);
|
||||
g_gs_device->StretchRect(src->m_texture, tmp_rect, tmp_texture, src_rect, false, false, false, true);
|
||||
g_gs_device->StretchRect(tmp_texture, tmp_rect, dst->m_texture, dst_rect, false, false, false, true);
|
||||
}
|
||||
@@ -5273,7 +5317,6 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
|
||||
{
|
||||
const ShaderConvert shader = ShaderConvert::COPY;
|
||||
const GSVector4 dst_rect = GSVector4(scaled_dx, scaled_dy, (scaled_dx + scaled_w), (scaled_dy + scaled_h));
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
g_gs_device->StretchRect(src->m_texture, src_rect, dst->m_texture, dst_rect, false, false, false, true, shader);
|
||||
}
|
||||
else if (src->m_type != dst->m_type)
|
||||
@@ -5291,13 +5334,11 @@ bool GSTextureCache::Move(u32 SBP, u32 SBW, u32 SPSM, int sx, int sy, u32 DBP, u
|
||||
default:
|
||||
break;
|
||||
}
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
g_gs_device->StretchRect(src->m_texture, src_rect, dst->m_texture, scaled_src_rect, shader, false);
|
||||
}
|
||||
else if (src->m_texture->IsDepthStencil())
|
||||
{
|
||||
const GSVector4 dst_rect = GSVector4(scaled_dx, scaled_dy, (scaled_dx + scaled_w), (scaled_dy + scaled_h));
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
g_gs_device->StretchRect(src->m_texture, src_rect, dst->m_texture, dst_rect, ShaderConvert::DEPTH_COPY, false);
|
||||
}
|
||||
else
|
||||
@@ -6239,7 +6280,6 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||
{
|
||||
if (dst->m_rt_alpha_scale)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
const GSVector4 sRectF = GSVector4(sRect) / GSVector4(1, 1, sTex->GetWidth(), sTex->GetHeight());
|
||||
g_gs_device->StretchRect(
|
||||
sTex, sRectF, dTex, GSVector4(destX, destY, sRect.width(), sRect.height()), ShaderConvert::RTA_DECORRECTION, false);
|
||||
@@ -6291,8 +6331,6 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
|
||||
sTex, sRectF, dTex, GSVector4(destX, destY, new_size.x, new_size.y), shader, false);
|
||||
}
|
||||
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
if (GSConfig.UseDebugDevice)
|
||||
{
|
||||
@@ -6742,7 +6780,6 @@ GSTextureCache::Source* GSTextureCache::CreateMergedSource(GIFRegTEX0 TEX0, GIFR
|
||||
// Sort rect list by the texture, we want to batch as many as possible together.
|
||||
g_gs_device->SortMultiStretchRects(copy_queue, copy_count);
|
||||
g_gs_device->DrawMultiStretchRects(copy_queue, copy_count, dtex, ShaderConvert::COPY);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
if (lmtex)
|
||||
g_gs_device->Recycle(lmtex);
|
||||
@@ -7033,7 +7070,6 @@ GSTexture* GSTextureCache::LookupPaletteSource(u32 CBP, u32 CPSM, u32 CBW, GSVec
|
||||
return nullptr;
|
||||
|
||||
g_gs_device->StretchRect(t->m_texture, GSVector4(0, 0, 1, 1), tex, GSVector4(GSVector4i::loadh(t->m_unscaled_size)), (t->m_type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
m_target_memory_usage = (m_target_memory_usage - t->m_texture->GetMemUsage()) + tex->GetMemUsage();
|
||||
g_gs_device->Recycle(t->m_texture);
|
||||
|
||||
@@ -7181,7 +7217,6 @@ void GSTextureCache::Read(Target* t, const GSVector4i& r)
|
||||
if (tmp)
|
||||
{
|
||||
g_gs_device->StretchRect(t->m_texture, src, tmp, GSVector4(drc), ps_shader, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
dltex->get()->CopyFromTexture(drc, tmp, drc, 0, true);
|
||||
g_gs_device->Recycle(tmp);
|
||||
}
|
||||
@@ -7850,7 +7885,6 @@ void GSTextureCache::Target::Update(bool cannot_scale)
|
||||
//GL_CACHE("TC: RT in RT Updating Z copy on draw %d z_offset %d", s_n, z_address_info.offset);
|
||||
const GSVector4i dRect = GSVector4i(total_rect.x * m_scale, (z_address_info.offset + total_rect.y) * m_scale, (total_rect.z + (1.0f / m_scale)) * m_scale, (z_address_info.offset + total_rect.w + (1.0f / m_scale)) * m_scale);
|
||||
g_gs_device->StretchRect(m_texture, GSVector4(total_rect.x / static_cast<float>(m_unscaled_size.x), total_rect.y / static_cast<float>(m_unscaled_size.y), (total_rect.z + (1.0f / m_scale)) / static_cast<float>(m_unscaled_size.x), (total_rect.w + (1.0f / m_scale)) / static_cast<float>(m_unscaled_size.y)), g_texture_cache->GetTemporaryZ(), GSVector4(dRect), ShaderConvert::DEPTH_COPY, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8000,14 +8034,12 @@ bool GSTextureCache::Target::ResizeTexture(int new_unscaled_width, int new_unsca
|
||||
{
|
||||
// Can't do partial copies in DirectX for depth textures, and it's probably not ideal in other
|
||||
// APIs either. So use a fullscreen quad setting depth instead.
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
g_gs_device->StretchRect(m_texture, tex, GSVector4(rc), ShaderConvert::DEPTH_COPY, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (require_new_rect)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
g_gs_device->StretchRect(m_texture, tex, GSVector4(rc), ShaderConvert::COPY, false);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1587,6 +1587,7 @@ void GSDeviceMTL::DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect
|
||||
[m_current_render.encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip
|
||||
vertexStart:0
|
||||
vertexCount:4];
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
|
||||
}
|
||||
|
||||
@@ -1597,6 +1598,7 @@ void GSDeviceMTL::RenderCopy(GSTexture* sTex, id<MTLRenderPipelineState> pipelin
|
||||
MRESetPipeline(pipeline);
|
||||
MRESetTexture(sTex, GSMTLTextureIndexNonHW);
|
||||
[m_current_render.encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
g_perfmon.Put(GSPerfMon::DrawCalls, 1);
|
||||
}
|
||||
|
||||
@@ -2164,7 +2166,6 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
|
||||
{
|
||||
BeginRenderPass(@"ColorClip Resolve", config.rt, MTLLoadActionLoad, nullptr, MTLLoadActionDontCare);
|
||||
RenderCopy(colclip_rt, m_colclip_resolve_pipeline, config.colclip_update_area);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
Recycle(colclip_rt);
|
||||
|
||||
@@ -2194,7 +2195,6 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
|
||||
case GSTexture::State::Dirty:
|
||||
BeginRenderPass(@"ColorClip Init", colclip_rt, MTLLoadActionDontCare, nullptr, MTLLoadActionDontCare);
|
||||
RenderCopy(config.rt, m_colclip_init_pipeline, copy_rect);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
break;
|
||||
|
||||
case GSTexture::State::Cleared:
|
||||
@@ -2306,7 +2306,6 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
|
||||
{
|
||||
BeginRenderPass(@"ColorClip Resolve", config.rt, MTLLoadActionLoad, nullptr, MTLLoadActionDontCare);
|
||||
RenderCopy(colclip_rt, m_colclip_resolve_pipeline, config.colclip_update_area);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
Recycle(colclip_rt);
|
||||
|
||||
|
||||
@@ -1301,11 +1301,11 @@ std::string GSDeviceOGL::GenGlslHeader(const std::string_view entry, GLenum type
|
||||
if (GLAD_GL_ARB_conservative_depth)
|
||||
{
|
||||
header += "#extension GL_ARB_conservative_depth : enable\n";
|
||||
header += "#define HAS_CONSERVATIVE_DEPTH 1\n";
|
||||
header += "#define PS_HAS_CONSERVATIVE_DEPTH 1\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
header += "#define HAS_CONSERVATIVE_DEPTH 0\n";
|
||||
header += "#define PS_HAS_CONSERVATIVE_DEPTH 0\n";
|
||||
}
|
||||
|
||||
// Allow to puts several shader in 1 files
|
||||
@@ -1423,10 +1423,11 @@ std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
|
||||
// Copy a sub part of texture (same as below but force a conversion)
|
||||
void GSDeviceOGL::BlitRect(GSTexture* sTex, const GSVector4i& r, const GSVector2i& dsize, bool at_origin, bool linear)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
CommitClear(sTex, true);
|
||||
|
||||
GL_PUSH(fmt::format("CopyRectConv from {}", static_cast<GSTextureOGL*>(sTex)->GetID()).c_str());
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
// NOTE: This previously used glCopyTextureSubImage2D(), but this appears to leak memory in
|
||||
// the loading screens of Evolution Snowboarding in Intel/NVIDIA drivers.
|
||||
@@ -1657,6 +1658,8 @@ void GSDeviceOGL::FilteredDownsampleTexture(GSTexture* sTex, GSTexture* dTex, u3
|
||||
|
||||
void GSDeviceOGL::DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
// Original code from DX
|
||||
const float left = dRect.x * 2 / ds.x - 1.0f;
|
||||
const float right = dRect.z * 2 / ds.x - 1.0f;
|
||||
@@ -1727,6 +1730,8 @@ void GSDeviceOGL::DrawMultiStretchRects(
|
||||
|
||||
void GSDeviceOGL::DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, const GSVector2& ds)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
const u32 vertex_reserve_size = num_rects * 4 * sizeof(GSVertexPT1);
|
||||
const u32 index_reserve_size = num_rects * 6 * sizeof(u16);
|
||||
auto vertex_map = m_vertex_stream_buffer->Map(sizeof(GSVertexPT1), vertex_reserve_size);
|
||||
@@ -1922,6 +1927,8 @@ void GSDeviceOGL::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float par
|
||||
|
||||
void GSDeviceOGL::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSVector4i& bbox)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
GL_PUSH("DATE First Pass");
|
||||
|
||||
// sfex3 (after the capcom logo), vf4 (first menu fading in), ffxii shadows, rumble roses shadows, persona4 shadows
|
||||
@@ -2458,7 +2465,6 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
||||
const GSVector4 dRect(config.colclip_update_area);
|
||||
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
|
||||
StretchRect(colclip_rt, sRect, config.rt, dRect, ShaderConvert::COLCLIP_RESOLVE, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
Recycle(colclip_rt);
|
||||
|
||||
g_gs_device->SetColorClipTexture(nullptr);
|
||||
@@ -2493,7 +2499,6 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
||||
const GSVector4 dRect = GSVector4((config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
|
||||
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
|
||||
StretchRect(config.rt, sRect, colclip_rt, dRect, ShaderConvert::COLCLIP_INIT, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2762,7 +2767,6 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
|
||||
const GSVector4 dRect(config.colclip_update_area);
|
||||
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
|
||||
StretchRect(colclip_rt, sRect, config.rt, dRect, ShaderConvert::COLCLIP_RESOLVE, false);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
Recycle(colclip_rt);
|
||||
|
||||
g_gs_device->SetColorClipTexture(nullptr);
|
||||
|
||||
@@ -629,9 +629,8 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT};
|
||||
VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT attachment_feedback_loop_feature = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_FEATURES_EXT};
|
||||
VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT swapchain_maintenance1_ext_feature = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT};
|
||||
VkPhysicalDeviceSwapchainMaintenance1FeaturesKHR swapchain_maintenance1_khr_feature = {
|
||||
// VK_EXT_swapchain_maintenance1 types/enums are aliases of VK_KHR_swapchain_maintenance1 types/enums.
|
||||
VkPhysicalDeviceSwapchainMaintenance1FeaturesKHR swapchain_maintenance1_feature = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_KHR};
|
||||
|
||||
if (m_optional_extensions.vk_ext_provoking_vertex)
|
||||
@@ -656,16 +655,8 @@ bool GSDeviceVK::CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer
|
||||
}
|
||||
if (m_optional_extensions.vk_swapchain_maintenance1)
|
||||
{
|
||||
if (m_optional_extensions.vk_swapchain_maintenance1_is_khr)
|
||||
{
|
||||
swapchain_maintenance1_khr_feature.swapchainMaintenance1 = VK_TRUE;
|
||||
Vulkan::AddPointerToChain(&device_info, &swapchain_maintenance1_khr_feature);
|
||||
}
|
||||
else
|
||||
{
|
||||
swapchain_maintenance1_ext_feature.swapchainMaintenance1 = VK_TRUE;
|
||||
Vulkan::AddPointerToChain(&device_info, &swapchain_maintenance1_ext_feature);
|
||||
}
|
||||
swapchain_maintenance1_feature.swapchainMaintenance1 = VK_TRUE;
|
||||
Vulkan::AddPointerToChain(&device_info, &swapchain_maintenance1_feature);
|
||||
}
|
||||
|
||||
VkResult res = vkCreateDevice(m_physical_device, &device_info, nullptr, &m_device);
|
||||
@@ -732,9 +723,8 @@ bool GSDeviceVK::ProcessDeviceExtensions()
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT};
|
||||
VkPhysicalDeviceRasterizationOrderAttachmentAccessFeaturesEXT rasterization_order_access_feature = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_FEATURES_EXT};
|
||||
VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT swapchain_maintenance1_ext_feature = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT, nullptr, VK_TRUE};
|
||||
VkPhysicalDeviceSwapchainMaintenance1FeaturesKHR swapchain_maintenance1_khr_feature = {
|
||||
// VK_EXT_swapchain_maintenance1 types/enums are aliases of VK_KHR_swapchain_maintenance1 types/enums.
|
||||
VkPhysicalDeviceSwapchainMaintenance1FeaturesKHR swapchain_maintenance1_feature = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_KHR, nullptr, VK_TRUE};
|
||||
VkPhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT attachment_feedback_loop_feature = {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_FEATURES_EXT};
|
||||
@@ -748,10 +738,8 @@ bool GSDeviceVK::ProcessDeviceExtensions()
|
||||
Vulkan::AddPointerToChain(&features2, &rasterization_order_access_feature);
|
||||
if (m_optional_extensions.vk_ext_attachment_feedback_loop_layout)
|
||||
Vulkan::AddPointerToChain(&features2, &attachment_feedback_loop_feature);
|
||||
if (m_optional_extensions.vk_swapchain_maintenance1 && m_optional_extensions.vk_swapchain_maintenance1_is_khr)
|
||||
Vulkan::AddPointerToChain(&features2, &swapchain_maintenance1_khr_feature);
|
||||
if (m_optional_extensions.vk_swapchain_maintenance1 && !m_optional_extensions.vk_swapchain_maintenance1_is_khr)
|
||||
Vulkan::AddPointerToChain(&features2, &swapchain_maintenance1_ext_feature);
|
||||
if (m_optional_extensions.vk_swapchain_maintenance1)
|
||||
Vulkan::AddPointerToChain(&features2, &swapchain_maintenance1_feature);
|
||||
|
||||
// query
|
||||
vkGetPhysicalDeviceFeatures2(m_physical_device, &features2);
|
||||
@@ -826,9 +814,8 @@ bool GSDeviceVK::ProcessDeviceExtensions()
|
||||
m_optional_extensions.vk_ext_calibrated_timestamps = false;
|
||||
}
|
||||
|
||||
m_optional_extensions.vk_swapchain_maintenance1 &= m_optional_extensions.vk_swapchain_maintenance1_is_khr ?
|
||||
(swapchain_maintenance1_khr_feature.swapchainMaintenance1 == VK_TRUE) :
|
||||
(swapchain_maintenance1_ext_feature.swapchainMaintenance1 == VK_TRUE);
|
||||
m_optional_extensions.vk_swapchain_maintenance1 &=
|
||||
(swapchain_maintenance1_feature.swapchainMaintenance1 == VK_TRUE);
|
||||
|
||||
Console.WriteLn(
|
||||
"VK_EXT_provoking_vertex is %s", m_optional_extensions.vk_ext_provoking_vertex ? "supported" : "NOT supported");
|
||||
@@ -2929,6 +2916,8 @@ void GSDeviceVK::DrawMultiStretchRects(
|
||||
void GSDeviceVK::DoMultiStretchRects(
|
||||
const MultiStretchRect* rects, u32 num_rects, GSTextureVK* dTex, ShaderConvert shader)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
// Set up vertices first.
|
||||
const u32 vertex_reserve_size = num_rects * 4 * sizeof(GSVertexPT1);
|
||||
const u32 index_reserve_size = num_rects * 6 * sizeof(u16);
|
||||
@@ -3082,6 +3071,8 @@ void GSDeviceVK::DoStretchRect(GSTextureVK* sTex, const GSVector4& sRect, GSText
|
||||
|
||||
void GSDeviceVK::DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
// ia
|
||||
const float left = dRect.x * 2 / ds.x - 1.0f;
|
||||
const float top = 1.0f - dRect.y * 2 / ds.y;
|
||||
@@ -5468,6 +5459,8 @@ void GSDeviceVK::SetPSConstantBuffer(const GSHWDrawConfig::PSConstantBuffer& cb)
|
||||
|
||||
void GSDeviceVK::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSVector4i& bbox)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
GL_PUSH("SetupDATE {%d,%d} %dx%d", bbox.left, bbox.top, bbox.width(), bbox.height());
|
||||
|
||||
const GSVector2i size(ds->GetSize());
|
||||
@@ -5495,6 +5488,8 @@ void GSDeviceVK::SetupDATE(GSTexture* rt, GSTexture* ds, SetDATM datm, const GSV
|
||||
|
||||
GSTextureVK* GSDeviceVK::SetupPrimitiveTrackingDATE(GSHWDrawConfig& config)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
// How this is done:
|
||||
// - can't put a barrier for the image in the middle of the normal render pass, so that's out
|
||||
// - so, instead of just filling the int texture with INT_MAX, we sample the RT and use -1 for failing values
|
||||
@@ -5676,7 +5671,6 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
||||
SetPipeline(m_colclip_finish_pipelines[pipe.ds][pipe.IsRTFeedbackLoop()]);
|
||||
SetUtilityTexture(colclip_rt, m_point_sampler);
|
||||
DrawStretchRect(sRect, drawareaf, rtsize);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
Recycle(colclip_rt);
|
||||
g_gs_device->SetColorClipTexture(nullptr);
|
||||
@@ -5810,26 +5804,11 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
||||
Console.Warning("VK: Failed to allocate temp texture for RT copy.");
|
||||
}
|
||||
|
||||
// We don't need the very first barrier if this is the first draw after switching to feedback loop,
|
||||
// because the layout change in itself enforces the execution dependency. colclip hw needs a barrier between
|
||||
// setup and the first draw to read it. TODO: Make colclip hw use subpasses instead.
|
||||
|
||||
// However, it turns out *not* doing this causes GPU resets on RDNA3, specifically Windows drivers.
|
||||
// Despite the layout changing enforcing the execution dependency between previous draws and the first
|
||||
// input attachment read, it still wants the region/fragment-local barrier...
|
||||
|
||||
const bool skip_first_barrier =
|
||||
(draw_rt && draw_rt->GetLayout() != GSTextureVK::Layout::FeedbackLoop && !pipe.ps.colclip_hw && !IsDeviceAMD());
|
||||
|
||||
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));
|
||||
if (pipe.IsRTFeedbackLoop())
|
||||
{
|
||||
pxAssertMsg(m_features.texture_barrier, "Texture barriers enabled");
|
||||
PSSetShaderResource(2, draw_rt, false);
|
||||
|
||||
// If this is the first draw to the target as a feedback loop, make sure we re-generate the texture descriptor.
|
||||
// Otherwise, we might have a previous descriptor left over, that has the RT in a different state.
|
||||
m_dirty_flags |= (skip_first_barrier ? static_cast<u32>(DIRTY_FLAG_TFX_TEXTURE_RT) : 0);
|
||||
}
|
||||
|
||||
// Begin render pass if new target or out of the area.
|
||||
@@ -5893,7 +5872,6 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
||||
const GSVector4 drawareaf = GSVector4((config.colclip_mode == GSHWDrawConfig::ColClipMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
|
||||
const GSVector4 sRect(drawareaf / GSVector4(rtsize).xyxy());
|
||||
DrawStretchRect(sRect, drawareaf, rtsize);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
GL_POP();
|
||||
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));
|
||||
@@ -5905,7 +5883,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
||||
|
||||
// now we can do the actual draw
|
||||
if (BindDrawPipeline(pipe))
|
||||
SendHWDraw(config, draw_rt, config.require_one_barrier, config.require_full_barrier, skip_first_barrier);
|
||||
SendHWDraw(config, draw_rt, config.require_one_barrier, config.require_full_barrier);
|
||||
|
||||
// blend second pass
|
||||
if (config.blend_multi_pass.enable)
|
||||
@@ -5941,7 +5919,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
||||
if (BindDrawPipeline(pipe))
|
||||
{
|
||||
SendHWDraw(config, draw_rt, config.alpha_second_pass.require_one_barrier,
|
||||
config.alpha_second_pass.require_full_barrier, false);
|
||||
config.alpha_second_pass.require_full_barrier);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5995,7 +5973,6 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
||||
SetPipeline(m_colclip_finish_pipelines[pipe.ds][pipe.IsRTFeedbackLoop()]);
|
||||
SetUtilityTexture(colclip_rt, m_point_sampler);
|
||||
DrawStretchRect(sRect, drawareaf, rtsize);
|
||||
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
|
||||
|
||||
Recycle(colclip_rt);
|
||||
g_gs_device->SetColorClipTexture(nullptr);
|
||||
@@ -6065,7 +6042,7 @@ VkDependencyFlags GSDeviceVK::GetColorBufferBarrierFlags() const
|
||||
}
|
||||
|
||||
void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt,
|
||||
bool one_barrier, bool full_barrier, bool skip_first_barrier)
|
||||
bool one_barrier, bool full_barrier)
|
||||
{
|
||||
if (!m_features.texture_barrier) [[unlikely]]
|
||||
{
|
||||
@@ -6087,21 +6064,9 @@ void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt,
|
||||
const u32 draw_list_size = static_cast<u32>(config.drawlist->size());
|
||||
|
||||
GL_PUSH("Split the draw");
|
||||
g_perfmon.Put(
|
||||
GSPerfMon::Barriers, static_cast<u32>(draw_list_size) - static_cast<u32>(skip_first_barrier));
|
||||
g_perfmon.Put(GSPerfMon::Barriers, static_cast<u32>(draw_list_size));
|
||||
|
||||
u32 p = 0;
|
||||
u32 n = 0;
|
||||
|
||||
if (skip_first_barrier)
|
||||
{
|
||||
const u32 count = (*config.drawlist)[n] * indices_per_prim;
|
||||
DrawIndexedPrimitive(p, count);
|
||||
p += count;
|
||||
++n;
|
||||
}
|
||||
|
||||
for (; n < draw_list_size; n++)
|
||||
for (u32 n = 0, p = 0; n < draw_list_size; n++)
|
||||
{
|
||||
vkCmdPipelineBarrier(GetCurrentCommandBuffer(), VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, barrier_flags, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
@@ -6114,7 +6079,7 @@ void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt,
|
||||
return;
|
||||
}
|
||||
|
||||
if (one_barrier && !skip_first_barrier)
|
||||
if (one_barrier)
|
||||
{
|
||||
g_perfmon.Put(GSPerfMon::Barriers, 1);
|
||||
|
||||
|
||||
@@ -574,7 +574,7 @@ public:
|
||||
VkImageMemoryBarrier GetColorBufferBarrier(GSTextureVK* rt) const;
|
||||
VkDependencyFlags GetColorBufferBarrierFlags() const;
|
||||
void SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt,
|
||||
bool one_barrier, bool full_barrier, bool skip_first_barrier);
|
||||
bool one_barrier, bool full_barrier);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Vulkan State
|
||||
|
||||
@@ -379,8 +379,12 @@ bool VKSwapChain::CreateSwapChain()
|
||||
|
||||
m_swap_chain = VK_NULL_HANDLE;
|
||||
|
||||
// VK_EXT_swapchain_maintenance1 types/enums are aliases of VK_KHR_swapchain_maintenance1 types/enums.
|
||||
const VkSwapchainPresentModesCreateInfoKHR modes_info{VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODES_CREATE_INFO_KHR, nullptr, 1u, &m_present_mode};
|
||||
|
||||
// Now we can actually create the swap chain
|
||||
VkSwapchainCreateInfoKHR swap_chain_info = {VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, nullptr, 0, m_surface,
|
||||
VkSwapchainCreateInfoKHR swap_chain_info = {VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
||||
GSDeviceVK::GetInstance()->GetOptionalExtensions().vk_swapchain_maintenance1 ? &modes_info : nullptr, 0, m_surface,
|
||||
image_count, surface_format->format, surface_format->colorSpace, size, 1u, image_usage,
|
||||
VK_SHARING_MODE_EXCLUSIVE, 0, nullptr, transform, alpha, m_present_mode, VK_TRUE, old_swap_chain};
|
||||
std::array<uint32_t, 2> indices = {{
|
||||
@@ -563,23 +567,17 @@ void VKSwapChain::ReleaseCurrentImage()
|
||||
{
|
||||
GSDeviceVK::GetInstance()->WaitForGPUIdle();
|
||||
|
||||
// VK_EXT_swapchain_maintenance1 types/enums are aliases of VK_KHR_swapchain_maintenance1 types/enums.
|
||||
const VkReleaseSwapchainImagesInfoKHR info = {.sType = VK_STRUCTURE_TYPE_RELEASE_SWAPCHAIN_IMAGES_INFO_KHR,
|
||||
.swapchain = m_swap_chain,
|
||||
.imageIndexCount = 1,
|
||||
.pImageIndices = &m_current_image};
|
||||
|
||||
VkResult res;
|
||||
if (GSDeviceVK::GetInstance()->GetOptionalExtensions().vk_swapchain_maintenance1_is_khr)
|
||||
{
|
||||
const VkReleaseSwapchainImagesInfoKHR info = {.sType = VK_STRUCTURE_TYPE_RELEASE_SWAPCHAIN_IMAGES_INFO_KHR,
|
||||
.swapchain = m_swap_chain,
|
||||
.imageIndexCount = 1,
|
||||
.pImageIndices = &m_current_image};
|
||||
res = vkReleaseSwapchainImagesKHR(GSDeviceVK::GetInstance()->GetDevice(), &info);
|
||||
}
|
||||
else
|
||||
{
|
||||
const VkReleaseSwapchainImagesInfoEXT info = {.sType = VK_STRUCTURE_TYPE_RELEASE_SWAPCHAIN_IMAGES_INFO_EXT,
|
||||
.swapchain = m_swap_chain,
|
||||
.imageIndexCount = 1,
|
||||
.pImageIndices = &m_current_image};
|
||||
res = vkReleaseSwapchainImagesEXT(GSDeviceVK::GetInstance()->GetDevice(), &info);
|
||||
}
|
||||
if (res != VK_SUCCESS)
|
||||
LOG_VULKAN_ERROR(res, "vkReleaseSwapchainImages() failed: ");
|
||||
}
|
||||
|
||||
@@ -380,7 +380,6 @@ static const char* s_gs_hw_fix_names[] = {
|
||||
"trilinearFiltering",
|
||||
"skipDrawStart",
|
||||
"skipDrawEnd",
|
||||
"halfBottomOverride",
|
||||
"halfPixelOffset",
|
||||
"roundSprite",
|
||||
"nativeScaling",
|
||||
|
||||
@@ -65,7 +65,6 @@ namespace GameDatabaseSchema
|
||||
TrilinearFiltering,
|
||||
SkipDrawStart,
|
||||
SkipDrawEnd,
|
||||
HalfBottomOverride,
|
||||
HalfPixelOffset,
|
||||
RoundSprite,
|
||||
NativeScaling,
|
||||
|
||||
@@ -314,6 +314,13 @@ void ipuSoftReset()
|
||||
g_ipu_thresh[0] = 0;
|
||||
g_ipu_thresh[1] = 0;
|
||||
|
||||
if (!decoder.intra_dc_precision)
|
||||
{
|
||||
decoder.dc_dct_pred[0] =
|
||||
decoder.dc_dct_pred[1] =
|
||||
decoder.dc_dct_pred[2] = 128 << decoder.intra_dc_precision;
|
||||
}
|
||||
|
||||
ipuRegs.ctrl.reset();
|
||||
ipuRegs.top = 0;
|
||||
ipu_cmd.clear();
|
||||
|
||||
+9
-6221
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,496 @@
|
||||
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FullscreenUI.h"
|
||||
#include "ImGuiFullscreen.h"
|
||||
|
||||
#include "common/Timer.h"
|
||||
#include "Input/InputManager.h"
|
||||
|
||||
#define TR_CONTEXT "FullscreenUI"
|
||||
|
||||
template <size_t L>
|
||||
class IconStackString : public SmallStackString<L>
|
||||
{
|
||||
public:
|
||||
__fi IconStackString(const char* icon, const char* str)
|
||||
{
|
||||
SmallStackString<L>::format("{} {}", icon, Host::TranslateToStringView(TR_CONTEXT, str));
|
||||
}
|
||||
__fi IconStackString(const char8_t* icon, const char* str)
|
||||
{
|
||||
SmallStackString<L>::format("{} {}", reinterpret_cast<const char*>(icon), Host::TranslateToStringView(TR_CONTEXT, str));
|
||||
}
|
||||
__fi IconStackString(const char* icon, const char* str, const char* suffix)
|
||||
{
|
||||
SmallStackString<L>::format("{} {}##{}", icon, Host::TranslateToStringView(TR_CONTEXT, str), suffix);
|
||||
}
|
||||
__fi IconStackString(const char8_t* icon, const char* str, const char* suffix)
|
||||
{
|
||||
SmallStackString<L>::format("{} {}##{}", reinterpret_cast<const char*>(icon), Host::TranslateToStringView(TR_CONTEXT, str), suffix);
|
||||
}
|
||||
};
|
||||
|
||||
#define FSUI_ICONSTR(icon, str) IconStackString<256>(icon, str).c_str()
|
||||
#define FSUI_ICONSTR_S(icon, str, suffix) IconStackString<256>(icon, str, suffix).c_str()
|
||||
#define FSUI_STR(str) Host::TranslateToString(TR_CONTEXT, str)
|
||||
#define FSUI_CSTR(str) Host::TranslateToCString(TR_CONTEXT, str)
|
||||
#define FSUI_VSTR(str) Host::TranslateToStringView(TR_CONTEXT, str)
|
||||
#define FSUI_FSTR(str) fmt::runtime(Host::TranslateToStringView(TR_CONTEXT, str))
|
||||
#define FSUI_NSTR(str) str
|
||||
|
||||
using ImGuiFullscreen::ActiveButton;
|
||||
using ImGuiFullscreen::AddNotification;
|
||||
using ImGuiFullscreen::BeginFullscreenColumns;
|
||||
using ImGuiFullscreen::BeginFullscreenColumnWindow;
|
||||
using ImGuiFullscreen::BeginFullscreenWindow;
|
||||
using ImGuiFullscreen::BeginHorizontalMenu;
|
||||
using ImGuiFullscreen::BeginMenuButtons;
|
||||
using ImGuiFullscreen::BeginNavBar;
|
||||
using ImGuiFullscreen::CenterImage;
|
||||
using ImGuiFullscreen::CloseChoiceDialog;
|
||||
using ImGuiFullscreen::CloseFileSelector;
|
||||
using ImGuiFullscreen::EndFullscreenColumns;
|
||||
using ImGuiFullscreen::EndFullscreenColumnWindow;
|
||||
using ImGuiFullscreen::EndFullscreenWindow;
|
||||
using ImGuiFullscreen::EndHorizontalMenu;
|
||||
using ImGuiFullscreen::EndMenuButtons;
|
||||
using ImGuiFullscreen::EndNavBar;
|
||||
using ImGuiFullscreen::EnumChoiceButton;
|
||||
using ImGuiFullscreen::FloatingButton;
|
||||
using ImGuiFullscreen::FocusResetType;
|
||||
using ImGuiFullscreen::ForceKeyNavEnabled;
|
||||
using ImGuiFullscreen::g_large_font;
|
||||
using ImGuiFullscreen::g_layout_padding_left;
|
||||
using ImGuiFullscreen::g_layout_padding_top;
|
||||
using ImGuiFullscreen::g_medium_font;
|
||||
using ImGuiFullscreen::GetCachedSvgTexture;
|
||||
using ImGuiFullscreen::GetCachedSvgTextureAsync;
|
||||
using ImGuiFullscreen::GetCachedTexture;
|
||||
using ImGuiFullscreen::GetCachedTextureAsync;
|
||||
using ImGuiFullscreen::GetPlaceholderTexture;
|
||||
using ImGuiFullscreen::GetQueuedFocusResetType;
|
||||
using ImGuiFullscreen::HorizontalMenuItem;
|
||||
using ImGuiFullscreen::HorizontalMenuSvgItem;
|
||||
using ImGuiFullscreen::InputFilterType;
|
||||
using ImGuiFullscreen::IsFocusResetQueued;
|
||||
using ImGuiFullscreen::IsGamepadInputSource;
|
||||
using ImGuiFullscreen::LAYOUT_FOOTER_HEIGHT;
|
||||
using ImGuiFullscreen::LAYOUT_LARGE_FONT_SIZE;
|
||||
using ImGuiFullscreen::LAYOUT_MEDIUM_FONT_SIZE;
|
||||
using ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT;
|
||||
using ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY;
|
||||
using ImGuiFullscreen::LAYOUT_MENU_BUTTON_X_PADDING;
|
||||
using ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING;
|
||||
using ImGuiFullscreen::LAYOUT_SCREEN_HEIGHT;
|
||||
using ImGuiFullscreen::LAYOUT_SCREEN_WIDTH;
|
||||
using ImGuiFullscreen::LayoutScale;
|
||||
using ImGuiFullscreen::LoadSvgTexture;
|
||||
using ImGuiFullscreen::LoadTexture;
|
||||
using ImGuiFullscreen::MenuButton;
|
||||
using ImGuiFullscreen::MenuButtonFrame;
|
||||
using ImGuiFullscreen::MenuButtonWithoutSummary;
|
||||
using ImGuiFullscreen::MenuButtonWithValue;
|
||||
using ImGuiFullscreen::MenuHeading;
|
||||
using ImGuiFullscreen::MenuHeadingButton;
|
||||
using ImGuiFullscreen::MenuImageButton;
|
||||
using ImGuiFullscreen::ModAlpha;
|
||||
using ImGuiFullscreen::MulAlpha;
|
||||
using ImGuiFullscreen::NavButton;
|
||||
using ImGuiFullscreen::NavTitle;
|
||||
using ImGuiFullscreen::OpenChoiceDialog;
|
||||
using ImGuiFullscreen::OpenConfirmMessageDialog;
|
||||
using ImGuiFullscreen::OpenFileSelector;
|
||||
using ImGuiFullscreen::OpenInfoMessageDialog;
|
||||
using ImGuiFullscreen::OpenInputStringDialog;
|
||||
using ImGuiFullscreen::PopPrimaryColor;
|
||||
using ImGuiFullscreen::PushPrimaryColor;
|
||||
using ImGuiFullscreen::QueueResetFocus;
|
||||
using ImGuiFullscreen::ResetFocusHere;
|
||||
using ImGuiFullscreen::RightAlignNavButtons;
|
||||
using ImGuiFullscreen::SetFullscreenFooterText;
|
||||
using ImGuiFullscreen::ShowToast;
|
||||
using ImGuiFullscreen::SvgScaling;
|
||||
using ImGuiFullscreen::ThreeWayToggleButton;
|
||||
using ImGuiFullscreen::ToggleButton;
|
||||
using ImGuiFullscreen::UIBackgroundColor;
|
||||
using ImGuiFullscreen::UIBackgroundHighlightColor;
|
||||
using ImGuiFullscreen::UIBackgroundLineColor;
|
||||
using ImGuiFullscreen::UIBackgroundTextColor;
|
||||
using ImGuiFullscreen::UIDisabledColor;
|
||||
using ImGuiFullscreen::UIPopupBackgroundColor;
|
||||
using ImGuiFullscreen::UIPrimaryColor;
|
||||
using ImGuiFullscreen::UIPrimaryDarkColor;
|
||||
using ImGuiFullscreen::UIPrimaryLightColor;
|
||||
using ImGuiFullscreen::UIPrimaryLineColor;
|
||||
using ImGuiFullscreen::UIPrimaryTextColor;
|
||||
using ImGuiFullscreen::UISecondaryColor;
|
||||
using ImGuiFullscreen::UISecondaryStrongColor;
|
||||
using ImGuiFullscreen::UISecondaryTextColor;
|
||||
using ImGuiFullscreen::UISecondaryWeakColor;
|
||||
using ImGuiFullscreen::UITextHighlightColor;
|
||||
using ImGuiFullscreen::WantsToCloseMenu;
|
||||
|
||||
namespace FullscreenUI
|
||||
{
|
||||
enum class MainWindowType
|
||||
{
|
||||
None,
|
||||
Landing,
|
||||
StartGame,
|
||||
Exit,
|
||||
GameList,
|
||||
GameListSettings,
|
||||
Settings,
|
||||
PauseMenu,
|
||||
Achievements,
|
||||
Leaderboards,
|
||||
};
|
||||
|
||||
enum class PauseSubMenu
|
||||
{
|
||||
None,
|
||||
Exit,
|
||||
Achievements,
|
||||
};
|
||||
|
||||
enum class SettingsPage
|
||||
{
|
||||
Summary,
|
||||
Interface,
|
||||
BIOS,
|
||||
Emulation,
|
||||
Graphics,
|
||||
Audio,
|
||||
MemoryCard,
|
||||
NetworkHDD,
|
||||
Folders,
|
||||
Achievements,
|
||||
Controller,
|
||||
Hotkey,
|
||||
Advanced,
|
||||
Patches,
|
||||
Cheats,
|
||||
GameFixes,
|
||||
Count
|
||||
};
|
||||
|
||||
enum class GameListView
|
||||
{
|
||||
Grid,
|
||||
List,
|
||||
Count
|
||||
};
|
||||
|
||||
enum class IPAddressType
|
||||
{
|
||||
PS2IP,
|
||||
SubnetMask,
|
||||
Gateway,
|
||||
DNS1,
|
||||
DNS2,
|
||||
Other
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Main
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void UpdateGameDetails(std::string path, std::string serial, std::string title, u32 disc_crc, u32 crc);
|
||||
bool AreAnyDialogsOpen();
|
||||
void PauseForMenuOpen(bool set_pause_menu_open);
|
||||
void ClosePauseMenu();
|
||||
void OpenPauseSubMenu(PauseSubMenu submenu);
|
||||
void DrawLandingTemplate(ImVec2* menu_pos, ImVec2* menu_size);
|
||||
void DrawLandingWindow();
|
||||
void DrawStartGameWindow();
|
||||
void DrawExitWindow();
|
||||
void DrawPauseMenu(MainWindowType type);
|
||||
void ExitFullscreenAndOpenURL(const std::string_view url);
|
||||
void CopyTextToClipboard(std::string title, const std::string_view text);
|
||||
void DrawAboutWindow();
|
||||
void OpenAboutWindow();
|
||||
void GetStandardSelectionFooterText(SmallStringBase& dest, bool back_instead_of_cancel);
|
||||
void ApplyLayoutSettings(const SettingsInterface* bsi = nullptr);
|
||||
|
||||
void DrawSvgTexture(GSTexture* padded_texture, ImVec2 unpadded_size);
|
||||
void DrawCachedSvgTexture(const std::string& path, ImVec2 size, SvgScaling mode);
|
||||
void DrawCachedSvgTextureAsync(const std::string& path, ImVec2 size, SvgScaling mode);
|
||||
void DrawListSvgTexture(ImDrawList* drawList, GSTexture* padded_texture, const ImVec2& p_min, const ImVec2& p_unpadded_max);
|
||||
|
||||
inline MainWindowType s_current_main_window = MainWindowType::None;
|
||||
inline PauseSubMenu s_current_pause_submenu = PauseSubMenu::None;
|
||||
inline bool s_initialized = false;
|
||||
inline bool s_tried_to_initialize = false;
|
||||
inline bool s_pause_menu_was_open = false;
|
||||
inline bool s_was_paused_on_quick_menu_open = false;
|
||||
inline bool s_about_window_open = false;
|
||||
|
||||
// achievements login dialog state
|
||||
inline bool s_achievements_login_open = false;
|
||||
inline bool s_achievements_login_logging_in = 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;
|
||||
|
||||
// local copies of the currently-running game
|
||||
inline std::string s_current_game_title;
|
||||
inline std::string s_current_game_subtitle;
|
||||
inline std::string s_current_disc_serial;
|
||||
inline std::string s_current_disc_path;
|
||||
inline u32 s_current_disc_crc;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Resources
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool LoadResources();
|
||||
bool LoadSvgResources();
|
||||
void DestroyResources();
|
||||
|
||||
inline std::array<std::shared_ptr<GSTexture>, static_cast<u32>(GameDatabaseSchema::Compatibility::Perfect)>
|
||||
s_game_compatibility_textures;
|
||||
inline std::shared_ptr<GSTexture> s_banner_texture;
|
||||
inline std::vector<std::unique_ptr<GSTexture>> s_cleanup_textures;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Landing
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void SwitchToLanding();
|
||||
ImGuiFullscreen::FileSelectorFilters GetOpenFileFilters();
|
||||
ImGuiFullscreen::FileSelectorFilters GetDiscImageFilters();
|
||||
ImGuiFullscreen::FileSelectorFilters GetAudioFileFilters();
|
||||
ImGuiFullscreen::FileSelectorFilters GetImageFileFilters();
|
||||
void DoVMInitialize(const VMBootParameters& boot_params, bool switch_to_landing_on_failure);
|
||||
void DoStartPath(
|
||||
const std::string& path, std::optional<s32> state_index = std::nullopt, std::optional<bool> fast_boot = std::nullopt);
|
||||
void DoStartFile();
|
||||
void DoStartBIOS();
|
||||
void DoStartDisc(const std::string& drive);
|
||||
void DoStartDisc();
|
||||
void DoToggleFrameLimit();
|
||||
void DoToggleSoftwareRenderer();
|
||||
void RequestShutdown(bool save_state);
|
||||
void DoShutdown(bool save_state);
|
||||
void RequestReset();
|
||||
void DoReset();
|
||||
void DoChangeDiscFromFile();
|
||||
void RequestChangeDisc();
|
||||
void DoRequestExit();
|
||||
void DoDesktopMode();
|
||||
void DoToggleFullscreen();
|
||||
|
||||
void ConfirmShutdownIfMemcardBusy(std::function<void(bool)> callback);
|
||||
|
||||
bool ShouldDefaultToGameList();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Save State List
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
struct SaveStateListEntry
|
||||
{
|
||||
std::string title;
|
||||
std::string summary;
|
||||
std::string path;
|
||||
std::unique_ptr<GSTexture> preview_texture;
|
||||
time_t timestamp;
|
||||
s32 slot;
|
||||
};
|
||||
|
||||
void InitializePlaceholderSaveStateListEntry(SaveStateListEntry* li, s32 slot);
|
||||
bool InitializeSaveStateListEntry(
|
||||
SaveStateListEntry* li, const std::string& title, const std::string& serial, u32 crc, s32 slot, bool backup = false);
|
||||
void ClearSaveStateEntryList();
|
||||
u32 PopulateSaveStateListEntries(const std::string& title, const std::string& serial, u32 crc);
|
||||
bool OpenLoadStateSelectorForGame(const std::string& game_path);
|
||||
bool OpenSaveStateSelector(bool is_loading);
|
||||
void CloseSaveStateSelector();
|
||||
void DrawSaveStateSelector(bool is_loading);
|
||||
bool OpenLoadStateSelectorForGameResume(const GameList::Entry* entry);
|
||||
void DrawResumeStateSelector();
|
||||
void DoLoadState(std::string path, std::optional<s32> slot, bool backup);
|
||||
void DoSaveState(s32 slot);
|
||||
|
||||
inline std::vector<SaveStateListEntry> s_save_state_selector_slots;
|
||||
inline std::string s_save_state_selector_game_path;
|
||||
inline s32 s_save_state_selector_submenu_index = -1;
|
||||
inline bool s_save_state_selector_open = false;
|
||||
inline bool s_save_state_selector_loading = true;
|
||||
inline bool s_save_state_selector_resuming = false;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Game List
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void DrawGameListWindow();
|
||||
void DrawGameList(const ImVec2& heading_size);
|
||||
void DrawGameGrid(const ImVec2& heading_size);
|
||||
void HandleGameListActivate(const GameList::Entry* entry);
|
||||
void HandleGameListOptions(const GameList::Entry* entry);
|
||||
void DrawGameListSettingsWindow();
|
||||
void SwitchToGameList();
|
||||
void PopulateGameListEntryList();
|
||||
GSTexture* GetTextureForGameListEntryType(GameList::EntryType type, const ImVec2& size, SvgScaling mode = SvgScaling::Stretch);
|
||||
GSTexture* GetGameListCover(const GameList::Entry* entry);
|
||||
void DrawGameCover(const GameList::Entry* entry, const ImVec2& size);
|
||||
void DrawGameCover(const GameList::Entry* entry, ImDrawList* draw_list, const ImVec2& min, const ImVec2& max);
|
||||
// For when we have no GameList entry
|
||||
void DrawFallbackCover(const ImVec2& size);
|
||||
void DrawFallbackCover(ImDrawList* draw_list, const ImVec2& min, const ImVec2& max);
|
||||
|
||||
// Lazily populated cover images.
|
||||
inline std::unordered_map<std::string, std::string> s_cover_image_map;
|
||||
inline std::vector<const GameList::Entry*> s_game_list_sorted_entries;
|
||||
inline GameListView s_game_list_view = GameListView::Grid;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Background
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void LoadCustomBackground();
|
||||
void DrawCustomBackground();
|
||||
|
||||
inline std::shared_ptr<GSTexture> s_custom_background_texture;
|
||||
inline std::string s_custom_background_path;
|
||||
inline bool s_custom_background_enabled = false;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Achievements
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void SwitchToAchievementsWindow();
|
||||
void SwitchToLeaderboardsWindow();
|
||||
void DrawAchievementsLoginWindow();
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Settings
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
static constexpr double INPUT_BINDING_TIMEOUT_SECONDS = 5.0;
|
||||
static constexpr u32 NUM_MEMORY_CARD_PORTS = 2;
|
||||
|
||||
void SwitchToSettings();
|
||||
void SwitchToGameSettings();
|
||||
void SwitchToGameSettings(const std::string& path);
|
||||
void SwitchToGameSettings(const GameList::Entry* entry);
|
||||
void SwitchToGameSettings(const std::string_view serial, u32 crc);
|
||||
void DrawSettingsWindow();
|
||||
void DrawSummarySettingsPage();
|
||||
void DrawInterfaceSettingsPage();
|
||||
void DrawBIOSSettingsPage();
|
||||
void DrawEmulationSettingsPage();
|
||||
void DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_advanced_settings);
|
||||
void DrawAudioSettingsPage();
|
||||
void DrawMemoryCardSettingsPage();
|
||||
void DrawNetworkHDDSettingsPage();
|
||||
void DrawFoldersSettingsPage();
|
||||
void DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& settings_lock);
|
||||
void DrawControllerSettingsPage();
|
||||
void DrawHotkeySettingsPage();
|
||||
void DrawAdvancedSettingsPage();
|
||||
void DrawPatchesOrCheatsSettingsPage(bool cheats);
|
||||
void DrawGameFixesSettingsPage();
|
||||
|
||||
bool IsEditingGameSettings(SettingsInterface* bsi);
|
||||
SettingsInterface* GetEditingSettingsInterface();
|
||||
SettingsInterface* GetEditingSettingsInterface(bool game_settings);
|
||||
bool ShouldShowAdvancedSettings(SettingsInterface* bsi);
|
||||
void SetSettingsChanged(SettingsInterface* bsi);
|
||||
bool GetEffectiveBoolSetting(SettingsInterface* bsi, const char* section, const char* key, bool default_value);
|
||||
s32 GetEffectiveIntSetting(SettingsInterface* bsi, const char* section, const char* key, s32 default_value);
|
||||
void DoCopyGameSettings();
|
||||
void DoClearGameSettings();
|
||||
void ResetControllerSettings();
|
||||
void DoLoadInputProfile();
|
||||
void DoSaveInputProfile();
|
||||
void DoSaveInputProfile(const std::string& name);
|
||||
void DoResetSettings();
|
||||
|
||||
bool DrawToggleSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
|
||||
bool default_value, bool enabled = true, bool allow_tristate = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT,
|
||||
std::pair<ImFont*, float> font = g_large_font, std::pair<ImFont*, float> summary_font = g_medium_font);
|
||||
void DrawIntListSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
|
||||
int default_value, const char* const* options, size_t option_count, bool translate_options, int option_offset = 0,
|
||||
bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font,
|
||||
std::pair<ImFont*, float> summary_font = g_medium_font);
|
||||
void DrawIntRangeSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
|
||||
int default_value, int min_value, int max_value, const char* format = "%d", bool enabled = true,
|
||||
float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font, std::pair<ImFont*, float> summary_font = g_medium_font);
|
||||
void DrawIntSpinBoxSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
|
||||
int default_value, int min_value, int max_value, int step_value, const char* format = "%d", bool enabled = true,
|
||||
float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font, std::pair<ImFont*, float> summary_font = g_medium_font);
|
||||
void DrawFloatRangeSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
|
||||
float default_value, float min_value, float max_value, const char* format = "%f", float multiplier = 1.0f, bool enabled = true,
|
||||
float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font, std::pair<ImFont*, float> summary_font = g_medium_font);
|
||||
void DrawFloatSpinBoxSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section,
|
||||
const char* key, float default_value, float min_value, float max_value, float step_value, float multiplier,
|
||||
const char* format = "%f", bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT,
|
||||
std::pair<ImFont*, float> font = g_large_font, std::pair<ImFont*, float> summary_font = g_medium_font);
|
||||
void DrawIntRectSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section,
|
||||
const char* left_key, int default_left, const char* top_key, int default_top, const char* right_key, int default_right,
|
||||
const char* bottom_key, int default_bottom, int min_value, int max_value, int step_value, const char* format = "%d",
|
||||
bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font,
|
||||
std::pair<ImFont*, float> summary_font = g_medium_font);
|
||||
void DrawStringListSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
|
||||
const char* default_value, const char* const* options, const char* const* option_values, size_t option_count,
|
||||
bool translate_options, bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font,
|
||||
std::pair<ImFont*, float> summary_font = g_medium_font, const char* translation_ctx = "FullscreenUI");
|
||||
void DrawStringListSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
|
||||
const char* default_value, SettingInfo::GetOptionsCallback options_callback, bool enabled = true,
|
||||
float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font, std::pair<ImFont*, float> summary_font = g_medium_font);
|
||||
void DrawIPAddressSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
|
||||
const char* default_value, bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT,
|
||||
std::pair<ImFont*, float> font = g_large_font, std::pair<ImFont*, float> summary_font = g_medium_font,
|
||||
IPAddressType ip_type = IPAddressType::Other);
|
||||
void DrawFloatListSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section, const char* key,
|
||||
float default_value, const char* const* options, const float* option_values, size_t option_count, bool translate_options,
|
||||
bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font,
|
||||
std::pair<ImFont*, float> summary_font = g_medium_font);
|
||||
template <typename DataType, typename SizeType>
|
||||
void DrawEnumSetting(SettingsInterface* bsi, const char* title, const char* summary, const char* section,
|
||||
const char* key, DataType default_value,
|
||||
std::optional<DataType> (*from_string_function)(const char* str),
|
||||
const char* (*to_string_function)(DataType value),
|
||||
const char* (*to_display_string_function)(DataType value), SizeType option_count,
|
||||
bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT,
|
||||
std::pair<ImFont*, float> font = g_large_font, std::pair<ImFont*, float> summary_font = g_medium_font);
|
||||
void DrawFolderSetting(SettingsInterface* bsi, const char* title, const char* section, const char* key,
|
||||
const std::string& runtime_var, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font,
|
||||
std::pair<ImFont*, float> summary_font = g_medium_font);
|
||||
void DrawPathSetting(SettingsInterface* bsi, const char* title, const char* section, const char* key, const char* default_value,
|
||||
bool enabled = true, float height = ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT, std::pair<ImFont*, float> font = g_large_font,
|
||||
std::pair<ImFont*, float> summary_font = g_medium_font);
|
||||
void DrawClampingModeSetting(SettingsInterface* bsi, const char* title, const char* summary, int vunum);
|
||||
void PopulateGraphicsAdapterList();
|
||||
void PopulateGameListDirectoryCache(SettingsInterface* si);
|
||||
void PopulatePatchesAndCheatsList(const std::string_view serial, u32 crc);
|
||||
void BeginInputBinding(SettingsInterface* bsi, InputBindingInfo::Type type, const std::string_view section,
|
||||
const std::string_view key, const std::string_view display_name);
|
||||
void DrawInputBindingWindow();
|
||||
void DrawInputBindingButton(SettingsInterface* bsi, InputBindingInfo::Type type, const char* section, const char* name, const char* display_name, const char* icon_name, bool show_type = true);
|
||||
void ClearInputBindingVariables();
|
||||
void StartAutomaticBinding(u32 port);
|
||||
void DrawSettingInfoSetting(SettingsInterface* bsi, const char* section, const char* key, const SettingInfo& si,
|
||||
const char* translation_ctx);
|
||||
void OpenMemoryCardCreateDialog();
|
||||
void DoCreateMemoryCard(std::string name, MemoryCardType type, MemoryCardFileType file_type, bool use_ntfs_compression = false);
|
||||
|
||||
inline SettingsPage s_settings_page = SettingsPage::Interface;
|
||||
inline std::unique_ptr<INISettingsInterface> s_game_settings_interface;
|
||||
inline std::unique_ptr<GameList::Entry> s_game_settings_entry;
|
||||
inline std::vector<std::pair<std::string, bool>> s_game_list_directories_cache;
|
||||
inline std::vector<GSAdapterInfo> s_graphics_adapter_list_cache;
|
||||
inline std::vector<Patch::PatchInfo> s_game_patch_list;
|
||||
inline std::vector<std::string> s_enabled_game_patch_cache;
|
||||
inline std::vector<Patch::PatchInfo> s_game_cheats_list;
|
||||
inline std::vector<std::string> s_enabled_game_cheat_cache;
|
||||
inline u32 s_game_cheat_unlabelled_count = 0;
|
||||
inline std::vector<const HotkeyInfo*> s_hotkey_list_cache;
|
||||
inline std::atomic_bool s_settings_changed{false};
|
||||
inline std::atomic_bool s_game_settings_changed{false};
|
||||
inline InputBindingInfo::Type s_input_binding_type = InputBindingInfo::Type::Unknown;
|
||||
inline std::string s_input_binding_section;
|
||||
inline std::string s_input_binding_key;
|
||||
inline std::string s_input_binding_display_name;
|
||||
inline std::vector<InputBindingKey> s_input_binding_new_bindings;
|
||||
inline std::vector<std::pair<InputBindingKey, std::pair<float, float>>> s_input_binding_value_ranges;
|
||||
inline Common::Timer s_input_binding_timer;
|
||||
|
||||
} // namespace FullscreenUI
|
||||
File diff suppressed because it is too large
Load Diff
@@ -782,8 +782,8 @@ void psxRcntSetNewIntrMode(int index)
|
||||
psxCounters[index].mode.overflowFlag = false;
|
||||
psxCounters[index].mode.intrEnable = true;
|
||||
|
||||
if (psxCounters[index].mode.repeatIntr != psxCounters[index].currentIrqMode.repeatInterrupt || psxCounters[index].mode.toggleIntr != psxCounters[index].currentIrqMode.toggleInterrupt)
|
||||
DevCon.Warning("Updating psxCounter[%d] mode old repeat %d new %d old toggle %d new %d", index, psxCounters[index].mode.repeatIntr, psxCounters[index].currentIrqMode.repeatInterrupt, psxCounters[index].mode.toggleIntr, psxCounters[index].currentIrqMode.toggleInterrupt);
|
||||
//if (psxCounters[index].mode.repeatIntr != psxCounters[index].currentIrqMode.repeatInterrupt || psxCounters[index].mode.toggleIntr != psxCounters[index].currentIrqMode.toggleInterrupt)
|
||||
// DevCon.Warning("Updating psxCounter[%d] mode old repeat %d new %d old toggle %d new %d", index, psxCounters[index].mode.repeatIntr, psxCounters[index].currentIrqMode.repeatInterrupt, psxCounters[index].mode.toggleIntr, psxCounters[index].currentIrqMode.toggleInterrupt);
|
||||
|
||||
psxCounters[index].currentIrqMode.repeatInterrupt = psxCounters[index].mode.repeatIntr;
|
||||
psxCounters[index].currentIrqMode.toggleInterrupt = psxCounters[index].mode.toggleIntr;
|
||||
|
||||
+7
-7
@@ -64,14 +64,14 @@ static const u32
|
||||
|
||||
// SIO2 is a DMA interface for the SIO.
|
||||
|
||||
HW_SIO2_DATAIN = 0x1F808260,
|
||||
HW_SIO2_FIFO = 0x1f808264,
|
||||
HW_SIO2_TX = 0x1F808260,
|
||||
HW_SIO2_RX = 0x1f808264,
|
||||
HW_SIO2_CTRL = 0x1f808268,
|
||||
HW_SIO2_RECV1 = 0x1f80826c,
|
||||
HW_SIO2_RECV2 = 0x1f808270,
|
||||
HW_SIO2_RECV3 = 0x1f808274,
|
||||
HW_SIO2_8278 = 0x1F808278, // May as well add defs
|
||||
HW_SIO2_827C = 0x1F80827C, // for these 2...
|
||||
HW_SIO2_CMD_STAT = 0x1f80826c,
|
||||
HW_SIO2_PORT_STAT = 0x1f808270,
|
||||
HW_SIO2_FIFO_STAT = 0x1f808274,
|
||||
HW_SIO2_FIFO_TX = 0x1F808278, // May as well add defs
|
||||
HW_SIO2_FIFO_RX = 0x1F80827C, // for these 2...
|
||||
HW_SIO2_INTR = 0x1f808280;
|
||||
|
||||
enum DMAMadrAddresses
|
||||
|
||||
+2
-2
@@ -522,11 +522,11 @@ void Patch::ExtractPatchInfo(std::vector<PatchInfo>* dst, const std::string& pna
|
||||
}
|
||||
}
|
||||
current_patch = {};
|
||||
last_place = std::nullopt;
|
||||
unknown_place = false;
|
||||
}
|
||||
|
||||
current_patch.name = line.substr(1, line.length() - 2);
|
||||
last_place = std::nullopt;
|
||||
unknown_place = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
+70
-65
@@ -29,25 +29,25 @@ bool Sio2::Initialize()
|
||||
{
|
||||
this->SoftReset();
|
||||
|
||||
for (size_t i = 0; i < send3.size(); i++)
|
||||
for (size_t i = 0; i < CmdQueue.size(); i++)
|
||||
{
|
||||
send3[i] = 0;
|
||||
CmdQueue[i] = 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < send1.size(); i++)
|
||||
for (size_t i = 0; i < PortCtrl0.size(); i++)
|
||||
{
|
||||
send1[i] = 0;
|
||||
send2[i] = 0;
|
||||
PortCtrl0[i] = 0;
|
||||
PortCtrl1[i] = 0;
|
||||
}
|
||||
|
||||
dataIn = 0;
|
||||
dataOut = 0;
|
||||
SetCtrl(Sio2Ctrl::SIO2MAN_RESET);
|
||||
SetRecv1(Recv1::DISCONNECTED);
|
||||
recv2 = Recv2::DEFAULT;
|
||||
recv3 = Recv3::DEFAULT;
|
||||
unknown1 = 0;
|
||||
unknown2 = 0;
|
||||
SetCmdStat(CmdStat::DISCONNECTED);
|
||||
PortStat = PortStat::DEFAULT;
|
||||
FifoStat = FifoStat::DEFAULT;
|
||||
FifoTxPos = 0;
|
||||
FifoRxPos = 0;
|
||||
iStat = 0;
|
||||
|
||||
port = 0;
|
||||
@@ -80,13 +80,13 @@ bool Sio2::Shutdown()
|
||||
|
||||
void Sio2::SoftReset()
|
||||
{
|
||||
send3Read = false;
|
||||
send3Position = 0;
|
||||
queueRead = false;
|
||||
queuePosition = 0;
|
||||
commandLength = 0;
|
||||
processedLength = 0;
|
||||
// Clear dmaBlockSize, in case the next SIO2 command is not sent over DMA11.
|
||||
dmaBlockSize = 0;
|
||||
send3Complete = false;
|
||||
queueComplete = false;
|
||||
|
||||
// Anything in g_Sio2FifoIn which was not necessary to consume should be cleared out prior to the next SIO2 cycle.
|
||||
while (!g_Sio2FifoIn.empty())
|
||||
@@ -94,13 +94,18 @@ void Sio2::SoftReset()
|
||||
g_Sio2FifoIn.pop_front();
|
||||
}
|
||||
|
||||
// RECV1 should always be reassembled based on the devices being probed by the packet.
|
||||
recv1 = 0;
|
||||
// cmd_stat should always be reassembled based on the devices being probed by the packet.
|
||||
CmdStat = 0;
|
||||
}
|
||||
|
||||
void Sio2::Interrupt()
|
||||
{
|
||||
iopIntcIrq(17);
|
||||
if (!iStat)
|
||||
iopIntcIrq(17);
|
||||
else
|
||||
DevCon.Warning("Nearly sent double SIO2 IRQ");
|
||||
|
||||
iStat |= 1;
|
||||
}
|
||||
|
||||
void Sio2::SetCtrl(u32 value)
|
||||
@@ -113,9 +118,9 @@ void Sio2::SetCtrl(u32 value)
|
||||
}
|
||||
}
|
||||
|
||||
void Sio2::SetSend3(size_t position, u32 value)
|
||||
void Sio2::SetCmd(size_t position, u32 value)
|
||||
{
|
||||
this->send3[position] = value;
|
||||
this->CmdQueue[position] = value;
|
||||
|
||||
if (position == 0)
|
||||
{
|
||||
@@ -123,9 +128,9 @@ void Sio2::SetSend3(size_t position, u32 value)
|
||||
}
|
||||
}
|
||||
|
||||
void Sio2::SetRecv1(u32 value)
|
||||
void Sio2::SetCmdStat(u32 value)
|
||||
{
|
||||
this->recv1 = value;
|
||||
this->CmdStat = value;
|
||||
}
|
||||
|
||||
void Sio2::Pad()
|
||||
@@ -134,29 +139,29 @@ void Sio2::Pad()
|
||||
PadBase* pad = Pad::GetPad(port, mtap.GetPadSlot());
|
||||
|
||||
// Update the third nibble with which ports have been accessed
|
||||
if (this->recv1 & Recv1::ONE_PORT_OPEN)
|
||||
if (this->CmdStat & CmdStat::ONE_PORT_OPEN)
|
||||
{
|
||||
this->recv1 &= ~(Recv1::ONE_PORT_OPEN);
|
||||
this->recv1 |= Recv1::TWO_PORTS_OPEN;
|
||||
this->CmdStat &= ~(CmdStat::ONE_PORT_OPEN);
|
||||
this->CmdStat |= CmdStat::TWO_PORTS_OPEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->recv1 |= Recv1::ONE_PORT_OPEN;
|
||||
this->CmdStat |= CmdStat::ONE_PORT_OPEN;
|
||||
}
|
||||
|
||||
// This bit is always set, whether the pad is present or missing
|
||||
this->recv1 |= Recv1::NO_DEVICES_MISSING;
|
||||
this->CmdStat |= CmdStat::NO_DEVICES_MISSING;
|
||||
|
||||
// If the currently accessed pad is missing, also tick those bits
|
||||
if (pad->GetType() == Pad::ControllerType::NotConnected || pad->ejectTicks)
|
||||
{
|
||||
if (!port)
|
||||
{
|
||||
this->recv1 |= Recv1::PORT_1_MISSING;
|
||||
this->CmdStat |= CmdStat::PORT_1_MISSING;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->recv1 |= Recv1::PORT_2_MISSING;
|
||||
this->CmdStat |= CmdStat::PORT_2_MISSING;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,18 +201,18 @@ void Sio2::Multitap()
|
||||
const bool multitapEnabled = EmuConfig.Pad.IsMultitapPortEnabled(this->port);
|
||||
|
||||
// Update the third nibble with which ports have been accessed
|
||||
if (this->recv1 & Recv1::ONE_PORT_OPEN)
|
||||
if (this->CmdStat & CmdStat::ONE_PORT_OPEN)
|
||||
{
|
||||
this->recv1 &= ~(Recv1::ONE_PORT_OPEN);
|
||||
this->recv1 |= Recv1::TWO_PORTS_OPEN;
|
||||
this->CmdStat &= ~(CmdStat::ONE_PORT_OPEN);
|
||||
this->CmdStat |= CmdStat::TWO_PORTS_OPEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->recv1 |= Recv1::ONE_PORT_OPEN;
|
||||
this->CmdStat |= CmdStat::ONE_PORT_OPEN;
|
||||
}
|
||||
|
||||
// This bit is always set, whether the pad is present or missing
|
||||
this->recv1 |= Recv1::NO_DEVICES_MISSING;
|
||||
this->CmdStat |= CmdStat::NO_DEVICES_MISSING;
|
||||
|
||||
// If the currently accessed multitap is missing, also tick those bits.
|
||||
// MTAPMAN is special though.
|
||||
@@ -217,11 +222,11 @@ void Sio2::Multitap()
|
||||
// then a disconnect value would look like 0x0002D100.
|
||||
//
|
||||
// MTAPMAN however does not check the bit set by 0x00020000. It only checks the bit
|
||||
// set by 0x00010000. So even if port 2 is being addressed, RECV1 should be 0x0001D100
|
||||
// set by 0x00010000. So even if port 2 is being addressed, cmd stat should be 0x0001D100
|
||||
// (or 0x0001D200 if there are both ports being accessed in that packet).
|
||||
if (!multitapEnabled)
|
||||
{
|
||||
this->recv1 |= Recv1::PORT_1_MISSING;
|
||||
this->CmdStat |= CmdStat::PORT_1_MISSING;
|
||||
}
|
||||
|
||||
g_MultitapArr.at(this->port).SendToMultitap();
|
||||
@@ -229,7 +234,7 @@ void Sio2::Multitap()
|
||||
|
||||
void Sio2::Infrared()
|
||||
{
|
||||
SetRecv1(Recv1::DISCONNECTED);
|
||||
SetCmdStat(CmdStat::DISCONNECTED);
|
||||
|
||||
g_Sio2FifoIn.pop_front();
|
||||
const u8 responseByte = 0xff;
|
||||
@@ -246,11 +251,11 @@ void Sio2::Memcard()
|
||||
|
||||
mcd = &mcds[port][mtap.GetMemcardSlot()];
|
||||
|
||||
// Check if auto ejection is active. If so, set RECV1 to DISCONNECTED,
|
||||
// Check if auto ejection is active. If so, set cmd stat to DISCONNECTED,
|
||||
// and zero out the fifo to simulate dead air over the wire.
|
||||
if (mcd->autoEjectTicks)
|
||||
{
|
||||
SetRecv1(Recv1::DISCONNECTED);
|
||||
SetCmdStat(CmdStat::DISCONNECTED);
|
||||
g_Sio2FifoOut.push_back(0xff); // Because Sio2::Write pops the first g_Sio2FifoIn member
|
||||
|
||||
while (!g_Sio2FifoIn.empty())
|
||||
@@ -262,7 +267,7 @@ void Sio2::Memcard()
|
||||
return;
|
||||
}
|
||||
|
||||
SetRecv1(mcd->IsPresent() ? Recv1::CONNECTED : Recv1::DISCONNECTED);
|
||||
SetCmdStat(mcd->IsPresent() ? CmdStat::CONNECTED : CmdStat::DISCONNECTED);
|
||||
|
||||
const u8 commandByte = g_Sio2FifoIn.front();
|
||||
g_Sio2FifoIn.pop_front();
|
||||
@@ -376,25 +381,25 @@ void Sio2::Write(u8 data)
|
||||
{
|
||||
Sio2Log.WriteLn("%s(%02X) SIO2 DATA Write", __FUNCTION__, data);
|
||||
|
||||
if (!send3Read)
|
||||
if (!queueRead)
|
||||
{
|
||||
// No more SEND3 positions to access, but the game is still sending us SIO2 writes. Lets ignore them.
|
||||
if (send3Position > send3.size())
|
||||
// No more queue positions to access, but the game is still sending us SIO2 writes. Lets ignore them.
|
||||
if (queuePosition > CmdQueue.size())
|
||||
{
|
||||
Console.Warning("%s(%02X) Received data after exhausting all SEND3 values!", __FUNCTION__, data);
|
||||
Console.Warning("%s(%02X) Received data after exhausting all queue entries!", __FUNCTION__, data);
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 currentSend3 = send3[send3Position];
|
||||
port = currentSend3 & Send3::PORT;
|
||||
commandLength = (currentSend3 >> 8) & Send3::COMMAND_LENGTH_MASK;
|
||||
send3Read = true;
|
||||
const u32 currentCmd = CmdQueue[queuePosition];
|
||||
port = currentCmd & Sio2Cmd::PORT;
|
||||
commandLength = (currentCmd >> 8) & Sio2Cmd::COMMAND_LENGTH_MASK;
|
||||
queueRead = true;
|
||||
|
||||
// The freshly read SEND3 position had a length of 0, so we are done handling SIO2 commands until
|
||||
// the next SEND3 writes.
|
||||
// The freshly read cmd position had a length of 0, so we are done handling SIO2 commands until
|
||||
// the next cmd writes.
|
||||
if (commandLength == 0)
|
||||
{
|
||||
send3Complete = true;
|
||||
queueComplete = true;
|
||||
}
|
||||
|
||||
// If the prior command did not need to fully pop g_Sio2FifoIn, do so now,
|
||||
@@ -405,7 +410,7 @@ void Sio2::Write(u8 data)
|
||||
}
|
||||
}
|
||||
|
||||
if (send3Complete)
|
||||
if (queueComplete)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -418,9 +423,9 @@ void Sio2::Write(u8 data)
|
||||
// ... These were from SIO2 DMA (DMA block size is non-zero when SIO2 DMA occurs)
|
||||
if ((g_Sio2FifoIn.size() == g_Sio2.commandLength && g_Sio2.dmaBlockSize == 0) || g_Sio2FifoIn.size() == g_Sio2.dmaBlockSize)
|
||||
{
|
||||
// Go ahead and prep so the next write triggers a load of the new SEND3 value.
|
||||
g_Sio2.send3Read = false;
|
||||
g_Sio2.send3Position++;
|
||||
// Go ahead and prep so the next write triggers a load of the new cmd value.
|
||||
g_Sio2.queueRead = false;
|
||||
g_Sio2.queuePosition++;
|
||||
|
||||
// Check the SIO mode
|
||||
const u8 sioMode = g_Sio2FifoIn.front();
|
||||
@@ -443,7 +448,7 @@ void Sio2::Write(u8 data)
|
||||
default:
|
||||
Console.Error("%s(%02X) Unhandled SIO mode %02X", __FUNCTION__, data, sioMode);
|
||||
g_Sio2FifoOut.push_back(0xff);
|
||||
SetRecv1(Recv1::DISCONNECTED);
|
||||
SetCmdStat(CmdStat::DISCONNECTED);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -488,25 +493,25 @@ bool Sio2::DoState(StateWrapper& sw)
|
||||
if (!sw.DoMarker("Sio2"))
|
||||
return false;
|
||||
|
||||
sw.Do(&send3);
|
||||
sw.Do(&send1);
|
||||
sw.Do(&send2);
|
||||
sw.Do(&CmdQueue);
|
||||
sw.Do(&PortCtrl0);
|
||||
sw.Do(&PortCtrl1);
|
||||
sw.Do(&dataIn);
|
||||
sw.Do(&dataOut);
|
||||
sw.Do(&ctrl);
|
||||
sw.Do(&recv1);
|
||||
sw.Do(&recv2);
|
||||
sw.Do(&recv3);
|
||||
sw.Do(&unknown1);
|
||||
sw.Do(&unknown2);
|
||||
sw.Do(&CmdStat);
|
||||
sw.Do(&PortStat);
|
||||
sw.Do(&FifoStat);
|
||||
sw.Do(&FifoTxPos);
|
||||
sw.Do(&FifoRxPos);
|
||||
sw.Do(&iStat);
|
||||
sw.Do(&port);
|
||||
sw.Do(&send3Read);
|
||||
sw.Do(&send3Position);
|
||||
sw.Do(&queueRead);
|
||||
sw.Do(&queuePosition);
|
||||
sw.Do(&commandLength);
|
||||
sw.Do(&processedLength);
|
||||
sw.Do(&dmaBlockSize);
|
||||
sw.Do(&send3Complete);
|
||||
sw.Do(&queueComplete);
|
||||
|
||||
sw.Do(&g_Sio2FifoIn);
|
||||
sw.Do(&g_Sio2FifoOut);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user